📄 cfi_cmdset_0002.c
字号:
/* * 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> * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com> * * 2_by_8 routines added by Simon Munton * * 4_by_16 work by Carolyn J. Smith * * XIP support hooks by Vitaly Wool (based on code for Intel flash * by Nicolas Pitre) * * 25/09/2008 Christopher Moore: TopBottom fixup for many Macronix with CFI V1.0 * * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * This code is GPL */#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>#include <linux/mtd/xip.h>#define AMD_BOOTLOC_BUG#define FORCE_WORD_WRITE 0#define MAX_WORD_RETRIES 3#define MANUFACTURER_AMD 0x0001#define MANUFACTURER_ATMEL 0x001F#define MANUFACTURER_MACRONIX 0x00C2#define MANUFACTURER_SST 0x00BF#define SST49LF004B 0x0060#define SST49LF040B 0x0050#define SST49LF008A 0x005a#define AT49BV6416 0x00d6static 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 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 mtd_info *);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"static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len);static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);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_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 mtd_info *mtd, void* param){ struct map_info *map = mtd->priv; 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 */ DEBUG(MTD_DEBUG_LEVEL1, "%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n", map->name, cfi->mfr, cfi->id); /* AFAICS all 29LV400 with a bottom boot block have a device ID * of 0x22BA in 16-bit mode and 0xBA in 8-bit mode. * These were badly detected as they have the 0x80 bit set * so treat them as a special case. */ if (((cfi->id == 0xBA) || (cfi->id == 0x22BA)) && /* Macronix added CFI to their 2nd generation * MX29LV400C B/T but AFAICS no other 29LV400 (AMD, * Fujitsu, Spansion, EON, ESI and older Macronix) * has CFI. * * Therefore also check the manufacturer. * This reduces the risk of false detection due to * the 8-bit device ID. */ (cfi->mfr == MANUFACTURER_MACRONIX)) { DEBUG(MTD_DEBUG_LEVEL1, "%s: Macronix MX29LV400C with bottom boot block" " detected\n", map->name); extp->TopBottom = 2; /* bottom boot */ } else 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 */ } DEBUG(MTD_DEBUG_LEVEL1, "%s: AMD CFI PRI V%c.%c has no boot block field;" " deduced %s from Device ID\n", map->name, major, minor, extp->TopBottom == 2 ? "bottom" : "top"); }}#endifstatic 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) { DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); mtd->write = cfi_amdstd_write_buffers; }}/* Atmel chips don't use the same PRI format as AMD 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_amdstd *extp = cfi->cmdset_priv; struct cfi_pri_atmel atmel_pri; memcpy(&atmel_pri, extp, sizeof(atmel_pri)); memset((char *)extp + 5, 0, sizeof(*extp) - 5); if (atmel_pri.Features & 0x02) extp->EraseSuspend = 2; /* Some chips got it backwards... */ if (cfi->id == AT49BV6416) { if (atmel_pri.BottomBoot) extp->TopBottom = 3; else extp->TopBottom = 2; } else { if (atmel_pri.BottomBoot) extp->TopBottom = 2; else extp->TopBottom = 3; } /* burst write mode not supported */ cfi->cfiq->BufWriteTimeoutTyp = 0; cfi->cfiq->BufWriteTimeoutMax = 0;}static void fixup_use_secsi(struct mtd_info *mtd, void *param){ /* Setup for chips with a secsi area */ mtd->read_user_prot_reg = cfi_amdstd_secsi_read; mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;}static void fixup_use_erase_chip(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; if ((cfi->cfiq->NumEraseRegions == 1) && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) { mtd->erase = cfi_amdstd_erase_chip; }}/* * Some Atmel chips (e.g. the AT49BV6416) power-up with all sectors * locked by default. */static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param){ mtd->lock = cfi_atmel_lock; mtd->unlock = cfi_atmel_unlock; mtd->flags |= MTD_POWERUP_LOCK;}static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { cfi->cfiq->EraseRegionInfo[0] |= 0x0040; pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name); }}static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name); }}static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },#ifdef AMD_BOOTLOC_BUG { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, { MANUFACTURER_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL },#endif { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors, NULL, }, { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },#if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },#endif { 0, 0, NULL, NULL }};static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_SST, SST49LF008A, 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_erase_chip, NULL }, { CFI_MFR_ATMEL, AT49BV6416, fixup_use_atmel_lock, NULL }, { 0, 0, NULL, NULL }};struct mtd_info *cfi_cmdset_0002(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_WARNING "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_amdstd_erase_varsize; mtd->write = cfi_amdstd_write_words; mtd->read = cfi_amdstd_read; mtd->sync = cfi_amdstd_sync; mtd->suspend = cfi_amdstd_suspend; mtd->resume = cfi_amdstd_resume; mtd->flags = MTD_CAP_NORFLASH; mtd->name = map->name; mtd->writesize = 1; if (cfi->cfi_mode==CFI_MODE_CFI){ unsigned char bootloc; /* * 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) { kfree(mtd); return NULL; } if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " "version %c.%c.\n", extp->MajorVersion, extp->MinorVersion); kfree(extp); kfree(mtd); return NULL; } /* Install our own private info structure */ cfi->cmdset_priv = extp; /* Apply cfi device specific fixups */ 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 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; } } /* Set the default CFI lock/unlock addresses */ cfi->addr_unlock1 = 0x555; cfi->addr_unlock2 = 0x2aa; } /* CFI mode */ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { /* Apply jedec specific fixups */ cfi_fixup(mtd, jedec_fixup_table); } /* Apply generic fixups */ cfi_fixup(mtd, fixup_table); 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; init_waitqueue_head(&(cfi->chips[i].wq)); } map->fldrv = &cfi_amdstd_chipdrv; return cfi_amdstd_setup(mtd);}EXPORT_SYMBOL_GPL(cfi_cmdset_0002);static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; unsigned long offset = 0; int i,j; printk(KERN_NOTICE "number of %s chips: %d\n", (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); /* Select the correct geometry setup */ 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 /* 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -