Viewing file: sharp.c (12.86 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* * 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.6 2001/10/02 15:05:12 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/version.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/cfi.h> #include <linux/delay.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) 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;
MOD_INC_USE_COUNT; 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; } }
if(instr->callback) instr->callback(instr);
return 0; }
static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip, unsigned long adr) { int ret; int timeo; int status; DECLARE_WAITQUEUE(wait, current);
map->write32(map,CMD_READ_STATUS,adr); status = map->read32(map,adr);
timeo = jiffies + HZ;
while(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");
|