⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cfi_cmdset_0002.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
	__module_get(THIS_MODULE);	return mtd; setup_err:	if(mtd) {		kfree(mtd->eraseregions);		kfree(mtd);	}	kfree(cfi->cmdset_priv);	kfree(cfi->cfiq);	return NULL;}/* * Return true if the chip is ready. * * Ready is one of: read mode, query mode, erase-suspend-read mode (in any * non-suspended sector) and is indicated by no toggle bits toggling. * * Note that anything more complicated than checking if no bits are toggling * (including checking DQ5 for an error status) is tricky to get working * correctly and is therefore not done	(particulary with interleaved chips * as each chip must be checked independantly of the others). */static int __xipram chip_ready(struct map_info *map, unsigned long addr){	map_word d, t;	d = map_read(map, addr);	t = map_read(map, addr);	return map_word_equal(map, d, t);}/* * Return true if the chip is ready and has the correct value. * * Ready is one of: read mode, query mode, erase-suspend-read mode (in any * non-suspended sector) and it is indicated by no bits toggling. * * Error are indicated by toggling bits or bits held with the wrong value, * or with bits toggling. * * Note that anything more complicated than checking if no bits are toggling * (including checking DQ5 for an error status) is tricky to get working * correctly and is therefore not done	(particulary with interleaved chips * as each chip must be checked independantly of the others). * */static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected){	map_word oldd, curd;	oldd = map_read(map, addr);	curd = map_read(map, addr);	return	map_word_equal(map, oldd, curd) &&		map_word_equal(map, curd, expected);}static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode){	DECLARE_WAITQUEUE(wait, current);	struct cfi_private *cfi = map->fldrv_priv;	unsigned long timeo;	struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; resettime:	timeo = jiffies + HZ; retry:	switch (chip->state) {	case FL_STATUS:		for (;;) {			if (chip_ready(map, adr))				break;			if (time_after(jiffies, timeo)) {				printk(KERN_ERR "Waiting for chip to be ready timed out.\n");				spin_unlock(chip->mutex);				return -EIO;			}			spin_unlock(chip->mutex);			cfi_udelay(1);			spin_lock(chip->mutex);			/* Someone else might have been playing with it. */			goto retry;		}	case FL_READY:	case FL_CFI_QUERY:	case FL_JEDEC_QUERY:		return 0;	case FL_ERASING:		if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */			goto sleep;		if (!(   mode == FL_READY		      || mode == FL_POINT		      || !cfip		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)		    )))			goto sleep;		/* We could check to see if we're trying to access the sector		 * that is currently being erased. However, no user will try		 * anything like that so we just wait for the timeout. */		/* Erase suspend */		/* It's harmless to issue the Erase-Suspend and Erase-Resume		 * commands when the erase algorithm isn't in progress. */		map_write(map, CMD(0xB0), chip->in_progress_block_addr);		chip->oldstate = FL_ERASING;		chip->state = FL_ERASE_SUSPENDING;		chip->erase_suspended = 1;		for (;;) {			if (chip_ready(map, adr))				break;			if (time_after(jiffies, timeo)) {				/* Should have suspended the erase by now.				 * Send an Erase-Resume command as either				 * there was an error (so leave the erase				 * routine to recover from it) or we trying to				 * use the erase-in-progress sector. */				map_write(map, CMD(0x30), chip->in_progress_block_addr);				chip->state = FL_ERASING;				chip->oldstate = FL_READY;				printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);				return -EIO;			}			spin_unlock(chip->mutex);			cfi_udelay(1);			spin_lock(chip->mutex);			/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.			   So we can just loop here. */		}		chip->state = FL_READY;		return 0;	case FL_XIP_WHILE_ERASING:		if (mode != FL_READY && mode != FL_POINT &&		    (!cfip || !(cfip->EraseSuspend&2)))			goto sleep;		chip->oldstate = chip->state;		chip->state = FL_READY;		return 0;	case FL_POINT:		/* Only if there's no operation suspended... */		if (mode == FL_READY && chip->oldstate == FL_READY)			return 0;	default:	sleep:		set_current_state(TASK_UNINTERRUPTIBLE);		add_wait_queue(&chip->wq, &wait);		spin_unlock(chip->mutex);		schedule();		remove_wait_queue(&chip->wq, &wait);		spin_lock(chip->mutex);		goto resettime;	}}static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr){	struct cfi_private *cfi = map->fldrv_priv;	switch(chip->oldstate) {	case FL_ERASING:		chip->state = chip->oldstate;		map_write(map, CMD(0x30), chip->in_progress_block_addr);		chip->oldstate = FL_READY;		chip->state = FL_ERASING;		break;	case FL_XIP_WHILE_ERASING:		chip->state = chip->oldstate;		chip->oldstate = FL_READY;		break;	case FL_READY:	case FL_STATUS:		/* We should really make set_vpp() count, rather than doing this */		DISABLE_VPP(map);		break;	default:		printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);	}	wake_up(&chip->wq);}#ifdef CONFIG_MTD_XIP/* * No interrupt what so ever can be serviced while the flash isn't in array * mode.  This is ensured by the xip_disable() and xip_enable() functions * enclosing any code path where the flash is known not to be in array mode. * And within a XIP disabled code path, only functions marked with __xipram * may be called and nothing else (it's a good thing to inspect generated * assembly to make sure inline functions were actually inlined and that gcc * didn't emit calls to its own support functions). Also configuring MTD CFI * support to a single buswidth and a single interleave is also recommended. */static void xip_disable(struct map_info *map, struct flchip *chip,			unsigned long adr){	/* TODO: chips with no XIP use should ignore and return */	(void) map_read(map, adr); /* ensure mmu mapping is up to date */	local_irq_disable();}static void __xipram xip_enable(struct map_info *map, struct flchip *chip,				unsigned long adr){	struct cfi_private *cfi = map->fldrv_priv;	if (chip->state != FL_POINT && chip->state != FL_READY) {		map_write(map, CMD(0xf0), adr);		chip->state = FL_READY;	}	(void) map_read(map, adr);	xip_iprefetch();	local_irq_enable();}/* * When a delay is required for the flash operation to complete, the * xip_udelay() function is polling for both the given timeout and pending * (but still masked) hardware interrupts.  Whenever there is an interrupt * pending then the flash erase operation is suspended, array mode restored * and interrupts unmasked.  Task scheduling might also happen at that * point.  The CPU eventually returns from the interrupt or the call to * schedule() and the suspended flash operation is resumed for the remaining * of the delay period. * * Warning: this function _will_ fool interrupt latency tracing tools. */static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,				unsigned long adr, int usec){	struct cfi_private *cfi = map->fldrv_priv;	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;	map_word status, OK = CMD(0x80);	unsigned long suspended, start = xip_currtime();	flstate_t oldstate;	do {		cpu_relax();		if (xip_irqpending() && extp &&		    ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&		    (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {			/*			 * Let's suspend the erase operation when supported.			 * Note that we currently don't try to suspend			 * interleaved chips if there is already another			 * operation suspended (imagine what happens			 * when one chip was already done with the current			 * operation while another chip suspended it, then			 * we resume the whole thing at once).  Yes, it			 * can happen!			 */			map_write(map, CMD(0xb0), adr);			usec -= xip_elapsed_since(start);			suspended = xip_currtime();			do {				if (xip_elapsed_since(suspended) > 100000) {					/*					 * The chip doesn't want to suspend					 * after waiting for 100 msecs.					 * This is a critical error but there					 * is not much we can do here.					 */					return;				}				status = map_read(map, adr);			} while (!map_word_andequal(map, status, OK, OK));			/* Suspend succeeded */			oldstate = chip->state;			if (!map_word_bitsset(map, status, CMD(0x40)))				break;			chip->state = FL_XIP_WHILE_ERASING;			chip->erase_suspended = 1;			map_write(map, CMD(0xf0), adr);			(void) map_read(map, adr);			xip_iprefetch();			local_irq_enable();			spin_unlock(chip->mutex);			xip_iprefetch();			cond_resched();			/*			 * We're back.  However someone else might have			 * decided to go write to the chip if we are in			 * a suspended erase state.  If so let's wait			 * until it's done.			 */			spin_lock(chip->mutex);			while (chip->state != FL_XIP_WHILE_ERASING) {				DECLARE_WAITQUEUE(wait, current);				set_current_state(TASK_UNINTERRUPTIBLE);				add_wait_queue(&chip->wq, &wait);				spin_unlock(chip->mutex);				schedule();				remove_wait_queue(&chip->wq, &wait);				spin_lock(chip->mutex);			}			/* Disallow XIP again */			local_irq_disable();			/* Resume the write or erase operation */			map_write(map, CMD(0x30), adr);			chip->state = oldstate;			start = xip_currtime();		} else if (usec >= 1000000/HZ) {			/*			 * Try to save on CPU power when waiting delay			 * is at least a system timer tick period.			 * No need to be extremely accurate here.			 */			xip_cpu_idle();		}		status = map_read(map, adr);	} while (!map_word_andequal(map, status, OK, OK)		 && xip_elapsed_since(start) < usec);}#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)/* * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while * the flash is actively programming or erasing since we have to poll for * the operation to complete anyway.  We can't do that in a generic way with * a XIP setup so do it before the actual flash operation in this case * and stub it out from INVALIDATE_CACHE_UDELAY. */#define XIP_INVAL_CACHED_RANGE(map, from, size)  \	INVALIDATE_CACHED_RANGE(map, from, size)#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \	UDELAY(map, chip, adr, usec)/* * Extra notes: * * Activating this XIP support changes the way the code works a bit.  For * example the code to suspend the current process when concurrent access * happens is never executed because xip_udelay() will always return with the * same chip state as it was entered with.  This is why there is no care for * the presence of add_wait_queue() or schedule() calls from within a couple * xip_disable()'d  areas of code, like in do_erase_oneblock for example. * The queueing and scheduling are always happening within xip_udelay(). * * Similarly, get_chip() and put_chip() just happen to always be executed * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state * is in array mode, therefore never executing many cases therein and not * causing any problem with XIP. */#else#define xip_disable(map, chip, adr)#define xip_enable(map, chip, adr)#define XIP_INVAL_CACHED_RANGE(x...)#define UDELAY(map, chip, adr, usec)  \do {  \	spin_unlock(chip->mutex);  \	cfi_udelay(usec);  \	spin_lock(chip->mutex);  \} while (0)#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \do {  \	spin_unlock(chip->mutex);  \	INVALIDATE_CACHED_RANGE(map, adr, len);  \	cfi_udelay(usec);  \	spin_lock(chip->mutex);  \} while (0)#endifstatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf){	unsigned long cmd_addr;	struct cfi_private *cfi = map->fldrv_priv;	int ret;	adr += chip->start;	/* Ensure cmd read/writes are aligned. */	cmd_addr = adr & ~(map_bankwidth(map)-1);	spin_lock(chip->mutex);	ret = get_chip(map, chip, cmd_addr, FL_READY);	if (ret) {		spin_unlock(chip->mutex);		return ret;	}	if (chip->state != FL_POINT && chip->state != FL_READY) {		map_write(map, CMD(0xf0), cmd_addr);		chip->state = FL_READY;	}	map_copy_from(map, buf, adr, len);	put_chip(map, chip, cmd_addr);	spin_unlock(chip->mutex);	return 0;}static int cfi_amdstd_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 inline int do_read_secsi_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;	struct cfi_private *cfi = map->fldrv_priv; retry:	spin_lock(chip->mutex);	if (chip->state != FL_READY){#if 0		printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);#endif		set_current_state(TASK_UNINTERRUPTIBLE);		add_wait_queue(&chip->wq, &wait);		spin_unlock(chip->mutex);		schedule();		remove_wait_queue(&chip->wq, &wait);#if 0		if(signal_pending(current))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -