📄 cfi_cmdset_0001.c
字号:
/* * Common Flash Interface support: * Intel Extended Vendor Command Set (ID 0x0001) * * (C) 2000 Red Hat. GPL'd * * $Id: cfi_cmdset_0001.c,v 1.114 2003/03/18 12:28:40 dwmw2 Exp $ * * * 10/10/2000 Nicolas Pitre <nico@cam.org> * - completely revamped method functions so they are aware and * independent of the flash geometry (buswidth, interleave, etc.) * - scalability vs code size is completely set at compile-time * (see include/linux/mtd/cfi.h for selection) * - optimized write buffer method * 02/05/2002 Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com> * - reworked lock/unlock/erase support for var size flash */#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>#include <linux/mtd/compatmac.h>// debugging, turns off buffer write mode #define FORCE_WORD_WRITEstatic int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);static void cfi_intelext_sync (struct mtd_info *);static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);static int cfi_intelext_suspend (struct mtd_info *);static void cfi_intelext_resume (struct mtd_info *);static void cfi_intelext_destroy(struct mtd_info *);struct mtd_info *cfi_cmdset_0001(struct map_info *, int);static struct mtd_info *cfi_intelext_setup (struct map_info *);static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len);static struct mtd_chip_driver cfi_intelext_chipdrv = { probe: NULL, /* Not usable directly */ destroy: cfi_intelext_destroy, name: "cfi_cmdset_0001", module: THIS_MODULE};/* #define DEBUG_LOCK_BITS *//* #define DEBUG_CFI_FEATURES */#ifdef DEBUG_CFI_FEATURESstatic void cfi_tell_features(struct cfi_pri_intelext *extp){ int i; printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); for (i=9; i<32; i++) { if (extp->FeatureSupport & (1<<i)) printk(" - Unknown Bit %X: supported\n", i); } printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); for (i=1; i<8; i++) { if (extp->SuspendCmdSupport & (1<<i)) printk(" - Unknown Bit %X: supported\n", i); } printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); for (i=2; i<16; i++) { if (extp->BlkStatusRegMask & (1<<i)) printk(" - Unknown Bit %X Active: yes\n",i); } printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", extp->VccOptimal >> 8, extp->VccOptimal & 0xf); if (extp->VppOptimal) printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", extp->VppOptimal >> 8, extp->VppOptimal & 0xf);}#endif/* This routine is made available to other mtd code via * inter_module_register. It must only be accessed through * inter_module_get which will bump the use count of this module. The * addresses passed back in cfi are valid as long as the use count of * this module is non-zero, i.e. between inter_module_get and * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. */struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary){ struct cfi_private *cfi = map->fldrv_priv; int i; __u32 base = cfi->chips[0].start; 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_intelext *extp; int ofs_factor = cfi->interleave * cfi->device_type; //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); if (!adr) return NULL; /* Switch it into Query Mode */ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); extp = kmalloc(sizeof(*extp), GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); return NULL; } /* Read in the Extended Query Table */ for (i=0; i<sizeof(*extp); i++) { ((unsigned char *)extp)[i] = cfi_read_query(map, (base+((adr+i)*ofs_factor))); } if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { printk(KERN_WARNING " Unknown IntelExt Extended Query " "version %c.%c.\n", extp->MajorVersion, extp->MinorVersion); kfree(extp); return NULL; } /* Do some byteswapping if necessary */ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ cfi_tell_features(extp);#endif if(extp->SuspendCmdSupport & 1) {//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ printk(KERN_WARNING "cfi_cmdset_0001: Suspend " "erase on write disabled.\n"); extp->SuspendCmdSupport &= ~1;#else printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");#endif } /* Install our own private info structure */ cfi->cmdset_priv = extp; } 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; cfi->chips[i].ref_point_counter = 0; } map->fldrv = &cfi_intelext_chipdrv; /* Make sure it's in read mode */ cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_intelext_setup(map);}static struct mtd_info *cfi_intelext_setup(struct map_info *map){ struct cfi_private *cfi = map->fldrv_priv; struct mtd_info *mtd; unsigned long offset = 0; int i,j; unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); if (!mtd) { printk(KERN_ERR "Failed to allocate memory for MTD device\n"); goto setup_err; } memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; mtd->type = MTD_NORFLASH; 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_ERR "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; } for (i=0; i<mtd->numeraseregions;i++){ printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", i,mtd->eraseregions[i].offset, mtd->eraseregions[i].erasesize, mtd->eraseregions[i].numblocks); } /* Also select the correct geometry setup too */ mtd->erase = cfi_intelext_erase_varsize; mtd->read = cfi_intelext_read; if(map->point && map->unpoint){ mtd->point = do_point; mtd->unpoint = do_unpoint; }#ifndef FORCE_WORD_WRITE if ( cfi->cfiq->BufWriteTimeoutTyp ) { printk("Using buffer write method\n" ); mtd->write = cfi_intelext_write_buffers; } else {#else {#endif printk("Using word write method\n" ); mtd->write = cfi_intelext_write_words; } mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; mtd->sync = cfi_intelext_sync; mtd->lock = cfi_intelext_lock; mtd->unlock = cfi_intelext_unlock; mtd->suspend = cfi_intelext_suspend; mtd->resume = cfi_intelext_resume; mtd->flags = MTD_CAP_NORFLASH; map->fldrv = &cfi_intelext_chipdrv; MOD_INC_USE_COUNT; mtd->name = map->name; return mtd; setup_err: if(mtd) { if(mtd->eraseregions) kfree(mtd->eraseregions); kfree(mtd); } kfree(cfi->cmdset_priv); kfree(cfi->cfiq); return NULL;}static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len){ cfi_word status, status_OK; unsigned long timeo; DECLARE_WAITQUEUE(wait, current); unsigned long cmd_addr; struct cfi_private *cfi = map->fldrv_priv; adr += chip->start; /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); timeo = jiffies + HZ; retry: spin_lock(chip->mutex); /* Check that the chip's ready to talk to us. * If it's in FL_ERASING state, suspend it and make it talk now. */ switch (chip->state) { case FL_READY: case FL_POINT: break; case FL_CFI_QUERY: case FL_JEDEC_QUERY: cfi_write(map, CMD(0x70), cmd_addr); chip->state = FL_STATUS; case FL_STATUS: status = cfi_read(map, cmd_addr); if ((status & status_OK) == status_OK) { cfi_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; } /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock(chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); cfi_udelay(1); goto retry; default: /* Stick ourselves on a wait queue to be woken when someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; goto retry; } chip->state = FL_POINT; chip->ref_point_counter++; spin_unlock(chip->mutex); return 0;}static int do_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 (from + len > mtd->size) return -EINVAL; *mtdbuf = map->point(map, from, len); if(*mtdbuf == NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -