sharp.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:13k
- /*
- * 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.7 2002/02/13 15:49:07 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 flashn");
- 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%08xn",
- read0,read4);
- }
- }else if((map->read32(map, base+0) == CMD_READ_ID)){
- /* RAM, probably */
- printk("Looks like RAMn");
- map->write32(map, tmp, base+0);
- }else{
- printk("Doesn't look like sharp flash, 0x%08x 0x%08xn",
- 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=%08xn",status);
- udelay(100);
- if((status & SR_READY)!=SR_READY){
- //printk(".status=%08xn",status);
- udelay(100);
- }
- break;
- default:
- printk("Waiting for chipn");
- 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 writingn");
- }
- if(!(status&SR_ERRORS))
- break;
- printk("sharp: error writing byte at addr=%08lx status=%08xn",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(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=%08xn",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=%08xn",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 blockn");
- }
- if(!(status&SR_ERRORS)){
- map->write32(map,CMD_RESET,adr);
- chip->state = FL_READY;
- return;
- }
- printk("sharp: error unlocking block at addr=%08lx status=%08xn",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");