Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

286
drivers/mtd/chips/Kconfig Normal file
View File

@@ -0,0 +1,286 @@
# drivers/mtd/chips/Kconfig
# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
config MTD_CFI
tristate "Detect flash chips by Common Flash Interface (CFI) probe"
depends on MTD
select MTD_GEN_PROBE
help
The Common Flash Interface specification was developed by Intel,
AMD and other flash manufactures that provides a universal method
for probing the capabilities of flash devices. If you wish to
support any device that is CFI-compliant, you need to enable this
option. Visit <http://www.amd.com/products/nvd/overview/cfi.html>
for more information on CFI.
config MTD_JEDECPROBE
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
depends on MTD
select MTD_GEN_PROBE
help
This option enables JEDEC-style probing of flash chips which are not
compatible with the Common Flash Interface, but will use the common
CFI-targetted flash drivers for any chips which are identified which
are in fact compatible in all but the probe method. This actually
covers most AMD/Fujitsu-compatible chips, and will shortly cover also
non-CFI Intel chips (that code is in MTD CVS and should shortly be sent
for inclusion in Linus' tree)
config MTD_GEN_PROBE
tristate
config MTD_CFI_ADV_OPTIONS
bool "Flash chip driver advanced configuration options"
depends on MTD_GEN_PROBE
help
If you need to specify a specific endianness for access to flash
chips, or if you wish to reduce the size of the kernel by including
support for only specific arrangements of flash chips, say 'Y'. This
option does not directly affect the code, but will enable other
configuration options which allow you to do so.
If unsure, say 'N'.
choice
prompt "Flash cmd/query data swapping"
depends on MTD_CFI_ADV_OPTIONS
default MTD_CFI_NOSWAP
config MTD_CFI_NOSWAP
bool "NO"
---help---
This option defines the way in which the CPU attempts to arrange
data bits when writing the 'magic' commands to the chips. Saying
'NO', which is the default when CONFIG_MTD_CFI_ADV_OPTIONS isn't
enabled, means that the CPU will not do any swapping; the chips
are expected to be wired to the CPU in 'host-endian' form.
Specific arrangements are possible with the BIG_ENDIAN_BYTE and
LITTLE_ENDIAN_BYTE, if the bytes are reversed.
If you have a LART, on which the data (and address) lines were
connected in a fashion which ensured that the nets were as short
as possible, resulting in a bit-shuffling which seems utterly
random to the untrained eye, you need the LART_ENDIAN_BYTE option.
Yes, there really exists something sicker than PDP-endian :)
config MTD_CFI_BE_BYTE_SWAP
bool "BIG_ENDIAN_BYTE"
config MTD_CFI_LE_BYTE_SWAP
bool "LITTLE_ENDIAN_BYTE"
endchoice
config MTD_CFI_GEOMETRY
bool "Specific CFI Flash geometry selection"
depends on MTD_CFI_ADV_OPTIONS
help
This option does not affect the code directly, but will enable
some other configuration options which would allow you to reduce
the size of the kernel by including support for only certain
arrangements of CFI chips. If unsure, say 'N' and all options
which are supported by the current code will be enabled.
config MTD_MAP_BANK_WIDTH_1
bool "Support 8-bit buswidth" if MTD_CFI_GEOMETRY
default y
help
If you wish to support CFI devices on a physical bus which is
8 bits wide, say 'Y'.
config MTD_MAP_BANK_WIDTH_2
bool "Support 16-bit buswidth" if MTD_CFI_GEOMETRY
default y
help
If you wish to support CFI devices on a physical bus which is
16 bits wide, say 'Y'.
config MTD_MAP_BANK_WIDTH_4
bool "Support 32-bit buswidth" if MTD_CFI_GEOMETRY
default y
help
If you wish to support CFI devices on a physical bus which is
32 bits wide, say 'Y'.
config MTD_MAP_BANK_WIDTH_8
bool "Support 64-bit buswidth" if MTD_CFI_GEOMETRY
default n
help
If you wish to support CFI devices on a physical bus which is
64 bits wide, say 'Y'.
config MTD_MAP_BANK_WIDTH_16
bool "Support 128-bit buswidth" if MTD_CFI_GEOMETRY
default n
help
If you wish to support CFI devices on a physical bus which is
128 bits wide, say 'Y'.
config MTD_MAP_BANK_WIDTH_32
bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY
default n
help
If you wish to support CFI devices on a physical bus which is
256 bits wide, say 'Y'.
config MTD_CFI_I1
bool "Support 1-chip flash interleave" if MTD_CFI_GEOMETRY
default y
help
If your flash chips are not interleaved - i.e. you only have one
flash chip addressed by each bus cycle, then say 'Y'.
config MTD_CFI_I2
bool "Support 2-chip flash interleave" if MTD_CFI_GEOMETRY
default y
help
If your flash chips are interleaved in pairs - i.e. you have two
flash chips addressed by each bus cycle, then say 'Y'.
config MTD_CFI_I4
bool "Support 4-chip flash interleave" if MTD_CFI_GEOMETRY
default n
help
If your flash chips are interleaved in fours - i.e. you have four
flash chips addressed by each bus cycle, then say 'Y'.
config MTD_CFI_I8
bool "Support 8-chip flash interleave" if MTD_CFI_GEOMETRY
default n
help
If your flash chips are interleaved in eights - i.e. you have eight
flash chips addressed by each bus cycle, then say 'Y'.
config MTD_CFI_INTELEXT
tristate "Support for Intel/Sharp flash chips"
depends on MTD_GEN_PROBE
select MTD_CFI_UTIL
help
The Common Flash Interface defines a number of different command
sets which a CFI-compliant chip may claim to implement. This code
provides support for one of those command sets, used on Intel
StrataFlash and other parts.
config MTD_CFI_AMDSTD
tristate "Support for AMD/Fujitsu flash chips"
depends on MTD_GEN_PROBE
select MTD_CFI_UTIL
help
The Common Flash Interface defines a number of different command
sets which a CFI-compliant chip may claim to implement. This code
provides support for one of those command sets, used on chips
including the AMD Am29LV320.
config MTD_CFI_AMDSTD_RETRY
int "Retry failed commands (erase/program)"
depends on MTD_CFI_AMDSTD
default "0"
help
Some chips, when attached to a shared bus, don't properly filter
bus traffic that is destined to other devices. This broken
behavior causes erase and program sequences to be aborted when
the sequences are mixed with traffic for other devices.
SST49LF040 (and related) chips are know to be broken.
config MTD_CFI_AMDSTD_RETRY_MAX
int "Max retries of failed commands (erase/program)"
depends on MTD_CFI_AMDSTD_RETRY
default "0"
help
If you have an SST49LF040 (or related chip) then this value should
be set to at least 1. This can also be adjusted at driver load
time with the retry_cmd_max module parameter.
config MTD_CFI_STAA
tristate "Support for ST (Advanced Architecture) flash chips"
depends on MTD_GEN_PROBE
select MTD_CFI_UTIL
help
The Common Flash Interface defines a number of different command
sets which a CFI-compliant chip may claim to implement. This code
provides support for one of those command sets.
config MTD_CFI_UTIL
tristate
config MTD_RAM
tristate "Support for RAM chips in bus mapping"
depends on MTD
help
This option enables basic support for RAM chips accessed through
a bus mapping driver.
config MTD_ROM
tristate "Support for ROM chips in bus mapping"
depends on MTD
help
This option enables basic support for ROM chips accessed through
a bus mapping driver.
config MTD_ABSENT
tristate "Support for absent chips in bus mapping"
depends on MTD
help
This option enables support for a dummy probing driver used to
allocated placeholder MTD devices on systems that have socketed
or removable media. Use of this driver as a fallback chip probe
preserves the expected registration order of MTD device nodes on
the system regardless of media presence. Device nodes created
with this driver will return -ENODEV upon access.
config MTD_OBSOLETE_CHIPS
depends on MTD && BROKEN
bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
help
This option does not enable any code directly, but will allow you to
select some other chip drivers which are now considered obsolete,
because the generic CONFIG_JEDECPROBE code above should now detect
the chips which are supported by these drivers, and allow the generic
CFI-compatible drivers to drive the chips. Say 'N' here unless you have
already tried the CONFIG_JEDECPROBE method and reported its failure
to the MTD mailing list at <linux-mtd@lists.infradead.org>
config MTD_AMDSTD
tristate "AMD compatible flash chip support (non-CFI)"
depends on MTD && MTD_OBSOLETE_CHIPS
help
This option enables support for flash chips using AMD-compatible
commands, including some which are not CFI-compatible and hence
cannot be used with the CONFIG_MTD_CFI_AMDSTD option.
It also works on AMD compatible chips that do conform to CFI.
config MTD_SHARP
tristate "pre-CFI Sharp chip support"
depends on MTD && MTD_OBSOLETE_CHIPS
help
This option enables support for flash chips using Sharp-compatible
commands, including some which are not CFI-compatible and hence
cannot be used with the CONFIG_MTD_CFI_INTELxxx options.
config MTD_JEDEC
tristate "JEDEC device support"
depends on MTD && MTD_OBSOLETE_CHIPS
help
Enable older older JEDEC flash interface devices for self
programming flash. It is commonly used in older AMD chips. It is
only called JEDEC because the JEDEC association
<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

View File

