diff options
| author | wbx <wbx@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-03-16 13:50:00 +0000 | 
|---|---|---|
| committer | wbx <wbx@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2005-03-16 13:50:00 +0000 | 
| commit | 4f531230a3c9c8984c5a8e8b38874be7f608f2d1 (patch) | |
| tree | 1f132d8a78f19c1172bf4dbd9fba583bbb45423d /package/linux/kernel-source/drivers/mtd | |
| parent | c7df5a6a2cd4103e0c52018ba81c0fd0ee60f74d (diff) | |
add all source code from linksys/broadcom which is free, to cvs for better maintainence inside
openwrt. this gives us the ability to better support different hardware models, without changing
any external tar-balls. only et.o and wl.o is missing and is fetched from my webserver.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk/openwrt@379 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/linux/kernel-source/drivers/mtd')
3 files changed, 1374 insertions, 0 deletions
diff --git a/package/linux/kernel-source/drivers/mtd/chips/cfi_cmdset_0701.c b/package/linux/kernel-source/drivers/mtd/chips/cfi_cmdset_0701.c new file mode 100644 index 000000000..5087477bb --- /dev/null +++ b/package/linux/kernel-source/drivers/mtd/chips/cfi_cmdset_0701.c @@ -0,0 +1,855 @@ +/* + * Common Flash Interface support: + *   SST Standard Vendor Command Set (ID 0x0701) + * + * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp> + * + * 2_by_8 routines added by Simon Munton + * + * This code is GPL + * + * $Id$ + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <asm/io.h> +#include <asm/byteorder.h> + +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mtd/map.h> +#include <linux/mtd/cfi.h> + +static int cfi_sststd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_sststd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_sststd_erase_onesize(struct mtd_info *, struct erase_info *); +static int cfi_sststd_erase_varsize(struct mtd_info *, struct erase_info *); +static void cfi_sststd_sync (struct mtd_info *); +static int cfi_sststd_suspend (struct mtd_info *); +static void cfi_sststd_resume (struct mtd_info *); + +static void cfi_sststd_destroy(struct mtd_info *); + +struct mtd_info *cfi_cmdset_0701(struct map_info *, int); +static struct mtd_info *cfi_sststd_setup (struct map_info *); + + +static struct mtd_chip_driver cfi_sststd_chipdrv = { +	probe: NULL, /* Not usable directly */ +	destroy: cfi_sststd_destroy, +	name: "cfi_cmdset_0701", +	module: THIS_MODULE +}; + +struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) +{ +	struct cfi_private *cfi = map->fldrv_priv; +	int ofs_factor = cfi->interleave * cfi->device_type; +	int i; +	__u8 major, minor; +	__u32 base = cfi->chips[0].start; + +	if (cfi->cfi_mode==1){ +		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + +		cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL); +		cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL); +		cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL); +		 +		major = cfi_read_query(map, base + (adr+3)*ofs_factor); +		minor = cfi_read_query(map, base + (adr+4)*ofs_factor); +		 +		printk(" SST Query Table v%c.%c at 0x%4.4X\n", +		       major, minor, adr); +		cfi_send_gen_cmd(0xf0, 0x5555, base, map, cfi, cfi->device_type, NULL); +		 +		cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL); +		cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL); +		cfi_send_gen_cmd(0x90, 0x5555, base, map, cfi, cfi->device_type, NULL); +		cfi->mfr = cfi_read_query(map, base); +		cfi->id = cfi_read_query(map, base + ofs_factor); + +		cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL); +		cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL); +		cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL); +		 +		switch (cfi->device_type) { +		case CFI_DEVICETYPE_X16: +			cfi->addr_unlock1 = 0x5555; +			cfi->addr_unlock2 = 0x2AAA; +			break; +		default: +			printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0701 device type %d\n", cfi->device_type); +			return NULL; +		} +	} /* CFI mode */ + +	for (i=0; i< cfi->numchips; i++) { +		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; +		cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; +		cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp; +	}		 +	 +	map->fldrv = &cfi_sststd_chipdrv; +	MOD_INC_USE_COUNT; + +	cfi_send_gen_cmd(0xf0, 0x5555, base, map, cfi, cfi->device_type, NULL); +	return cfi_sststd_setup(map); +} + +static struct mtd_info *cfi_sststd_setup(struct map_info *map) +{ +	struct cfi_private *cfi = map->fldrv_priv; +	struct mtd_info *mtd; +	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; + +	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); +	printk("number of %s chips: %d\n", (cfi->cfi_mode)?"JEDEC":"CFI",cfi->numchips); + +	if (!mtd) { +	  printk("Failed to allocate memory for MTD device\n"); +	  kfree(cfi->cmdset_priv); +	  return NULL; +	} + +	memset(mtd, 0, sizeof(*mtd)); +	mtd->priv = map; +	mtd->type = MTD_NORFLASH; +	/* Also select the correct geometry setup too */  +	mtd->size = devsize * cfi->numchips; +	 +	if (cfi->cfiq->NumEraseRegions == 1) { +		/* No need to muck about with multiple erase sizes */ +		mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; +	} else { +		unsigned long offset = 0; +		int i,j; + +		mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; +		mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); +		if (!mtd->eraseregions) {  +			printk("Failed to allocate memory for MTD erase region info\n"); +			kfree(cfi->cmdset_priv); +			return NULL; +		} +			 +		for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { +			unsigned long ernum, ersize; +			ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; +			ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; +			 +			if (mtd->erasesize < ersize) { +				mtd->erasesize = ersize; +			} +			for (j=0; j<cfi->numchips; j++) { +				mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; +				mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; +				mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; +			} +			offset += (ersize * ernum); +		} + +		// debug +		for (i=0; i<mtd->numeraseregions;i++){ +			printk("%d: offset=0x%x,size=0x%x,blocks=%d\n", +			       i,mtd->eraseregions[i].offset, +			       mtd->eraseregions[i].erasesize, +			       mtd->eraseregions[i].numblocks); +		} +	} + +	switch (CFIDEV_BUSWIDTH) +	{ +	case 1: +	case 2: +	case 4: +		if (mtd->numeraseregions > 1) +			mtd->erase = cfi_sststd_erase_varsize; +		else +			mtd->erase = cfi_sststd_erase_onesize; +		mtd->read = cfi_sststd_read; +		mtd->write = cfi_sststd_write; +		break; + +	default: +	        printk("Unsupported buswidth\n"); +		kfree(mtd); +		kfree(cfi->cmdset_priv); +		return NULL; +		break; +	} +	mtd->sync = cfi_sststd_sync; +	mtd->suspend = cfi_sststd_suspend; +	mtd->resume = cfi_sststd_resume; +	mtd->flags = MTD_CAP_NORFLASH; +	map->fldrv = &cfi_sststd_chipdrv; +	mtd->name = map->name; +	MOD_INC_USE_COUNT; +	return mtd; +} + +static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ +	DECLARE_WAITQUEUE(wait, current); +	unsigned long timeo = jiffies + HZ; + + retry: +	cfi_spin_lock(chip->mutex); + +	if (chip->state != FL_READY){ +	        printk("Waiting for chip to read, status = %d\n", chip->state); +		set_current_state(TASK_UNINTERRUPTIBLE); +		add_wait_queue(&chip->wq, &wait); +                 +		cfi_spin_unlock(chip->mutex); + +		schedule(); +		remove_wait_queue(&chip->wq, &wait); +		timeo = jiffies + HZ; + +		goto retry; +	}	 + +	adr += chip->start; + +	chip->state = FL_READY; + +	map->copy_from(map, buf, adr, len); + +	wake_up(&chip->wq); +	cfi_spin_unlock(chip->mutex); + +	return 0; +} + +static int cfi_sststd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	unsigned long ofs; +	int chipnum; +	int ret = 0; + +	/* ofs: offset within the first chip that the first read should start */ + +	chipnum = (from >> cfi->chipshift); +	ofs = from - (chipnum <<  cfi->chipshift); + + +	*retlen = 0; + +	while (len) { +		unsigned long thislen; + +		if (chipnum >= cfi->numchips) +			break; + +		if ((len + ofs -1) >> cfi->chipshift) +			thislen = (1<<cfi->chipshift) - ofs; +		else +			thislen = len; + +		ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); +		if (ret) +			break; + +		*retlen += thislen; +		len -= thislen; +		buf += thislen; + +		ofs = 0; +		chipnum++; +	} +	return ret; +} + +static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) +{ +	unsigned long timeo = jiffies + HZ; +	unsigned int Last[4]; +	unsigned long Count = 0; +	struct cfi_private *cfi = map->fldrv_priv; +	DECLARE_WAITQUEUE(wait, current); +	int ret = 0; + + retry: +	cfi_spin_lock(chip->mutex); + +	if (chip->state != FL_READY){ +	        printk("Waiting for chip to write, status = %d\n", chip->state); +		set_current_state(TASK_UNINTERRUPTIBLE); +		add_wait_queue(&chip->wq, &wait); +                 +		cfi_spin_unlock(chip->mutex); + +		schedule(); +		remove_wait_queue(&chip->wq, &wait); +		printk("Wake up to write:\n"); +		timeo = jiffies + HZ; + +		goto retry; +	}	 + +	chip->state = FL_WRITING; + +	adr += chip->start; +	ENABLE_VPP(map); +    cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +    cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +    cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); + +	cfi_write(map, datum, adr); + +	cfi_spin_unlock(chip->mutex); +	cfi_udelay(chip->word_write_time); +	cfi_spin_lock(chip->mutex); + +	Last[0] = cfi_read(map, adr); +	//	printk("Last[0] is %x\n", Last[0]); +	Last[1] = cfi_read(map, adr); +	//	printk("Last[1] is %x\n", Last[1]); +	Last[2] = cfi_read(map, adr); +	//	printk("Last[2] is %x\n", Last[2]); + +	for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++){ +		cfi_spin_unlock(chip->mutex); +		cfi_udelay(10); +		cfi_spin_lock(chip->mutex); +		 +	        Last[Count % 4] = cfi_read(map, adr); +		//		printk("Last[%d%%4] is %x\n", Count, Last[Count%4]); +	} +	 +	if (Last[(Count - 1) % 4] != datum){ +		printk("Last[%ld] is %x, datum is %x\n",(Count - 1) % 4,Last[(Count - 1) % 4],datum); +	        cfi_send_gen_cmd(0xF0, 0, chip->start, map, cfi, cfi->device_type, NULL); +		DISABLE_VPP(map); +		ret = -EIO; +	}        +	DISABLE_VPP(map); +	chip->state = FL_READY; +	wake_up(&chip->wq); +	cfi_spin_unlock(chip->mutex); +	 +	return ret; +} + +static int cfi_sststd_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 cfi_private *cfi = map->fldrv_priv; +	int ret = 0; +	int chipnum; +	unsigned long ofs, chipstart; + +	*retlen = 0; +	if (!len) +		return 0; + +	chipnum = to >> cfi->chipshift; +	ofs = to  - (chipnum << cfi->chipshift); +	chipstart = cfi->chips[chipnum].start; + +	/* If it's not bus-aligned, do the first byte write */ +	if (ofs & (CFIDEV_BUSWIDTH-1)) { +		unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); +		int i = ofs - bus_ofs; +		int n = 0; +		u_char tmp_buf[4]; +		__u32 datum; + +		map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); +		while (len && i < CFIDEV_BUSWIDTH) +			tmp_buf[i++] = buf[n++], len--; + +		if (cfi_buswidth_is_2()) { +			datum = *(__u16*)tmp_buf; +		} else if (cfi_buswidth_is_4()) { +			datum = *(__u32*)tmp_buf; +		} else { +			return -EINVAL;  /* should never happen, but be safe */ +		} + +		ret = do_write_oneword(map, &cfi->chips[chipnum],  +				bus_ofs, datum, 0); +		if (ret)  +			return ret; +		 +		ofs += n; +		buf += n; +		(*retlen) += n; + +		if (ofs >> cfi->chipshift) { +			chipnum ++;  +			ofs = 0; +			if (chipnum == cfi->numchips) +				return 0; +		} +	} +	 +	/* We are now aligned, write as much as possible */ +	while(len >= CFIDEV_BUSWIDTH) { +		__u32 datum; + +		if (cfi_buswidth_is_1()) { +			datum = *(__u8*)buf; +		} else if (cfi_buswidth_is_2()) { +			datum = *(__u16*)buf; +		} else if (cfi_buswidth_is_4()) { +			datum = *(__u32*)buf; +		} else { +			return -EINVAL; +		} +		ret = do_write_oneword(map, &cfi->chips[chipnum], +				       ofs, datum, cfi->fast_prog); +		if (ret) { +			return ret; +		} + +		ofs += CFIDEV_BUSWIDTH; +		buf += CFIDEV_BUSWIDTH; +		(*retlen) += CFIDEV_BUSWIDTH; +		len -= CFIDEV_BUSWIDTH; + +		if (ofs >> cfi->chipshift) { +			chipnum ++;  +			ofs = 0; +			if (chipnum == cfi->numchips) +				return 0; +			chipstart = cfi->chips[chipnum].start; +		} +	} + +	if (len & (CFIDEV_BUSWIDTH-1)) { +		int i = 0, n = 0; +		u_char tmp_buf[4]; +		__u32 datum; + +		map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); +		while (len--) +			tmp_buf[i++] = buf[n++]; + +		if (cfi_buswidth_is_2()) { +			datum = *(__u16*)tmp_buf; +		} else if (cfi_buswidth_is_4()) { +			datum = *(__u32*)tmp_buf; +		} else { +			return -EINVAL;  /* should never happen, but be safe */ +		} + +		ret = do_write_oneword(map, &cfi->chips[chipnum],  +				ofs, datum, 0); +		if (ret)  +			return ret; +		 +		(*retlen) += n; +	} + +	return 0; +} + +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ +	unsigned int status; +	unsigned long timeo = jiffies + HZ; +	struct cfi_private *cfi = map->fldrv_priv; +	unsigned int rdy_mask; +	DECLARE_WAITQUEUE(wait, current); + + retry: +	cfi_spin_lock(chip->mutex); + +	if (chip->state != FL_READY){ +		set_current_state(TASK_UNINTERRUPTIBLE); +		add_wait_queue(&chip->wq, &wait); +                 +		cfi_spin_unlock(chip->mutex); + +		schedule(); +		remove_wait_queue(&chip->wq, &wait); +		timeo = jiffies + HZ; + +		goto retry; +	}	 + +	chip->state = FL_ERASING; + +	adr += chip->start; +	ENABLE_VPP(map); +	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X16, NULL); +	cfi_write(map, CMD(0x30), adr); +	 +	timeo = jiffies + (HZ*20); + +	cfi_spin_unlock(chip->mutex); +	schedule_timeout(HZ); +	cfi_spin_lock(chip->mutex); +	 +	rdy_mask = CMD(0x80); + +	/* Once the state machine's known to be working I'll do that */ + +	while ( ( (status = cfi_read(map,adr)) & rdy_mask ) != rdy_mask ) { +		static int z=0; + +		if (chip->state != FL_ERASING) { +			/* Someone's suspended the erase. Sleep */ +			set_current_state(TASK_UNINTERRUPTIBLE); +			add_wait_queue(&chip->wq, &wait); +			 +			cfi_spin_unlock(chip->mutex); +			printk("erase suspended. Sleeping\n"); +			 +			schedule(); +			remove_wait_queue(&chip->wq, &wait); +			timeo = jiffies + (HZ*2);  +			cfi_spin_lock(chip->mutex); +			continue; +		} + +		/* OK Still waiting */ +		if (time_after(jiffies, timeo)) { +			chip->state = FL_READY; +			cfi_spin_unlock(chip->mutex); +			printk("waiting for erase to complete timed out."); +			DISABLE_VPP(map); +			return -EIO; +		} +		 +		/* Latency issues. Drop the lock, wait a while and retry */ +		cfi_spin_unlock(chip->mutex); + +		z++; +		if ( 0 && !(z % 100 ))  +			printk("chip not ready yet after erase. looping\n"); + +		cfi_udelay(1); +		 +		cfi_spin_lock(chip->mutex); +		continue; +	} +	 +	/* Done and happy. */ +	DISABLE_VPP(map); +	chip->state = FL_READY; +	wake_up(&chip->wq); +	cfi_spin_unlock(chip->mutex); +	return 0; +} + +static int cfi_sststd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	unsigned long adr, len; +	int chipnum, ret = 0; +	int i, first; +	struct mtd_erase_region_info *regions = mtd->eraseregions; + +	if (instr->addr > mtd->size) +		return -EINVAL; + +	if ((instr->len + instr->addr) > mtd->size) +		return -EINVAL; + +	/* Check that both start and end of the requested erase are +	 * aligned with the erasesize at the appropriate addresses. +	 */ + +	i = 0; + +	/* Skip all erase regions which are ended before the start of  +	   the requested erase. Actually, to save on the calculations, +	   we skip to the first erase region which starts after the +	   start of the requested erase, and then go back one. +	*/ +	 +	while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) +	       i++; +	i--; + +	/* OK, now i is pointing at the erase region in which this  +	   erase request starts. Check the start of the requested +	   erase range is aligned with the erase size which is in +	   effect here. +	*/ + +	if (instr->addr & (regions[i].erasesize-1)) +		return -EINVAL; + +	/* Remember the erase region we start on */ +	first = i; + +	/* Next, check that the end of the requested erase is aligned +	 * with the erase region at that address. +	 */ + +	while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) +		i++; + +	/* As before, drop back one to point at the region in which +	   the address actually falls +	*/ +	i--; +	 +	if ((instr->addr + instr->len) & (regions[i].erasesize-1)) +		return -EINVAL; +	 +	chipnum = instr->addr >> cfi->chipshift; +	adr = instr->addr - (chipnum << cfi->chipshift); +	len = instr->len; + +	i=first; + +	while(len) { +		ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + +		if (ret) +			return ret; + +		adr += regions[i].erasesize; +		len -= regions[i].erasesize; + +		if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) +			i++; + +		if (adr >> cfi->chipshift) { +			adr = 0; +			chipnum++; +			 +			if (chipnum >= cfi->numchips) +			break; +		} +	} + +	instr->state = MTD_ERASE_DONE; +	if (instr->callback) +		instr->callback(instr); +	 +	return 0; +} + +static int cfi_sststd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	unsigned long adr, len; +	int chipnum, ret = 0; + +	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 >> cfi->chipshift; +	adr = instr->addr - (chipnum << cfi->chipshift); +	len = instr->len; + +	while(len) { +		ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + +		if (ret) +			return ret; + +		adr += mtd->erasesize; +		len -= mtd->erasesize; + +		if (adr >> cfi->chipshift) { +			adr = 0; +			chipnum++; +			 +			if (chipnum >= cfi->numchips) +			break; +		} +	} +		 +	instr->state = MTD_ERASE_DONE; +	if (instr->callback) +		instr->callback(instr); +	 +	return 0; +} + +static void cfi_sststd_sync (struct mtd_info *mtd) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	int i; +	struct flchip *chip; +	int ret = 0; +	DECLARE_WAITQUEUE(wait, current); + +	for (i=0; !ret && i<cfi->numchips; i++) { +		chip = &cfi->chips[i]; + +	retry: +		cfi_spin_lock(chip->mutex); + +		switch(chip->state) { +		case FL_READY: +		case FL_STATUS: +		case FL_CFI_QUERY: +		case FL_JEDEC_QUERY: +			chip->oldstate = chip->state; +			chip->state = FL_SYNCING; +			/* No need to wake_up() on this state change -  +			 * as the whole point is that nobody can do anything +			 * with the chip now anyway. +			 */ +		case FL_SYNCING: +			cfi_spin_unlock(chip->mutex); +			break; + +		default: +			/* Not an idle state */ +			add_wait_queue(&chip->wq, &wait); +			 +			cfi_spin_unlock(chip->mutex); + +			schedule(); + +		        remove_wait_queue(&chip->wq, &wait); +			 +			goto retry; +		} +	} + +	/* Unlock the chips again */ + +	for (i--; i >=0; i--) { +		chip = &cfi->chips[i]; + +		cfi_spin_lock(chip->mutex); +		 +		if (chip->state == FL_SYNCING) { +			chip->state = chip->oldstate; +			wake_up(&chip->wq); +		} +		cfi_spin_unlock(chip->mutex); +	} +} + + +static int cfi_sststd_suspend(struct mtd_info *mtd) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	int i; +	struct flchip *chip; +	int ret = 0; +//printk("suspend\n"); + +	for (i=0; !ret && i<cfi->numchips; i++) { +		chip = &cfi->chips[i]; + +		cfi_spin_lock(chip->mutex); + +		switch(chip->state) { +		case FL_READY: +		case FL_STATUS: +		case FL_CFI_QUERY: +		case FL_JEDEC_QUERY: +			chip->oldstate = chip->state; +			chip->state = FL_PM_SUSPENDED; +			/* No need to wake_up() on this state change -  +			 * as the whole point is that nobody can do anything +			 * with the chip now anyway. +			 */ +		case FL_PM_SUSPENDED: +			break; + +		default: +			ret = -EAGAIN; +			break; +		} +		cfi_spin_unlock(chip->mutex); +	} + +	/* Unlock the chips again */ + +	if (ret) { +    		for (i--; i >=0; i--) { +			chip = &cfi->chips[i]; + +			cfi_spin_lock(chip->mutex); +		 +			if (chip->state == FL_PM_SUSPENDED) { +				chip->state = chip->oldstate; +				wake_up(&chip->wq); +			} +			cfi_spin_unlock(chip->mutex); +		} +	} +	 +	return ret; +} + +static void cfi_sststd_resume(struct mtd_info *mtd) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	int i; +	struct flchip *chip; +//printk("resume\n"); + +	for (i=0; i<cfi->numchips; i++) { +	 +		chip = &cfi->chips[i]; + +		cfi_spin_lock(chip->mutex); +		 +		if (chip->state == FL_PM_SUSPENDED) { +			chip->state = FL_READY; +			cfi_write(map, CMD(0xF0), chip->start); +			wake_up(&chip->wq); +		} +		else +			printk("Argh. Chip not in PM_SUSPENDED state upon resume()\n"); + +		cfi_spin_unlock(chip->mutex); +	} +} + +static void cfi_sststd_destroy(struct mtd_info *mtd) +{ +	struct map_info *map = mtd->priv; +	struct cfi_private *cfi = map->fldrv_priv; +	kfree(cfi->cmdset_priv); +	kfree(cfi); +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define cfi_sststd_init init_module +#define cfi_sststd_exit cleanup_module +#endif + +static char im_name[]="cfi_cmdset_0701"; + +mod_init_t cfi_sststd_init(void) +{ +	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0701); +	return 0; +} + +mod_exit_t cfi_sststd_exit(void) +{ +	inter_module_unregister(im_name); +} + +module_init(cfi_sststd_init); +module_exit(cfi_sststd_exit); + diff --git a/package/linux/kernel-source/drivers/mtd/devices/sflash.c b/package/linux/kernel-source/drivers/mtd/devices/sflash.c new file mode 100644 index 000000000..4dbd76d87 --- /dev/null +++ b/package/linux/kernel-source/drivers/mtd/devices/sflash.c @@ -0,0 +1,283 @@ +/* + * Broadcom SiliconBackplane chipcommon serial flash interface + * + * Copyright 2004, Broadcom Corporation       + * All Rights Reserved.       + *        + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY       + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM       + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS       + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.       + * + * $Id$ + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/mtd/compatmac.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <asm/io.h> + +#include <typedefs.h> +#include <bcmdevs.h> +#include <bcmutils.h> +#include <osl.h> +#include <bcmutils.h> +#include <bcmnvram.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <sflash.h> + +#ifdef CONFIG_MTD_PARTITIONS +extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size); +#endif + +struct sflash_mtd { +	chipcregs_t *cc; +	struct semaphore lock; +	struct mtd_info mtd; +	struct mtd_erase_region_info region; +}; + +/* Private global state */ +static struct sflash_mtd sflash; + +static int +sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout) +{ +	int now = jiffies; +	int ret = 0; + +	for (;;) { +		if (!sflash_poll(sflash->cc, offset)) { +			ret = 0; +			break; +		} +		if (time_after(jiffies, now + timeout)) { +			printk(KERN_ERR "sflash: timeout\n"); +			ret = -ETIMEDOUT; +			break; +		} +		if (current->need_resched) { +			set_current_state(TASK_UNINTERRUPTIBLE); +			schedule_timeout(timeout / 10); +		} else +			udelay(1); +	} + +	return ret; +} + +static int +sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ +	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; +	int bytes, ret = 0; + +	/* Check address range */ +	if (!len) +		return 0; +	if ((from + len) > mtd->size) +		return -EINVAL; +	 +	down(&sflash->lock); + +	*retlen = 0; +	while (len) { +		if ((bytes = sflash_read(sflash->cc, (uint) from, len, buf)) < 0) { +			ret = bytes; +			break; +		} +		from += (loff_t) bytes; +		len -= bytes; +		buf += bytes; +		*retlen += bytes; +	} + +	up(&sflash->lock); + +	return ret; +} + +static int +sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ +	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; +	int bytes, ret = 0; + +	/* Check address range */ +	if (!len) +		return 0; +	if ((to + len) > mtd->size) +		return -EINVAL; + +	down(&sflash->lock); + +	*retlen = 0; +	while (len) { +		if ((bytes = sflash_write(sflash->cc, (uint) to, len, buf)) < 0) { +			ret = bytes; +			break; +		} +		if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10))) +			break; +		to += (loff_t) bytes; +		len -= bytes; +		buf += bytes; +		*retlen += bytes; +	} + +	up(&sflash->lock); + +	return ret; +} + +static int +sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) +{ +	struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; +	int i, j, ret = 0; +	unsigned int addr, len; + +	/* Check address range */ +	if (!erase->len) +		return 0; +	if ((erase->addr + erase->len) > mtd->size) +		return -EINVAL; + +	addr = erase->addr; +	len = erase->len; + +	down(&sflash->lock); + +	/* Ensure that requested region is aligned */ +	for (i = 0; i < mtd->numeraseregions; i++) { +		for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { +			if (addr == mtd->eraseregions[i].offset + mtd->eraseregions[i].erasesize * j && +			    len >= mtd->eraseregions[i].erasesize) { +				if ((ret = sflash_erase(sflash->cc, addr)) < 0) +					break; +				if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ))) +					break; +				addr += mtd->eraseregions[i].erasesize; +				len -= mtd->eraseregions[i].erasesize; +			} +		} +		if (ret) +			break; +	} + +	up(&sflash->lock); + +	/* Set erase status */ +	if (ret) +		erase->state = MTD_ERASE_FAILED; +	else  +		erase->state = MTD_ERASE_DONE; + +	/* Call erase callback */ +	if (erase->callback) +		erase->callback(erase); + +	return ret; +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define sflash_mtd_init init_module +#define sflash_mtd_exit cleanup_module +#endif + +mod_init_t +sflash_mtd_init(void) +{ +	struct pci_dev *pdev; +	int ret = 0; +	struct sflash *info; +	uint i; +#ifdef CONFIG_MTD_PARTITIONS +	struct mtd_partition *parts; +#endif + +	if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_CC, NULL))) { +		printk(KERN_ERR "sflash: chipcommon not found\n"); +		return -ENODEV; +	} + +	memset(&sflash, 0, sizeof(struct sflash_mtd)); +	init_MUTEX(&sflash.lock); + +	/* Map registers and flash base */ +	if (!(sflash.cc = ioremap_nocache(pci_resource_start(pdev, 0), +					  pci_resource_len(pdev, 0)))) { +		printk(KERN_ERR "sflash: error mapping registers\n"); +		ret = -EIO; +		goto fail; +	} + +	/* Initialize serial flash access */ +	info = sflash_init(sflash.cc); + +	if (!info) { +		printk(KERN_ERR "sflash: found no supported devices\n"); +		ret = -ENODEV; +		goto fail; +	} + +	/* Setup region info */ +	sflash.region.offset = 0; +	sflash.region.erasesize = info->blocksize; +	sflash.region.numblocks = info->numblocks; +	if (sflash.region.erasesize > sflash.mtd.erasesize) +		sflash.mtd.erasesize = sflash.region.erasesize; +	sflash.mtd.size = info->size; +	sflash.mtd.numeraseregions = 1; + +	/* Register with MTD */ +	sflash.mtd.name = "sflash"; +	sflash.mtd.type = MTD_NORFLASH; +	sflash.mtd.flags = MTD_CAP_NORFLASH; +	sflash.mtd.eraseregions = &sflash.region; +	sflash.mtd.module = THIS_MODULE; +	sflash.mtd.erase = sflash_mtd_erase; +	sflash.mtd.read = sflash_mtd_read; +	sflash.mtd.write = sflash_mtd_write; +	sflash.mtd.priv = &sflash; + +#ifdef CONFIG_MTD_PARTITIONS +	parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size); +	for (i = 0; parts[i].name; i++); +	ret = add_mtd_partitions(&sflash.mtd, parts, i); +#else +	ret = add_mtd_device(&sflash.mtd); +#endif +	if (ret) { +		printk(KERN_ERR "sflash: add_mtd failed\n"); +		goto fail; +	} + +	return 0; + + fail: +	if (sflash.cc) +		iounmap((void *) sflash.cc); +	return ret; +} + +mod_exit_t +sflash_mtd_exit(void) +{ +#ifdef CONFIG_MTD_PARTITIONS +	del_mtd_partitions(&sflash.mtd); +#else +	del_mtd_device(&sflash.mtd); +#endif +	iounmap((void *) sflash.cc); +} + +module_init(sflash_mtd_init); +module_exit(sflash_mtd_exit); diff --git a/package/linux/kernel-source/drivers/mtd/maps/bcm947xx-flash.c b/package/linux/kernel-source/drivers/mtd/maps/bcm947xx-flash.c new file mode 100644 index 000000000..716867ff1 --- /dev/null +++ b/package/linux/kernel-source/drivers/mtd/maps/bcm947xx-flash.c @@ -0,0 +1,236 @@ +/* + * Flash mapping for BCM947XX boards + * + * Copyright 2004, Broadcom Corporation + * All Rights Reserved. + *  + * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY + * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM + * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. + * + * $Id$ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/config.h> + +#include <typedefs.h> +#include <bcmnvram.h> +#include <bcmutils.h> +#include <sbconfig.h> +#include <sbchipc.h> +#include <sbutils.h> +#include <trxhdr.h> + +/* Global SB handle */ +extern void *bcm947xx_sbh; +extern spinlock_t bcm947xx_sbh_lock; + +/* Convenience */ +#define sbh bcm947xx_sbh +#define sbh_lock bcm947xx_sbh_lock + +#ifdef CONFIG_MTD_PARTITIONS +extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size); +#endif + +#define WINDOW_ADDR 0x1fc00000 +#define WINDOW_SIZE 0x400000 +#define BUSWIDTH 2 + +/* e.g., flash=2M or flash=4M */ +static int flash = 0; +MODULE_PARM(flash, "i"); +static int __init +bcm947xx_setup(char *str) +{ +	flash = memparse(str, &str); +	return 1; +} +__setup("flash=", bcm947xx_setup); + +static struct mtd_info *bcm947xx_mtd; + +__u8 bcm947xx_map_read8(struct map_info *map, unsigned long ofs) +{ +	if (map->map_priv_2 == 1) +		return __raw_readb(map->map_priv_1 + ofs); + +	u16 val = __raw_readw(map->map_priv_1 + (ofs & ~1)); +	if (ofs & 1) +		return ((val >> 8) & 0xff); +	else +		return (val & 0xff); +} + +__u16 bcm947xx_map_read16(struct map_info *map, unsigned long ofs) +{ +	return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 bcm947xx_map_read32(struct map_info *map, unsigned long ofs) +{ +	return __raw_readl(map->map_priv_1 + ofs); +} + +void bcm947xx_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ +	if (len==1) { +		memcpy_fromio(to, map->map_priv_1 + from, len); +	} else { +		int i; +		u16 *dest = (u16 *) to; +		u16 *src  = (u16 *) (map->map_priv_1 + from); +		for (i = 0; i < (len / 2); i++) { +			dest[i] = src[i]; +		} +		if (len & 1) +			*((u8 *)dest+len-1) = src[i] & 0xff; +	} +} + +void bcm947xx_map_write8(struct map_info *map, __u8 d, unsigned long adr) +{ +	__raw_writeb(d, map->map_priv_1 + adr); +	mb(); +} + +void bcm947xx_map_write16(struct map_info *map, __u16 d, unsigned long adr) +{ +	__raw_writew(d, map->map_priv_1 + adr); +	mb(); +} + +void bcm947xx_map_write32(struct map_info *map, __u32 d, unsigned long adr) +{ +	__raw_writel(d, map->map_priv_1 + adr); +	mb(); +} + +void bcm947xx_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ +	memcpy_toio(map->map_priv_1 + to, from, len); +} + +struct map_info bcm947xx_map = { +	name: "Physically mapped flash", +	size: WINDOW_SIZE, +	buswidth: BUSWIDTH, +	read8: bcm947xx_map_read8, +	read16: bcm947xx_map_read16, +	read32: bcm947xx_map_read32, +	copy_from: bcm947xx_map_copy_from, +	write8: bcm947xx_map_write8, +	write16: bcm947xx_map_write16, +	write32: bcm947xx_map_write32, +	copy_to: bcm947xx_map_copy_to +}; + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define init_bcm947xx_map init_module +#define cleanup_bcm947xx_map cleanup_module +#endif + +mod_init_t init_bcm947xx_map(void) +{ +	ulong flags; + 	uint coreidx; +	chipcregs_t *cc; +	uint32 fltype; +	uint window_addr = 0, window_size = 0; +	size_t size; +	int ret = 0; +#ifdef CONFIG_MTD_PARTITIONS +	struct mtd_partition *parts; +	int i; +#endif + +	spin_lock_irqsave(&sbh_lock, flags); +	coreidx = sb_coreidx(sbh); + +	/* Check strapping option if chipcommon exists */ +	if ((cc = sb_setcore(sbh, SB_CC, 0))) { +		fltype = readl(&cc->capabilities) & CAP_FLASH_MASK; +		if (fltype == PFLASH) { +			bcm947xx_map.map_priv_2 = 1; +			window_addr = 0x1c000000; +			bcm947xx_map.size = window_size = 32 * 1024 * 1024; +			if ((readl(&cc->flash_config) & CC_CFG_DS) == 0) +				bcm947xx_map.buswidth = 1; +		} +	} else { +		fltype = PFLASH; +		bcm947xx_map.map_priv_2 = 0; +		window_addr = WINDOW_ADDR; +		window_size = WINDOW_SIZE; +	} + +	sb_setcoreidx(sbh, coreidx); +	spin_unlock_irqrestore(&sbh_lock, flags); + +	if (fltype != PFLASH) { +		printk(KERN_ERR "pflash: found no supported devices\n"); +		ret = -ENODEV; +		goto fail; +	} + +	bcm947xx_map.map_priv_1 = (unsigned long) ioremap(window_addr, window_size); +	if (!bcm947xx_map.map_priv_1) { +		printk(KERN_ERR "pflash: ioremap failed\n"); +		ret = -EIO; +		goto fail; +	} + +	if (!(bcm947xx_mtd = do_map_probe("cfi_probe", &bcm947xx_map))) { +		printk(KERN_ERR "pflash: cfi_probe failed\n"); +		ret = -ENXIO; +		goto fail; +	} + +	bcm947xx_mtd->module = THIS_MODULE; + +	/* Allow size override for testing */ +	size = flash ? : bcm947xx_mtd->size; + +	printk(KERN_NOTICE "Flash device: 0x%x at 0x%x\n", size, window_addr); + +#ifdef CONFIG_MTD_PARTITIONS +	parts = init_mtd_partitions(bcm947xx_mtd, size); +	for (i = 0; parts[i].name; i++); +	ret = add_mtd_partitions(bcm947xx_mtd, parts, i); +	if (ret) { +		printk(KERN_ERR "pflash: add_mtd_partitions failed\n"); +		goto fail; +	} +#endif + +	return 0; + + fail: +	if (bcm947xx_mtd) +		map_destroy(bcm947xx_mtd); +	if (bcm947xx_map.map_priv_1) +		iounmap((void *) bcm947xx_map.map_priv_1); +	bcm947xx_map.map_priv_1 = 0; +	return ret; +} + +mod_exit_t cleanup_bcm947xx_map(void) +{ +#ifdef CONFIG_MTD_PARTITIONS +	del_mtd_partitions(bcm947xx_mtd); +#endif +	map_destroy(bcm947xx_mtd); +	iounmap((void *) bcm947xx_map.map_priv_1); +	bcm947xx_map.map_priv_1 = 0; +} + +module_init(init_bcm947xx_map); +module_exit(cleanup_bcm947xx_map);  | 
