cfi_cmdset_0001.c

来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 1,870 行 · 第 1/4 页

C
1,870
字号
			prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];			mtd->writesize = cfi->interleave << prinfo->ProgRegShift;			mtd->flags &= ~MTD_BIT_WRITEABLE;			printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",			       map->name, mtd->writesize,			       cfi->interleave * prinfo->ControlValid,			       cfi->interleave * prinfo->ControlInvalid);		}		/*		 * All functions below currently rely on all chips having		 * the same geometry so we'll just assume that all hardware		 * partitions are of the same size too.		 */		partshift = cfi->chipshift - __ffs(numparts);		if ((1 << partshift) < mtd->erasesize) {			printk( KERN_ERR				"%s: bad number of hw partitions (%d)\n",				__FUNCTION__, numparts);			return -EINVAL;		}		numvirtchips = cfi->numchips * numparts;		newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL);		if (!newcfi)			return -ENOMEM;		shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL);		if (!shared) {			kfree(newcfi);			return -ENOMEM;		}		memcpy(newcfi, cfi, sizeof(struct cfi_private));		newcfi->numchips = numvirtchips;		newcfi->chipshift = partshift;		chip = &newcfi->chips[0];		for (i = 0; i < cfi->numchips; i++) {			shared[i].writing = shared[i].erasing = NULL;			spin_lock_init(&shared[i].lock);			for (j = 0; j < numparts; j++) {				*chip = cfi->chips[i];				chip->start += j << partshift;				chip->priv = &shared[i];				/* those should be reset too since				   they create memory references. */				init_waitqueue_head(&chip->wq);				spin_lock_init(&chip->_spinlock);				chip->mutex = &chip->_spinlock;				chip++;			}		}		printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips "				  "--> %d partitions of %d KiB\n",				  map->name, cfi->numchips, cfi->interleave,				  newcfi->numchips, 1<<(newcfi->chipshift-10));		map->fldrv_priv = newcfi;		*pcfi = newcfi;		kfree(cfi);	}	return 0;}/* *  *********** CHIP ACCESS FUNCTIONS *********** */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;	map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01);	unsigned long timeo;	struct cfi_pri_intelext *cfip = cfi->cmdset_priv; resettime:	timeo = jiffies + HZ; retry:	if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {		/*		 * OK. We have possibility for contension on the write/erase		 * operations which are global to the real chip and not per		 * partition.  So let's fight it over in the partition which		 * currently has authority on the operation.		 *		 * The rules are as follows:		 *		 * - any write operation must own shared->writing.		 *		 * - any erase operation must own _both_ shared->writing and		 *   shared->erasing.		 *		 * - contension arbitration is handled in the owner's context.		 *		 * The 'shared' struct can be read and/or written only when		 * its lock is taken.		 */		struct flchip_shared *shared = chip->priv;		struct flchip *contender;		spin_lock(&shared->lock);		contender = shared->writing;		if (contender && contender != chip) {			/*			 * The engine to perform desired operation on this			 * partition is already in use by someone else.			 * Let's fight over it in the context of the chip			 * currently using it.  If it is possible to suspend,			 * that other partition will do just that, otherwise			 * it'll happily send us to sleep.  In any case, when			 * get_chip returns success we're clear to go ahead.			 */			int ret = spin_trylock(contender->mutex);			spin_unlock(&shared->lock);			if (!ret)				goto retry;			spin_unlock(chip->mutex);			ret = get_chip(map, contender, contender->start, mode);			spin_lock(chip->mutex);			if (ret) {				spin_unlock(contender->mutex);				return ret;			}			timeo = jiffies + HZ;			spin_lock(&shared->lock);			spin_unlock(contender->mutex);		}		/* We now own it */		shared->writing = chip;		if (mode == FL_ERASING)			shared->erasing = chip;		spin_unlock(&shared->lock);	}	switch (chip->state) {	case FL_STATUS:		for (;;) {			status = map_read(map, adr);			if (map_word_andequal(map, status, status_OK, status_OK))				break;			/* At this point we're fine with write operations			   in other partitions as they don't conflict. */			if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS))				break;			if (time_after(jiffies, timeo)) {				printk(KERN_ERR "%s: Waiting for chip to be ready timed out. Status %lx\n",				       map->name, status.x[0]);				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 (!cfip ||		    !(cfip->FeatureSupport & 2) ||		    !(mode == FL_READY || mode == FL_POINT ||		     (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))			goto sleep;		/* Erase suspend */		map_write(map, CMD(0xB0), adr);		/* If the flash has finished erasing, then 'erase suspend'		 * appears to make some (28F320) flash devices switch to		 * 'read' mode.  Make sure that we switch to 'read status'		 * mode so we get the right data. --rmk		 */		map_write(map, CMD(0x70), adr);		chip->oldstate = FL_ERASING;		chip->state = FL_ERASE_SUSPENDING;		chip->erase_suspended = 1;		for (;;) {			status = map_read(map, adr);			if (map_word_andequal(map, status, status_OK, status_OK))			        break;			if (time_after(jiffies, timeo)) {				/* Urgh. Resume and pretend we weren't here.  */				map_write(map, CMD(0xd0), adr);				/* Make sure we're in 'read status' mode if it had finished */				map_write(map, CMD(0x70), adr);				chip->state = FL_ERASING;				chip->oldstate = FL_READY;				printk(KERN_ERR "%s: Chip not ready after erase "				       "suspended: status = 0x%lx\n", map->name, status.x[0]);				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_STATUS;		return 0;	case FL_XIP_WHILE_ERASING:		if (mode != FL_READY && mode != FL_POINT &&		    (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1)))			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;	if (chip->priv) {		struct flchip_shared *shared = chip->priv;		spin_lock(&shared->lock);		if (shared->writing == chip && chip->oldstate == FL_READY) {			/* We own the ability to write, but we're done */			shared->writing = shared->erasing;			if (shared->writing && shared->writing != chip) {				/* give back ownership to who we loaned it from */				struct flchip *loaner = shared->writing;				spin_lock(loaner->mutex);				spin_unlock(&shared->lock);				spin_unlock(chip->mutex);				put_chip(map, loaner, loaner->start);				spin_lock(chip->mutex);				spin_unlock(loaner->mutex);				wake_up(&chip->wq);				return;			}			shared->erasing = NULL;			shared->writing = NULL;		} else if (shared->erasing == chip && shared->writing != chip) {			/*			 * We own the ability to erase without the ability			 * to write, which means the erase was suspended			 * and some other partition is currently writing.			 * Don't let the switch below mess things up since			 * we don't have ownership to resume anything.			 */			spin_unlock(&shared->lock);			wake_up(&chip->wq);			return;		}		spin_unlock(&shared->lock);	}	switch(chip->oldstate) {	case FL_ERASING:		chip->state = chip->oldstate;		/* What if one interleaved chip has finished and the		   other hasn't? The old code would leave the finished		   one in READY mode. That's bad, and caused -EROFS		   errors to be returned from do_erase_oneblock because		   that's the only bit it checked for at the time.		   As the state machine appears to explicitly allow		   sending the 0x70 (Read Status) command to an erasing		   chip and expecting it to be ignored, that's what we		   do. */		map_write(map, CMD(0xd0), adr);		map_write(map, CMD(0x70), adr);		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:	case FL_JEDEC_QUERY:		/* We should really make set_vpp() count, rather than doing this */		DISABLE_VPP(map);		break;	default:		printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate);	}	wake_up(&chip->wq);}#define xip_disable(map, chip, adr)#define xip_enable(map, chip, adr)#define XIP_INVAL_CACHED_RANGE(x...)#define INVAL_CACHE_AND_WAIT inval_cache_and_wait_for_operationstatic int inval_cache_and_wait_for_operation(		struct map_info *map, struct flchip *chip,		unsigned long cmd_adr, unsigned long inval_adr, int inval_len,		unsigned int chip_op_time){	struct cfi_private *cfi = map->fldrv_priv;	map_word status, status_OK = CMD(0x80);	int chip_state = chip->state;	unsigned int timeo, sleep_time;	spin_unlock(chip->mutex);	if (inval_len)		INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len);	spin_lock(chip->mutex);	/* set our timeout to 8 times the expected delay */	timeo = chip_op_time * 8;	if (!timeo)		timeo = 500000;	sleep_time = chip_op_time / 2;	for (;;) {		status = map_read(map, cmd_adr);		if (map_word_andequal(map, status, status_OK, status_OK))			break;		if (!timeo) {			map_write(map, CMD(0x70), cmd_adr);			chip->state = FL_STATUS;			return -ETIME;		}		/* OK Still waiting. Drop the lock, wait a while and retry. */		spin_unlock(chip->mutex);		if (sleep_time >= 1000000/HZ) {			/*			 * Half of the normal delay still remaining			 * can be performed with a sleeping delay instead			 * of busy waiting.			 */			msleep(sleep_time/1000);			timeo -= sleep_time;			sleep_time = 1000000/HZ;		} else {			udelay(1);			cond_resched();			timeo--;		}		spin_lock(chip->mutex);		while (chip->state != chip_state) {			/* Someone's suspended the operation: sleep */			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);		}	}	/* Done and happy. */ 	chip->state = FL_STATUS;	return 0;}#define WAIT_TIMEOUT(map, chip, adr, udelay) \	INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay);static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len){	unsigned long cmd_addr;	struct cfi_private *cfi = map->fldrv_priv;	int ret = 0;	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_POINT);	if (!ret) {		if (chip->state != FL_POINT && chip->state != FL_READY)			map_write(map, CMD(0xff), cmd_addr);		chip->state = FL_POINT;		chip->ref_point_counter++;	}	spin_unlock(chip->mutex);	return ret;}static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf){	struct map_info *map = mtd->priv;	struct cfi_private *cfi = map->fldrv_priv;	unsigned long ofs;	int chipnum;	int ret = 0;	if (!map->virt || (from + len > mtd->size))		return -EINVAL;	*mtdbuf = (void *)map->virt + from;	*retlen = 0;	/* Now lock the chip(s) to POINT state */	/* ofs: offset within the first chip that the first read should start */	chipnum = (from >> cfi->chipshift);	ofs = from - (chipnum << cfi->chipshift);	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_point_onechip(map, &cfi->chips[chipnum], ofs, thislen);		if (ret)			break;		*retlen += thislen;		len -= thislen;		ofs = 0;		chipnum++;	}	return 0;}static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len){	struct map_info *map = mtd->priv;	struct cfi_private *cfi = map->fldrv_priv;	unsigned long ofs;	int chipnum;

⌨️ 快捷键说明

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