[MTD] [CHIPS] Remove MTD_OBSOLETE_CHIPS (jedec, amd_flash, sharp)

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
This commit is contained in:
David Woodhouse 2007-05-09 13:34:37 +01:00
parent 42f209d3c9
commit b7aa48be1e
5 changed files with 0 additions and 2976 deletions

View file

@ -1,5 +1,4 @@
# drivers/mtd/chips/Kconfig
# $Id: Kconfig,v 1.18 2005/11/07 11:14:22 gleixner Exp $
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
@ -231,45 +230,6 @@ config MTD_ABSENT
the system regardless of media presence. Device nodes created
with this driver will return -ENODEV upon access.
config MTD_OBSOLETE_CHIPS
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_OBSOLETE_CHIPS && BROKEN
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_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_OBSOLETE_CHIPS && BROKEN
help
Enable 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 || MTD_CFI_AMDSTD) && EXPERIMENTAL && ARCH_MTD_XIP

View file

@ -1,19 +1,15 @@
#
# linux/drivers/chips/Makefile
#
# $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $
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

View file

@ -1,935 +0,0 @@
/* 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/slab.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 = kzalloc(sizeof(struct mtd_info) + sizeof(struct jedec_private), GFP_KERNEL);
if (!MTD)
return NULL;
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->writesize = 1;
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");

View file

@ -1,601 +0,0 @@
/*
* 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.17 2005/11/29 14:28:28 gleixner 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>
#include <linux/slab.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 */
static 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];
};
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
};
static struct mtd_info *sharp_probe(struct map_info *map)
{
struct mtd_info *mtd = NULL;
struct sharp_info *sharp = NULL;
int width;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
if(!mtd)
return NULL;
sharp = kzalloc(sizeof(*sharp), GFP_KERNEL);
if(!sharp) {
kfree(mtd);
return NULL;
}
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->writesize = 1;
mtd->name = map->name;
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 inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr)
{
map_word map_cmd;
map_cmd.x[0] = cmd;
map_write(map, map_cmd, adr);
}
static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd)
{
map_word tmp, read0, read4;
unsigned long base = 0;
int width = 4;
tmp = map_read(map, base+0);
sharp_send_cmd(map, CMD_READ_ID, base+0);
read0 = map_read(map, base+0);
read4 = map_read(map, base+4);
if(read0.x[0] == 0x89898989){
printk("Looks like sharp flash\n");
switch(read4.x[0]){
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%08lx 0x%08lx\n",
read0.x[0], read4.x[0]);
}
}else if((map_read(map, base+0).x[0] == CMD_READ_ID)){
/* RAM, probably */
printk("Looks like RAM\n");
map_write(map, tmp, base+0);
}else{
printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n",
read0.x[0], read4.x[0]);
}
return 0;
}
/* This function returns with the chip->mutex lock held. */
static int sharp_wait(struct map_info *map, struct flchip *chip)
{
int i;
map_word 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:
sharp_send_cmd(map, CMD_READ_STATUS, adr);
chip->state = FL_STATUS;
case FL_STATUS:
for(i=0;i<100;i++){
status = map_read(map, adr);
if((status.x[0] & SR_READY)==SR_READY)
break;
udelay(1);
}
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;
}
sharp_send_cmd(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;
map_word data, status;
status.x[0] = 0;
ret = sharp_wait(map,chip);
for(try=0;try<10;try++){
sharp_send_cmd(map, CMD_BYTE_WRITE, adr);
/* cpu_to_le32 -> hack to fix the writel be->le conversion */
data.x[0] = cpu_to_le32(datum);
map_write(map, data, adr);
chip->state = FL_WRITING;
timeo = jiffies + (HZ/2);
sharp_send_cmd(map, CMD_READ_STATUS, adr);
for(i=0;i<100;i++){
status = map_read(map, adr);
if((status.x[0] & SR_READY) == SR_READY)
break;
}
if(i==100){
printk("sharp: timed out writing\n");
}
if(!(status.x[0] & SR_ERRORS))
break;
printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]);
sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
}
sharp_send_cmd(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;
map_word status;
DECLARE_WAITQUEUE(wait, current);
sharp_send_cmd(map, CMD_READ_STATUS, adr);
status = map_read(map, adr);
timeo = jiffies + HZ;
while(time_before(jiffies, timeo)){
sharp_send_cmd(map, CMD_READ_STATUS, adr);
status = map_read(map, adr);
if((status.x[0] & 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;
map_word 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
sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr);
sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr);
chip->state = FL_ERASING;
ret = sharp_do_wait_for_ready(map,chip,adr);
if(ret<0)return ret;
sharp_send_cmd(map, CMD_READ_STATUS, adr);
status = map_read(map, adr);
if(!(status.x[0] & SR_ERRORS)){
sharp_send_cmd(map, CMD_RESET, adr);
chip->state = FL_READY;
//spin_unlock_bh(chip->mutex);
return 0;
}
printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]);
sharp_send_cmd(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;
map_word status;
sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr);
sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr);
udelay(100);
status = map_read(map, adr);
printk("status=%08lx\n", status.x[0]);
for(i=0;i<1000;i++){
//sharp_send_cmd(map, CMD_READ_STATUS, adr);
status = map_read(map, adr);
if((status.x[0] & SR_READY) == SR_READY)
break;
udelay(100);
}
if(i==1000){
printk("sharp: timed out unlocking block\n");
}
if(!(status.x[0] & SR_ERRORS)){
sharp_send_cmd(map, CMD_RESET, adr);
chip->state = FL_READY;
return;
}
printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]);
sharp_send_cmd(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");
}
static 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");