Commit ba7cc09c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge git://git.infradead.org/mtd-2.6

* git://git.infradead.org/mtd-2.6: (21 commits)
  [MTD] [CHIPS] Remove MTD_OBSOLETE_CHIPS (jedec, amd_flash, sharp)
  [MTD] Delete allegedly obsolete "bank_size" field of mtd_info.
  [MTD] Remove unnecessary user space check from mtd.h.
  [MTD] [MAPS] Remove flash maps for no longer supported 405LP boards
  [MTD] [MAPS] Fix missing printk() parameter in physmap_of.c MTD driver
  [MTD] [NAND] platform NAND driver: add driver
  [MTD] [NAND] platform NAND driver: update header
  [JFFS2] Simplify and clean up jffs2_add_tn_to_tree() some more.
  [JFFS2] Remove another bogus optimisation in jffs2_add_tn_to_tree()
  [JFFS2] Remove broken insert_point optimisation in jffs2_add_tn_to_tree()
  [JFFS2] Remember to calculate overlap on nodes which replace older nodes
  [JFFS2] Don't advance c->wbuf_ofs to next eraseblock after wbuf flush
  [MTD] [NAND] at91_nand.c: CMDLINE_PARTS support
  [MTD] [NAND] Tidy up handling of page number in nand_block_bad()
  [MTD] block2mtd_paramline[] mustn't be __initdata
  [MTD] [NAND] Support multiple chips in CAFÉ driver
  [MTD] [NAND] Rename cafe.c to cafe_nand.c and remove the multi-obj magic
  [MTD] [NAND] Use rslib for CAFÉ ECC
  [RSLIB] Support non-canonical GF representations
  [JFFS2] Remove dead file histo_mips.h
  ...
parents d84c4124 b7aa48be
......@@ -2648,6 +2648,12 @@ M: corbet@lwn.net
L: video4linux-list@redhat.com
S: Maintained
ONENAND FLASH DRIVER
P: Kyungmin Park
M: kyungmin.park@samsung.com
L: linux-mtd@lists.infradead.org
S: Maintained
ONSTREAM SCSI TAPE DRIVER
P: Willem Riede
M: osst@riede.org
......
# 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
......
#
# 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
This diff is collapsed.
This diff is collapsed.
/*
* 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);