📄 cfi_cmdset_0001.c
字号:
/* * Common Flash Interface support: * Intel Extended Vendor Command Set (ID 0x0001) * * (C) 2000 Red Hat. GPL'd * * * 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 * 21/03/2007 Rodolfo Giometti <giometti@linux.it> * - auto unlock sectors on resume for auto locking flash on power up */#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/reboot.h>#include <linux/bitmap.h>#include <linux/mtd/xip.h>#include <linux/mtd/map.h>#include <linux/mtd/mtd.h>#include <linux/mtd/compatmac.h>#include <linux/mtd/cfi.h>/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE *//* #define CMDSET0001_DISABLE_WRITE_SUSPEND */// debugging, turns off buffer write mode if set to 1#define FORCE_WORD_WRITE 0#define MANUFACTURER_INTEL 0x0089#define I82802AB 0x00ad#define I82802AC 0x00ac#define MANUFACTURER_ST 0x0020#define M50LPW080 0x002F#define M50FLW080A 0x0080#define M50FLW080B 0x0081#define AT49BV640D 0x02destatic int cfi_intelext_read (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_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *);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);#ifdef CONFIG_MTD_OTPstatic int cfi_intelext_read_fact_prot_reg (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_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);static int cfi_intelext_get_fact_prot_info (struct mtd_info *, struct otp_info *, size_t);static int cfi_intelext_get_user_prot_info (struct mtd_info *, struct otp_info *, size_t);#endifstatic int cfi_intelext_suspend (struct mtd_info *);static void cfi_intelext_resume (struct mtd_info *);static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *);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 mtd_info *);static int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **);static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys);static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode);static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);#include "fwh_lock.h"/* * *********** SETUP AND PROBE BITS *********** */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(" Extended Query version %c.%c\n", extp->MajorVersion, extp->MinorVersion); 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"); printk(" - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported"); printk(" - Extended Flash Array: %s\n", extp->FeatureSupport&1024?"supported":"unsupported"); for (i=11; 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(" - Lock-Down Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); for (i=2; i<3; i++) { if (extp->BlkStatusRegMask & (1<<i)) printk(" - Unknown Bit %X Active: yes\n",i); } printk(" - EFA Lock Bit: %s\n", extp->BlkStatusRegMask&16?"yes":"no"); printk(" - EFA Lock-Down Bit: %s\n", extp->BlkStatusRegMask&32?"yes":"no"); for (i=6; 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 >> 4, extp->VccOptimal & 0xf); if (extp->VppOptimal) printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", extp->VppOptimal >> 4, extp->VppOptimal & 0xf);}#endif/* Atmel chips don't use the same PRI format as Intel chips */static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *extp = cfi->cmdset_priv; struct cfi_pri_atmel atmel_pri; uint32_t features = 0; /* Reverse byteswapping */ extp->FeatureSupport = cpu_to_le32(extp->FeatureSupport); extp->BlkStatusRegMask = cpu_to_le16(extp->BlkStatusRegMask); extp->ProtRegAddr = cpu_to_le16(extp->ProtRegAddr); memcpy(&atmel_pri, extp, sizeof(atmel_pri)); memset((char *)extp + 5, 0, sizeof(*extp) - 5); printk(KERN_ERR "atmel Features: %02x\n", atmel_pri.Features); if (atmel_pri.Features & 0x01) /* chip erase supported */ features |= (1<<0); if (atmel_pri.Features & 0x02) /* erase suspend supported */ features |= (1<<1); if (atmel_pri.Features & 0x04) /* program suspend supported */ features |= (1<<2); if (atmel_pri.Features & 0x08) /* simultaneous operations supported */ features |= (1<<9); if (atmel_pri.Features & 0x20) /* page mode read supported */ features |= (1<<7); if (atmel_pri.Features & 0x40) /* queued erase supported */ features |= (1<<4); if (atmel_pri.Features & 0x80) /* Protection bits supported */ features |= (1<<6); extp->FeatureSupport = features; /* burst write mode not supported */ cfi->cfiq->BufWriteTimeoutTyp = 0; cfi->cfiq->BufWriteTimeoutMax = 0;}#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */static void fixup_intel_strataflash(struct mtd_info *mtd, void* param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *extp = cfi->cmdset_priv; printk(KERN_WARNING "cfi_cmdset_0001: Suspend " "erase on write disabled.\n"); extp->SuspendCmdSupport &= ~1;}#endif#ifdef CMDSET0001_DISABLE_WRITE_SUSPENDstatic void fixup_no_write_suspend(struct mtd_info *mtd, void* param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *cfip = cfi->cmdset_priv; if (cfip && (cfip->FeatureSupport&4)) { cfip->FeatureSupport &= ~4; printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); }}#endifstatic void fixup_st_m28w320ct(struct mtd_info *mtd, void* param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */}static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; /* Note this is done after the region info is endian swapped */ cfi->cfiq->EraseRegionInfo[1] = (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;};static void fixup_use_point(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; if (!mtd->point && map_is_linear(map)) { mtd->point = cfi_intelext_point; mtd->unpoint = cfi_intelext_unpoint; }}static void fixup_use_write_buffers(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; if (cfi->cfiq->BufWriteTimeoutTyp) { printk(KERN_INFO "Using buffer write method\n" ); mtd->write = cfi_intelext_write_buffers; mtd->writev = cfi_intelext_writev; }}/* * Some chips power-up with all sectors locked by default. */static void fixup_unlock_powerup_lock(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *cfip = cfi->cmdset_priv; if (cfip->FeatureSupport&32) { printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); mtd->flags |= MTD_POWERUP_LOCK; }}static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },#endif#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL },#endif#if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL },#endif { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, { MANUFACTURER_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, }, { 0, 0, NULL, NULL }};static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_ST, M50FLW080A, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_ST, M50FLW080B, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL }};static struct cfi_fixup fixup_table[] = { /* The CFI vendor ids and the JEDEC vendor IDs appear * to be common. It is like the devices id's are as * well. This table is to pick all cases where * we know that is the case. */ { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL }, { 0, 0, NULL, NULL }};static inline struct cfi_pri_intelext *read_pri_intelext(struct map_info *map, __u16 adr){ struct cfi_pri_intelext *extp; unsigned int extp_size = sizeof(*extp); again: extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp"); if (!extp) return NULL; if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { printk(KERN_ERR " Unknown Intel/Sharp 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); if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') { unsigned int extra_size = 0; int nb_parts, i; /* Protection Register info */ extra_size += (extp->NumProtectionFields - 1) * sizeof(struct cfi_intelext_otpinfo); /* Burst Read info */ extra_size += 2; if (extp_size < sizeof(*extp) + extra_size) goto need_more; extra_size += extp->extra[extra_size-1]; /* Number of hardware-partitions */ extra_size += 1; if (extp_size < sizeof(*extp) + extra_size) goto need_more; nb_parts = extp->extra[extra_size - 1]; /* skip the sizeof(partregion) field in CFI 1.4 */ if (extp->MinorVersion >= '4') extra_size += 2; for (i = 0; i < nb_parts; i++) { struct cfi_intelext_regioninfo *rinfo; rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size]; extra_size += sizeof(*rinfo); if (extp_size < sizeof(*extp) + extra_size) goto need_more; rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions); extra_size += (rinfo->NumBlockTypes - 1) * sizeof(struct cfi_intelext_blockinfo); } if (extp->MinorVersion >= '4') extra_size += sizeof(struct cfi_intelext_programming_regioninfo); if (extp_size < sizeof(*extp) + extra_size) { need_more: extp_size = sizeof(*extp) + extra_size; kfree(extp); if (extp_size > 4096) { printk(KERN_ERR "%s: cfi_pri_intelext is too fat\n", __func__); return NULL; } goto again; } } return extp;}struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary){ struct cfi_private *cfi = map->fldrv_priv; struct mtd_info *mtd; int i; mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) { printk(KERN_ERR "Failed to allocate memory for MTD device\n"); return NULL; } mtd->priv = map; mtd->type = MTD_NORFLASH; /* Fill in the default mtd operations */ mtd->erase = cfi_intelext_erase_varsize; mtd->read = cfi_intelext_read; mtd->write = cfi_intelext_write_words; 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; mtd->name = map->name; mtd->writesize = 1; mtd->reboot_notifier.notifier_call = cfi_intelext_reboot; 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; extp = read_pri_intelext(map, adr); if (!extp) { kfree(mtd); return NULL; } /* Install our own private info structure */ cfi->cmdset_priv = extp; cfi_fixup(mtd, cfi_fixup_table);#ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ cfi_tell_features(extp);#endif if(extp->SuspendCmdSupport & 1) { printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); } } else if (cfi->cfi_mode == CFI_MODE_JEDEC) { /* Apply jedec specific fixups */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -