601 lines · 13.2 KB · C
RawDownload
/* * 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 AUTOUNLOCKstatic 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);

Lines 1–500 of 601.