@@ -0,0 +1,26 @@
#
# linux/drivers/chips/Makefile
#
# $Id: Makefile.common,v 1.4 2004/07/12 16:07:30 dwmw2 Exp $
# *** BIG UGLY NOTE ***
#
# The removal of get_module_symbol() and replacement with
# inter_module_register() et al has introduced a link order dependency
# here where previously there was none. We now have to ensure that
# the CFI command set drivers are linked before gen_probe.o
obj-$(CONFIG_MTD) += chipreg.o
obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o
obj-$(CONFIG_MTD_CFI) += cfi_probe.o
obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o
obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o
obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o
obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o
obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o
obj-$(CONFIG_MTD_JEDEC) += jedec.o
obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o
obj-$(CONFIG_MTD_RAM) += map_ram.o
obj-$(CONFIG_MTD_ROM) += map_rom.o
obj-$(CONFIG_MTD_SHARP) += sharp.o
obj-$(CONFIG_MTD_ABSENT) += map_absent.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,445 @@
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
$Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <linux/errno.h>
#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>
//#define DEBUG_CFI
#ifdef DEBUG_CFI
static void print_cfi_ident(struct cfi_ident *);
#endif
static int cfi_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi);
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 __xipram qry_present(struct map_info *map, __u32 base,
struct cfi_private *cfi)
{
int osf = cfi->interleave * cfi->device_type; // scale factor
map_word val[3];
map_word qry[3];
qry[0] = cfi_build_cmd('Q', map, cfi);
qry[1] = cfi_build_cmd('R', map, cfi);
qry[2] = cfi_build_cmd('Y', map, cfi);
val[0] = map_read(map, base + osf*0x10);
val[1] = map_read(map, base + osf*0x11);
val[2] = map_read(map, base + osf*0x12);
if (!map_word_equal(map, qry[0], val[0]))
return 0;
if (!map_word_equal(map, qry[1], val[1]))
return 0;
if (!map_word_equal(map, qry[2], val[2]))
return 0;
return 1; // "QRY" found
}
static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi)
{
int i;
if ((base + 0) >= map->size) {
printk(KERN_NOTICE
"Probe at base[0x00](0x%08lx) past the end of the map(0x%08lx)\n",
(unsigned long)base, map->size -1);
return 0;
}
if ((base + 0xff) >= map->size) {
printk(KERN_NOTICE
"Probe at base[0x55](0x%08lx) past the end of the map(0x%08lx)\n",
(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)) {
xip_enable(base, map, cfi);
return 0;
}
if (!cfi->numchips) {
/* This is the first time we're called. Set up the CFI
stuff accordingly and return */
return cfi_chip_setup(map, cfi);
}
/* Check each previous chip to see if it's an alias */
for (i=0; i < (base >> cfi->chipshift); i++) {
unsigned long start;
if(!test_bit(i, chip_map)) {
/* Skip location; no valid chip at this address */
continue;
}
start = i << cfi->chipshift;
/* This chip should be in read mode if it's one
we've already touched. */
if (qry_present(map, start, cfi)) {
/* Eep. This chip also had the QRY marker.
* Is it an alias for the new one? */
cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
/* 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;
}
/* Yes, it's actually got QRY for data. Most
* unfortunate. Stick the new chip in read mode
* too and if it's the same, assume it's an alias. */
/* FIXME: Use other modes to do a proper check */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
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;
}
}
}
/* OK, if we got to here, then none of the previous chips appear to
be aliases for the current one. */
set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
cfi->numchips++;
/* 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,
map->bankwidth*8);
return 1;
}
static int __xipram cfi_chip_setup(struct map_info *map,
struct cfi_private *cfi)
{
int ofs_factor = cfi->interleave*cfi->device_type;
__u32 base = 0;
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
if (!num_erase_regions)
return 0;
cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
if (!cfi->cfiq) {
printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
return 0;
}
memset(cfi->cfiq,0,sizeof(struct cfi_ident));
cfi->cfi_mode = CFI_MODE_CFI;
/* Read the CFI info structure */
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);
cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR);
cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID);
cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR);
cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc);
cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize);
#ifdef DEBUG_CFI
/* Dump the information therein */
print_cfi_ident(cfi->cfiq);
#endif
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
cfi->cfiq->EraseRegionInfo[i] = le32_to_cpu(cfi->cfiq->EraseRegionInfo[i]);
#ifdef DEBUG_CFI
printk(" Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks\n",
i, (cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff,
(cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
#endif
}
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);
return 1;
}
#ifdef DEBUG_CFI
static char *vendorname(__u16 vendor)
{
switch (vendor) {
case P_ID_NONE:
return "None";
case P_ID_INTEL_EXT:
return "Intel/Sharp Extended";
case P_ID_AMD_STD:
return "AMD/Fujitsu Standard";
case P_ID_INTEL_STD:
return "Intel/Sharp Standard";
case P_ID_AMD_EXT:
return "AMD/Fujitsu Extended";
case P_ID_WINBOND:
return "Winbond Standard";
case P_ID_ST_ADV:
return "ST Advanced";
case P_ID_MITSUBISHI_STD:
return "Mitsubishi Standard";
case P_ID_MITSUBISHI_EXT:
return "Mitsubishi Extended";
case P_ID_SST_PAGE:
return "SST Page Write";
case P_ID_INTEL_PERFORMANCE:
return "Intel Performance Code";
case P_ID_INTEL_DATA:
return "Intel Data";
case P_ID_RESERVED:
return "Not Allowed / Reserved for Future Use";
default:
return "Unknown";
}
}
static void print_cfi_ident(struct cfi_ident *cfip)
{
#if 0
if (cfip->qry[0] != 'Q' || cfip->qry[1] != 'R' || cfip->qry[2] != 'Y') {
printk("Invalid CFI ident structure.\n");
return;
}
#endif
printk("Primary Vendor Command Set: %4.4X (%s)\n", cfip->P_ID, vendorname(cfip->P_ID));
if (cfip->P_ADR)
printk("Primary Algorithm Table at %4.4X\n", cfip->P_ADR);
else
printk("No Primary Algorithm Table\n");
printk("Alternative Vendor Command Set: %4.4X (%s)\n", cfip->A_ID, vendorname(cfip->A_ID));
if (cfip->A_ADR)
printk("Alternate Algorithm Table at %4.4X\n", cfip->A_ADR);
else
printk("No Alternate Algorithm Table\n");
printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
if (cfip->VppMin) {
printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
}
else
printk("No Vpp line\n");
printk("Typical byte/word write timeout: %d <20>s\n", 1<<cfip->WordWriteTimeoutTyp);
printk("Maximum byte/word write timeout: %d <20>s\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
printk("Typical full buffer write timeout: %d <20>s\n", 1<<cfip->BufWriteTimeoutTyp);
printk("Maximum full buffer write timeout: %d <20>s\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
}
else
printk("Full buffer write not supported\n");
printk("Typical block erase timeout: %d ms\n", 1<<cfip->BlockEraseTimeoutTyp);
printk("Maximum block erase timeout: %d ms\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp));
if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) {
printk("Typical chip erase timeout: %d ms\n", 1<<cfip->ChipEraseTimeoutTyp);
printk("Maximum chip erase timeout: %d ms\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->ChipEraseTimeoutTyp));
}
else
printk("Chip erase not supported\n");
printk("Device size: 0x%X bytes (%d MiB)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20));
printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc);
switch(cfip->InterfaceDesc) {
case 0:
printk(" - x8-only asynchronous interface\n");
break;
case 1:
printk(" - x16-only asynchronous interface\n");
break;
case 2:
printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n");
break;
case 3:
printk(" - x32-only asynchronous interface\n");
break;
case 4:
printk(" - supports x16 and x32 via Word# with asynchronous interface\n");
break;
case 65535:
printk(" - Not Allowed / Reserved\n");
break;
default:
printk(" - Unknown\n");
break;
}
printk("Max. bytes in buffer write: 0x%x\n", 1<< cfip->MaxBufWriteSize);
printk("Number of Erase Block Regions: %d\n", cfip->NumEraseRegions);
}
#endif /* DEBUG_CFI */
static struct chip_probe cfi_chip_probe = {
.name = "CFI",
.probe_chip = cfi_probe_chip
};
struct mtd_info *cfi_probe(struct map_info *map)
{
/*
* Just use the generic probe stuff to call our CFI-specific
* chip_probe routine in all the possible permutations, etc.
*/
return mtd_do_chip_probe(map, &cfi_chip_probe);
}
static struct mtd_chip_driver cfi_chipdrv = {
.probe = cfi_probe,
.name = "cfi_probe",
.module = THIS_MODULE
};
int __init cfi_probe_init(void)
{
register_mtd_chip_driver(&cfi_chipdrv);
return 0;
}
static void __exit cfi_probe_exit(void)
{
unregister_mtd_chip_driver(&cfi_chipdrv);
}
module_init(cfi_probe_init);
module_exit(cfi_probe_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
MODULE_DESCRIPTION("Probe code for CFI-compliant flash chips");

View File

@@ -0,0 +1,196 @@
/*
* Common Flash Interface support:
* Generic utility functions not dependant on command set
*
* Copyright (C) 2002 Red Hat
* Copyright (C) 2003 STMicroelectronics Limited
*
* This code is covered by the GPL.
*
* $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <linux/errno.h>
#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 *
__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;
int ofs_factor = cfi->interleave * cfi->device_type;
int i;
struct cfi_extquery *extp = NULL;
printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
if (!adr)
goto out;
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 "
"version %c.%c.\n", name, extp->MajorVersion,
extp->MinorVersion);
kfree(extp);
extp = NULL;
}
out: return extp;
}
EXPORT_SYMBOL(cfi_read_pri);
void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_fixup *f;
for (f=fixups; f->fixup; f++) {
if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
((f->id == CFI_ID_ANY) || (f->id == cfi->id))) {
f->fixup(mtd, f->param);
}
}
}
EXPORT_SYMBOL(cfi_fixup);
int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
loff_t ofs, size_t len, void *thunk)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
unsigned long adr;
int chipnum, ret = 0;
int i, first;
struct mtd_erase_region_info *regions = mtd->eraseregions;
if (ofs > mtd->size)
return -EINVAL;
if ((len + ofs) > mtd->size)
return -EINVAL;
/* Check that both start and end of the requested erase are
* aligned with the erasesize at the appropriate addresses.
*/
i = 0;
/* Skip all erase regions which are ended before the start of
the requested erase. Actually, to save on the calculations,
we skip to the first erase region which starts after the
start of the requested erase, and then go back one.
*/
while (i < mtd->numeraseregions && ofs >= regions[i].offset)
i++;
i--;
/* OK, now i is pointing at the erase region in which this
erase request starts. Check the start of the requested
erase range is aligned with the erase size which is in
effect here.
*/
if (ofs & (regions[i].erasesize-1))
return -EINVAL;
/* Remember the erase region we start on */
first = i;
/* Next, check that the end of the requested erase is aligned
* with the erase region at that address.
*/
while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
i++;
/* As before, drop back one to point at the region in which
the address actually falls
*/
i--;
if ((ofs + len) & (regions[i].erasesize-1))
return -EINVAL;
chipnum = ofs >> cfi->chipshift;
adr = ofs - (chipnum << cfi->chipshift);
i=first;
while(len) {
int size = regions[i].erasesize;
ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
if (ret)
return ret;
adr += size;
ofs += size;
len -= size;
if (ofs == regions[i].offset + size * regions[i].numblocks)
i++;
if (adr >> cfi->chipshift) {
adr = 0;
chipnum++;
if (chipnum >= cfi->numchips)
break;
}
}
return 0;
}
EXPORT_SYMBOL(cfi_varsize_frob);
MODULE_LICENSE("GPL");

111
drivers/mtd/chips/chipreg.c Normal file
View File

@@ -0,0 +1,111 @@
/*
* $Id: chipreg.c,v 1.17 2004/11/16 18:29:00 dwmw2 Exp $
*
* Registration for chip drivers
*
*/
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
static DEFINE_SPINLOCK(chip_drvs_lock);
static LIST_HEAD(chip_drvs_list);
void register_mtd_chip_driver(struct mtd_chip_driver *drv)
{
spin_lock(&chip_drvs_lock);
list_add(&drv->list, &chip_drvs_list);
spin_unlock(&chip_drvs_lock);
}
void unregister_mtd_chip_driver(struct mtd_chip_driver *drv)
{
spin_lock(&chip_drvs_lock);
list_del(&drv->list);
spin_unlock(&chip_drvs_lock);
}
static struct mtd_chip_driver *get_mtd_chip_driver (const char *name)
{
struct list_head *pos;
struct mtd_chip_driver *ret = NULL, *this;
spin_lock(&chip_drvs_lock);
list_for_each(pos, &chip_drvs_list) {
this = list_entry(pos, typeof(*this), list);
if (!strcmp(this->name, name)) {
ret = this;
break;
}
}
if (ret && !try_module_get(ret->module))
ret = NULL;
spin_unlock(&chip_drvs_lock);
return ret;
}
/* Hide all the horrid details, like some silly person taking
get_module_symbol() away from us, from the caller. */
struct mtd_info *do_map_probe(const char *name, struct map_info *map)
{
struct mtd_chip_driver *drv;
struct mtd_info *ret;
drv = get_mtd_chip_driver(name);
if (!drv && !request_module("%s", name))
drv = get_mtd_chip_driver(name);
if (!drv)
return NULL;
ret = drv->probe(map);
/* We decrease the use count here. It may have been a
probe-only module, which is no longer required from this
point, having given us a handle on (and increased the use
count of) the actual driver code.
*/
module_put(drv->module);
if (ret)
return ret;
return NULL;
}
/*
* Destroy an MTD device which was created for a map device.
* Make sure the MTD device is already unregistered before calling this
*/
void map_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
if (map->fldrv->destroy)
map->fldrv->destroy(mtd);
module_put(map->fldrv->module);
kfree(mtd);
}
EXPORT_SYMBOL(register_mtd_chip_driver);
EXPORT_SYMBOL(unregister_mtd_chip_driver);
EXPORT_SYMBOL(do_map_probe);
EXPORT_SYMBOL(map_destroy);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Core routines for registering and invoking MTD chip drivers");

View File

@@ -0,0 +1,107 @@
#ifndef FWH_LOCK_H
#define FWH_LOCK_H
enum fwh_lock_state {
FWH_UNLOCKED = 0,
FWH_DENY_WRITE = 1,
FWH_IMMUTABLE = 2,
FWH_DENY_READ = 4,
};
struct fwh_xxlock_thunk {
enum fwh_lock_state val;
flstate_t state;
};
#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING})
#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING})
/*
* This locking/unlock is specific to firmware hub parts. Only one
* is known that supports the Intel command set. Firmware
* hub parts cannot be interleaved as they are on the LPC bus
* so this code has not been tested with interleaved chips,
* and will likely fail in that context.
*/
static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk;
int ret;
/* Refuse the operation if the we cannot look behind the chip */
if (chip->start < 0x400000) {
DEBUG( MTD_DEBUG_LEVEL3,
"MTD %s(): chip->start: %lx wanted >= 0x400000\n",
__func__, chip->start );
return -EIO;
}
/*
* lock block registers:
* - on 64k boundariesand
* - bit 1 set high
* - block lock registers are 4MiB lower - overflow subtract (danger)
*
* The address manipulation is first done on the logical address
* which is 0 at the start of the chip, and then the offset of
* the individual chip is addted to it. Any other order a weird
* map offset could cause problems.
*/
adr = (adr & ~0xffffUL) | 0x2;
adr += chip->start - 0x400000;
/*
* This is easy because these are writes to registers and not writes
* to flash memory - that means that we don't have to check status
* and timeout.
*/
cfi_spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_LOCKING);
if (ret) {
cfi_spin_unlock(chip->mutex);
return ret;
}
chip->state = xxlt->state;
map_write(map, CMD(xxlt->val), adr);
/* Done and happy. */
chip->state = FL_READY;
put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex);
return 0;
}
static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
{
int ret;
ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
(void *)&FWH_XXLOCK_ONEBLOCK_LOCK);
return ret;
}
static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
{
int ret;
ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
(void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK);
return ret;
}
static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param)
{
printk(KERN_NOTICE "using fwh lock/unlock method\n");
/* Setup for the chips with the fwh lock method */
mtd->lock = fwh_lock_varsize;
mtd->unlock = fwh_unlock_varsize;
}
#endif /* FWH_LOCK_H */

View File

@@ -0,0 +1,255 @@
/*
* Routines common to all CFI-type probes.
* (C) 2001-2003 Red Hat, Inc.
* GPL'd
* $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/gen_probe.h>
static struct mtd_info *check_cmd_set(struct map_info *, int);
static struct cfi_private *genprobe_ident_chips(struct map_info *map,
struct chip_probe *cp);
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi);
struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
{
struct mtd_info *mtd = NULL;
struct cfi_private *cfi;
/* First probe the map to see if we have CFI stuff there. */
cfi = genprobe_ident_chips(map, cp);
if (!cfi)
return NULL;
map->fldrv_priv = cfi;
/* OK we liked it. Now find a driver for the command set it talks */
mtd = check_cmd_set(map, 1); /* First the primary cmdset */
if (!mtd)
mtd = check_cmd_set(map, 0); /* Then the secondary */
if (mtd)
return mtd;
printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n");
kfree(cfi->cfiq);
kfree(cfi);
map->fldrv_priv = NULL;
return NULL;
}
EXPORT_SYMBOL(mtd_do_chip_probe);
static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
{
struct cfi_private cfi;
struct cfi_private *retcfi;
unsigned long *chip_map;
int i, j, mapsize;
int max_chips;
memset(&cfi, 0, sizeof(cfi));
/* Call the probetype-specific code with all permutations of
interleave and device type, etc. */
if (!genprobe_new_chip(map, cp, &cfi)) {
/* The probe didn't like it */
printk(KERN_DEBUG "%s: Found no %s device at location zero\n",
cp->name, map->name);
return NULL;
}
#if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD
probe routines won't ever return a broken CFI structure anyway,
because they make them up themselves.
*/
if (cfi.cfiq->NumEraseRegions == 0) {
printk(KERN_WARNING "Number of erase regions is zero\n");
kfree(cfi.cfiq);
return NULL;
}
#endif
cfi.chipshift = cfi.cfiq->DevSize;
if (cfi_interleave_is_1(&cfi)) {
;
} else if (cfi_interleave_is_2(&cfi)) {
cfi.chipshift++;
} else if (cfi_interleave_is_4((&cfi))) {
cfi.chipshift += 2;
} else if (cfi_interleave_is_8(&cfi)) {
cfi.chipshift += 3;
} else {
BUG();
}
cfi.numchips = 1;
/*
* Allocate memory for bitmap of valid chips.
* Align bitmap storage size to full byte.
*/
max_chips = map->size >> cfi.chipshift;
mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0);
chip_map = kmalloc(mapsize, GFP_KERNEL);
if (!chip_map) {
printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
kfree(cfi.cfiq);
return NULL;
}
memset (chip_map, 0, mapsize);
set_bit(0, chip_map); /* Mark first chip valid */
/*
* Now probe for other chips, checking sensibly for aliases while
* we're at it. The new_chip probe above should have let the first
* chip in read mode.
*/
for (i = 1; i < max_chips; i++) {
cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
}
/*
* Now allocate the space for the structures we need to return to
* our caller, and copy the appropriate data into them.
*/
retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
if (!retcfi) {
printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
kfree(cfi.cfiq);
kfree(chip_map);
return NULL;
}
memcpy(retcfi, &cfi, sizeof(cfi));
memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips);
for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) {
if(test_bit(i, chip_map)) {
struct flchip *pchip = &retcfi->chips[j++];
pchip->start = (i << cfi.chipshift);
pchip->state = FL_READY;
init_waitqueue_head(&pchip->wq);
spin_lock_init(&pchip->_spinlock);
pchip->mutex = &pchip->_spinlock;
}
}
kfree(chip_map);
return retcfi;
}
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi)
{
int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */
int max_chips = map_bankwidth(map); /* And minimum 1 */
int nr_chips, type;
for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) {
if (!cfi_interleave_supported(nr_chips))
continue;
cfi->interleave = nr_chips;
/* Minimum device size. Don't look for one 8-bit device
in a 16-bit bus, etc. */
type = map_bankwidth(map) / nr_chips;
for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
cfi->device_type = type;
if (cp->probe_chip(map, 0, NULL, cfi))
return 1;
}
}
return 0;
}
typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int);
extern cfi_cmdset_fn_t cfi_cmdset_0001;
extern cfi_cmdset_fn_t cfi_cmdset_0002;
extern cfi_cmdset_fn_t cfi_cmdset_0020;
static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
#if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE)
char probename[32];
cfi_cmdset_fn_t *probe_function;
sprintf(probename, "cfi_cmdset_%4.4X", type);
probe_function = inter_module_get_request(probename, probename);
if (probe_function) {
struct mtd_info *mtd;
mtd = (*probe_function)(map, primary);
/* If it was happy, it'll have increased its own use count */
inter_module_put(probename);
return mtd;
}
#endif
printk(KERN_NOTICE "Support for command set %04X not present\n",
type);
return NULL;
}
static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
{
struct cfi_private *cfi = map->fldrv_priv;
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
if (type == P_ID_NONE || type == P_ID_RESERVED)
return NULL;
switch(type){
/* Urgh. Ifdefs. The version with weak symbols was
* _much_ nicer. Shame it didn't seem to work on
* anything but x86, really.
* But we can't rely in inter_module_get() because
* that'd mean we depend on link order.
*/
#ifdef CONFIG_MTD_CFI_INTELEXT
case 0x0001:
case 0x0003:
return cfi_cmdset_0001(map, primary);
#endif
#ifdef CONFIG_MTD_CFI_AMDSTD
case 0x0002:
return cfi_cmdset_0002(map, primary);
#endif
#ifdef CONFIG_MTD_CFI_STAA
case 0x0020:
return cfi_cmdset_0020(map, primary);
#endif
}
return cfi_cmdset_unknown(map, primary);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Helper routines for flash chip probe code");

934
drivers/mtd/chips/jedec.c Normal file
View File

@@ -0,0 +1,934 @@
/* JEDEC Flash Interface.
* This is an older type of interface for self programming flash. It is
* commonly use in older AMD chips and is obsolete compared with CFI.
* It is called JEDEC because the JEDEC association distributes the ID codes
* for the chips.
*
* See the AMD flash databook for information on how to operate the interface.
*
* This code does not support anything wider than 8 bit flash chips, I am
* not going to guess how to send commands to them, plus I expect they will
* all speak CFI..
*
* $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mtd/jedec.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
static struct mtd_info *jedec_probe(struct map_info *);
static int jedec_probe8(struct map_info *map,unsigned long base,
struct jedec_private *priv);
static int jedec_probe16(struct map_info *map,unsigned long base,
struct jedec_private *priv);
static int jedec_probe32(struct map_info *map,unsigned long base,
struct jedec_private *priv);
static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
unsigned long len);
static int flash_erase(struct mtd_info *mtd, struct erase_info *instr);
static int flash_write(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, const u_char *buf);
static unsigned long my_bank_size;
/* Listing of parts and sizes. We need this table to learn the sector
size of the chip and the total length */
static const struct JEDECTable JEDEC_table[] = {
{
.jedec = 0x013D,
.name = "AMD Am29F017D",
.size = 2*1024*1024,
.sectorsize = 64*1024,
.capabilities = MTD_CAP_NORFLASH
},
{
.jedec = 0x01AD,
.name = "AMD Am29F016",
.size = 2*1024*1024,
.sectorsize = 64*1024,
.capabilities = MTD_CAP_NORFLASH
},
{
.jedec = 0x01D5,
.name = "AMD Am29F080",
.size = 1*1024*1024,
.sectorsize = 64*1024,
.capabilities = MTD_CAP_NORFLASH
},
{
.jedec = 0x01A4,
.name = "AMD Am29F040",
.size = 512*1024,
.sectorsize = 64*1024,
.capabilities = MTD_CAP_NORFLASH
},
{
.jedec = 0x20E3,
.name = "AMD Am29W040B",
.size = 512*1024,
.sectorsize = 64*1024,
.capabilities = MTD_CAP_NORFLASH
},
{
.jedec = 0xC2AD,
.name = "Macronix MX29F016",
.size = 2*1024*1024,
.sectorsize = 64*1024,
.capabilities = MTD_CAP_NORFLASH
},
{ .jedec = 0x0 }
};
static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id);
static void jedec_sync(struct mtd_info *mtd) {};
static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
static struct mtd_info *jedec_probe(struct map_info *map);
static struct mtd_chip_driver jedec_chipdrv = {
.probe = jedec_probe,
.name = "jedec",
.module = THIS_MODULE
};
/* Probe entry point */
static struct mtd_info *jedec_probe(struct map_info *map)
{
struct mtd_info *MTD;
struct jedec_private *priv;
unsigned long Base;
unsigned long SectorSize;
unsigned count;
unsigned I,Uniq;
char Part[200];
memset(&priv,0,sizeof(priv));
MTD = kmalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL);
if (!MTD)
return NULL;
memset(MTD, 0, sizeof(struct mtd_info) + sizeof(struct jedec_private));
priv = (struct jedec_private *)&MTD[1];
my_bank_size = map->size;
if (map->size/my_bank_size > MAX_JEDEC_CHIPS)
{
printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n");
kfree(MTD);
return NULL;
}
for (Base = 0; Base < map->size; Base += my_bank_size)
{
// Perhaps zero could designate all tests?
if (map->buswidth == 0)
map->buswidth = 1;
if (map->buswidth == 1){
if (jedec_probe8(map,Base,priv) == 0) {
printk("did recognize jedec chip\n");
kfree(MTD);
return NULL;
}
}
if (map->buswidth == 2)
jedec_probe16(map,Base,priv);
if (map->buswidth == 4)
jedec_probe32(map,Base,priv);
}
// Get the biggest sector size
SectorSize = 0;
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
{
// printk("priv->chips[%d].jedec is %x\n",I,priv->chips[I].jedec);
// printk("priv->chips[%d].sectorsize is %lx\n",I,priv->chips[I].sectorsize);
if (priv->chips[I].sectorsize > SectorSize)
SectorSize = priv->chips[I].sectorsize;
}
// Quickly ensure that the other sector sizes are factors of the largest
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
{
if ((SectorSize/priv->chips[I].sectorsize)*priv->chips[I].sectorsize != SectorSize)
{
printk("mtd: Failed. Device has incompatible mixed sector sizes\n");
kfree(MTD);
return NULL;
}
}
/* Generate a part name that includes the number of different chips and
other configuration information */
count = 1;
strlcpy(Part,map->name,sizeof(Part)-10);
strcat(Part," ");
Uniq = 0;
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
{
const struct JEDECTable *JEDEC;
if (priv->chips[I+1].jedec == priv->chips[I].jedec)
{
count++;
continue;
}
// Locate the chip in the jedec table
JEDEC = jedec_idtoinf(priv->chips[I].jedec >> 8,priv->chips[I].jedec);
if (JEDEC == 0)
{
printk("mtd: Internal Error, JEDEC not set\n");
kfree(MTD);
return NULL;
}
if (Uniq != 0)
strcat(Part,",");
Uniq++;
if (count != 1)
sprintf(Part+strlen(Part),"%x*[%s]",count,JEDEC->name);
else
sprintf(Part+strlen(Part),"%s",JEDEC->name);
if (strlen(Part) > sizeof(Part)*2/3)
break;
count = 1;
}
/* Determine if the chips are organized in a linear fashion, or if there
are empty banks. Note, the last bank does not count here, only the
first banks are important. Holes on non-bank boundaries can not exist
due to the way the detection algorithm works. */
if (priv->size < my_bank_size)
my_bank_size = priv->size;
priv->is_banked = 0;
//printk("priv->size is %x, my_bank_size is %x\n",priv->size,my_bank_size);
//printk("priv->bank_fill[0] is %x\n",priv->bank_fill[0]);
if (!priv->size) {
printk("priv->size is zero\n");
kfree(MTD);
return NULL;
}
if (priv->size/my_bank_size) {
if (priv->size/my_bank_size == 1) {
priv->size = my_bank_size;
}
else {
for (I = 0; I != priv->size/my_bank_size - 1; I++)
{
if (priv->bank_fill[I] != my_bank_size)
priv->is_banked = 1;
/* This even could be eliminated, but new de-optimized read/write
functions have to be written */
printk("priv->bank_fill[%d] is %lx, priv->bank_fill[0] is %lx\n",I,priv->bank_fill[I],priv->bank_fill[0]);
if (priv->bank_fill[I] != priv->bank_fill[0])
{
printk("mtd: Failed. Cannot handle unsymmetric banking\n");
kfree(MTD);
return NULL;
}
}
}
}
if (priv->is_banked == 1)
strcat(Part,", banked");
// printk("Part: '%s'\n",Part);
memset(MTD,0,sizeof(*MTD));
// strlcpy(MTD->name,Part,sizeof(MTD->name));
MTD->name = map->name;
MTD->type = MTD_NORFLASH;
MTD->flags = MTD_CAP_NORFLASH;
MTD->erasesize = SectorSize*(map->buswidth);
// printk("MTD->erasesize is %x\n",(unsigned int)MTD->erasesize);
MTD->size = priv->size;
// printk("MTD->size is %x\n",(unsigned int)MTD->size);
//MTD->module = THIS_MODULE; // ? Maybe this should be the low level module?
MTD->erase = flash_erase;
if (priv->is_banked == 1)
MTD->read = jedec_read_banked;
else
MTD->read = jedec_read;
MTD->write = flash_write;
MTD->sync = jedec_sync;
MTD->priv = map;
map->fldrv_priv = priv;
map->fldrv = &jedec_chipdrv;
__module_get(THIS_MODULE);
return MTD;
}
/* Helper for the JEDEC function, JEDEC numbers all have odd parity */
static int checkparity(u_char C)
{
u_char parity = 0;
while (C != 0)
{
parity ^= C & 1;
C >>= 1;
}
return parity == 1;
}
/* Take an array of JEDEC numbers that represent interleved flash chips
and process them. Check to make sure they are good JEDEC numbers, look
them up and then add them to the chip list */
static int handle_jedecs(struct map_info *map,__u8 *Mfg,__u8 *Id,unsigned Count,
unsigned long base,struct jedec_private *priv)
{
unsigned I,J;
unsigned long Size;
unsigned long SectorSize;
const struct JEDECTable *JEDEC;
// Test #2 JEDEC numbers exhibit odd parity
for (I = 0; I != Count; I++)
{
if (checkparity(Mfg[I]) == 0 || checkparity(Id[I]) == 0)
return 0;
}
// Finally, just make sure all the chip sizes are the same
JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
if (JEDEC == 0)
{
printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
return 0;
}
Size = JEDEC->size;
SectorSize = JEDEC->sectorsize;
for (I = 0; I != Count; I++)
{
JEDEC = jedec_idtoinf(Mfg[0],Id[0]);
if (JEDEC == 0)
{
printk("mtd: Found JEDEC flash chip, but do not have a table entry for %x:%x\n",Mfg[0],Mfg[1]);
return 0;
}
if (Size != JEDEC->size || SectorSize != JEDEC->sectorsize)
{
printk("mtd: Failed. Interleved flash does not have matching characteristics\n");
return 0;
}
}
// Load the Chips
for (I = 0; I != MAX_JEDEC_CHIPS; I++)
{
if (priv->chips[I].jedec == 0)
break;
}
if (I + Count > MAX_JEDEC_CHIPS)
{
printk("mtd: Device has too many chips. Increase MAX_JEDEC_CHIPS\n");
return 0;
}
// Add them to the table
for (J = 0; J != Count; J++)
{
unsigned long Bank;
JEDEC = jedec_idtoinf(Mfg[J],Id[J]);
priv->chips[I].jedec = (Mfg[J] << 8) | Id[J];
priv->chips[I].size = JEDEC->size;
priv->chips[I].sectorsize = JEDEC->sectorsize;
priv->chips[I].base = base + J;
priv->chips[I].datashift = J*8;
priv->chips[I].capabilities = JEDEC->capabilities;
priv->chips[I].offset = priv->size + J;
// log2 n :|
priv->chips[I].addrshift = 0;
for (Bank = Count; Bank != 1; Bank >>= 1, priv->chips[I].addrshift++);
// Determine how filled this bank is.
Bank = base & (~(my_bank_size-1));
if (priv->bank_fill[Bank/my_bank_size] < base +
(JEDEC->size << priv->chips[I].addrshift) - Bank)
priv->bank_fill[Bank/my_bank_size] = base + (JEDEC->size << priv->chips[I].addrshift) - Bank;
I++;
}
priv->size += priv->chips[I-1].size*Count;
return priv->chips[I-1].size;
}
/* Lookup the chip information from the JEDEC ID table. */
static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id)
{
__u16 Id = (mfr << 8) | id;
unsigned long I = 0;
for (I = 0; JEDEC_table[I].jedec != 0; I++)
if (JEDEC_table[I].jedec == Id)
return JEDEC_table + I;
return NULL;
}
// Look for flash using an 8 bit bus interface
static int jedec_probe8(struct map_info *map,unsigned long base,
struct jedec_private *priv)
{
#define flread(x) map_read8(map,base+x)
#define flwrite(v,x) map_write8(map,v,base+x)
const unsigned long AutoSel1 = 0xAA;
const unsigned long AutoSel2 = 0x55;
const unsigned long AutoSel3 = 0x90;
const unsigned long Reset = 0xF0;
__u32 OldVal;
__u8 Mfg[1];
__u8 Id[1];
unsigned I;
unsigned long Size;
// Wait for any write/erase operation to settle
OldVal = flread(base);
for (I = 0; OldVal != flread(base) && I < 10000; I++)
OldVal = flread(base);
// Reset the chip
flwrite(Reset,0x555);
// Send the sequence
flwrite(AutoSel1,0x555);
flwrite(AutoSel2,0x2AA);
flwrite(AutoSel3,0x555);
// Get the JEDEC numbers
Mfg[0] = flread(0);
Id[0] = flread(1);
// printk("Mfg is %x, Id is %x\n",Mfg[0],Id[0]);
Size = handle_jedecs(map,Mfg,Id,1,base,priv);
// printk("handle_jedecs Size is %x\n",(unsigned int)Size);
if (Size == 0)
{
flwrite(Reset,0x555);
return 0;
}
// Reset.
flwrite(Reset,0x555);
return 1;
#undef flread
#undef flwrite
}
// Look for flash using a 16 bit bus interface (ie 2 8-bit chips)
static int jedec_probe16(struct map_info *map,unsigned long base,
struct jedec_private *priv)
{
return 0;
}
// Look for flash using a 32 bit bus interface (ie 4 8-bit chips)
static int jedec_probe32(struct map_info *map,unsigned long base,
struct jedec_private *priv)
{
#define flread(x) map_read32(map,base+((x)<<2))
#define flwrite(v,x) map_write32(map,v,base+((x)<<2))
const unsigned long AutoSel1 = 0xAAAAAAAA;
const unsigned long AutoSel2 = 0x55555555;
const unsigned long AutoSel3 = 0x90909090;
const unsigned long Reset = 0xF0F0F0F0;
__u32 OldVal;
__u8 Mfg[4];
__u8 Id[4];
unsigned I;
unsigned long Size;
// Wait for any write/erase operation to settle
OldVal = flread(base);
for (I = 0; OldVal != flread(base) && I < 10000; I++)
OldVal = flread(base);
// Reset the chip
flwrite(Reset,0x555);
// Send the sequence
flwrite(AutoSel1,0x555);
flwrite(AutoSel2,0x2AA);
flwrite(AutoSel3,0x555);
// Test #1, JEDEC numbers are readable from 0x??00/0x??01
if (flread(0) != flread(0x100) ||
flread(1) != flread(0x101))
{
flwrite(Reset,0x555);
return 0;
}
// Split up the JEDEC numbers
OldVal = flread(0);
for (I = 0; I != 4; I++)
Mfg[I] = (OldVal >> (I*8));
OldVal = flread(1);
for (I = 0; I != 4; I++)
Id[I] = (OldVal >> (I*8));
Size = handle_jedecs(map,Mfg,Id,4,base,priv);
if (Size == 0)
{
flwrite(Reset,0x555);
return 0;
}
/* Check if there is address wrap around within a single bank, if this
returns JEDEC numbers then we assume that it is wrap around. Notice
we call this routine with the JEDEC return still enabled, if two or
more flashes have a truncated address space the probe test will still
work */
if (base + (Size<<2)+0x555 < map->size &&
base + (Size<<2)+0x555 < (base & (~(my_bank_size-1))) + my_bank_size)
{
if (flread(base+Size) != flread(base+Size + 0x100) ||
flread(base+Size + 1) != flread(base+Size + 0x101))
{
jedec_probe32(map,base+Size,priv);
}
}
// Reset.
flwrite(0xF0F0F0F0,0x555);
return 1;
#undef flread
#undef flwrite
}
/* Linear read. */
static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
map_copy_from(map, buf, from, len);
*retlen = len;
return 0;
}
/* Banked read. Take special care to jump past the holes in the bank
mapping. This version assumes symetry in the holes.. */
static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
struct jedec_private *priv = map->fldrv_priv;
*retlen = 0;
while (len > 0)
{
// Determine what bank and offset into that bank the first byte is
unsigned long bank = from & (~(priv->bank_fill[0]-1));
unsigned long offset = from & (priv->bank_fill[0]-1);
unsigned long get = len;
if (priv->bank_fill[0] - offset < len)
get = priv->bank_fill[0] - offset;
bank /= priv->bank_fill[0];
map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
len -= get;
*retlen += get;
from += get;
}
return 0;
}
/* Pass the flags value that the flash return before it re-entered read
mode. */
static void jedec_flash_failed(unsigned char code)
{
/* Bit 5 being high indicates that there was an internal device
failure, erasure time limits exceeded or something */
if ((code & (1 << 5)) != 0)
{
printk("mtd: Internal Flash failure\n");
return;
}
printk("mtd: Programming didn't take\n");
}
/* This uses the erasure function described in the AMD Flash Handbook,
it will work for flashes with a fixed sector size only. Flashes with
a selection of sector sizes (ie the AMD Am29F800B) will need a different
routine. This routine tries to parallize erasing multiple chips/sectors
where possible */
static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
// Does IO to the currently selected chip
#define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
#define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
unsigned long Time = 0;
unsigned long NoTime = 0;
unsigned long start = instr->addr, len = instr->len;
unsigned int I;
struct map_info *map = mtd->priv;
struct jedec_private *priv = map->fldrv_priv;
// Verify the arguments..
if (start + len > mtd->size ||
(start % mtd->erasesize) != 0 ||
(len % mtd->erasesize) != 0 ||
(len/mtd->erasesize) == 0)
return -EINVAL;
jedec_flash_chip_scan(priv,start,len);
// Start the erase sequence on each chip
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
{
unsigned long off;
struct jedec_flash_chip *chip = priv->chips + I;
if (chip->length == 0)
continue;
if (chip->start + chip->length > chip->size)
{
printk("DIE\n");
return -EIO;
}
flwrite(0xF0,chip->start + 0x555);
flwrite(0xAA,chip->start + 0x555);
flwrite(0x55,chip->start + 0x2AA);
flwrite(0x80,chip->start + 0x555);
flwrite(0xAA,chip->start + 0x555);
flwrite(0x55,chip->start + 0x2AA);
/* Once we start selecting the erase sectors the delay between each
command must not exceed 50us or it will immediately start erasing
and ignore the other sectors */
for (off = 0; off < len; off += chip->sectorsize)
{
// Check to make sure we didn't timeout
flwrite(0x30,chip->start + off);
if (off == 0)
continue;
if ((flread(chip->start + off) & (1 << 3)) != 0)
{
printk("mtd: Ack! We timed out the erase timer!\n");
return -EIO;
}
}
}
/* We could split this into a timer routine and return early, performing
background erasure.. Maybe later if the need warrents */
/* Poll the flash for erasure completion, specs say this can take as long
as 480 seconds to do all the sectors (for a 2 meg flash).
Erasure time is dependent on chip age, temp and wear.. */
/* This being a generic routine assumes a 32 bit bus. It does read32s
and bundles interleved chips into the same grouping. This will work
for all bus widths */
Time = 0;
NoTime = 0;
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
{
struct jedec_flash_chip *chip = priv->chips + I;
unsigned long off = 0;
unsigned todo[4] = {0,0,0,0};
unsigned todo_left = 0;
unsigned J;
if (chip->length == 0)
continue;
/* Find all chips in this data line, realistically this is all
or nothing up to the interleve count */
for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
{
if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
(chip->base & (~((1<<chip->addrshift)-1))))
{
todo_left++;
todo[priv->chips[J].base & ((1<<chip->addrshift)-1)] = 1;
}
}
/* printk("todo: %x %x %x %x\n",(short)todo[0],(short)todo[1],
(short)todo[2],(short)todo[3]);
*/
while (1)
{
__u32 Last[4];
unsigned long Count = 0;
/* During erase bit 7 is held low and bit 6 toggles, we watch this,
should it stop toggling or go high then the erase is completed,
or this is not really flash ;> */
switch (map->buswidth) {
case 1:
Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
break;
case 2:
Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
break;
case 3:
Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
break;
}
Count = 3;
while (todo_left != 0)
{
for (J = 0; J != 4; J++)
{
__u8 Byte1 = (Last[(Count-1)%4] >> (J*8)) & 0xFF;
__u8 Byte2 = (Last[(Count-2)%4] >> (J*8)) & 0xFF;
__u8 Byte3 = (Last[(Count-3)%4] >> (J*8)) & 0xFF;
if (todo[J] == 0)
continue;
if ((Byte1 & (1 << 7)) == 0 && Byte1 != Byte2)
{
// printk("Check %x %x %x\n",(short)J,(short)Byte1,(short)Byte2);
continue;
}
if (Byte1 == Byte2)
{
jedec_flash_failed(Byte3);
return -EIO;
}
todo[J] = 0;
todo_left--;
}
/* if (NoTime == 0)
Time += HZ/10 - schedule_timeout(HZ/10);*/
NoTime = 0;
switch (map->buswidth) {
case 1:
Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
break;
case 2:
Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
break;
case 4:
Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
break;
}
Count++;
/* // Count time, max of 15s per sector (according to AMD)
if (Time > 15*len/mtd->erasesize*HZ)
{
printk("mtd: Flash Erase Timed out\n");
return -EIO;
} */
}
// Skip to the next chip if we used chip erase
if (chip->length == chip->size)
off = chip->size;
else
off += chip->sectorsize;
if (off >= chip->length)
break;
NoTime = 1;
}
for (J = 0; priv->chips[J].jedec != 0 && J < MAX_JEDEC_CHIPS; J++)
{
if ((priv->chips[J].base & (~((1<<chip->addrshift)-1))) ==
(chip->base & (~((1<<chip->addrshift)-1))))
priv->chips[J].length = 0;
}
}
//printk("done\n");
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
#undef flread
#undef flwrite
}
/* This is the simple flash writing function. It writes to every byte, in
sequence. It takes care of how to properly address the flash if
the flash is interleved. It can only be used if all the chips in the
array are identical!*/
static int flash_write(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, const u_char *buf)
{
/* Does IO to the currently selected chip. It takes the bank addressing
base (which is divisible by the chip size) adds the necessary lower bits
of addrshift (interleave index) and then adds the control register index. */
#define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
#define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
struct map_info *map = mtd->priv;
struct jedec_private *priv = map->fldrv_priv;
unsigned long base;
unsigned long off;
size_t save_len = len;
if (start + len > mtd->size)
return -EIO;
//printk("Here");
//printk("flash_write: start is %x, len is %x\n",start,(unsigned long)len);
while (len != 0)
{
struct jedec_flash_chip *chip = priv->chips;
unsigned long bank;
unsigned long boffset;
// Compute the base of the flash.
off = ((unsigned long)start) % (chip->size << chip->addrshift);
base = start - off;
// Perform banked addressing translation.
bank = base & (~(priv->bank_fill[0]-1));
boffset = base & (priv->bank_fill[0]-1);
bank = (bank/priv->bank_fill[0])*my_bank_size;
base = bank + boffset;
// printk("Flasing %X %X %X\n",base,chip->size,len);
// printk("off is %x, compare with %x\n",off,chip->size << chip->addrshift);
// Loop over this page
for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++)
{
unsigned char oldbyte = map_read8(map,base+off);
unsigned char Last[4];
unsigned long Count = 0;
if (oldbyte == *buf) {
// printk("oldbyte and *buf is %x,len is %x\n",oldbyte,len);
continue;
}
if (((~oldbyte) & *buf) != 0)
printk("mtd: warn: Trying to set a 0 to a 1\n");
// Write
flwrite(0xAA,0x555);
flwrite(0x55,0x2AA);
flwrite(0xA0,0x555);
map_write8(map,*buf,base + off);
Last[0] = map_read8(map,base + off);
Last[1] = map_read8(map,base + off);
Last[2] = map_read8(map,base + off);
/* Wait for the flash to finish the operation. We store the last 4
status bytes that have been retrieved so we can determine why
it failed. The toggle bits keep toggling when there is a
failure */
for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
Count < 10000; Count++)
Last[Count % 4] = map_read8(map,base + off);
if (Last[(Count - 1) % 4] != *buf)
{
jedec_flash_failed(Last[(Count - 3) % 4]);
return -EIO;
}
}
}
*retlen = save_len;
return 0;
}
/* This is used to enhance the speed of the erase routine,
when things are being done to multiple chips it is possible to
parallize the operations, particularly full memory erases of multi
chip memories benifit */
static void jedec_flash_chip_scan(struct jedec_private *priv,unsigned long start,
unsigned long len)
{
unsigned int I;
// Zero the records
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
priv->chips[I].start = priv->chips[I].length = 0;
// Intersect the region with each chip
for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
{
struct jedec_flash_chip *chip = priv->chips + I;
unsigned long ByteStart;
unsigned long ChipEndByte = chip->offset + (chip->size << chip->addrshift);
// End is before this chip or the start is after it
if (start+len < chip->offset ||
ChipEndByte - (1 << chip->addrshift) < start)
continue;
if (start < chip->offset)
{
ByteStart = chip->offset;
chip->start = 0;
}
else
{
chip->start = (start - chip->offset + (1 << chip->addrshift)-1) >> chip->addrshift;
ByteStart = start;
}
if (start + len >= ChipEndByte)
chip->length = (ChipEndByte - ByteStart) >> chip->addrshift;
else
chip->length = (start + len - ByteStart + (1 << chip->addrshift)-1) >> chip->addrshift;
}
}
int __init jedec_init(void)
{
register_mtd_chip_driver(&jedec_chipdrv);
return 0;
}
static void __exit jedec_exit(void)
{
unregister_mtd_chip_driver(&jedec_chipdrv);
}
module_init(jedec_init);
module_exit(jedec_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com> et al.");
MODULE_DESCRIPTION("Old MTD chip driver for JEDEC-compliant flash chips");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
/*
* Common code to handle absent "placeholder" devices
* Copyright 2001 Resilience Corporation <ebrower@resilience.com>
* $Id: map_absent.c,v 1.5 2004/11/16 18:29:00 dwmw2 Exp $
*
* This map driver is used to allocate "placeholder" MTD
* devices on systems that have socketed/removable media.
* Use of this driver as a fallback preserves the expected
* registration of MTD device nodes regardless of probe outcome.
* A usage example is as follows:
*
* my_dev[i] = do_map_probe("cfi", &my_map[i]);
* if(NULL == my_dev[i]) {
* my_dev[i] = do_map_probe("map_absent", &my_map[i]);
* }
*
* Any device 'probed' with this driver will return -ENODEV
* upon open.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/compatmac.h>
static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static int map_absent_erase (struct mtd_info *, struct erase_info *);
static void map_absent_sync (struct mtd_info *);
static struct mtd_info *map_absent_probe(struct map_info *map);
static void map_absent_destroy (struct mtd_info *);
static struct mtd_chip_driver map_absent_chipdrv = {
.probe = map_absent_probe,
.destroy = map_absent_destroy,
.name = "map_absent",
.module = THIS_MODULE
};
static struct mtd_info *map_absent_probe(struct map_info *map)
{
struct mtd_info *mtd;
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd) {
return NULL;
}
memset(mtd, 0, sizeof(*mtd));
map->fldrv = &map_absent_chipdrv;
mtd->priv = map;
mtd->name = map->name;
mtd->type = MTD_ABSENT;
mtd->size = map->size;
mtd->erase = map_absent_erase;
mtd->read = map_absent_read;
mtd->write = map_absent_write;
mtd->sync = map_absent_sync;
mtd->flags = 0;
mtd->erasesize = PAGE_SIZE;
__module_get(THIS_MODULE);
return mtd;
}
static int map_absent_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
*retlen = 0;
return -ENODEV;
}
static int map_absent_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
*retlen = 0;
return -ENODEV;
}
static int map_absent_erase(struct mtd_info *mtd, struct erase_info *instr)
{
return -ENODEV;
}
static void map_absent_sync(struct mtd_info *mtd)
{
/* nop */
}
static void map_absent_destroy(struct mtd_info *mtd)
{
/* nop */
}
static int __init map_absent_init(void)
{
register_mtd_chip_driver(&map_absent_chipdrv);
return 0;
}
static void __exit map_absent_exit(void)
{
unregister_mtd_chip_driver(&map_absent_chipdrv);
}
module_init(map_absent_init);
module_exit(map_absent_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Resilience Corporation - Eric Brower <ebrower@resilience.com>");
MODULE_DESCRIPTION("Placeholder MTD chip driver for 'absent' chips");

143
drivers/mtd/chips/map_ram.c Normal file
View File

@@ -0,0 +1,143 @@
/*
* Common code to handle map devices which are simple RAM
* (C) 2000 Red Hat. GPL'd.
* $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/compatmac.h>
static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static int mapram_erase (struct mtd_info *, struct erase_info *);
static void mapram_nop (struct mtd_info *);
static struct mtd_info *map_ram_probe(struct map_info *map);
static struct mtd_chip_driver mapram_chipdrv = {
.probe = map_ram_probe,
.name = "map_ram",
.module = THIS_MODULE
};
static struct mtd_info *map_ram_probe(struct map_info *map)
{
struct mtd_info *mtd;
/* Check the first byte is RAM */
#if 0
map_write8(map, 0x55, 0);
if (map_read8(map, 0) != 0x55)
return NULL;
map_write8(map, 0xAA, 0);
if (map_read8(map, 0) != 0xAA)
return NULL;
/* Check the last byte is RAM */
map_write8(map, 0x55, map->size-1);
if (map_read8(map, map->size-1) != 0x55)
return NULL;
map_write8(map, 0xAA, map->size-1);
if (map_read8(map, map->size-1) != 0xAA)
return NULL;
#endif
/* OK. It seems to be RAM. */
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd)
return NULL;
memset(mtd, 0, sizeof(*mtd));
map->fldrv = &mapram_chipdrv;
mtd->priv = map;
mtd->name = map->name;
mtd->type = MTD_RAM;
mtd->size = map->size;
mtd->erase = mapram_erase;
mtd->read = mapram_read;
mtd->write = mapram_write;
mtd->sync = mapram_nop;
mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
mtd->erasesize = PAGE_SIZE;
while(mtd->size & (mtd->erasesize - 1))
mtd->erasesize >>= 1;
__module_get(THIS_MODULE);
return mtd;
}
static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
map_copy_from(map, buf, from, len);
*retlen = len;
return 0;
}
static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
map_copy_to(map, to, buf, len);
*retlen = len;
return 0;
}
static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr)
{
/* Yeah, it's inefficient. Who cares? It's faster than a _real_
flash erase. */
struct map_info *map = mtd->priv;
map_word allff;
unsigned long i;
allff = map_word_ff(map);
for (i=0; i<instr->len; i += map_bankwidth(map))
map_write(map, allff, instr->addr + i);
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
static void mapram_nop(struct mtd_info *mtd)
{
/* Nothing to see here */
}
static int __init map_ram_init(void)
{
register_mtd_chip_driver(&mapram_chipdrv);
return 0;
}
static void __exit map_ram_exit(void)
{
unregister_mtd_chip_driver(&mapram_chipdrv);
}
module_init(map_ram_init);
module_exit(map_ram_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("MTD chip driver for RAM chips");

View File

@@ -0,0 +1,94 @@
/*
* Common code to handle map devices which are simple ROM
* (C) 2000 Red Hat. GPL'd.
* $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/byteorder.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/compatmac.h>
static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static void maprom_nop (struct mtd_info *);
static struct mtd_info *map_rom_probe(struct map_info *map);
static struct mtd_chip_driver maprom_chipdrv = {
.probe = map_rom_probe,
.name = "map_rom",
.module = THIS_MODULE
};
static struct mtd_info *map_rom_probe(struct map_info *map)
{
struct mtd_info *mtd;
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd)
return NULL;
memset(mtd, 0, sizeof(*mtd));
map->fldrv = &maprom_chipdrv;
mtd->priv = map;
mtd->name = map->name;
mtd->type = MTD_ROM;
mtd->size = map->size;
mtd->read = maprom_read;
mtd->write = maprom_write;
mtd->sync = maprom_nop;
mtd->flags = MTD_CAP_ROM;
mtd->erasesize = 131072;
while(mtd->size & (mtd->erasesize - 1))
mtd->erasesize >>= 1;
__module_get(THIS_MODULE);
return mtd;
}
static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
map_copy_from(map, buf, from, len);
*retlen = len;
return 0;
}
static void maprom_nop(struct mtd_info *mtd)
{
/* Nothing to see here */
}
static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
printk(KERN_NOTICE "maprom_write called\n");
return -EIO;
}
static int __init map_rom_init(void)
{
register_mtd_chip_driver(&maprom_chipdrv);
return 0;
}
static void __exit map_rom_exit(void)
{
unregister_mtd_chip_driver(&maprom_chipdrv);
}
module_init(map_rom_init);
module_exit(map_rom_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("MTD chip driver for ROM chips");

596
drivers/mtd/chips/sharp.c Normal file
View File

@@ -0,0 +1,596 @@
/*
* MTD chip driver for pre-CFI Sharp flash chips
*
* Copyright 2000,2001 David A. Schleef <ds@schleef.org>
* 2000,2001 Lineo, Inc.
*
* $Id: sharp.c,v 1.14 2004/08/09 13:19:43 dwmw2 Exp $
*
* Devices supported:
* LH28F016SCT Symmetrical block flash memory, 2Mx8
* LH28F008SCT Symmetrical block flash memory, 1Mx8
*
* Documentation:
* http://www.sharpmeg.com/datasheets/memic/flashcmp/
* http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf
* 016sctl9.pdf
*
* Limitations:
* This driver only supports 4x1 arrangement of chips.
* Not tested on anything but PowerPC.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
#include <linux/delay.h>
#include <linux/init.h>
#define CMD_RESET 0xffffffff
#define CMD_READ_ID 0x90909090
#define CMD_READ_STATUS 0x70707070
#define CMD_CLEAR_STATUS 0x50505050
#define CMD_BLOCK_ERASE_1 0x20202020
#define CMD_BLOCK_ERASE_2 0xd0d0d0d0
#define CMD_BYTE_WRITE 0x40404040
#define CMD_SUSPEND 0xb0b0b0b0
#define CMD_RESUME 0xd0d0d0d0
#define CMD_SET_BLOCK_LOCK_1 0x60606060
#define CMD_SET_BLOCK_LOCK_2 0x01010101
#define CMD_SET_MASTER_LOCK_1 0x60606060
#define CMD_SET_MASTER_LOCK_2 0xf1f1f1f1
#define CMD_CLEAR_BLOCK_LOCKS_1 0x60606060
#define CMD_CLEAR_BLOCK_LOCKS_2 0xd0d0d0d0
#define SR_READY 0x80808080 // 1 = ready
#define SR_ERASE_SUSPEND 0x40404040 // 1 = block erase suspended
#define SR_ERROR_ERASE 0x20202020 // 1 = error in block erase or clear lock bits
#define SR_ERROR_WRITE 0x10101010 // 1 = error in byte write or set lock bit
#define SR_VPP 0x08080808 // 1 = Vpp is low
#define SR_WRITE_SUSPEND 0x04040404 // 1 = byte write suspended
#define SR_PROTECT 0x02020202 // 1 = lock bit set
#define SR_RESERVED 0x01010101
#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT)
/* Configuration options */
#undef AUTOUNLOCK /* automatically unlocks blocks before erasing */
struct mtd_info *sharp_probe(struct map_info *);
static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd);
static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf);
static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, const u_char *buf);
static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr);
static void sharp_sync(struct mtd_info *mtd);
static int sharp_suspend(struct mtd_info *mtd);
static void sharp_resume(struct mtd_info *mtd);
static void sharp_destroy(struct mtd_info *mtd);
static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, __u32 datum);
static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr);
#ifdef AUTOUNLOCK
static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr);
#endif
struct sharp_info{
struct flchip *chip;
int bogus;
int chipshift;
int numchips;
struct flchip chips[1];
};
struct mtd_info *sharp_probe(struct map_info *map);
static void sharp_destroy(struct mtd_info *mtd);
static struct mtd_chip_driver sharp_chipdrv = {
.probe = sharp_probe,
.destroy = sharp_destroy,
.name = "sharp",
.module = THIS_MODULE
};
struct mtd_info *sharp_probe(struct map_info *map)
{
struct mtd_info *mtd = NULL;
struct sharp_info *sharp = NULL;
int width;
mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
if(!mtd)
return NULL;
sharp = kmalloc(sizeof(*sharp), GFP_KERNEL);
if(!sharp) {
kfree(mtd);
return NULL;
}
memset(mtd, 0, sizeof(*mtd));
width = sharp_probe_map(map,mtd);
if(!width){
kfree(mtd);
kfree(sharp);
return NULL;
}
mtd->priv = map;
mtd->type = MTD_NORFLASH;
mtd->erase = sharp_erase;
mtd->read = sharp_read;
mtd->write = sharp_write;
mtd->sync = sharp_sync;
mtd->suspend = sharp_suspend;
mtd->resume = sharp_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
memset(sharp, 0, sizeof(*sharp));
sharp->chipshift = 23;
sharp->numchips = 1;
sharp->chips[0].start = 0;
sharp->chips[0].state = FL_READY;
sharp->chips[0].mutex = &sharp->chips[0]._spinlock;
sharp->chips[0].word_write_time = 0;
init_waitqueue_head(&sharp->chips[0].wq);
spin_lock_init(&sharp->chips[0]._spinlock);
map->fldrv = &sharp_chipdrv;
map->fldrv_priv = sharp;
__module_get(THIS_MODULE);
return mtd;
}
static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd)
{
unsigned long tmp;
unsigned long base = 0;
u32 read0, read4;
int width = 4;
tmp = map_read32(map, base+0);
map_write32(map, CMD_READ_ID, base+0);
read0=map_read32(map, base+0);
read4=map_read32(map, base+4);
if(read0 == 0x89898989){
printk("Looks like sharp flash\n");
switch(read4){
case 0xaaaaaaaa:
case 0xa0a0a0a0:
/* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/
/* a0 - LH28F016SCT-Z4 2Mx8, 32 64k blocks*/
mtd->erasesize = 0x10000 * width;
mtd->size = 0x200000 * width;
return width;
case 0xa6a6a6a6:
/* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/
/* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/
mtd->erasesize = 0x10000 * width;
mtd->size = 0x100000 * width;
return width;
#if 0
case 0x00000000: /* unknown */
/* XX - LH28F004SCT 512kx8, 8 64k blocks*/
mtd->erasesize = 0x10000 * width;
mtd->size = 0x80000 * width;
return width;
#endif
default:
printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n",
read0,read4);
}
}else if((map_read32(map, base+0) == CMD_READ_ID)){
/* RAM, probably */
printk("Looks like RAM\n");
map_write32(map, tmp, base+0);
}else{
printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n",
read0,read4);
}
return 0;
}
/* This function returns with the chip->mutex lock held. */
static int sharp_wait(struct map_info *map, struct flchip *chip)
{
__u16 status;
unsigned long timeo = jiffies + HZ;
DECLARE_WAITQUEUE(wait, current);
int adr = 0;
retry:
spin_lock_bh(chip->mutex);
switch(chip->state){
case FL_READY:
map_write32(map,CMD_READ_STATUS,adr);
chip->state = FL_STATUS;
case FL_STATUS:
status = map_read32(map,adr);
//printk("status=%08x\n",status);
udelay(100);
if((status & SR_READY)!=SR_READY){
//printk(".status=%08x\n",status);
udelay(100);
}
break;
default:
printk("Waiting for chip\n");
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
spin_unlock_bh(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
if(signal_pending(current))
return -EINTR;
timeo = jiffies + HZ;
goto retry;
}
map_write32(map,CMD_RESET, adr);
chip->state = FL_READY;
return 0;
}
static void sharp_release(struct flchip *chip)
{
wake_up(&chip->wq);
spin_unlock_bh(chip->mutex);
}
static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
struct sharp_info *sharp = map->fldrv_priv;
int chipnum;
int ret = 0;
int ofs = 0;
chipnum = (from >> sharp->chipshift);
ofs = from & ((1 << sharp->chipshift)-1);
*retlen = 0;
while(len){
unsigned long thislen;
if(chipnum>=sharp->numchips)
break;
thislen = len;
if(ofs+thislen >= (1<<sharp->chipshift))
thislen = (1<<sharp->chipshift) - ofs;
ret = sharp_wait(map,&sharp->chips[chipnum]);
if(ret<0)
break;
map_copy_from(map,buf,ofs,thislen);
sharp_release(&sharp->chips[chipnum]);
*retlen += thislen;
len -= thislen;
buf += thislen;
ofs = 0;
chipnum++;
}
return ret;
}
static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
struct sharp_info *sharp = map->fldrv_priv;
int ret = 0;
int i,j;
int chipnum;
unsigned long ofs;
union { u32 l; unsigned char uc[4]; } tbuf;
*retlen = 0;
while(len){
tbuf.l = 0xffffffff;
chipnum = to >> sharp->chipshift;
ofs = to & ((1<<sharp->chipshift)-1);
j=0;
for(i=ofs&3;i<4 && len;i++){
tbuf.uc[i] = *buf;
buf++;
to++;
len--;
j++;
}
sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l);
if(ret<0)
return ret;
(*retlen)+=j;
}
return 0;
}
static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, __u32 datum)
{
int ret;
int timeo;
int try;
int i;
int status = 0;
ret = sharp_wait(map,chip);
for(try=0;try<10;try++){
map_write32(map,CMD_BYTE_WRITE,adr);
/* cpu_to_le32 -> hack to fix the writel be->le conversion */
map_write32(map,cpu_to_le32(datum),adr);
chip->state = FL_WRITING;
timeo = jiffies + (HZ/2);
map_write32(map,CMD_READ_STATUS,adr);
for(i=0;i<100;i++){
status = map_read32(map,adr);
if((status & SR_READY)==SR_READY)
break;
}
if(i==100){
printk("sharp: timed out writing\n");
}
if(!(status&SR_ERRORS))
break;
printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status);
map_write32(map,CMD_CLEAR_STATUS,adr);
}
map_write32(map,CMD_RESET,adr);
chip->state = FL_READY;
wake_up(&chip->wq);
spin_unlock_bh(chip->mutex);
return 0;
}
static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
struct sharp_info *sharp = map->fldrv_priv;
unsigned long adr,len;
int chipnum, ret=0;
//printk("sharp_erase()\n");
if(instr->addr & (mtd->erasesize - 1))
return -EINVAL;
if(instr->len & (mtd->erasesize - 1))
return -EINVAL;
if(instr->len + instr->addr > mtd->size)
return -EINVAL;
chipnum = instr->addr >> sharp->chipshift;
adr = instr->addr & ((1<<sharp->chipshift)-1);
len = instr->len;
while(len){
ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr);
if(ret)return ret;
adr += mtd->erasesize;
len -= mtd->erasesize;
if(adr >> sharp->chipshift){
adr = 0;
chipnum++;
if(chipnum>=sharp->numchips)
break;
}
}
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
int ret;
unsigned long timeo;
int status;
DECLARE_WAITQUEUE(wait, current);
map_write32(map,CMD_READ_STATUS,adr);
status = map_read32(map,adr);
timeo = jiffies + HZ;
while(time_before(jiffies, timeo)){
map_write32(map,CMD_READ_STATUS,adr);
status = map_read32(map,adr);
if((status & SR_READY)==SR_READY){
ret = 0;
goto out;
}
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
//spin_unlock_bh(chip->mutex);
schedule_timeout(1);
schedule();
remove_wait_queue(&chip->wq, &wait);
//spin_lock_bh(chip->mutex);
if (signal_pending(current)){
ret = -EINTR;
goto out;
}
}
ret = -ETIME;
out:
return ret;
}
static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
int ret;
//int timeo;
int status;
//int i;
//printk("sharp_erase_oneblock()\n");
#ifdef AUTOUNLOCK
/* This seems like a good place to do an unlock */
sharp_unlock_oneblock(map,chip,adr);
#endif
map_write32(map,CMD_BLOCK_ERASE_1,adr);
map_write32(map,CMD_BLOCK_ERASE_2,adr);
chip->state = FL_ERASING;
ret = sharp_do_wait_for_ready(map,chip,adr);
if(ret<0)return ret;
map_write32(map,CMD_READ_STATUS,adr);
status = map_read32(map,adr);
if(!(status&SR_ERRORS)){
map_write32(map,CMD_RESET,adr);
chip->state = FL_READY;
//spin_unlock_bh(chip->mutex);
return 0;
}
printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status);
map_write32(map,CMD_CLEAR_STATUS,adr);
//spin_unlock_bh(chip->mutex);
return -EIO;
}
#ifdef AUTOUNLOCK
static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
unsigned long adr)
{
int i;
int status;
map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
udelay(100);
status = map_read32(map,adr);
printk("status=%08x\n",status);
for(i=0;i<1000;i++){
//map_write32(map,CMD_READ_STATUS,adr);
status = map_read32(map,adr);
if((status & SR_READY)==SR_READY)
break;
udelay(100);
}
if(i==1000){
printk("sharp: timed out unlocking block\n");
}
if(!(status&SR_ERRORS)){
map_write32(map,CMD_RESET,adr);
chip->state = FL_READY;
return;
}
printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status);
map_write32(map,CMD_CLEAR_STATUS,adr);
}
#endif
static void sharp_sync(struct mtd_info *mtd)
{
//printk("sharp_sync()\n");
}
static int sharp_suspend(struct mtd_info *mtd)
{
printk("sharp_suspend()\n");
return -EINVAL;
}
static void sharp_resume(struct mtd_info *mtd)
{
printk("sharp_resume()\n");
}
static void sharp_destroy(struct mtd_info *mtd)
{
printk("sharp_destroy()\n");
}
int __init sharp_probe_init(void)
{
printk("MTD Sharp chip driver <ds@lineo.com>\n");
register_mtd_chip_driver(&sharp_chipdrv);
return 0;
}
static void __exit sharp_probe_exit(void)
{
unregister_mtd_chip_driver(&sharp_chipdrv);
}
module_init(sharp_probe_init);
module_exit(sharp_probe_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Schleef <ds@schleef.org>");
MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips");