Commit 3815f407 authored by David Woodhouse's avatar David Woodhouse

Merge shinybook.infradead.org:/home/dwmw2/bk/linus-2.6

into shinybook.infradead.org:/home/dwmw2/bk/mtd-2.6
parents 38838b73 6b2bdc37
......@@ -3569,7 +3569,6 @@ S: The Netherlands
N: David Woodhouse
E: dwmw2@infradead.org
E: dwmw2@redhat.com
D: ARCnet stuff, Applicom board driver, SO_BINDTODEVICE,
D: some Alpha platform porting from 2.0, Memory Technology Devices,
D: Acquire watchdog timer, PC speaker driver maintenance,
......
......@@ -1439,7 +1439,7 @@ S: Maintained
MEMORY TECHNOLOGY DEVICES
P: David Woodhouse
M: dwmw2@redhat.com
M: dwmw2@infradead.org
W: http://www.linux-mtd.infradead.org/
L: linux-mtd@lists.infradead.org
S: Maintained
......
/* Derived from Applicom driver ac.c for SCO Unix */
/* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */
/* dwmw2@redhat.com 30/8/98 */
/* dwmw2@infradead.org 30/8/98 */
/* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */
/* This module is for Linux 2.1 and 2.2 series kernels. */
/*****************************************************************************/
......
# $Id: Kconfig,v 1.6 2004/08/09 13:19:42 dwmw2 Exp $
# $Id: Kconfig,v 1.7 2004/11/22 11:33:56 ijc Exp $
menu "Memory Technology Devices (MTD)"
......@@ -54,8 +54,8 @@ config MTD_REDBOOT_PARTS
depends on MTD_PARTITIONS
---help---
RedBoot is a ROM monitor and bootloader which deals with multiple
'images' in flash devices by putting a table in the last erase
block of the device, similar to a partition table, which gives
'images' in flash devices by putting a table one of the erase
blocks on the device, similar to a partition table, which gives
the offsets, lengths and names of all the images stored in the
flash.
......@@ -68,6 +68,23 @@ config MTD_REDBOOT_PARTS
SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
example.
config MTD_REDBOOT_DIRECTORY_BLOCK
int "Location of RedBoot partition table"
depends on MTD_REDBOOT_PARTS
default "-1"
---help---
This option is the Linux counterpart to the
CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time
option.
The option specifies which Flash sectors holds the RedBoot
partition table. A zero or positive value gives an absolete
erase block number. A negative value specifies a number of
sectors before the end of the device.
For example "2" means block number 2, "-1" means the last
block and "-2" means the penultimate block.
config MTD_REDBOOT_PARTS_UNALLOCATED
bool " Include unallocated flash regions"
depends on MTD_REDBOOT_PARTS
......
# drivers/mtd/chips/Kconfig
# $Id: Kconfig,v 1.9 2004/07/16 15:32:14 dwmw2 Exp $
# $Id: Kconfig,v 1.10 2004/11/05 22:41:04 nico Exp $
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
......@@ -272,5 +272,14 @@ config MTD_JEDEC
<http://www.jedec.org/> distributes the identification codes for the
chips.
config MTD_XIP
bool "XIP aware MTD support"
depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL
default y if XIP_KERNEL
help
This allows MTD support to work with flash memory which is also
used for XIP purposes. If you're not sure what this is all about
then say N.
endmenu
......@@ -3,7 +3,7 @@
*
* Author: Jonas Holmberg <jonas.holmberg@axis.com>
*
* $Id: amd_flash.c,v 1.25 2004/08/09 13:19:43 dwmw2 Exp $
* $Id: amd_flash.c,v 1.26 2004/11/20 12:49:04 dwmw2 Exp $
*
* Copyright (c) 2001 Axis Communications AB
*
......@@ -1122,7 +1122,7 @@ static inline int erase_one_block(struct map_info *map, struct flchip *chip,
timeo = jiffies + (HZ * 20);
spin_unlock_bh(chip->mutex);
schedule_timeout(HZ);
msleep(1000);
spin_lock_bh(chip->mutex);
while (flash_is_busy(map, adr, private->interleave)) {
......
......@@ -4,9 +4,8 @@
*
* (C) 2000 Red Hat. GPL'd
*
* $Id: cfi_cmdset_0001.c,v 1.160 2004/11/01 06:02:24 nico Exp $
* (+ suspend fix from v1.162)
* (+ partition detection fix from v1.163)
* $Id: cfi_cmdset_0001.c,v 1.164 2004/11/16 18:29:00 dwmw2 Exp $
*
*
* 10/10/2000 Nicolas Pitre <nico@cam.org>
* - completely revamped method functions so they are aware and
......@@ -30,6 +29,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
......@@ -37,6 +37,10 @@
/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
#ifdef CONFIG_MTD_XIP
#define CMDSET0001_DISABLE_WRITE_SUSPEND
#endif
// debugging, turns off buffer write mode if set to 1
#define FORCE_WORD_WRITE 0
......@@ -147,6 +151,21 @@ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
}
#endif
#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
/* The XIP config appears to have problems using write suspend at the moment */
static void fixup_no_write_suspend(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
if (cfip && (cfip->FeatureSupport&4)) {
cfip->FeatureSupport &= ~4;
printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n");
}
}
#endif
static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param)
{
struct map_info *map = mtd->priv;
......@@ -189,6 +208,9 @@ static struct cfi_fixup cfi_fixup_table[] = {
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
#endif
#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL },
#endif
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL },
#endif
......@@ -679,6 +701,14 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
chip->state = FL_STATUS;
return 0;
case FL_XIP_WHILE_ERASING:
if (mode != FL_READY && mode != FL_POINT &&
(mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1)))
goto sleep;
chip->oldstate = chip->state;
chip->state = FL_READY;
return 0;
case FL_POINT:
/* Only if there's no operation suspended... */
if (mode == FL_READY && chip->oldstate == FL_READY)
......@@ -746,6 +776,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
chip->state = FL_ERASING;
break;
case FL_XIP_WHILE_ERASING:
chip->state = chip->oldstate;
chip->oldstate = FL_READY;
break;
case FL_READY:
case FL_STATUS:
case FL_JEDEC_QUERY:
......@@ -758,6 +793,201 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
wake_up(&chip->wq);
}
#ifdef CONFIG_MTD_XIP
/*
* No interrupt what so ever can be serviced while the flash isn't in array
* mode. This is ensured by the xip_disable() and xip_enable() functions
* enclosing any code path where the flash is known not to be in array mode.
* And within a XIP disabled code path, only functions marked with __xipram
* may be called and nothing else (it's a good thing to inspect generated
* assembly to make sure inline functions were actually inlined and that gcc
* didn't emit calls to its own support functions). Also configuring MTD CFI
* support to a single buswidth and a single interleave is also recommended.
* Note that not only IRQs are disabled but the preemption count is also
* increased to prevent other locking primitives (namely spin_unlock) from
* decrementing the preempt count to zero and scheduling the CPU away while
* not in array mode.
*/
static void xip_disable(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
/* TODO: chips with no XIP use should ignore and return */
(void) map_read(map, adr); /* ensure mmu mapping is up to date */
preempt_disable();
local_irq_disable();
}
static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
struct cfi_private *cfi = map->fldrv_priv;
if (chip->state != FL_POINT && chip->state != FL_READY) {
map_write(map, CMD(0xff), adr);
chip->state = FL_READY;
}
(void) map_read(map, adr);
asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */
local_irq_enable();
preempt_enable();
}
/*
* When a delay is required for the flash operation to complete, the
* xip_udelay() function is polling for both the given timeout and pending
* (but still masked) hardware interrupts. Whenever there is an interrupt
* pending then the flash erase 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)
{
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();
flstate_t oldstate, newstate;
do {
cpu_relax();
if (xip_irqpending() && cfip &&
((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) ||
(chip->state == FL_WRITING && (cfip->FeatureSupport&4))) &&
(cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
/*
* Let's suspend the erase or write operation when
* supported. Note that we currently don't try to
* suspend interleaved chips if there is already
* another operation suspended (imagine what happens
* when one chip was already done with the current
* operation while another chip suspended it, then
* we resume the whole thing at once). Yes, it
* can happen!
*/
map_write(map, CMD(0xb0), adr);
map_write(map, CMD(0x70), adr);
usec -= xip_elapsed_since(start);
suspended = xip_currtime();
do {
if (xip_elapsed_since(suspended) > 100000) {
/*
* The chip doesn't want to suspend
* after waiting for 100 msecs.
* This is a critical error but there
* is not much we can do here.
*/
return;
}
status = map_read(map, adr);
} while (!map_word_andequal(map, status, OK, OK));
/* Suspend succeeded */
oldstate = chip->state;
if (oldstate == FL_ERASING) {
if (!map_word_bitsset(map, status, CMD(0x40)))
break;
newstate = FL_XIP_WHILE_ERASING;
chip->erase_suspended = 1;
} else {
if (!map_word_bitsset(map, status, CMD(0x04)))
break;
newstate = FL_XIP_WHILE_WRITING;
chip->write_suspended = 1;
}
chip->state = newstate;
map_write(map, CMD(0xff), adr);
(void) map_read(map, adr);
asm volatile (".rep 8; nop; .endr");
local_irq_enable();
preempt_enable();
asm volatile (".rep 8; nop; .endr");
cond_resched();
/*
* We're back. However someone else might have
* decided to go write to the chip if we are in
* a suspended erase state. If so let's wait
* until it's done.
*/
preempt_disable();
while (chip->state != newstate) {
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
preempt_enable();
schedule();
remove_wait_queue(&chip->wq, &wait);
preempt_disable();
}
/* Disallow XIP again */
local_irq_disable();
/* Resume the write or erase operation */
map_write(map, CMD(0xd0), adr);
map_write(map, CMD(0x70), adr);
chip->state = oldstate;
start = xip_currtime();
} else if (usec >= 1000000/HZ) {
/*
* Try to save on CPU power when waiting delay
* is at least a system timer tick period.
* No need to be extremely accurate here.
*/
xip_cpu_idle();
}
status = map_read(map, adr);
} while (!map_word_andequal(map, status, OK, OK)
&& xip_elapsed_since(start) < usec);
}
#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec)
/*
* The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
* the flash is actively programming or erasing since we have to poll for
* the operation to complete anyway. We can't do that in a generic way with
* a XIP setup so do it before the actual flash operation in this case.
*/
#undef INVALIDATE_CACHED_RANGE
#define INVALIDATE_CACHED_RANGE(x...)
#define XIP_INVAL_CACHED_RANGE(map, from, size) \
do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
/*
* Extra notes:
*
* Activating this XIP support changes the way the code works a bit. For
* example the code to suspend the current process when concurrent access
* happens is never executed because xip_udelay() will always return with the
* same chip state as it was entered with. This is why there is no care for
* the presence of add_wait_queue() or schedule() calls from within a couple
* xip_disable()'d areas of code, like in do_erase_oneblock for example.
* The queueing and scheduling are always happening within xip_udelay().
*
* Similarly, get_chip() and put_chip() just happen to always be executed
* with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
* is in array mode, therefore never executing many cases therein and not
* causing any problem with XIP.
*/
#else
#define xip_disable(map, chip, adr)
#define xip_enable(map, chip, adr)
#define UDELAY(map, chip, adr, usec) cfi_udelay(usec)
#define XIP_INVAL_CACHED_RANGE(x...)
#endif
static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
{
unsigned long cmd_addr;
......@@ -944,7 +1174,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
}
#if 0
static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz)
static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd,
loff_t from, size_t len,
size_t *retlen,
u_char *buf,
int base_offst, int reg_sz)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
......@@ -973,6 +1207,8 @@ static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t
return (len-count)?:ret;
}
xip_disable(map, chip, chip->start);
if (chip->state != FL_JEDEC_QUERY) {
map_write(map, CMD(0x90), chip->start);
chip->state = FL_JEDEC_QUERY;
......@@ -985,6 +1221,7 @@ static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t
count--;
}
xip_enable(map, chip, chip->start);
put_chip(map, chip, chip->start);
spin_unlock(chip->mutex);
......@@ -1036,7 +1273,8 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, s
}
#endif
static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, map_word datum)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word status, status_OK;
......@@ -1055,14 +1293,16 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
return ret;
}
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map);
xip_disable(map, chip, adr);
map_write(map, CMD(0x40), adr);
map_write(map, datum, adr);
chip->state = FL_WRITING;
spin_unlock(chip->mutex);
INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map));
cfi_udelay(chip->word_write_time);
UDELAY(map, chip, adr, chip->word_write_time);
spin_lock(chip->mutex);
timeo = jiffies + (HZ/2);
......@@ -1089,6 +1329,7 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
/* OK Still waiting */
if (time_after(jiffies, timeo)) {
chip->state = FL_STATUS;
xip_enable(map, chip, adr);
printk(KERN_ERR "waiting for chip to be ready timed out in word write\n");
ret = -EIO;
goto out;
......@@ -1097,7 +1338,7 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
/* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
z++;
cfi_udelay(1);
UDELAY(map, chip, adr, 1);
spin_lock(chip->mutex);
}
if (!z) {
......@@ -1119,8 +1360,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
map_write(map, CMD(0x70), adr);
ret = -EROFS;
}
out:
put_chip(map, chip, adr);
xip_enable(map, chip, adr);
out: put_chip(map, chip, adr);
spin_unlock(chip->mutex);
return ret;
......@@ -1210,7 +1452,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
}
static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
......@@ -1232,6 +1474,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
return ret;
}
XIP_INVAL_CACHED_RANGE(map, adr, len);
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
[...], 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
......@@ -1240,12 +1486,13 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0x70), cmd_adr);
status = map_read(map, cmd_adr);
if (map_word_bitsset(map, status, CMD(0x30))) {
xip_enable(map, chip, cmd_adr);
printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
xip_disable(map, chip, cmd_adr);
map_write(map, CMD(0x50), cmd_adr);
map_write(map, CMD(0x70), cmd_adr);
}
ENABLE_VPP(map);
chip->state = FL_WRITING_TO_BUFFER;
z = 0;
......@@ -1257,7 +1504,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
break;
spin_unlock(chip->mutex);
cfi_udelay(1);
UDELAY(map, chip, cmd_adr, 1);
spin_lock(chip->mutex);
if (++z > 20) {
......@@ -1269,6 +1516,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
/* 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 "Chip not ready for buffer write. status = %lx, Xstatus = %lx\n",
status.x[0], Xstatus.x[0]);
ret = -EIO;
......@@ -1305,7 +1553,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
spin_unlock(chip->mutex);
INVALIDATE_CACHED_RANGE(map, adr, len);
cfi_udelay(chip->buffer_write_time);
UDELAY(map, chip, cmd_adr, chip->buffer_write_time);
spin_lock(chip->mutex);
timeo = jiffies + (HZ/2);
......@@ -1331,6 +1579,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
/* OK Still waiting */
if (time_after(jiffies, timeo)) {
chip->state = FL_STATUS;
xip_enable(map, chip, cmd_adr);
printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n");
ret = -EIO;
goto out;
......@@ -1338,7 +1587,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
/* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
cfi_udelay(1);
UDELAY(map, chip, cmd_adr, 1);
z++;
spin_lock(chip->mutex);
}
......@@ -1362,8 +1611,8 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
ret = -EROFS;
}
out:
put_chip(map, chip, cmd_adr);
xip_enable(map, chip, cmd_adr);
out: put_chip(map, chip, cmd_adr);
spin_unlock(chip->mutex);
return ret;
}
......@@ -1432,7 +1681,7 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to,
return 0;
}
static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
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;
......@@ -1455,7 +1704,10 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
return ret;
}
XIP_INVAL_CACHED_RANGE(map, adr, len);
ENABLE_VPP(map);
xip_disable(map, chip, adr);
/* Clear the status register first */
map_write(map, CMD(0x50), adr);
......@@ -1467,7 +1719,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
spin_unlock(chip->mutex);
INVALIDATE_CACHED_RANGE(map, adr, len);
msleep(chip->erase_time / 2);
UDELAY(map, chip, adr, chip->erase_time*1000/2);
spin_lock(chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */
......@@ -1505,6 +1757,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
/* Clear status bits */
map_write(map, CMD(0x50), adr);
map_write(map, CMD(0x70), adr);
xip_enable(map, chip, adr);
printk(KERN_ERR "waiting for erase at %08lx to complete timed out. status = %lx, Xstatus = %lx.\n",
adr, status.x[0], Xstatus.x[0]);
ret = -EIO;
......@@ -1513,8 +1766,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
/* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
UDELAY(map, chip, adr, 1000000/HZ);
spin_lock(chip->mutex);
}
......@@ -1530,6 +1782,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
/* Reset the error bits */
map_write(map, CMD(0x50), adr);
map_write(map, CMD(0x70), adr);
xip_enable(map, chip, adr);
chipstatus = status.x[0];
if (!map_word_equal(map, status, CMD(chipstatus))) {
......@@ -1565,6 +1818,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
ret = -EIO;
}
} else {
xip_enable(map, chip, adr);
ret = 0;
}
......@@ -1632,15 +1886,19 @@ static void cfi_intelext_sync (struct mtd_info *mtd)
}
#ifdef DEBUG_LOCK_BITS
static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
static int __xipram do_printlockstatus_oneblock(struct map_info *map,
struct flchip *chip,
unsigned long adr,
int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
int status, ofs_factor = cfi->interleave * cfi->device_type;
xip_disable(map, chip, adr+(2*ofs_factor));
cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
chip->state = FL_JEDEC_QUERY;
status = cfi_read_query(map, adr+(2*ofs_factor));
xip_enable(map, chip, 0);
printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
adr, status);
return 0;
......@@ -1650,7 +1908,7 @@ static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip
#define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1)
#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2)
static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
......@@ -1671,8 +1929,9 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
}
ENABLE_VPP(map);
map_write(map, CMD(0x60), adr);
xip_disable(map, chip, adr);
map_write(map, CMD(0x60), adr);
if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
map_write(map, CMD(0x01), adr);
chip->state = FL_LOCKING;
......@@ -1683,7 +1942,7 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
BUG();
spin_unlock(chip->mutex);
schedule_timeout(HZ);
UDELAY(map, chip, adr, 1000000/HZ);
spin_lock(chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */
......@@ -1702,6 +1961,7 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
Xstatus = map_read(map, adr);
xip_enable(map, chip, adr);
printk(KERN_ERR "waiting for unlock to complete timed out. status = %lx, Xstatus = %lx.\n",
status.x[0], Xstatus.x[0]);
put_chip(map, chip, adr);
......@@ -1711,12 +1971,13 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
/* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
cfi_udelay(1);
UDELAY(map, chip, adr, 1);
spin_lock(chip->mutex);
}
/* Done and happy. */
chip->state = FL_STATUS;
xip_enable(map, chip, adr);
put_chip(map, chip, adr);
spin_unlock(chip->mutex);
return 0;
......@@ -1875,7 +2136,7 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
static char im_name_1[]="cfi_cmdset_0001";
static char im_name_3[]="cfi_cmdset_0003";
int __init cfi_intelext_init(void)
static int __init cfi_intelext_init(void)
{
inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001);
inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001);
......
......@@ -13,7 +13,7 @@
*
* This code is GPL
*
* $Id: cfi_cmdset_0002.c,v 1.111 2004/11/16 18:29:00 dwmw2 Exp $
* $Id: cfi_cmdset_0002.c,v 1.112 2004/11/20 12:49:04 dwmw2 Exp $
*
*/
......@@ -1173,8 +1173,7 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
chip->in_progress_block_addr = adr;
cfi_spin_unlock(chip->mutex);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((chip->erase_time*HZ)/(2*1000));
msleep(chip->erase_time/2);
cfi_spin_lock(chip->mutex);
timeo = jiffies + (HZ*20);
......@@ -1259,8 +1258,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
chip->in_progress_block_addr = adr;
cfi_spin_unlock(chip->mutex);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((chip->erase_time*HZ)/(2*1000));
msleep(chip->erase_time/2);
cfi_spin_lock(chip->mutex);
timeo = jiffies + (HZ*20);
......
......@@ -4,7 +4,7 @@
*
* (C) 2000 Red Hat. GPL'd
*
* $Id: cfi_cmdset_0020.c,v 1.16 2004/11/16 18:29:00 dwmw2 Exp $
* $Id: cfi_cmdset_0020.c,v 1.17 2004/11/20 12:49:04 dwmw2 Exp $
*
* 10/10/2000 Nicolas Pitre <nico@cam.org>
* - completely revamped method functions so they are aware and
......@@ -788,7 +788,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
chip->state = FL_ERASING;
spin_unlock_bh(chip->mutex);
schedule_timeout(HZ);
msleep(1000);
spin_lock_bh(chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */
......@@ -1087,7 +1087,7 @@ static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, un
chip->state = FL_LOCKING;
spin_unlock_bh(chip->mutex);
schedule_timeout(HZ);
msleep(1000);
spin_lock_bh(chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */
......@@ -1236,7 +1236,7 @@ static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip,
chip->state = FL_UNLOCKING;
spin_unlock_bh(chip->mutex);
schedule_timeout(HZ);
msleep(1000);
spin_lock_bh(chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */
......
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
$Id: cfi_probe.c,v 1.79 2004/10/20 23:04:01 dwmw2 Exp $
$Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $
*/
#include <linux/config.h>
......@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/gen_probe.h>
......@@ -31,11 +32,47 @@ static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi);
struct mtd_info *cfi_probe(struct map_info *map);
#ifdef CONFIG_MTD_XIP
/* only needed for short periods, so this is rather simple */
#define xip_disable() local_irq_disable()
#define xip_allowed(base, map) \
do { \
(void) map_read(map, base); \
asm volatile (".rep 8; nop; .endr"); \
local_irq_enable(); \
} while (0)
#define xip_enable(base, map, cfi) \
do { \
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
xip_allowed(base, map); \
} while (0)
#define xip_disable_qry(base, map, cfi) \
do { \
xip_disable(); \
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \
} while (0)
#else
#define xip_disable() do { } while (0)
#define xip_allowed(base, map) do { } while (0)
#define xip_enable(base, map, cfi) do { } while (0)
#define xip_disable_qry(base, map, cfi) do { } while (0)
#endif
/* check for QRY.
in: interleave,type,mode
ret: table index, <0 for error
*/
static int qry_present(struct map_info *map, __u32 base,
static int __xipram qry_present(struct map_info *map, __u32 base,
struct cfi_private *cfi)
{
int osf = cfi->interleave * cfi->device_type; // scale factor
......@@ -59,10 +96,10 @@ static int qry_present(struct map_info *map, __u32 base,
if (!map_word_equal(map, qry[2], val[2]))
return 0;
return 1; // nothing found
return 1; // "QRY" found
}
static int cfi_probe_chip(struct map_info *map, __u32 base,
static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi)
{
int i;
......@@ -79,12 +116,16 @@ static int cfi_probe_chip(struct map_info *map, __u32 base,
(unsigned long)base + 0x55, map->size -1);
return 0;
}
xip_disable();
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
if (!qry_present(map,base,cfi))
if (!qry_present(map,base,cfi)) {
xip_enable(base, map, cfi);
return 0;
}
if (!cfi->numchips) {
/* This is the first time we're called. Set up the CFI
......@@ -110,6 +151,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base,
/* If the QRY marker goes away, it's an alias */
if (!qry_present(map, start, cfi)) {
xip_allowed(base, map);
printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
map->name, base, start);
return 0;
......@@ -122,6 +164,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base,
cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
if (qry_present(map, base, cfi)) {
xip_allowed(base, map);
printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
map->name, base, start);
return 0;
......@@ -137,6 +180,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base,
/* Put it back into Read Mode */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
xip_allowed(base, map);
printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
map->name, cfi->interleave, cfi->device_type*8, base,
......@@ -145,7 +189,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base,
return 1;
}
static int cfi_chip_setup(struct map_info *map,
static int __xipram cfi_chip_setup(struct map_info *map,
struct cfi_private *cfi)
{
int ofs_factor = cfi->interleave*cfi->device_type;
......@@ -153,6 +197,7 @@ static int cfi_chip_setup(struct map_info *map,
int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor);
int i;
xip_enable(base, map, cfi);
#ifdef DEBUG_CFI
printk("Number of erase regions: %d\n", num_erase_regions);
#endif
......@@ -170,9 +215,29 @@ static int cfi_chip_setup(struct map_info *map,
cfi->cfi_mode = CFI_MODE_CFI;
/* Read the CFI info structure */
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) {
xip_disable_qry(base, map, cfi);
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor);
}
/* Note we put the device back into Read Mode BEFORE going into Auto
* Select Mode, as some devices support nesting of modes, others
* don't. This way should always work.
* On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
* so should be treated as nops or illegal (and so put the device
* back into Read Mode, which is a nop in this case).
*/
cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi->mfr = cfi_read_query(map, base);
cfi->id = cfi_read_query(map, base + ofs_factor);
/* Put it back into Read Mode */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
/* ... even if it's an Intel chip */
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
xip_allowed(base, map);
/* Do any necessary byteswapping */
cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID);
......@@ -198,25 +263,6 @@ static int cfi_chip_setup(struct map_info *map,
#endif
}
/* Note we put the device back into Read Mode BEFORE going into Auto
* Select Mode, as some devices support nesting of modes, others
* don't. This way should always work.
* On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
* so should be treated as nops or illegal (and so put the device
* back into Read Mode, which is a nop in this case).
*/
cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi->mfr = cfi_read_query(map, base);
cfi->id = cfi_read_query(map, base + ofs_factor);
/* Put it back into Read Mode */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
/* ... even if it's an Intel chip */
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
map->name, cfi->interleave, cfi->device_type*8, base,
map->bankwidth*8);
......
......@@ -7,7 +7,7 @@
*
* This code is covered by the GPL.
*
* $Id: cfi_util.c,v 1.5 2004/08/12 06:40:23 eric Exp $
* $Id: cfi_util.c,v 1.7 2004/11/05 22:41:05 nico Exp $
*
*/
......@@ -22,13 +22,14 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/compatmac.h>
struct cfi_extquery *
cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
{
struct cfi_private *cfi = map->fldrv_priv;
__u32 base = 0; // cfi->chips[0].start;
......@@ -40,21 +41,35 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
if (!adr)
goto out;
/* Switch it into Query Mode */
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
extp = kmalloc(size, GFP_KERNEL);
if (!extp) {
printk(KERN_ERR "Failed to allocate memory\n");
goto out;
}
#ifdef CONFIG_MTD_XIP
local_irq_disable();
#endif
/* Switch it into Query Mode */
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
/* Read in the Extended Query Table */
for (i=0; i<size; i++) {
((unsigned char *)extp)[i] =
cfi_read_query(map, base+((adr+i)*ofs_factor));
}
/* Make sure it returns to read mode */
cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL);
#ifdef CONFIG_MTD_XIP
(void) map_read(map, base);
asm volatile (".rep 8; nop; .endr");
local_irq_enable();
#endif
if (extp->MajorVersion != '1' ||
(extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
printk(KERN_WARNING " Unknown %s Extended Query "
......@@ -62,15 +77,9 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
extp->MinorVersion);
kfree(extp);
extp = NULL;
goto out;
}
out:
/* Make sure it's in read mode */
cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL);
return extp;
out: return extp;
}
EXPORT_SYMBOL(cfi_read_pri);
......
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
$Id: jedec_probe.c,v 1.58 2004/11/16 18:29:00 dwmw2 Exp $
$Id: jedec_probe.c,v 1.61 2004/11/19 20:52:16 thayne Exp $
See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
for the standard this probe goes back to.
......@@ -227,6 +227,11 @@ static const struct unlock_addr unlock_addrs[] = {
[MTD_UADDR_DONT_CARE] = {
.addr1 = 0x0000, /* Doesn't matter which address */
.addr2 = 0x0000 /* is used - must be last entry */
},
[MTD_UADDR_UNNECESSARY] = {
.addr1 = 0x0000,
.addr2 = 0x0000
}
};
......@@ -514,15 +519,20 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x10000,8),
}
}, {
mfr_id: MANUFACTURER_AMD,
dev_id: AM29F002T,
name: "AMD AM29F002T",
DevSize: SIZE_256KiB,
NumEraseRegions: 4,
regions: {ERASEINFO(0x10000,3),
.mfr_id = MANUFACTURER_AMD,
.dev_id = AM29F002T,
.name = "AMD AM29F002T",
.uaddr = {
[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
},
.DevSize = SIZE_256KiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x10000,3),
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
ERASEINFO(0x04000,1),
}
}, {
.mfr_id = MANUFACTURER_ATMEL,
......@@ -770,15 +780,20 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x04000,1)
}
}, {
mfr_id: MANUFACTURER_HYUNDAI,
dev_id: HY29F002T,
name: "Hyundai HY29F002T",
DevSize: SIZE_256KiB,
NumEraseRegions: 4,
regions: {ERASEINFO(0x10000,3),
.mfr_id = MANUFACTURER_HYUNDAI,
.dev_id = HY29F002T,
.name = "Hyundai HY29F002T",
.uaddr = {
[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
},
.DevSize = SIZE_256KiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x10000,3),
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
ERASEINFO(0x04000,1),
}
}, {
.mfr_id = MANUFACTURER_INTEL,
......@@ -1177,15 +1192,20 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x10000,7),
}
}, {
mfr_id: MANUFACTURER_MACRONIX,
dev_id: MX29F002T,
name: "Macronix MX29F002T",
DevSize: SIZE_256KiB,
NumEraseRegions: 4,
regions: {ERASEINFO(0x10000,3),
.mfr_id = MANUFACTURER_MACRONIX,
.dev_id = MX29F002T,
.name = "Macronix MX29F002T",
.uaddr = {
[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
},
.DevSize = SIZE_256KiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x10000,3),
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
ERASEINFO(0x04000,1),
}
}, {
.mfr_id = MANUFACTURER_PMC,
......@@ -1780,7 +1800,6 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
return 0;
}
/* Mask out address bits which are smaller than the device type */
p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1;
p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2;
......@@ -1923,7 +1942,6 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
if (MTD_UADDR_UNNECESSARY == uaddr_idx)
return 0;
/* Mask out address bits which are smaller than the device type */
cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1;
cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2;
}
......
/*
* $Id: cmdlinepart.c,v 1.16 2004/11/16 18:28:59 dwmw2 Exp $
* $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
*
* Read flash partition table from command line
*
......@@ -338,8 +338,10 @@ static int parse_cmdline_partitions(struct mtd_info *master,
* This is the handler for our kernel parameter, called from
* main.c::checksetup(). Note that we can not yet kmalloc() anything,
* so we only save the commandline for later processing.
*
* This function needs to be visible for bootloaders.
*/
static int mtdpart_setup(char *s)
int mtdpart_setup(char *s)
{
cmdline = s;
return 1;
......
/**
*
* $Id: phram.c,v 1.3 2004/11/16 18:29:01 dwmw2 Exp $
* $Id: phram.c,v 1.6 2004/11/25 16:51:09 joern Exp $
*
* Copyright (c) Jochen Schaeuble <psionic@psionic.de>
* 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de>
......@@ -15,9 +15,12 @@
* phram=<name>,<start>,<len>
* <name> may be up to 63 characters.
* <start> and <len> can be octal, decimal or hexadecimal. If followed
* by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
* by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or
* gigabytes.
*
* Example:
* phram=swap,896Mi,110Mi phram=test,1006Mi,1Mi
*
*/
#include <asm/io.h>
......@@ -184,7 +187,9 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base)
result *= 1024;
case 'k':
result *= 1024;
endp++;
/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
if ((*endp)[1] == 'i')
(*endp) += 2;
}
return result;
}
......@@ -235,7 +240,7 @@ static int phram_setup(const char *val, struct kernel_param *kp)
uint32_t len;
int i, ret;
if (strnlen(val, sizeof(str)) >= sizeof(str))
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
parse_err("parameter too long\n");
strcpy(str, val);
......@@ -283,7 +288,7 @@ static int __init slram_setup(const char *val, struct kernel_param *kp)
if (!val || !val[0])
parse_err("no arguments to \"slram=\"\n");
if (strnlen(val, sizeof(str)) >= sizeof(str))
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
parse_err("parameter too long\n");
strcpy(str, val);
......@@ -342,7 +347,6 @@ MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><len
static int __init init_phram(void)
{
printk(KERN_ERR "phram loaded\n");
return 0;
}
......
......@@ -8,7 +8,7 @@
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
*
* $Id: inftlmount.c,v 1.15 2004/11/05 21:55:55 kalev Exp $
* $Id: inftlmount.c,v 1.16 2004/11/22 13:50:53 kalev 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
......@@ -41,7 +41,7 @@
#include <linux/mtd/inftl.h>
#include <linux/mtd/compatmac.h>
char inftlmountrev[]="$Revision: 1.15 $";
char inftlmountrev[]="$Revision: 1.16 $";
/*
* find_boot_record: Find the INFTL Media Header and its Spare copy which
......@@ -389,8 +389,6 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
struct erase_info *instr = &inftl->instr;
int physblock;
instr->mtd = inftl->mbd.mtd;
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
"block=%d)\n", inftl, block);
......@@ -400,6 +398,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
_first_? */
/* Use async erase interface, test return code */
instr->mtd = inftl->mbd.mtd;
instr->addr = block * inftl->EraseSize;
instr->len = inftl->mbd.mtd->erasesize;
/* Erase one physical eraseblock at a time, even though the NAND api
......
# drivers/mtd/maps/Kconfig
# $Id: Kconfig,v 1.37 2004/10/20 22:57:18 dwmw2 Exp $
# $Id: Kconfig,v 1.38 2004/11/24 19:42:51 rpurdie Exp $
menu "Mapping drivers for chip access"
depends on MTD!=n
......@@ -645,5 +645,11 @@ config MTD_BAST_MAXSIZE
depends on MTD_BAST
default "4"
config MTD_SHARP_SL
bool "ROM maped on Sharp SL Series"
depends on MTD && ARCH_PXA
help
This enables access to the flash chip on the Sharp SL Series of PDAs.
endmenu
#
# linux/drivers/maps/Makefile
#
# $Id: Makefile.common,v 1.19 2004/09/21 14:27:16 bjd Exp $
# $Id: Makefile.common,v 1.20 2004/11/24 19:42:51 rpurdie Exp $
ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
obj-$(CONFIG_MTD) += map_funcs.o
......@@ -69,3 +69,4 @@ obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
obj-$(CONFIG_MTD_DMV182) += dmv182.o
obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o
/*
* sharpsl-flash.c
*
* Copyright (C) 2001 Lineo Japan, Inc.
* Copyright (C) 2002 SHARP
*
* $Id: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $
*
* based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp
* Handle mapping of the flash on the RPX Lite and CLLF boards
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#define WINDOW_ADDR 0x00000000
#define WINDOW_SIZE 0x01000000
#define BANK_WIDTH 2
static struct mtd_info *mymtd;
struct map_info sharpsl_map = {
.name = "sharpsl-flash",
.size = WINDOW_SIZE,
.bankwidth = BANK_WIDTH,
.phys = WINDOW_ADDR
};
static struct mtd_partition sharpsl_partitions[1] = {
{
name: "Filesystem",
size: 0x006d0000,
offset: 0x00120000
}
};
#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
int __init init_sharpsl(void)
{
struct mtd_partition *parts;
int nb_parts = 0;
char *part_type = "static";
printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!sharpsl_map.virt) {
printk("Failed to ioremap\n");
return -EIO;
}
mymtd = do_map_probe("map_rom", &sharpsl_map);
if (!mymtd) {
iounmap(sharpsl_map.virt);
return -ENXIO;
}
mymtd->owner = THIS_MODULE;
parts = sharpsl_partitions;
nb_parts = NB_OF(sharpsl_partitions);
printk(KERN_NOTICE "Using %s partision definition\n", part_type);
add_mtd_partitions(mymtd, parts, nb_parts);
return 0;
}
static void __exit cleanup_sharpsl(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
if (sharpsl_map.virt) {
iounmap(sharpsl_map.virt);
sharpsl_map.virt = 0;
}
}
module_init(init_sharpsl);
module_exit(cleanup_sharpsl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SHARP (Original: Arnold Christensen <AKC@pel.dk>)");
MODULE_DESCRIPTION("MTD map driver for SHARP SL series");
/*
* Direct MTD block device access
*
* $Id: mtdblock.c,v 1.65 2004/11/16 18:28:59 dwmw2 Exp $
* $Id: mtdblock.c,v 1.66 2004/11/25 13:52:52 joern Exp $
*
* (C) 2000-2003 Nicolas Pitre <nico@cam.org>
* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
......@@ -248,7 +248,7 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
unsigned long block, char *buf)
{
struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
if (unlikely(!mtdblk->cache_data)) {
if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
if (!mtdblk->cache_data)
return -EINTR;
......
# drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.22 2004/10/05 22:11:46 gleixner Exp $
# $Id: Kconfig,v 1.24 2004/11/26 12:28:22 dedekind Exp $
menu "NAND Flash Device Drivers"
depends on MTD!=n
......@@ -192,4 +192,17 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
Even if you leave this disabled, you can enable BBT writes at module
load time (assuming you build diskonchip as a module) with the module
parameter "inftl_bbt_write=1".
config MTD_NAND_SHARPSL
bool "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on MTD_NAND && ARCH_PXA
config MTD_NAND_NANDSIM
bool "Support for NAND Flash Simulator"
depends on MTD_NAND
help
The simulator may simulate verious NAND flash chips for the
MTD nand layer.
endmenu
#
# linux/drivers/nand/Makefile
#
# $Id: Makefile.common,v 1.13 2004/09/28 22:04:23 bjd Exp $
# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $
obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
......@@ -18,5 +18,7 @@ obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
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_NANDSIM) += nandsim.o
nand-objs = nand_base.o nand_bbt.o
......@@ -41,7 +41,7 @@
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
*
* $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 gleixner Exp $
* $Id: nand_base.c,v 1.123 2004/11/02 22:36:59 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -840,18 +840,8 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
}
this->write_buf(mtd, this->data_poi, mtd->oobblock);
break;
/* Hardware ecc 8 byte / 512 byte data */
case NAND_ECC_HW8_512:
eccbytes += 2;
/* Hardware ecc 6 byte / 512 byte data */
case NAND_ECC_HW6_512:
eccbytes += 3;
/* Hardware ecc 3 byte / 256 data */
/* Hardware ecc 3 byte / 512 byte data */
case NAND_ECC_HW3_256:
case NAND_ECC_HW3_512:
eccbytes += 3;
default:
eccbytes = this->eccbytes;
for (; eccsteps; eccsteps--) {
/* enable hardware ecc logic for write */
this->enable_hwecc(mtd, NAND_ECC_WRITE);
......@@ -864,14 +854,9 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
* the data bytes (words) */
if (this->options & NAND_HWECC_SYNDROME)
this->write_buf(mtd, ecc_code, eccbytes);
datidx += this->eccsize;
}
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* Write out OOB data */
......@@ -1051,7 +1036,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
int eccmode, eccsteps;
int *oob_config, datidx;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
int eccbytes = 3;
int eccbytes;
int compareecc = 1;
int oobreadlen;
......@@ -1092,19 +1077,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
end = mtd->oobblock;
ecc = this->eccsize;
switch (eccmode) {
case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */
eccbytes = 6;
break;
case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */
eccbytes = 8;
break;
case NAND_ECC_NONE:
compareecc = 0;
break;
}
eccbytes = this->eccbytes;
if (this->options & NAND_HWECC_SYNDROME)
if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
compareecc = 0;
oobreadlen = mtd->oobsize;
......@@ -1165,10 +1140,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
break;
case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */
case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */
case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */
case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */
default:
for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
this->enable_hwecc(mtd, NAND_ECC_READ);
this->read_buf(mtd, &data_poi[datidx], ecc);
......@@ -1193,10 +1165,6 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
}
}
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* read oobdata */
......@@ -2433,8 +2401,19 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
* fallback to software ECC
*/
this->eccsize = 256; /* set default eccsize */
this->eccbytes = 3;
switch (this->eccmode) {
case NAND_ECC_HW12_2048:
if (mtd->oobblock < 2048) {
printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
mtd->oobblock);
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
} else
this->eccsize = 2048;
break;
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
......@@ -2444,15 +2423,12 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
break;
} else
this->eccsize = 512; /* set eccsize to 512 and fall through for function check */
this->eccsize = 512; /* set eccsize to 512 */
break;
case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
BUG();
case NAND_ECC_NONE:
printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
......@@ -2469,10 +2445,31 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
BUG();
}
/* Check hardware ecc function availability and adjust number of ecc bytes per
* calculation step
*/
switch (this->eccmode) {
case NAND_ECC_HW12_2048:
this->eccbytes += 4;
case NAND_ECC_HW8_512:
this->eccbytes += 2;
case NAND_ECC_HW6_512:
this->eccbytes += 3;
case NAND_ECC_HW3_512:
case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
BUG();
}
mtd->eccsize = this->eccsize;
/* Set the number of read / write steps for one page to ensure ECC generation */
switch (this->eccmode) {
case NAND_ECC_HW12_2048:
this->eccsteps = mtd->oobblock / 2048;
break;
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
case NAND_ECC_HW8_512:
......
......@@ -6,7 +6,7 @@
*
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
*
* $Id: nand_bbt.c,v 1.26 2004/10/05 13:50:20 gleixner Exp $
* $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -1001,6 +1001,7 @@ int nand_default_bbt (struct mtd_info *mtd)
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 */
......@@ -1008,18 +1009,19 @@ int nand_default_bbt (struct mtd_info *mtd)
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
if (mtd->oobblock > 512)
return nand_scan_bbt (mtd, &largepage_flashbased);
else
return nand_scan_bbt (mtd, &smallpage_flashbased);
if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->oobblock > 512) ?
&largepage_flashbased : &smallpage_flashbased;
}
} else {
this->bbt_td = NULL;
this->bbt_md = NULL;
if (mtd->oobblock > 512)
return nand_scan_bbt (mtd, &largepage_memorybased);
else
return nand_scan_bbt (mtd, &smallpage_memorybased);
if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->oobblock > 512) ?
&largepage_memorybased : &smallpage_memorybased;
}
}
return nand_scan_bbt (mtd, this->badblock_pattern);
}
/**
......
/*
* NAND flash simulator.
*
* Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
*
* Copyright (C) 2004 Nokia Corporation
*
* Note: NS means "NAND Simulator".
* Note: Input means input TO flash chip, output means output FROM chip.
*
* 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, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*
* $Id: nandsim.c,v 1.3 2004/11/26 13:00:24 dedekind Exp $
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#ifdef CONFIG_NS_ABS_POS
#include <asm/io.h>
#endif
/* Default simulator parameters values */
#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
!defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \
!defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \
!defined(CONFIG_NANDSIM_FOURTH_ID_BYTE)
#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98
#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x36
#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */
#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */
#endif
#ifndef CONFIG_NANDSIM_ACCESS_DELAY
#define CONFIG_NANDSIM_ACCESS_DELAY 25
#endif
#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY
#define CONFIG_NANDSIM_PROGRAMM_DELAY 200
#endif
#ifndef CONFIG_NANDSIM_ERASE_DELAY
#define CONFIG_NANDSIM_ERASE_DELAY 2
#endif
#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE
#define CONFIG_NANDSIM_OUTPUT_CYCLE 40
#endif
#ifndef CONFIG_NANDSIM_INPUT_CYCLE
#define CONFIG_NANDSIM_INPUT_CYCLE 50
#endif
#ifndef CONFIG_NANDSIM_BUS_WIDTH
#define CONFIG_NANDSIM_BUS_WIDTH 8
#endif
#ifndef CONFIG_NANDSIM_DO_DELAYS
#define CONFIG_NANDSIM_DO_DELAYS 0
#endif
#ifndef CONFIG_NANDSIM_LOG
#define CONFIG_NANDSIM_LOG 0
#endif
#ifndef CONFIG_NANDSIM_DBG
#define CONFIG_NANDSIM_DBG 0
#endif
static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE;
static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE;
static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;
static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY;
static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY;
static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE;
static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE;
static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;
static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;
static uint log = CONFIG_NANDSIM_LOG;
static uint dbg = CONFIG_NANDSIM_DBG;
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
module_param(third_id_byte, uint, 0400);
module_param(fourth_id_byte, uint, 0400);
module_param(access_delay, uint, 0400);
module_param(programm_delay, uint, 0400);
module_param(erase_delay, uint, 0400);
module_param(output_cycle, uint, 0400);
module_param(input_cycle, uint, 0400);
module_param(bus_width, uint, 0400);
module_param(do_delays, uint, 0400);
module_param(log, uint, 0400);
module_param(dbg, uint, 0400);
MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)");
MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)");
MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)");
MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)");
MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");
MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");
MODULE_PARM_DESC(log, "Perform logging if not zero");
MODULE_PARM_DESC(dbg, "Output debug information if not zero");
/*
* There is no macro for 0x30 command which is used in "large page"
* devices in standard mtd header, define it here.
*/
#define NAND_CMD_READ2LP 0x30
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 2048
/* The prefix for simulator output */
#define NS_OUTPUT_PREFIX "[nandsim]"
/* Simulator's output macros (logging, debugging, warning, error) */
#define NS_LOG(args...) \
do { if (log) printk(KERN_INFO NS_OUTPUT_PREFIX " log: " args); } while(0)
#define NS_DBG(args...) \
do { if (dbg) printk(KERN_INFO NS_OUTPUT_PREFIX " debug: " args); } while(0)
#define NS_WARN(args...) \
do { printk(KERN_INFO NS_OUTPUT_PREFIX " warnig: " args); } while(0)
#define NS_ERR(args...) \
do { printk(KERN_INFO NS_OUTPUT_PREFIX " errorr: " args); } while(0)
/* Busy-wait delay macros (microseconds, milliseconds) */
#define NS_UDELAY(us) \
do { if (do_delays) udelay(us); } while(0)
#define NS_MDELAY(us) \
do { if (do_delays) mdelay(us); } while(0)
/* Is the nandsim structure initialized ? */
#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
/* Good operation completion status */
#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
/* Operation failed completion status */
#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns))
/* Calculate the page offset in flash RAM image by (row, column) address */
#define NS_RAW_OFFSET(ns) \
(((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)
/* Calculate the OOB offset in flash RAM image by (row, column) address */
#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
/* After a command is input, the simulator goes to one of the following states */
#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */
#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */
#define STATE_CMD_READ2LP 0x00000003 /* read data second command (large page devices) */
#define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */
#define STATE_CMD_READOOB 0x00000005 /* read OOB area */
#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */
#define STATE_CMD_STATUS 0x00000007 /* read status */
#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */
#define STATE_CMD_SEQIN 0x00000009 /* sequential data imput */
#define STATE_CMD_READID 0x0000000A /* read ID */
#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */
#define STATE_CMD_RESET 0x0000000C /* reset */
#define STATE_CMD_MASK 0x0000000F /* command states mask */
/* After an addres is input, the simulator goes to one of these states */
#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */
#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */
#define STATE_ADDR_ZERO 0x00000030 /* one byte zero address was accepted */
#define STATE_ADDR_MASK 0x00000030 /* address states mask */
/* Durind data input/output the simulator is in these states */
#define STATE_DATAIN 0x00000100 /* waiting for data input */
#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */
#define STATE_DATAOUT 0x00001000 /* waiting for page data output */
#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */
#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */
#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */
#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */
/* Previous operation is done, ready to accept new requests */
#define STATE_READY 0x00000000
/* This state is used to mark that the next state isn't known yet */
#define STATE_UNKNOWN 0x10000000
/* Simulator's actions bit masks */
#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */
#define ACTION_PRGPAGE 0x00200000 /* programm the internal buffer to flash */
#define ACTION_SECERASE 0x00300000 /* erase sector */
#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */
#define ACTION_HALFOFF 0x00500000 /* add to address half of page */
#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */
#define ACTION_MASK 0x00700000 /* action mask */
#define NS_OPER_NUM 12 /* Number of operations supported by the simulator */
#define NS_OPER_STATES 6 /* Maximum number of states in operation */
#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */
#define OPT_PAGE256 0x00000001 /* 256-byte page chips */
#define OPT_PAGE512 0x00000002 /* 512-byte page chips */
#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */
#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */
#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */
#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */
#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */
/* Remove action bits ftom state */
#define NS_STATE(x) ((x) & ~ACTION_MASK)
/*
* Maximum previous states which need to be saved. Currently saving is
* only needed for page programm operation with preceeded read command
* (which is only valid for 512-byte pages).
*/
#define NS_MAX_PREVSTATES 1
/*
* The structure which describes all the internal simulator data.
*/
struct nandsim {
struct mtd_partition part;
uint busw; /* flash chip bus width (8 or 16) */
u_char ids[4]; /* chip's ID bytes */
uint32_t options; /* chip's characteristic bits */
uint32_t state; /* current chip state */
uint32_t nxstate; /* next expected state */
uint32_t *op; /* current operation, NULL operations isn't known yet */
uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
uint16_t npstates; /* number of previous states saved */
uint16_t stateidx; /* current state index */
/* The simulated NAND flash image */
union flash_media {
u_char *byte;
uint16_t *word;
} mem;
/* Internal buffer of page + OOB size bytes */
union internal_buffer {
u_char *byte; /* for byte access */
uint16_t *word; /* for 16-bit word access */
} buf;
/* NAND flash "geometry" */
struct nandsin_geometry {
uint32_t totsz; /* total flash size, bytes */
uint32_t secsz; /* flash sector (erase block) size, bytes */
uint pgsz; /* NAND flash page size, bytes */
uint oobsz; /* page OOB area size, bytes */
uint32_t totszoob; /* total flash size including OOB, bytes */
uint pgszoob; /* page size including OOB , bytes*/
uint secszoob; /* sector size including OOB, bytes */
uint pgnum; /* total number of pages */
uint pgsec; /* number of pages per sector */
uint secshift; /* bits number in sector size */
uint pgshift; /* bits number in page size */
uint oobshift; /* bits number in OOB size */
uint pgaddrbytes; /* bytes per page address */
uint secaddrbytes; /* bytes per sector address */
uint idbytes; /* the number ID bytes that this chip outputs */
} geom;
/* NAND flash internal registers */
struct nandsim_regs {
unsigned command; /* the command register */
u_char status; /* the status register */
uint row; /* the page number */
uint column; /* the offset within page */
uint count; /* internal counter */
uint num; /* number of bytes which must be processed */
uint off; /* fixed page offset */
} regs;
/* NAND flash lines state */
struct ns_lines_status {
int ce; /* chip Enable */
int cle; /* command Latch Enable */
int ale; /* address Latch Enable */
int wp; /* write Protect */
} lines;
};
/*
* Operations array. To perform any operation the simulator must pass
* through the correspondent states chain.
*/
static struct nandsim_operations {
uint32_t reqopts; /* options which are required to perform the operation */
uint32_t states[NS_OPER_STATES]; /* operation's states */
} ops[NS_OPER_NUM] = {
/* Read page + OOB from the beginning */
{OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY,
STATE_DATAOUT, STATE_READY}},
/* Read page + OOB from the second half */
{OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY,
STATE_DATAOUT, STATE_READY}},
/* Read OOB */
{OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY,
STATE_DATAOUT, STATE_READY}},
/* Programm page starting from the beginning */
{OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN,
STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
/* Programm page starting from the beginning */
{OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE,
STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
/* Programm page starting from the second half */
{OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE,
STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
/* Programm OOB */
{OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE,
STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
/* Erase sector */
{OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
/* Read status */
{OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
/* Read multi-plane status */
{OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}},
/* Read ID */
{OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
/* Large page devices read page */
{OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READ2LP | ACTION_CPY,
STATE_DATAOUT, STATE_READY}}
};
/* MTD structure for NAND controller */
static struct mtd_info *nsmtd;
static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
/*
* Initialize the nandsim structure.
*
* RETURNS: 0 if success, -ERRNO if failure.
*/
static int
init_nandsim(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
struct nandsim *ns = (struct nandsim *)(chip->priv);
int i;
if (NS_IS_INITIALIZED(ns)) {
NS_ERR("init_nandsim: nandsim is already initialized\n");
return -EIO;
}
/* Force mtd to not do delays */
chip->chip_delay = 0;
/* 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.oobsz = mtd->oobsize;
ns->geom.secsz = mtd->erasesize;
ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz;
ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz;
ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz;
ns->geom.secshift = ffs(ns->geom.secsz) - 1;
ns->geom.pgshift = chip->page_shift;
ns->geom.oobshift = ffs(ns->geom.oobsz) - 1;
ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz;
ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
ns->options = 0;
if (ns->geom.pgsz == 256) {
ns->options |= OPT_PAGE256;
}
else if (ns->geom.pgsz == 512) {
ns->options |= (OPT_PAGE512 | OPT_AUTOINCR);
if (ns->busw == 8)
ns->options |= OPT_PAGE512_8BIT;
} else if (ns->geom.pgsz == 2048) {
ns->options |= OPT_PAGE2048;
} else {
NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
return -EIO;
}
if (ns->options & OPT_SMALLPAGE) {
if (ns->geom.totsz < (64 << 20)) {
ns->geom.pgaddrbytes = 3;
ns->geom.secaddrbytes = 2;
} else {
ns->geom.pgaddrbytes = 4;
ns->geom.secaddrbytes = 3;
}
} else {
if (ns->geom.totsz <= (128 << 20)) {
ns->geom.pgaddrbytes = 5;
ns->geom.secaddrbytes = 2;
} else {
ns->geom.pgaddrbytes = 5;
ns->geom.secaddrbytes = 3;
}
}
/* Detect how many ID bytes the NAND chip outputs */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (second_id_byte != nand_flash_ids[i].id)
continue;
if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR))
ns->options |= OPT_AUTOINCR;
}
if (ns->busw == 16)
NS_WARN("16-bit flashes support wasn't tested\n");
printk("flash size: %u MiB\n", ns->geom.totsz >> 20);
printk("page size: %u bytes\n", ns->geom.pgsz);
printk("OOB area size: %u bytes\n", ns->geom.oobsz);
printk("sector size: %u KiB\n", ns->geom.secsz >> 10);
printk("pages number: %u\n", ns->geom.pgnum);
printk("pages per sector: %u\n", ns->geom.pgsec);
printk("bus width: %u\n", ns->busw);
printk("bits in sector size: %u\n", ns->geom.secshift);
printk("bits in page size: %u\n", ns->geom.pgshift);
printk("bits in OOB size: %u\n", ns->geom.oobshift);
printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10);
printk("page address bytes: %u\n", ns->geom.pgaddrbytes);
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
printk("options: %#x\n", ns->options);
/* Map / allocate and initialize the flash image */
#ifdef CONFIG_NS_ABS_POS
ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob);
if (!ns->mem.byte) {
NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n",
(void *)CONFIG_NS_ABS_POS);
return -ENOMEM;
}
#else
ns->mem.byte = vmalloc(ns->geom.totszoob);
if (!ns->mem.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
ns->geom.totszoob);
return -ENOMEM;
}
memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
#endif
/* Allocate / initialize the internal buffer */
ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
if (!ns->buf.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
ns->geom.pgszoob);
goto error;
}
memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
/* Fill the partition_info structure */
ns->part.name = "NAND simulator partition";
ns->part.offset = 0;
ns->part.size = ns->geom.totsz;
return 0;
error:
#ifdef CONFIG_NS_ABS_POS
iounmap(ns->mem.byte);
#else
vfree(ns->mem.byte);
#endif
return -ENOMEM;
}
/*
* Free the nandsim structure.
*/
static void
free_nandsim(struct nandsim *ns)
{
kfree(ns->buf.byte);
#ifdef CONFIG_NS_ABS_POS
iounmap(ns->mem.byte);
#else
vfree(ns->mem.byte);
#endif
return;
}
/*
* Returns the string representation of 'state' state.
*/
static char *
get_state_name(uint32_t state)
{
switch (NS_STATE(state)) {
case STATE_CMD_READ0:
return "STATE_CMD_READ0";
case STATE_CMD_READ1:
return "STATE_CMD_READ1";
case STATE_CMD_PAGEPROG:
return "STATE_CMD_PAGEPROG";
case STATE_CMD_READOOB:
return "STATE_CMD_READOOB";
case STATE_CMD_READ2LP:
return "STATE_CMD_READ2LP";
case STATE_CMD_ERASE1:
return "STATE_CMD_ERASE1";
case STATE_CMD_STATUS:
return "STATE_CMD_STATUS";
case STATE_CMD_STATUS_M:
return "STATE_CMD_STATUS_M";
case STATE_CMD_SEQIN:
return "STATE_CMD_SEQIN";
case STATE_CMD_READID:
return "STATE_CMD_READID";
case STATE_CMD_ERASE2:
return "STATE_CMD_ERASE2";
case STATE_CMD_RESET:
return "STATE_CMD_RESET";
case STATE_ADDR_PAGE:
return "STATE_ADDR_PAGE";
case STATE_ADDR_SEC:
return "STATE_ADDR_SEC";
case STATE_ADDR_ZERO:
return "STATE_ADDR_ZERO";
case STATE_DATAIN:
return "STATE_DATAIN";
case STATE_DATAOUT:
return "STATE_DATAOUT";
case STATE_DATAOUT_ID:
return "STATE_DATAOUT_ID";
case STATE_DATAOUT_STATUS:
return "STATE_DATAOUT_STATUS";
case STATE_DATAOUT_STATUS_M:
return "STATE_DATAOUT_STATUS_M";
case STATE_READY:
return "STATE_READY";
case STATE_UNKNOWN:
return "STATE_UNKNOWN";
}
NS_ERR("get_state_name: unknown state, BUG\n");
return NULL;
}
/*
* Check if command is valid.
*
* RETURNS: 1 if wrong command, 0 if right.
*/
static int
check_command(int cmd)
{
switch (cmd) {
case NAND_CMD_READ0:
case NAND_CMD_READ2LP:
case NAND_CMD_PAGEPROG:
case NAND_CMD_READOOB:
case NAND_CMD_ERASE1:
case NAND_CMD_STATUS:
case NAND_CMD_SEQIN:
case NAND_CMD_READID:
case NAND_CMD_ERASE2:
case NAND_CMD_RESET:
case NAND_CMD_READ1:
return 0;
case NAND_CMD_STATUS_MULTI:
default:
return 1;
}
}
/*
* Returns state after command is accepted by command number.
*/
static uint32_t
get_state_by_command(unsigned command)
{
switch (command) {
case NAND_CMD_READ0:
return STATE_CMD_READ0;
case NAND_CMD_READ1:
return STATE_CMD_READ1;
case NAND_CMD_PAGEPROG:
return STATE_CMD_PAGEPROG;
case NAND_CMD_READ2LP:
return STATE_CMD_READ2LP;
case NAND_CMD_READOOB:
return STATE_CMD_READOOB;
case NAND_CMD_ERASE1:
return STATE_CMD_ERASE1;
case NAND_CMD_STATUS:
return STATE_CMD_STATUS;
case NAND_CMD_STATUS_MULTI:
return STATE_CMD_STATUS_M;
case NAND_CMD_SEQIN:
return STATE_CMD_SEQIN;
case NAND_CMD_READID:
return STATE_CMD_READID;
case NAND_CMD_ERASE2:
return STATE_CMD_ERASE2;
case NAND_CMD_RESET:
return STATE_CMD_RESET;
}
NS_ERR("get_state_by_command: unknown command, BUG\n");
return 0;
}
/*
* Move an address byte to the correspondent internal register.
*/
static inline void
accept_addr_byte(struct nandsim *ns, u_char bt)
{
uint byte = (uint)bt;
if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
ns->regs.column |= (byte << 8 * ns->regs.count);
else {
ns->regs.row |= (byte << 8 * (ns->regs.count -
ns->geom.pgaddrbytes +
ns->geom.secaddrbytes));
}
return;
}
/*
* Switch to STATE_READY state.
*/
static inline void
switch_to_ready_state(struct nandsim *ns, u_char status)
{
NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
ns->state = STATE_READY;
ns->nxstate = STATE_UNKNOWN;
ns->op = NULL;
ns->npstates = 0;
ns->stateidx = 0;
ns->regs.num = 0;
ns->regs.count = 0;
ns->regs.off = 0;
ns->regs.row = 0;
ns->regs.column = 0;
ns->regs.status = status;
}
/*
* If the operation isn't known yet, try to find it in the global array
* of supported operations.
*
* Operation can be unknown because of the following.
* 1. New command was accepted and this is the firs call to find the
* correspondent states chain. In this case ns->npstates = 0;
* 2. There is several operations which begin with the same command(s)
* (for example program from the second half and read from the
* second half operations both begin with the READ1 command). In this
* case the ns->pstates[] array contains previous states.
*
* Thus, the function tries to find operation containing the following
* states (if the 'flag' parameter is 0):
* ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
*
* If (one and only one) matching operation is found, it is accepted (
* ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
* zeroed).
*
* If there are several maches, the current state is pushed to the
* ns->pstates.
*
* The operation can be unknown only while commands are input to the chip.
* As soon as address command is accepted, the operation must be known.
* In such situation the function is called with 'flag' != 0, and the
* operation is searched using the following pattern:
* ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
*
* It is supposed that this pattern must either match one operation on
* none. There can't be ambiguity in that case.
*
* If no matches found, the functions does the following:
* 1. if there are saved states present, try to ignore them and search
* again only using the last command. If nothing was found, switch
* to the STATE_READY state.
* 2. if there are no saved states, switch to the STATE_READY state.
*
* RETURNS: -2 - no matched operations found.
* -1 - several matches.
* 0 - operation is found.
*/
static int
find_operation(struct nandsim *ns, uint32_t flag)
{
int opsfound = 0;
int i, j, idx = 0;
for (i = 0; i < NS_OPER_NUM; i++) {
int found = 1;
if (!(ns->options & ops[i].reqopts))
/* Ignore operations we can't perform */
continue;
if (flag) {
if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
continue;
} else {
if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
continue;
}
for (j = 0; j < ns->npstates; j++)
if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
&& (ns->options & ops[idx].reqopts)) {
found = 0;
break;
}
if (found) {
idx = i;
opsfound += 1;
}
}
if (opsfound == 1) {
/* Exact match */
ns->op = &ops[idx].states[0];
if (flag) {
/*
* In this case the find_operation function was
* called when address has just began input. But it isn't
* yet fully input and the current state must
* not be one of STATE_ADDR_*, but the STATE_ADDR_*
* state must be the next state (ns->nxstate).
*/
ns->stateidx = ns->npstates - 1;
} else {
ns->stateidx = ns->npstates;
}
ns->npstates = 0;
ns->state = ns->op[ns->stateidx];
ns->nxstate = ns->op[ns->stateidx + 1];
NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
idx, get_state_name(ns->state), get_state_name(ns->nxstate));
return 0;
}
if (opsfound == 0) {
/* Nothing was found. Try to ignore previous commands (if any) and search again */
if (ns->npstates != 0) {
NS_DBG("find_operation: no operation found, try again with state %s\n",
get_state_name(ns->state));
ns->npstates = 0;
return find_operation(ns, 0);
}
NS_DBG("find_operation: no operations found\n");
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return -2;
}
if (flag) {
/* This shouldn't happen */
NS_DBG("find_operation: BUG, operation must be known if address is input\n");
return -2;
}
NS_DBG("find_operation: there is still ambiguity\n");
ns->pstates[ns->npstates++] = ns->state;
return -1;
}
/*
* If state has any action bit, perform this action.
*
* RETURNS: 0 if success, -1 if error.
*/
static int
do_state_action(struct nandsim *ns, uint32_t action)
{
int i, num;
int busdiv = ns->busw == 8 ? 1 : 2;
action &= ACTION_MASK;
/* Check that page address input is correct */
if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
return -1;
}
switch (action) {
case ACTION_CPY:
/*
* Copy page data to the internal buffer.
*/
/* Column shouldn't be very large */
if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
NS_ERR("do_state_action: column number is too large\n");
break;
}
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
num, NS_RAW_OFFSET(ns) + ns->regs.off);
if (ns->regs.off == 0)
NS_LOG("read page %d\n", ns->regs.row);
else if (ns->regs.off < ns->geom.pgsz)
NS_LOG("read page %d (second half)\n", ns->regs.row);
else
NS_LOG("read OOB of page %d\n", ns->regs.row);
NS_UDELAY(access_delay);
NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
break;
case ACTION_SECERASE:
/*
* Erase sector.
*/
if (ns->lines.wp) {
NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
return -1;
}
if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
|| (ns->regs.row & ~(ns->geom.secsz - 1))) {
NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
return -1;
}
ns->regs.row = (ns->regs.row <<
8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
ns->regs.column = 0;
NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
ns->regs.row, NS_RAW_OFFSET(ns));
NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
NS_MDELAY(erase_delay);
break;
case ACTION_PRGPAGE:
/*
* Programm page - move internal buffer data to the page.
*/
if (ns->lines.wp) {
NS_WARN("do_state_action: device is write-protected, programm\n");
return -1;
}
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
if (num != ns->regs.count) {
NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
ns->regs.count, num);
return -1;
}
for (i = 0; i < num; i++)
ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
NS_LOG("programm page %d\n", ns->regs.row);
NS_UDELAY(programm_delay);
NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
break;
case ACTION_ZEROOFF:
NS_DBG("do_state_action: set internal offset to 0\n");
ns->regs.off = 0;
break;
case ACTION_HALFOFF:
if (!(ns->options & OPT_PAGE512_8BIT)) {
NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
"byte page size 8x chips\n");
return -1;
}
NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
ns->regs.off = ns->geom.pgsz/2;
break;
case ACTION_OOBOFF:
NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
ns->regs.off = ns->geom.pgsz;
break;
default:
NS_DBG("do_state_action: BUG! unknown action\n");
}
return 0;
}
/*
* Switch simulator's state.
*/
static void
switch_state(struct nandsim *ns)
{
if (ns->op) {
/*
* The current operation have already been identified.
* Just follow the states chain.
*/
ns->stateidx += 1;
ns->state = ns->nxstate;
ns->nxstate = ns->op[ns->stateidx + 1];
NS_DBG("switch_state: operation is known, switch to the next state, "
"state: %s, nxstate: %s\n",
get_state_name(ns->state), get_state_name(ns->nxstate));
/* See, whether we need to do some action */
if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
} else {
/*
* We don't yet know which operation we perform.
* Try to identify it.
*/
/*
* The only event causing the switch_state function to
* be called with yet unknown operation is new command.
*/
ns->state = get_state_by_command(ns->regs.command);
NS_DBG("switch_state: operation is unknown, try to find it\n");
if (find_operation(ns, 0) != 0)
return;
if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
}
/* For 16x devices column means the page offset in words */
if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
NS_DBG("switch_state: double the column number for 16x device\n");
ns->regs.column <<= 1;
}
if (NS_STATE(ns->nxstate) == STATE_READY) {
/*
* The current state is the last. Return to STATE_READY
*/
u_char status = NS_STATUS_OK(ns);
/* In case of data states, see if all bytes were input/output */
if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
&& ns->regs.count != ns->regs.num) {
NS_WARN("switch_state: not all bytes were processed, %d left\n",
ns->regs.num - ns->regs.count);
status = NS_STATUS_FAILED(ns);
}
NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
switch_to_ready_state(ns, status);
return;
} else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
/*
* If the next state is data input/output, switch to it now
*/
ns->state = ns->nxstate;
ns->nxstate = ns->op[++ns->stateidx + 1];
ns->regs.num = ns->regs.count = 0;
NS_DBG("switch_state: the next state is data I/O, switch, "
"state: %s, nxstate: %s\n",
get_state_name(ns->state), get_state_name(ns->nxstate));
/*
* Set the internal register to the count of bytes which
* are expected to be input or output
*/
switch (NS_STATE(ns->state)) {
case STATE_DATAIN:
case STATE_DATAOUT:
ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
break;
case STATE_DATAOUT_ID:
ns->regs.num = ns->geom.idbytes;
break;
case STATE_DATAOUT_STATUS:
case STATE_DATAOUT_STATUS_M:
ns->regs.count = ns->regs.num = 0;
break;
default:
NS_ERR("switch_state: BUG! unknown data state\n");
}
} else if (ns->nxstate & STATE_ADDR_MASK) {
/*
* If the next state is address input, set the internal
* register to the number of expected address bytes
*/
ns->regs.count = 0;
switch (NS_STATE(ns->nxstate)) {
case STATE_ADDR_PAGE:
ns->regs.num = ns->geom.pgaddrbytes;
break;
case STATE_ADDR_SEC:
ns->regs.num = ns->geom.secaddrbytes;
break;
case STATE_ADDR_ZERO:
ns->regs.num = 1;
break;
default:
NS_ERR("switch_state: BUG! unknown address state\n");
}
} else {
/*
* Just reset internal counters.
*/
ns->regs.num = 0;
ns->regs.count = 0;
}
}
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)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
u_char outb = 0x00;
/* Sanity and correctness checks */
if (!ns->lines.ce) {
NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb);
return outb;
}
if (ns->lines.ale || ns->lines.cle) {
NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb);
return outb;
}
if (!(ns->state & STATE_DATAOUT_MASK)) {
NS_WARN("read_byte: unexpected data output cycle, state is %s "
"return %#x\n", get_state_name(ns->state), (uint)outb);
return outb;
}
/* Status register may be read as many times as it is wanted */
if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) {
NS_DBG("read_byte: return %#x status\n", ns->regs.status);
return ns->regs.status;
}
/* Check if there is any data in the internal buffer which may be read */
if (ns->regs.count == ns->regs.num) {
NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb);
return outb;
}
switch (NS_STATE(ns->state)) {
case STATE_DATAOUT:
if (ns->busw == 8) {
outb = ns->buf.byte[ns->regs.count];
ns->regs.count += 1;
} else {
outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]);
ns->regs.count += 2;
}
break;
case STATE_DATAOUT_ID:
NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num);
outb = ns->ids[ns->regs.count];
ns->regs.count += 1;
break;
default:
BUG();
}
if (ns->regs.count == ns->regs.num) {
NS_DBG("read_byte: all bytes were read\n");
/*
* The OPT_AUTOINCR allows to read next conseqitive pages without
* new read operation cycle.
*/
if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
ns->regs.count = 0;
if (ns->regs.row + 1 < ns->geom.pgnum)
ns->regs.row += 1;
NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row);
do_state_action(ns, ACTION_CPY);
}
else if (NS_STATE(ns->nxstate) == STATE_READY)
switch_state(ns);
}
return outb;
}
static void
ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
/* Sanity and correctness checks */
if (!ns->lines.ce) {
NS_ERR("write_byte: chip is disabled, ignore write\n");
return;
}
if (ns->lines.ale && ns->lines.cle) {
NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
return;
}
if (ns->lines.cle == 1) {
/*
* The byte written is a command.
*/
if (byte == NAND_CMD_RESET) {
NS_LOG("reset chip\n");
switch_to_ready_state(ns, NS_STATUS_OK(ns));
return;
}
/*
* Chip might still be in STATE_DATAOUT
* (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or
* STATE_DATAOUT_STATUS_M state. If so, switch state.
*/
if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
|| NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M
|| ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT))
switch_state(ns);
/* Check if chip is expecting command */
if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
/*
* We are in situation when something else (not command)
* was expected but command was input. In this case ignore
* previous command(s)/state(s) and accept the last one.
*/
NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
"ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
}
/* Check that the command byte is correct */
if (check_command(byte)) {
NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
return;
}
NS_DBG("command byte corresponding to %s state accepted\n",
get_state_name(get_state_by_command(byte)));
ns->regs.command = byte;
switch_state(ns);
} else if (ns->lines.ale == 1) {
/*
* The byte written is an address.
*/
if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
NS_DBG("write_byte: operation isn't known yet, identify it\n");
if (find_operation(ns, 1) < 0)
return;
if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
ns->regs.count = 0;
switch (NS_STATE(ns->nxstate)) {
case STATE_ADDR_PAGE:
ns->regs.num = ns->geom.pgaddrbytes;
break;
case STATE_ADDR_SEC:
ns->regs.num = ns->geom.secaddrbytes;
break;
case STATE_ADDR_ZERO:
ns->regs.num = 1;
break;
default:
BUG();
}
}
/* Check that chip is expecting address */
if (!(ns->nxstate & STATE_ADDR_MASK)) {
NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, "
"switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate));
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
/* Check if this is expected byte */
if (ns->regs.count == ns->regs.num) {
NS_ERR("write_byte: no more address bytes expected\n");
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
accept_addr_byte(ns, byte);
ns->regs.count += 1;
NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
(uint)byte, ns->regs.count, ns->regs.num);
if (ns->regs.count == ns->regs.num) {
NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
switch_state(ns);
}
} else {
/*
* The byte written is an input data.
*/
/* Check that chip is expecting data input */
if (!(ns->state & STATE_DATAIN_MASK)) {
NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
"switch to %s\n", (uint)byte,
get_state_name(ns->state), get_state_name(STATE_READY));
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
/* Check if this is expected byte */
if (ns->regs.count == ns->regs.num) {
NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
ns->regs.num);
return;
}
if (ns->busw == 8) {
ns->buf.byte[ns->regs.count] = byte;
ns->regs.count += 1;
} else {
ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
ns->regs.count += 2;
}
}
return;
}
static int
ns_device_ready(struct mtd_info *mtd)
{
NS_DBG("device_ready\n");
return 1;
}
static uint16_t
ns_nand_read_word(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
NS_DBG("read_word\n");
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)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
/* Check that chip is expecting data input */
if (!(ns->state & STATE_DATAIN_MASK)) {
NS_ERR("write_buf: data input isn't expected, state is %s, "
"switch to STATE_READY\n", get_state_name(ns->state));
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
/* Check if these are expected bytes */
if (ns->regs.count + len > ns->regs.num) {
NS_ERR("write_buf: too many input bytes\n");
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
memcpy(ns->buf.byte + ns->regs.count, buf, len);
ns->regs.count += len;
if (ns->regs.count == ns->regs.num) {
NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
}
}
static void
ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
/* Sanity and correctness checks */
if (!ns->lines.ce) {
NS_ERR("read_buf: chip is disabled\n");
return;
}
if (ns->lines.ale || ns->lines.cle) {
NS_ERR("read_buf: ALE or CLE pin is high\n");
return;
}
if (!(ns->state & STATE_DATAOUT_MASK)) {
NS_WARN("read_buf: unexpected data output cycle, current state is %s\n",
get_state_name(ns->state));
return;
}
if (NS_STATE(ns->state) != STATE_DATAOUT) {
int i;
for (i = 0; i < len; i++)
buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd);
return;
}
/* Check if these are expected bytes */
if (ns->regs.count + len > ns->regs.num) {
NS_ERR("read_buf: too many bytes to read\n");
switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
return;
}
memcpy(buf, ns->buf.byte + ns->regs.count, len);
ns->regs.count += len;
if (ns->regs.count == ns->regs.num) {
if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
ns->regs.count = 0;
if (ns->regs.row + 1 < ns->geom.pgnum)
ns->regs.row += 1;
NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row);
do_state_action(ns, ACTION_CPY);
}
else if (NS_STATE(ns->nxstate) == STATE_READY)
switch_state(ns);
}
return;
}
static int
ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
if (!memcmp(buf, &ns_verify_buf[0], len)) {
NS_DBG("verify_buf: the buffer is OK\n");
return 0;
} else {
NS_DBG("verify_buf: the buffer is wrong\n");
return -EFAULT;
}
}
/*
* Having only NAND chip IDs we call nand_scan which detects NAND flash
* parameters and then calls scan_bbt in order to scan/find/build the
* NAND flash bad block table. But since at that moment the NAND flash
* image isn't allocated in the simulator, errors arise. To avoid this
* we redefine the scan_bbt callback and initialize the nandsim structure
* before the flash media scanning.
*/
int ns_scan_bbt(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
struct nandsim *ns = (struct nandsim *)(chip->priv);
int retval;
if (!NS_IS_INITIALIZED(ns))
if ((retval = init_nandsim(mtd)) != 0) {
NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
return retval;
}
if ((retval = nand_default_bbt(mtd)) != 0) {
free_nandsim(ns);
return retval;
}
return 0;
}
/*
* Module initialization function
*/
int __init ns_init_module(void)
{
struct nand_chip *chip;
struct nandsim *nand;
int retval = -ENOMEM;
if (bus_width != 8 && bus_width != 16) {
NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
return -EINVAL;
}
/* Allocate and initialize mtd_info, nand_chip and nandsim structures */
nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
+ sizeof(struct nandsim), GFP_KERNEL);
if (!nsmtd) {
NS_ERR("unable to allocate core structures.\n");
return -ENOMEM;
}
memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) +
sizeof(struct nandsim));
chip = (struct nand_chip *)(nsmtd + 1);
nsmtd->priv = (void *)chip;
nand = (struct nandsim *)(chip + 1);
chip->priv = (void *)nand;
/*
* Register simulator's callbacks.
*/
chip->hwcontrol = ns_hwcontrol;
chip->read_byte = ns_nand_read_byte;
chip->dev_ready = ns_device_ready;
chip->scan_bbt = ns_scan_bbt;
chip->write_byte = ns_nand_write_byte;
chip->write_buf = ns_nand_write_buf;
chip->read_buf = ns_nand_read_buf;
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;
/*
* Perform minimum nandsim structure initialization to handle
* the initial ID read command correctly
*/
if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
nand->geom.idbytes = 4;
else
nand->geom.idbytes = 2;
nand->regs.status = NS_STATUS_OK(nand);
nand->nxstate = STATE_UNKNOWN;
nand->options |= OPT_PAGE256; /* temporary value */
nand->ids[0] = first_id_byte;
nand->ids[1] = second_id_byte;
nand->ids[2] = third_id_byte;
nand->ids[3] = fourth_id_byte;
if (bus_width == 16) {
nand->busw = 16;
chip->options |= NAND_BUSWIDTH_16;
}
if ((retval = nand_scan(nsmtd, 1)) != 0) {
NS_ERR("can't register NAND Simulator\n");
if (retval > 0)
retval = -ENXIO;
goto error;
}
/* Register NAND as one big partition */
add_mtd_partitions(nsmtd, &nand->part, 1);
return 0;
error:
kfree(nsmtd);
return retval;
}
module_init(ns_init_module);
/*
* Module clean-up function
*/
static void __exit ns_cleanup_module(void)
{
struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
free_nandsim(ns); /* Free nandsim private resources */
nand_release(nsmtd); /* Unregisterd drived */
kfree(nsmtd); /* Free other structures */
}
module_exit(ns_cleanup_module);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Artem B. Bityuckiy");
MODULE_DESCRIPTION ("The NAND flash simulator");
......@@ -11,7 +11,7 @@
* 28-Sep-2004 BJD Fixed ECC placement for Hardware mode
* 12-Oct-2004 BJD Fixed errors in use of platform data
*
* $Id: s3c2410.c,v 1.5 2004/10/12 10:10:15 bjd Exp $
* $Id: s3c2410.c,v 1.6 2004/11/24 12:25:48 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
......@@ -167,7 +167,7 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
if (plat != NULL) {
tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
twrph1 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
} else {
/* default timings */
tacls = 8;
......
/*
* drivers/mtd/nand/sharpsl.c
*
* Copyright (C) 2004 Richard Purdie
*
* $Id: sharpsl.c,v 1.2 2004/11/24 20:38:07 rpurdie Exp $
*
* Based on Sharp's NAND driver sharp_sl.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.
*
*/
#include <linux/genhd.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/hardware.h>
static void __iomem *sharpsl_io_base;
static int sharpsl_phys_base = 0x0C000000;
/* register offset */
#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */
#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */
#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */
#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */
#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */
#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */
#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */
/* Flash control bit */
#define FLRYBY (1 << 5)
#define FLCE1 (1 << 4)
#define FLWP (1 << 3)
#define FLALE (1 << 2)
#define FLCLE (1 << 1)
#define FLCE0 (1 << 0)
/*
* MTD structure for SharpSL
*/
static struct mtd_info *sharpsl_mtd = NULL;
/*
* Define partitions for flash device
*/
#define DEFAULT_NUM_PARTITIONS 3
#if defined CONFIG_MACH_POODLE
#define SHARPSL_ROOTFS_SIZE 22
#define SHARPSL_FLASH_SIZE 64
#elif defined CONFIG_MACH_CORGI
#define SHARPSL_ROOTFS_SIZE 25
#define SHARPSL_FLASH_SIZE 32
#elif defined CONFIG_MACH_SHEPHERD
#define SHARPSL_ROOTFS_SIZE 25
#define SHARPSL_FLASH_SIZE 64
#elif defined CONFIG_MACH_HUSKY
#define SHARPSL_ROOTFS_SIZE 53
#define SHARPSL_FLASH_SIZE 128
#elif defined CONFIG_MACH_TOSA
#define SHARPSL_ROOTFS_SIZE 28
#define SHARPSL_FLASH_SIZE 64
#else
#define SHARPSL_ROOTFS_SIZE 30
#define SHARPSL_FLASH_SIZE 64
#endif
static int nr_partitions;
static struct mtd_partition sharpsl_nand_default_partition_info[] = {
{
.name = "NAND flash partition 0",
.offset = 0,
.size = 7 * 1024 * 1024,
},
{
.name = "NAND flash partition 1",
.offset = 7 * 1024 * 1024,
.size = SHARPSL_ROOTFS_SIZE * 1024 * 1024,
},
{
.name = "NAND flash partition 2",
.offset = (SHARPSL_ROOTFS_SIZE+7) * 1024 * 1024,
.size = (SHARPSL_FLASH_SIZE - SHARPSL_ROOTFS_SIZE - 7) * 1024 * 1024,
},
};
/*
* hardware specific access to control-lines
*/
static void
sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd)
{
switch (cmd) {
case NAND_CTL_SETCLE:
writeb(readb(FLASHCTL) | FLCLE, FLASHCTL);
break;
case NAND_CTL_CLRCLE:
writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL);
break;
case NAND_CTL_SETALE:
writeb(readb(FLASHCTL) | FLALE, FLASHCTL);
break;
case NAND_CTL_CLRALE:
writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL);
break;
case NAND_CTL_SETNCE:
writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL);
break;
case NAND_CTL_CLRNCE:
writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL);
break;
}
}
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr sharpsl_bbt = {
.options = 0,
.offs = 4,
.len = 2,
.pattern = scan_ff_pattern
};
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)
{
writeb(0 ,ECCCLRR);
}
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);
ecc_code[2] = (~readb(ECCCP) << 2) | 0x03;
return readb(ECCCNTR) != 0;
}
#ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/*
* Main initialization routine
*/
int __init
sharpsl_nand_init(void)
{
struct nand_chip *this;
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);
if (!sharpsl_mtd) {
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){
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]);
/* Initialize structures */
memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
sharpsl_mtd->priv = this;
/*
* PXA initialize
*/
writeb(readb(FLASHCTL) | FLWP, FLASHCTL);
/* Set address of NAND IO lines */
this->IO_ADDR_R = FLASHIO;
this->IO_ADDR_W = FLASHIO;
/* Set address of hardware control function */
this->hwcontrol = 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->enable_hwecc = sharpsl_nand_enable_hwecc;
this->calculate_ecc = sharpsl_nand_calculate_ecc;
this->correct_data = nand_correct_data;
this->badblock_pattern = &sharpsl_bbt;
/* Scan to find existence of the device */
err=nand_scan(sharpsl_mtd,1);
if (err) {
iounmap(sharpsl_io_base);
kfree(sharpsl_mtd);
return err;
}
/* Register the partitions */
sharpsl_mtd->name = "sharpsl-nand";
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;
}
#ifdef CONFIG_MACH_HUSKY
/* Need to use small eraseblock size for backward compatibility */
sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS;
#endif
add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions);
/* 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];
/* Release resources, unregister device */
nand_release(sharpsl_mtd);
iounmap(sharpsl_io_base);
/* Free the MTD device structure */
kfree(sharpsl_mtd);
}
module_exit(sharpsl_nand_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series");
......@@ -4,7 +4,7 @@
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A.
*
* $Id: nftlmount.c,v 1.39 2004/11/05 22:51:41 kalev Exp $
* $Id: nftlmount.c,v 1.40 2004/11/22 14:38:29 kalev 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
......@@ -31,7 +31,7 @@
#define SECTORSIZE 512
char nftlmountrev[]="$Revision: 1.39 $";
char nftlmountrev[]="$Revision: 1.40 $";
/* 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
......@@ -302,8 +302,6 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
struct nftl_uci1 uci;
struct erase_info *instr = &nftl->instr;
instr->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)
......@@ -320,6 +318,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
memset(instr, 0, sizeof(struct erase_info));
/* XXX: use async erase interface, XXX: test return code */
instr->mtd = nftl->mbd.mtd;
instr->addr = block * nftl->EraseSize;
instr->len = nftl->EraseSize;
MTD_ERASE(nftl->mbd.mtd, instr);
......
/*
* $Id: redboot.c,v 1.15 2004/08/10 07:55:16 dwmw2 Exp $
* $Id: redboot.c,v 1.17 2004/11/22 11:33:56 ijc Exp $
*
* Parse RedBoot-style Flash Image System (FIS) tables and
* produce a Linux partition array to match.
......@@ -30,6 +30,9 @@ struct fis_list {
struct fis_list *next;
};
static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK;
module_param(directory, int, 0);
static inline int redboot_checksum(struct fis_image_desc *img)
{
/* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
......@@ -50,6 +53,8 @@ static int parse_redboot_partitions(struct mtd_info *master,
char *nullname;
int namelen = 0;
int nulllen = 0;
int numslots;
unsigned long offset;
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
static char nullstring[] = "unallocated";
#endif
......@@ -59,8 +64,15 @@ static int parse_redboot_partitions(struct mtd_info *master,
if (!buf)
return -ENOMEM;
/* Read the start of the last erase block */
ret = master->read(master, master->size - master->erasesize,
if ( directory < 0 )
offset = master->size + directory*master->erasesize;
else
offset = directory*master->erasesize;
printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n",
master->name, offset);
ret = master->read(master, offset,
master->erasesize, &retlen, (void *)buf);
if (ret)
......@@ -71,12 +83,16 @@ static int parse_redboot_partitions(struct mtd_info *master,
goto out;
}
/* RedBoot image could appear in any of the first three slots */
for (i = 0; i < 3; i++) {
if (!memcmp(buf[i].name, "RedBoot", 8))
numslots = (master->erasesize / sizeof(struct fis_image_desc));
for (i = 0; i < numslots; i++) {
if (buf[i].name[0] == 0xff) {
i = numslots;
break;
}
if (!memcmp(buf[i].name, "FIS directory", 14))
break;
}
if (i == 3) {
if (i == numslots) {
/* Didn't find it */
printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
master->name);
......@@ -84,7 +100,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
goto out;
}
for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) {
for (i = 0; i < numslots; i++) {
struct fis_list *new_fl, **prev;
if (buf[i].name[0] == 0xff)
......
......@@ -1179,6 +1179,15 @@ config JFFS2_FS_NAND
Say 'N' unless you have NAND flash.
config JFFS2_FS_NOR_ECC
bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)"
depends on JFFS2_FS && EXPERIMENTAL
default n
help
This enables the experimental support for NOR flash with transparent
ECC for JFFS2. This type of flash chip is not common, however it is
available from ST Microelectronics.
config JFFS2_COMPRESSION_OPTIONS
bool "Advanced compression options for JFFS2"
depends on JFFS2_FS
......
#
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
#
# $Id: Makefile.common,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $
# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $
#
obj-$(CONFIG_JFFS2_FS) += jffs2.o
......@@ -12,6 +12,7 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o
jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
$Id: README.Locking,v 1.4 2002/03/08 16:20:06 dwmw2 Exp $
$Id: README.Locking,v 1.9 2004/11/20 10:35:40 dwmw2 Exp $
JFFS2 LOCKING DOCUMENTATION
---------------------------
......@@ -80,10 +80,10 @@ per-eraseblock lists of physical jffs2_raw_node_ref structures, and
(NB) the per-inode list of physical nodes. The latter is a special
case - see below.
As the MTD API permits erase-completion callback functions to be
called from bottom-half (timer) context, and these functions access
the data structures protected by this lock, it must be locked with
spin_lock_bh().
As the MTD API no longer permits erase-completion callback functions
to be called from bottom-half (timer) context (on the basis that nobody
ever actually implemented such a thing), it's now sufficient to use
a simple spin_lock() rather than spin_lock_bh().
Note that the per-inode list of physical nodes (f->nodes) is a special
case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in
......@@ -99,8 +99,27 @@ pointer when the garbage collection thread exits. The code to kill the
GC thread locks it, sends the signal, then unlocks it - while the GC
thread itself locks it, zeroes c->gc_task, then unlocks on the exit path.
node_free_sem
-------------
inocache_lock spinlock
----------------------
This spinlock protects the hashed list (c->inocache_list) of the
in-core jffs2_inode_cache objects (each inode in JFFS2 has the
correspondent jffs2_inode_cache object). So, the inocache_lock
has to be locked while walking the c->inocache_list hash buckets.
Note, the f->sem guarantees that the correspondent jffs2_inode_cache
will not be removed. So, it is allowed to access it without locking
the inocache_lock spinlock.
Ordering constraints:
If both erase_completion_lock and inocache_lock are needed, the
c->erase_completion has to be acquired first.
erase_free_sem
--------------
This semaphore is only used by the erase code which frees obsolete
node references and the jffs2_garbage_collect_deletion_dirent()
......@@ -114,3 +133,16 @@ the jffs2_raw_node_ref structures in question while the garbage
collection code is looking at them.
Suggestions for alternative solutions to this problem would be welcomed.
wbuf_sem
--------
This read/write semaphore protects against concurrent access to the
write-behind buffer ('wbuf') used for flash chips where we must write
in blocks. It protects both the contents of the wbuf and the metadata
which indicates which flash region (if any) is currently covered by
the buffer.
Ordering constraints:
Lock wbuf_sem last, after the alloc_sem or and f->sem.
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $
* $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $
*
*/
......
......@@ -3,17 +3,19 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $
* $Id: build.c,v 1.68 2004/11/27 13:38:10 gleixner Exp $
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mtd/mtd.h>
#include "nodelist.h"
static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
......@@ -62,6 +64,7 @@ static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2
if (!child_ic) {
printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
fd->name, fd->ino, ic->ino);
jffs2_mark_node_obsolete(c, fd->raw);
continue;
}
......@@ -88,6 +91,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
int ret;
int i;
struct jffs2_inode_cache *ic;
struct jffs2_full_dirent *fd;
struct jffs2_full_dirent *dead_fds = NULL;
/* First, scan the medium and build all the inode caches with
......@@ -95,13 +99,11 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
c->flags |= JFFS2_SB_FLAG_MOUNTING;
ret = jffs2_scan_medium(c);
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
if (ret)
return ret;
goto exit;
D1(printk(KERN_DEBUG "Scanned flash completely\n"));
D1(jffs2_dump_block_lists(c));
D2(jffs2_dump_block_lists(c));
/* Now scan the directory tree, increasing nlink according to every dirent found. */
for_each_inode(i, c, ic) {
......@@ -114,6 +116,8 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
cond_resched();
}
}
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
/* Next, scan for inodes with nlink == 0 and remove them. If
......@@ -135,9 +139,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
D1(printk(KERN_DEBUG "Pass 2a starting\n"));
while (dead_fds) {
struct jffs2_inode_cache *ic;
struct jffs2_full_dirent *fd = dead_fds;
fd = dead_fds;
dead_fds = fd->next;
ic = jffs2_get_ino_cache(c, fd->ino);
......@@ -152,7 +154,6 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
/* Finally, we can scan again and free the dirent structs */
for_each_inode(i, c, ic) {
struct jffs2_full_dirent *fd;
D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
while(ic->scan_dents) {
......@@ -164,11 +165,24 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
cond_resched();
}
D1(printk(KERN_DEBUG "Pass 3 complete\n"));
D1(jffs2_dump_block_lists(c));
D2(jffs2_dump_block_lists(c));
/* Rotate the lists by some number to ensure wear levelling */
jffs2_rotate_lists(c);
ret = 0;
exit:
if (ret) {
for_each_inode(i, c, ic) {
while(ic->scan_dents) {
fd = ic->scan_dents;
ic->scan_dents = fd->next;
jffs2_free_full_dirent(fd);
}
}
}
return ret;
}
......@@ -179,9 +193,12 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jf
D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
raw = ic->nodes;
while (raw != (void *)ic) {
struct jffs2_raw_node_ref *next = raw->next_in_ino;
D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
jffs2_mark_node_obsolete(c, raw);
raw = next;
}
if (ic->scan_dents) {
......@@ -297,6 +314,9 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
c->free_size = c->flash_size;
c->nr_blocks = c->flash_size / c->sector_size;
if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks);
else
c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
if (!c->blocks)
return -ENOMEM;
......@@ -310,6 +330,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
c->blocks[i].used_size = 0;
c->blocks[i].first_node = NULL;
c->blocks[i].last_node = NULL;
c->blocks[i].bad_count = 0;
}
init_MUTEX(&c->alloc_sem);
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $
* $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: dir.c,v 1.83 2004/10/19 07:48:44 havasi Exp $
* $Id: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: erase.c,v 1.61 2004/10/20 23:59:49 dwmw2 Exp $
* $Id: erase.c,v 1.66 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......@@ -43,6 +43,7 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
jffs2_erase_succeeded(c, jeb);
return;
}
bad_offset = jeb->offset;
#else /* Linux */
struct erase_info *instr;
......@@ -386,6 +387,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jeb->dirty_size = 0;
jeb->wasted_size = 0;
} else {
struct kvec vecs[1];
struct jffs2_unknown_node marker = {
.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK),
.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
......@@ -394,8 +396,10 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
/* We only write the header; the rest was noise or padding anyway */
ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
vecs[0].iov_base = (unsigned char *) &marker;
vecs[0].iov_len = sizeof(marker);
ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
if (ret) {
printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
jeb->offset, ret);
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $
* $Id: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $
* $Id: fs.c,v 1.50 2004/11/23 15:37:31 gleixner Exp $
*
*/
......@@ -202,7 +202,7 @@ int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
D1(jffs2_dump_block_lists(c));
D2(jffs2_dump_block_lists(c));
spin_unlock(&c->erase_completion_lock);
......@@ -463,10 +463,12 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
*/
c->sector_size = c->mtd->erasesize;
blocks = c->flash_size / c->sector_size;
if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
blocks >>= 1;
c->sector_size <<= 1;
}
}
/*
* Size alignment check
......@@ -533,6 +535,9 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
out_nodes:
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
vfree(c->blocks);
else
kfree(c->blocks);
out_inohash:
kfree(c->inocache_list);
......@@ -649,6 +654,11 @@ int jffs2_flash_setup(struct jffs2_sb_info *c) {
}
/* add setups for other bizarre flashes here... */
if (jffs2_nor_ecc(c)) {
ret = jffs2_nor_ecc_flash_setup(c);
if (ret)
return ret;
}
return ret;
}
......@@ -659,4 +669,7 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
}
/* add cleanups for other bizarre flashes here... */
if (jffs2_nor_ecc(c)) {
jffs2_nor_ecc_flash_cleanup(c);
}
}
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: gc.c,v 1.140 2004/11/13 10:59:22 dedekind Exp $
* $Id: gc.c,v 1.143 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......@@ -103,7 +103,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
ret->wasted_size = 0;
}
D1(jffs2_dump_block_lists(c));
D2(jffs2_dump_block_lists(c));
return ret;
}
......@@ -134,7 +134,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (c->checked_ino > c->highest_ino) {
printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
D1(jffs2_dump_block_lists(c));
D2(jffs2_dump_block_lists(c));
spin_unlock(&c->erase_completion_lock);
BUG();
}
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $
* $Id: ioctl.c,v 1.9 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $
* $Id: malloc.c,v 1.28 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.c,v 1.87 2004/11/14 17:07:07 dedekind Exp $
* $Id: nodelist.c,v 1.88 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodelist.h,v 1.121 2004/11/14 17:07:07 dedekind Exp $
* $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $
*
*/
......@@ -107,16 +107,6 @@ 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)
/*
Used for keeping track of deletion nodes &c, which can only be marked
as obsolete when the node which they mark as deleted has actually been
removed from the flash.
*/
struct jffs2_raw_node_ref_list {
struct jffs2_raw_node_ref *rew;
struct jffs2_raw_node_ref_list *next;
};
/* 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
......@@ -148,13 +138,6 @@ struct jffs2_inode_cache {
#define INOCACHE_HASHSIZE 128
struct jffs2_scan_info {
struct jffs2_full_dirent *dents;
struct jffs2_tmp_dnode_info *tmpnodes;
/* Latest i_size info */
uint32_t version;
uint32_t isize;
};
/*
Larger representation of a raw node, kept in-core only when the
struct inode for this particular ino is instantiated.
......@@ -163,12 +146,11 @@ struct jffs2_scan_info {
struct jffs2_full_dnode
{
struct jffs2_raw_node_ref *raw;
uint32_t ofs; /* Don't really need this, but optimisation */
uint32_t ofs; /* The offset to which the data of this node belongs */
uint32_t size;
uint32_t frags; /* Number of fragments which currently refer
to this node. When this reaches zero,
the node is obsolete.
*/
the node is obsolete. */
};
/*
......@@ -193,6 +175,7 @@ struct jffs2_full_dirent
unsigned char type;
unsigned char name[0];
};
/*
Fragments - used to build a map of which raw node to obtain
data from for each part of the ino
......@@ -202,7 +185,7 @@ struct jffs2_node_frag
struct rb_node rb;
struct jffs2_full_dnode *node; /* NULL for holes */
uint32_t size;
uint32_t ofs; /* Don't really need this, but optimisation */
uint32_t ofs; /* The offset to which this fragment belongs */
};
struct jffs2_eraseblock
......@@ -221,14 +204,6 @@ struct jffs2_eraseblock
struct jffs2_raw_node_ref *last_node;
struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */
/* For deletia. When a dirent node in this eraseblock is
deleted by a node elsewhere, that other node can only
be marked as obsolete when this block is actually erased.
So we keep a list of the nodes to mark as obsolete when
the erase is completed.
*/
// MAYBE struct jffs2_raw_node_ref_list *deletia;
};
#define ACCT_SANITY_CHECK(c, jeb) do { \
......@@ -396,7 +371,7 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
#define frag_erase(frag, list) rb_erase(&frag->rb, list);
/* nodelist.c */
D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
D2(void jffs2_print_frag_list(struct jffs2_inode_info *f));
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: nodemgmt.c,v 1.109 2004/10/07 15:08:47 havasi Exp $
* $Id: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $
*
*/
......@@ -399,6 +399,17 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
}
jeb = &c->blocks[blocknr];
if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
!(c->flags & JFFS2_SB_FLAG_MOUNTING)) {
/* Hm. This may confuse static lock analysis. If any of the above
three conditions is false, we're going to return from this
function without actually obliterating any nodes or freeing
any jffs2_raw_node_refs. So we don't need to stop erases from
happening, or protect against people holding an obsolete
jffs2_raw_node_ref without the erase_completion_lock. */
down(&c->erase_free_sem);
}
spin_lock(&c->erase_completion_lock);
if (ref_flags(ref) == REF_UNCHECKED) {
......@@ -463,6 +474,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
marked obsolete on the flash at the time they _became_
obsolete, there was probably a reason for that. */
spin_unlock(&c->erase_completion_lock);
/* We didn't lock the erase_free_sem */
return;
}
......@@ -515,61 +527,87 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
spin_unlock(&c->erase_completion_lock);
if (!jffs2_can_mark_obsolete(c))
return;
if (jffs2_is_readonly(c))
if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) {
/* We didn't lock the erase_free_sem */
return;
}
/* 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. */
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);
if (ret) {
printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
return;
goto out_erase_sem;
}
if (retlen != sizeof(n)) {
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
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));
return;
goto out_erase_sem;
}
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype)));
return;
goto out_erase_sem;
}
/* XXX FIXME: This is ugly now */
n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE);
ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
if (ret) {
printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
return;
goto out_erase_sem;
}
if (retlen != sizeof(n)) {
printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
return;
goto out_erase_sem;
}
/* Nodes which have been marked obsolete no longer need to be
associated with any inode. Remove them from the per-inode list */
associated with any inode. Remove them from the per-inode list.
Note we can't do this for NAND at the moment because we need
obsolete dirent nodes to stay on the lists, because of the
horridness in jffs2_garbage_collect_deletion_dirent(). Also
because we delete the inocache, and on NAND we need that to
stay around until all the nodes are actually erased, in order
to stop us from giving the same inode number to another newly
created inode. */
if (ref->next_in_ino) {
struct jffs2_inode_cache *ic;
struct jffs2_raw_node_ref **p;
spin_lock(&c->erase_completion_lock);
ic = jffs2_raw_ref_to_ic(ref);
for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
;
*p = ref->next_in_ino;
ref->next_in_ino = NULL;
if (ic->nodes == (void *)ic) {
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
jffs2_del_ino_cache(c, ic);
jffs2_free_inode_cache(ic);
}
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. */
if (ref->next_phys && ref_obsolete(ref->next_phys) ) {
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;
......@@ -577,7 +615,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
/* gc will be happy continuing gc on this node */
jeb->gc_node=ref;
}
BUG_ON(n->next_in_ino);
spin_unlock(&c->erase_completion_lock);
jffs2_free_raw_node_ref(n);
}
......@@ -586,10 +625,12 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
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) ) {
if (ref_obsolete(p) && !ref->next_in_ino) {
p->__totlen += ref->__totlen;
if (jeb->last_node == ref) {
jeb->last_node = p;
......@@ -601,10 +642,13 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
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);
}
#if CONFIG_JFFS2_FS_DEBUG > 0
#if CONFIG_JFFS2_FS_DEBUG >= 2
void jffs2_dump_block_lists(struct jffs2_sb_info *c)
{
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2002-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $
* $Id: os-linux.h,v 1.51 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......@@ -99,7 +99,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
#ifndef CONFIG_JFFS2_FS_NAND
#if (!defined CONFIG_JFFS2_FS_NAND && !defined CONFIG_JFFS2_FS_NOR_ECC)
#define jffs2_can_mark_obsolete(c) (1)
#define jffs2_cleanmarker_oob(c) (0)
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
......@@ -115,10 +115,13 @@ 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_nor_ecc_flash_setup(c) (0)
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
#else /* NAND support present */
#else /* NAND and/or ECC'd NOR support present */
#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM)
#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf))
......@@ -135,8 +138,19 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
void jffs2_wbuf_timeout(unsigned long data);
void jffs2_wbuf_process(void *data);
int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
#ifdef CONFIG_JFFS2_FS_NOR_ECC
#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
#else
#define jffs2_nor_ecc(c) (0)
#define jffs2_nor_ecc_flash_setup(c) (0)
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
#endif /* NOR ECC */
#endif /* NAND */
/* erase.c */
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $
* $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $
* $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $
*
*/
......@@ -174,7 +174,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
if (frag) {
D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset));
holesize = min(holesize, frag->ofs - offset);
D1(jffs2_print_frag_list(f));
D2(jffs2_print_frag_list(f));
}
D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
memset(buf, 0, holesize);
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: readinode.c,v 1.114 2004/11/14 17:07:07 dedekind Exp $
* $Id: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $
*
*/
......@@ -22,7 +22,7 @@
static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
#if CONFIG_JFFS2_FS_DEBUG >= 1
#if CONFIG_JFFS2_FS_DEBUG >= 2
static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
{
struct jffs2_node_frag *this = frag_first(list);
......@@ -56,7 +56,9 @@ void jffs2_print_frag_list(struct jffs2_inode_info *f)
printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
}
}
#endif
#if CONFIG_JFFS2_FS_DEBUG >= 1
static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
{
struct jffs2_node_frag *frag;
......@@ -225,7 +227,7 @@ static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *l
If so, both 'this' and the new node get marked REF_NORMAL so
the GC can take a look.
*/
if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) {
if (this->node)
mark_ref_normal(this->node->raw);
mark_ref_normal(newfrag->node->raw);
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: scan.c,v 1.112 2004/09/12 09:56:13 gleixner Exp $
* $Id: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $
*
*/
#include <linux/kernel.h>
......@@ -68,7 +68,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
static inline int min_free(struct jffs2_sb_info *c)
{
uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
#ifdef CONFIG_JFFS2_FS_NAND
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
return c->wbuf_pagesize;
#endif
......@@ -160,11 +160,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
case BLK_STATE_PARTDIRTY:
/* Some data, but not full. Dirty list. */
/* Except that we want to remember the block with most free space,
and stick it in the 'nextblock' position to start writing to it.
Later when we do snapshots, this must be the most recent block,
not the one with most free space.
*/
/* We want to remember the block with most free space
and stick it in the 'nextblock' position to start writing to it. */
if (jeb->free_size > min_free(c) &&
(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
/* Better candidate for the next writes to go to */
......@@ -223,7 +220,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
c->dirty_size -= c->nextblock->dirty_size;
c->nextblock->dirty_size = 0;
}
#ifdef CONFIG_JFFS2_FS_NAND
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
/* If we're going to start writing into a block which already
contains data, and the end of the data isn't page-aligned,
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: super.c,v 1.102 2004/11/12 02:42:17 tpoynor Exp $
* $Id: super.c,v 1.104 2004/11/23 15:37:31 gleixner Exp $
*
*/
......@@ -277,6 +277,9 @@ static void jffs2_put_super (struct super_block *sb)
up(&c->alloc_sem);
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
vfree(c->blocks);
else
kfree(c->blocks);
jffs2_flash_cleanup(c);
kfree(c->inocache_list);
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $
* $Id: symlink.c,v 1.14 2004/11/16 20:36:12 dwmw2 Exp $
*
*/
......
......@@ -4,12 +4,12 @@
* Copyright (C) 2001-2003 Red Hat, Inc.
* Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
* Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: wbuf.c,v 1.72 2004/09/11 19:22:43 gleixner Exp $
* $Id: wbuf.c,v 1.82 2004/11/20 22:08:31 dwmw2 Exp $
*
*/
......@@ -130,22 +130,8 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
}
}
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */
static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
struct jffs2_eraseblock *jeb, *new_jeb;
struct jffs2_raw_node_ref **first_raw, **raw;
size_t retlen;
int ret;
unsigned char *buf;
uint32_t start, end, ofs, len;
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
D1(printk("About to refile bad block at %08x\n", jeb->offset));
D2(jffs2_dump_block_lists(c));
......@@ -175,6 +161,25 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
ACCT_SANITY_CHECK(c,jeb);
D1(ACCT_PARANOIA_CHECK(jeb));
}
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */
static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
{
struct jffs2_eraseblock *jeb, *new_jeb;
struct jffs2_raw_node_ref **first_raw, **raw;
size_t retlen;
int ret;
unsigned char *buf;
uint32_t start, end, ofs, len;
spin_lock(&c->erase_completion_lock);
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
jffs2_block_refile(c, jeb);
/* Find the first node to be recovered, by skipping over every
node which ends before the wbuf starts, or which is obsolete. */
......@@ -224,7 +229,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
}
/* Do the read... */
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
else
ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) {
/* ECC recovered */
ret = 0;
......@@ -281,8 +290,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
ret = -EIO;
} else
#endif
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
buf, NULL, c->oobinfo);
else
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, buf);
if (ret || retlen != towrite) {
/* Argh. We tried. Really we did. */
......@@ -392,6 +404,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
1: Pad, do not adjust nextblock free_size
2: Pad, adjust nextblock free_size
*/
#define NOPAD 0
#define PAD_NOACCOUNT 1
#define PAD_ACCOUNTING 2
static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
{
int ret;
......@@ -420,15 +436,16 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
if (pad) {
c->wbuf_len = PAD(c->wbuf_len);
/* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR
with 8 byte page size */
memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) {
struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len);
padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING);
padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len);
padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4));
} else {
/* Pad with JFFS2_DIRTY_BITMASK */
memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len);
}
}
/* else jffs2_flash_writev has actually filled in the rest of the
......@@ -444,8 +461,11 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
ret = -EIO;
} else
#endif
ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
else
ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
if (ret || retlen != c->wbuf_pagesize) {
if (ret)
......@@ -525,7 +545,9 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
if (c->unchecked_size) {
/* GC won't make any progress for a while */
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
ret = __jffs2_flush_wbuf(c, 2);
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
up_write(&c->wbuf_sem);
} else while (old_wbuf_len &&
old_wbuf_ofs == c->wbuf_ofs) {
......@@ -537,7 +559,9 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
if (ret) {
/* GC failed. Flush it with padding instead */
down(&c->alloc_sem);
ret = __jffs2_flush_wbuf(c, 2);
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
up_write(&c->wbuf_sem);
break;
}
down(&c->alloc_sem);
......@@ -552,9 +576,14 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
/* Pad write-buffer to end and write it, wasting space. */
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
{
return __jffs2_flush_wbuf(c, 1);
}
int ret;
down_write(&c->wbuf_sem);
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
up_write(&c->wbuf_sem);
return ret;
}
#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
......@@ -575,6 +604,8 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
if (!c->wbuf)
return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
down_write(&c->wbuf_sem);
/* If wbuf_ofs is not initialized, set it to target address */
if (c->wbuf_ofs == 0xFFFFFFFF) {
c->wbuf_ofs = PAGE_DIV(to);
......@@ -582,6 +613,17 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
memset(c->wbuf,0xff,c->wbuf_pagesize);
}
/* Fixup the wbuf if we are moving to a new eraseblock. The checks below
fail for ECC'd NOR because cleanmarker == 16, so a block starts at
xxx0010. */
if (jffs2_nor_ecc(c)) {
if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
c->wbuf_ofs = PAGE_DIV(to);
c->wbuf_len = PAGE_MOD(to);
memset(c->wbuf,0xff,c->wbuf_pagesize);
}
}
/* Sanity checks on target address.
It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs),
and it's permitted to write at the beginning of a new
......@@ -592,12 +634,12 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
/* It's a write to a new block */
if (c->wbuf_len) {
D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
ret = jffs2_flush_wbuf_pad(c);
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
if (ret) {
/* the underlying layer has to check wbuf_len to do the cleanup */
D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
*retlen = 0;
return ret;
goto exit;
}
}
/* set pointer to new block */
......@@ -623,7 +665,6 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
invec = 0;
outvec = 0;
/* Fill writebuffer first, if already in use */
if (c->wbuf_len) {
uint32_t invec_ofs = 0;
......@@ -658,14 +699,14 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
}
/* write buffer is full, flush buffer */
ret = __jffs2_flush_wbuf(c, 0);
ret = __jffs2_flush_wbuf(c, NOPAD);
if (ret) {
/* the underlying layer has to check wbuf_len to do the cleanup */
D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret));
/* Retlen zero to make sure our caller doesn't mark the space dirty.
We've already done everything that's necessary */
*retlen = 0;
return ret;
goto exit;
}
outvec_to += donelen;
c->wbuf_ofs = outvec_to;
......@@ -709,19 +750,22 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
if (splitvec != -1) {
uint32_t remainder;
int ret;
remainder = outvecs[splitvec].iov_len - split_ofs;
outvecs[splitvec].iov_len = split_ofs;
/* We did cross a page boundary, so we write some now */
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo);
else
ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
/* At this point we have no problem,
c->wbuf is empty.
*/
*retlen = donelen;
return ret;
goto exit;
}
donelen += wbuf_retlen;
......@@ -760,7 +804,11 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
if (c->wbuf_len && ino)
jffs2_wbuf_dirties_inode(c, ino);
return 0;
ret = 0;
exit:
up_write(&c->wbuf_sem);
return ret;
}
/*
......@@ -789,7 +837,12 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
/* Read flash */
if (!jffs2_can_mark_obsolete(c)) {
down_read(&c->wbuf_sem);
if (jffs2_cleanmarker_oob(c))
ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
else
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
if ( (ret == -EBADMSG) && (*retlen == len) ) {
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
......@@ -811,23 +864,23 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
/* if no writebuffer available or write buffer empty, return */
if (!c->wbuf_pagesize || !c->wbuf_len)
return ret;
goto exit;
/* if we read in a different block, return */
if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) )
return ret;
goto exit;
if (ofs >= c->wbuf_ofs) {
owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */
if (owbf > c->wbuf_len) /* is read beyond write buffer ? */
return ret;
goto exit;
lwbf = c->wbuf_len - owbf; /* number of bytes to copy */
if (lwbf > len)
lwbf = len;
} else {
orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */
if (orbf > len) /* is write beyond write buffer ? */
return ret;
goto exit;
lwbf = len - orbf; /* number of bytes to copy */
if (lwbf > c->wbuf_len)
lwbf = c->wbuf_len;
......@@ -835,6 +888,8 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
if (lwbf > 0)
memcpy(buf+orbf,c->wbuf+owbf,lwbf);
exit:
up_read(&c->wbuf_sem);
return ret;
}
......@@ -1079,10 +1134,10 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
int res;
/* Initialise write buffer */
init_rwsem(&c->wbuf_sem);
c->wbuf_pagesize = c->mtd->oobblock;
c->wbuf_ofs = 0xFFFFFFFF;
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
return -ENOMEM;
......@@ -1105,3 +1160,25 @@ void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
kfree(c->wbuf);
}
#ifdef CONFIG_JFFS2_FS_NOR_ECC
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
/* Cleanmarker is actually larger on the flashes */
c->cleanmarker_size = 16;
/* Initialize write buffer */
init_rwsem(&c->wbuf_sem);
c->wbuf_pagesize = c->mtd->eccsize;
c->wbuf_ofs = 0xFFFFFFFF;
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf)
return -ENOMEM;
return 0;
}
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
kfree(c->wbuf);
}
#endif
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: write.c,v 1.86 2004/11/13 10:44:26 dedekind Exp $
* $Id: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $
*
*/
......
......@@ -3,11 +3,11 @@
*
* Copyright (C) 2001, 2002 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
* $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $
* $Id: writev.c,v 1.6 2004/11/16 20:36:12 dwmw2 Exp $
*
*/
......
......@@ -3,12 +3,12 @@
*
* Copyright (C) 2001-2003 Red Hat, Inc.
*
* Created by David Woodhouse <dwmw2@redhat.com>
* Created by David Woodhouse <dwmw2@infradead.org>
*
* For licensing information, see the file 'LICENCE' in the
* jffs2 directory.
*
* $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $
* $Id: jffs2.h,v 1.34 2004/11/16 20:36:14 dwmw2 Exp $
*
*/
......
/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */
/* $Id: jffs2_fs_sb.h,v 1.48 2004/11/20 10:41:12 dwmw2 Exp $ */
#ifndef _JFFS2_FS_SB
#define _JFFS2_FS_SB
......@@ -11,6 +11,7 @@
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/rwsem.h>
#define JFFS2_SB_FLAG_RO 1
#define JFFS2_SB_FLAG_MOUNTING 2
......@@ -35,9 +36,7 @@ struct jffs2_sb_info {
struct semaphore alloc_sem; /* Used to protect all the following
fields, and also to protect against
out-of-order writing of nodes.
And GC.
*/
out-of-order writing of nodes. And GC. */
uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER
(i.e. zero for OOB CLEANMARKER */
......@@ -95,7 +94,7 @@ struct jffs2_sb_info {
to an obsoleted node. I don't like this. Alternatives welcomed. */
struct semaphore erase_free_sem;
#ifdef CONFIG_JFFS2_FS_NAND
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
/* Write-behind buffer for NAND flash */
unsigned char *wbuf;
uint32_t wbuf_ofs;
......@@ -103,6 +102,8 @@ struct jffs2_sb_info {
uint32_t wbuf_pagesize;
struct jffs2_inodirty *wbuf_inodes;
struct rw_semaphore wbuf_sem; /* Protects the write buffer */
/* Information about out-of-band area usage... */
struct nand_oobinfo *oobinfo;
uint32_t badblock_pos;
......
/* Common Flash Interface structures
* See http://support.intel.com/design/flash/technote/index.htm
* $Id: cfi.h,v 1.49 2004/11/15 20:56:32 nico Exp $
* $Id: cfi.h,v 1.50 2004/11/20 12:46:51 dwmw2 Exp $
*/
#ifndef __MTD_CFI_H__
......@@ -349,14 +349,12 @@ static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
static inline void cfi_udelay(int us)
{
unsigned long t = us * HZ / 1000000;
if (t) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(t);
return;
}
if (us >= 1000) {
msleep((us+999)/1000);
} else {
udelay(us);
cond_resched();
}
}
static inline void cfi_spin_lock(spinlock_t *mutex)
......
......@@ -6,7 +6,7 @@
*
* (C) 2000 Red Hat. GPLd.
*
* $Id: flashchip.h,v 1.14 2004/06/15 16:44:59 nico Exp $
* $Id: flashchip.h,v 1.15 2004/11/05 22:41:06 nico Exp $
*
*/
......@@ -37,6 +37,8 @@ typedef enum {
FL_LOCKING,
FL_UNLOCKING,
FL_POINT,
FL_XIP_WHILE_ERASING,
FL_XIP_WHILE_WRITING,
FL_UNKNOWN
} flstate_t;
......
......@@ -5,7 +5,7 @@
* Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de>
*
* $Id: nand.h,v 1.66 2004/10/02 10:07:08 gleixner Exp $
* $Id: nand.h,v 1.68 2004/11/12 10:40:37 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -138,6 +138,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
#define NAND_ECC_HW6_512 4
/* Hardware ECC 8 byte ECC per 512 Byte data */
#define NAND_ECC_HW8_512 6
/* Hardware ECC 12 byte ECC per 2048 Byte data */
#define NAND_ECC_HW12_2048 7
/*
* Constants for Hardware ECC
......@@ -253,6 +255,7 @@ struct nand_hw_control {
* @scan_bbt: [REPLACEABLE] function to scan bad block table
* @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
* @eccsize: [INTERN] databytes used per ecc-calculation
* @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step
* @eccsteps: [INTERN] number of ecc calculation steps per page
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
* @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip
......@@ -277,6 +280,7 @@ struct nand_hw_control {
* @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
* @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
* @priv: [OPTIONAL] pointer to private chip date
*/
......@@ -307,6 +311,7 @@ struct nand_chip {
int (*scan_bbt)(struct mtd_info *mtd);
int eccmode;
int eccsize;
int eccbytes;
int eccsteps;
int chip_delay;
spinlock_t chip_lock;
......@@ -330,6 +335,7 @@ struct nand_chip {
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
struct nand_hw_control *controller;
void *priv;
};
......
/*
* MTD primitives for XIP support
*
* Author: Nicolas Pitre
* Created: Nov 2, 2004
* Copyright: (C) 2004 MontaVista Software, Inc.
*
* This XIP support for MTD has been loosely inspired
* by an earlier patch authored by David Woodhouse.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* $Id: xip.h,v 1.1 2004/11/05 22:41:06 nico Exp $
*/
#ifndef __LINUX_MTD_XIP_H__
#define __LINUX_MTD_XIP_H__
#include <linux/config.h>
#ifdef CONFIG_MTD_XIP
/*
* Function that are modifying the flash state away from array mode must
* obviously not be running from flash. The __xipram is therefore marking
* those functions so they get relocated to ram.
*/
#define __xipram __attribute__ ((__section__ (".data")))
/*
* We really don't want gcc to guess anything.
* We absolutely _need_ proper inlining.
*/
#include <linux/compiler.h>
/*
* Each architecture has to provide the following macros. They must access
* the hardware directly and not rely on any other (XIP) functions since they
* won't be available when used (flash not in array mode).
*
* xip_irqpending()
*
* return non zero when any hardware interrupt is pending.
*
* xip_currtime()
*
* return a platform specific time reference to be used with
* xip_elapsed_since().
*
* xip_elapsed_since(x)
*
* return in usecs the elapsed timebetween now and the reference x as
* returned by xip_currtime().
*
* note 1: convertion to usec can be approximated, as long as the
* returned value is <= the real elapsed time.
* note 2: this should be able to cope with a few seconds without
* overflowing.
*/
#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
#include <asm/hardware.h>
#ifdef CONFIG_ARCH_PXA
#include <asm/arch/pxa-regs.h>
#endif
#define xip_irqpending() (ICIP & ICMR)
/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
#define xip_currtime() (OSCR)
#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4)
#else
#error "missing IRQ and timer primitives for XIP MTD support"
#endif
/*
* xip_cpu_idle() is used when waiting for a delay equal or larger than
* the system timer tick period. This should put the CPU into idle mode
* to save power and to be woken up only when some interrupts are pending.
* As above, this should not rely upon standard kernel code.
*/
#if defined(CONFIG_CPU_XSCALE)
#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
#else
#define xip_cpu_idle() do { } while (0)
#endif
#else
#define __xipram
#endif /* CONFIG_MTD_XIP */
#endif /* __LINUX_MTD_XIP_H__ */
/*
* $Id: mtd-abi.h,v 1.6 2004/08/09 13:38:30 dwmw2 Exp $
* $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $
*
* Portions of MTD ABI definition which are shared by kernel and user space
*/
......@@ -40,6 +40,7 @@ struct mtd_oob_buf {
#define MTD_XIP 32 // eXecute-In-Place possible
#define MTD_OOB 64 // Out-of-band data (NAND flash)
#define MTD_ECC 128 // Device capable of automatic ECC
#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed
// Some common devices / combinations of capabilities
#define MTD_CAP_ROM 0
......
......@@ -8,7 +8,7 @@
*
* Derived from the taskqueue/keventd code by:
*
* David Woodhouse <dwmw2@redhat.com>
* David Woodhouse <dwmw2@infradead.org>
* Andrew Morton <andrewm@uow.edu.au>
* Kai Petzke <wpp@marie.physik.tu-berlin.de>
* Theodore Ts'o <tytso@mit.edu>
......
......@@ -9,7 +9,7 @@
* Reed Solomon code lifted from reed solomon library written by Phil Karn
* Copyright 2002 Phil Karn, KA9Q
*
* $Id: rslib.c,v 1.4 2004/10/05 22:07:53 gleixner Exp $
* $Id: rslib.c,v 1.5 2004/10/22 15:41:47 gleixner Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -22,19 +22,19 @@
* Each user must call init_rs to get a pointer to a rs_control
* structure for the given rs parameters. This structure is either
* generated or a already available matching control structure is used.
* If a structure is generated then the polynominal arrays for
* If a structure is generated then the polynomial arrays for
* fast encoding / decoding are built. This can take some time so
* make sure not to call this function from a timecritical path.
* Usually a module / driver should initialize the neccecary
* make sure not to call this function from a time critical path.
* Usually a module / driver should initialize the necessary
* rs_control structure on module / driver init and release it
* on exit.
* The encoding puts the calculated syndrome into a given syndrom
* The encoding puts the calculated syndrome into a given syndrome
* buffer.
* The decoding is a two step process. The first step calculates
* the syndrome over the received (data + syndrom) and calls the
* the syndrome over the received (data + syndrome) and calls the
* second stage, which does the decoding / error correction itself.
* Many hw encoders provide a syndrom calculation over the received
* data + syndrom and can call the second stage directly.
* Many hw encoders provide a syndrome calculation over the received
* data + syndrome and can call the second stage directly.
*
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment