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

📄 cfi_cmdset_0002.c

📁 ARMer9 s3c2410开发系统 mtd文件系统的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Common Flash Interface support: *   AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) * * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp> * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com> * * 2_by_8 routines added by Simon Munton * * 4_by_16 work by Carolyn J. Smith * * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * This code is GPL * * $Id: cfi_cmdset_0002.c,v 1.104 2004/07/16 17:42:35 dwmw2 Exp $ * */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/init.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/compatmac.h>#include <linux/mtd/map.h>#include <linux/mtd/mtd.h>#include <linux/mtd/cfi.h>#define AMD_BOOTLOC_BUG#define FORCE_WORD_WRITE 0#define MAX_WORD_RETRIES 3static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t);static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t);static void cfi_amdstd_sync (struct mtd_info *);static int cfi_amdstd_suspend (struct mtd_info *);static void cfi_amdstd_resume (struct mtd_info *);static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static void cfi_amdstd_destroy(struct mtd_info *);struct mtd_info *cfi_cmdset_0002(struct map_info *, int);static struct mtd_info *cfi_amdstd_setup (struct map_info *);static struct mtd_chip_driver cfi_amdstd_chipdrv = {	.probe		= NULL, /* Not usable directly */	.destroy	= cfi_amdstd_destroy,	.name		= "cfi_cmdset_0002",	.module		= THIS_MODULE};/* #define DEBUG_LOCK_BITS *//* #define DEBUG_CFI_FEATURES */#ifdef DEBUG_CFI_FEATURESstatic void cfi_tell_features(struct cfi_pri_amdstd *extp){	const char* erase_suspend[3] = {		"Not supported", "Read only", "Read/write"	};	const char* top_bottom[6] = {		"No WP", "8x8KiB sectors at top & bottom, no WP",		"Bottom boot", "Top boot",		"Uniform, Bottom WP", "Uniform, Top WP"	};	printk("  Silicon revision: %d\n", extp->SiliconRevision >> 1);	printk("  Address sensitive unlock: %s\n", 	       (extp->SiliconRevision & 1) ? "Not required" : "Required");	if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))		printk("  Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);	else		printk("  Erase Suspend: Unknown value %d\n", extp->EraseSuspend);	if (extp->BlkProt == 0)		printk("  Block protection: Not supported\n");	else		printk("  Block protection: %d sectors per group\n", extp->BlkProt);	printk("  Temporary block unprotect: %s\n",	       extp->TmpBlkUnprotect ? "Supported" : "Not supported");	printk("  Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);	printk("  Number of simultaneous operations: %d\n", extp->SimultaneousOps);	printk("  Burst mode: %s\n",	       extp->BurstMode ? "Supported" : "Not supported");	if (extp->PageMode == 0)		printk("  Page mode: Not supported\n");	else		printk("  Page mode: %d word page\n", extp->PageMode << 2);	printk("  Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", 	       extp->VppMin >> 4, extp->VppMin & 0xf);	printk("  Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", 	       extp->VppMax >> 4, extp->VppMax & 0xf);	if (extp->TopBottom < ARRAY_SIZE(top_bottom))		printk("  Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);	else		printk("  Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);}#endif#ifdef AMD_BOOTLOC_BUG/* Wheee. Bring me the head of someone at AMD. */static void fixup_amd_bootblock(struct map_info *map, void* param){	struct cfi_private *cfi = map->fldrv_priv;	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;	__u8 major = extp->MajorVersion;	__u8 minor = extp->MinorVersion;	if (((major << 8) | minor) < 0x3131) {		/* CFI version 1.0 => don't trust bootloc */		if (cfi->id & 0x80) {			printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);			extp->TopBottom = 3;	/* top boot */		} else {			extp->TopBottom = 2;	/* bottom boot */		}	}}#endifstatic struct cfi_fixup fixup_table[] = {#ifdef AMD_BOOTLOC_BUG	{		0x0001,		/* AMD */		CFI_ID_ANY,		fixup_amd_bootblock, NULL	},#endif	{ 0, 0, NULL, NULL }};struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary){	struct cfi_private *cfi = map->fldrv_priv;	unsigned char bootloc;	int i;	if (cfi->cfi_mode==CFI_MODE_CFI){		/* 		 * It's a real CFI chip, not one for which the probe		 * routine faked a CFI structure. So we read the feature		 * table from it.		 */		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;		struct cfi_pri_amdstd *extp;		extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");		if (!extp)			return NULL;		/* Install our own private info structure */		cfi->cmdset_priv = extp;			cfi_fixup(map, fixup_table);#ifdef DEBUG_CFI_FEATURES		/* Tell the user about it in lots of lovely detail */		cfi_tell_features(extp);#endif			bootloc = extp->TopBottom;		if ((bootloc != 2) && (bootloc != 3)) {			printk(KERN_WARNING "%s: CFI does not contain boot "			       "bank location. Assuming top.\n", map->name);			bootloc = 2;		}		if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {			printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);						for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) {				int j = (cfi->cfiq->NumEraseRegions-1)-i;				__u32 swap;								swap = cfi->cfiq->EraseRegionInfo[i];				cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j];				cfi->cfiq->EraseRegionInfo[j] = swap;			}		}		/*		 * These might already be setup (more correctly) by		 * jedec_probe.c - still need it for cfi_probe.c path.		 */		if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {			switch (cfi->device_type) {			case CFI_DEVICETYPE_X8:				cfi->addr_unlock1 = 0x555; 				cfi->addr_unlock2 = 0x2aa; 				break;			case CFI_DEVICETYPE_X16:				cfi->addr_unlock1 = 0xaaa;				if (map_bankwidth(map) == cfi_interleave(cfi)) {					/* X16 chip(s) in X8 mode */					cfi->addr_unlock2 = 0x555;				} else {					cfi->addr_unlock2 = 0x554;				}				break;			case CFI_DEVICETYPE_X32:				cfi->addr_unlock1 = 0x1554;				if (map_bankwidth(map) == cfi_interleave(cfi)*2) {					/* X32 chip(s) in X16 mode */					cfi->addr_unlock1 = 0xaaa;				} else {					cfi->addr_unlock2 = 0xaa8; 				}				break;			default:				printk(KERN_WARNING				       "MTD %s(): Unsupported device type %d\n",				       __func__, 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_amdstd_chipdrv;	return cfi_amdstd_setup(map);}static struct mtd_info *cfi_amdstd_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;	unsigned long offset = 0;	int i,j;	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);	printk(KERN_NOTICE "number of %s chips: %d\n", 	       (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);	if (!mtd) {		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");		goto setup_err;	}	memset(mtd, 0, sizeof(*mtd));	mtd->priv = map;	mtd->type = MTD_NORFLASH;	/* Also select the correct geometry setup too */ 	mtd->size = devsize * cfi->numchips;	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;	mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)				    * mtd->numeraseregions, GFP_KERNEL);	if (!mtd->eraseregions) { 		printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");		goto setup_err;	}				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);	}	if (offset != devsize) {		/* Argh */		printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);		goto setup_err;	}#if 0	// 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);	}#endif	if (mtd->numeraseregions == 1	    && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {		mtd->erase = cfi_amdstd_erase_chip;	} else {		mtd->erase = cfi_amdstd_erase_varsize;		mtd->lock = cfi_amdstd_lock_varsize;		mtd->unlock = cfi_amdstd_unlock_varsize;	}	if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {		DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );		mtd->write = cfi_amdstd_write_buffers;	} else {		DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" );		mtd->write = cfi_amdstd_write_words;	}	mtd->read = cfi_amdstd_read;	/* FIXME: erase-suspend-program is broken.  See	   http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */	printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");	/* does this chip have a secsi area? */	if(cfi->mfr==1){				switch(cfi->id){		case 0x50:		case 0x53:		case 0x55:		case 0x56:		case 0x5C:		case 0x5F:			/* Yes */			mtd->read_user_prot_reg = cfi_amdstd_secsi_read;			mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;		default:		       			;		}	}				mtd->sync = cfi_amdstd_sync;	mtd->suspend = cfi_amdstd_suspend;	mtd->resume = cfi_amdstd_resume;	mtd->flags = MTD_CAP_NORFLASH;	map->fldrv = &cfi_amdstd_chipdrv;	mtd->name = map->name;	__module_get(THIS_MODULE);	return mtd; setup_err:	if(mtd) {		if(mtd->eraseregions)			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 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);}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");				cfi_spin_unlock(chip->mutex);				return -EIO;			}			cfi_spin_unlock(chip->mutex);			cfi_udelay(1);			cfi_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		      || (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;			}						cfi_spin_unlock(chip->mutex);			cfi_udelay(1);			cfi_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_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);		cfi_spin_unlock(chip->mutex);		schedule();		remove_wait_queue(&chip->wq, &wait);		cfi_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_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);}static 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); 	cfi_spin_lock(chip->mutex);	ret = get_chip(map, chip, cmd_addr, FL_READY);	if (ret) {		cfi_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);	cfi_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;

⌨️ 快捷键说明

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