patch-2.4.6 linux/drivers/mtd/chips/sharp.c

Next file: linux/drivers/mtd/devices/Config.in
Previous file: linux/drivers/mtd/chips/map_rom.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/drivers/mtd/chips/sharp.c linux/drivers/mtd/chips/sharp.c
@@ -0,0 +1,593 @@
+/*
+ * 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.4 2001/04/29 16:21:17 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/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");
+
+}
+
+#if LINUX_VERSION_CODE < 0x020212 && defined(MODULE)
+#define sharp_probe_init init_module
+#define sharp_probe_exit cleanup_module
+#endif
+
+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);
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)