📄 cfi_cmdset_0002_v4.c
字号:
/* * Common Flash Interface support: * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) * * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp> * * 2_by_8 routines added by Simon Munton * * This code is GPL * * $Id: cfi_cmdset_0002.c,v 1.62 2003/01/24 23:30:13 dwmw2 Exp $ * */#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 <asm/unaligned.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>#define AMD_BOOTLOC_BUGstatic 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_onesize(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 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};struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary){ struct cfi_private *cfi = map->fldrv_priv; unsigned char bootloc; int ofs_factor = cfi->interleave * cfi->device_type; int i; __u8 major, minor; __u32 base = cfi->chips[0].start; if (cfi->cfi_mode==CFI_MODE_CFI){ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); major = cfi_read_query(map, base + (adr+3)*ofs_factor); minor = cfi_read_query(map, base + (adr+4)*ofs_factor); printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", major, minor, adr); cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xaa, 0xaaa, base, map, cfi, 1, NULL); cfi_send_gen_cmd(0x55, 0x555, base, map, cfi, 1, NULL); cfi_send_gen_cmd(0x90, 0xaaa, base, map, cfi, 1, NULL); cfi->mfr = cfi_read_query(map, base); cfi->id = cfi_read_query(map, base + ofs_factor); /* Wheee. Bring me the head of someone at AMD. */#ifdef AMD_BOOTLOC_BUG 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); bootloc = 3; /* top boot */ } else { bootloc = 2; /* bottom boot */ } } else#endif { cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor); } 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; } } switch (cfi->device_type) { case CFI_DEVICETYPE_X8: printk("device_type:CFI_DEVICETYPE_X8\n"); cfi->addr_unlock1 = 0x555; cfi->addr_unlock2 = 0x2aa; break; case CFI_DEVICETYPE_X16: printk("device_type:CFI_DEVICETYPE_X16=%d\n",CFI_DEVICETYPE_X16); cfi->addr_unlock1 = 0xaaa; if (map->buswidth == cfi->interleave) { /* X16 chip(s) in X8 mode */ printk("X16 chip(s) in X8 mode,buswidth is %d\n",map->buswidth); cfi->addr_unlock2 = 0x555; } else { cfi->addr_unlock2 = 0x554; } break; case CFI_DEVICETYPE_X32: printk("device_type:CFI_DEVICETYPE_X32\n"); cfi->addr_unlock1 = 0x1555; cfi->addr_unlock2 = 0xaaa; break; default: printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", 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; cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); 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; //add by zxh //int buswt = CFIDEV_BUSWIDTH; 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; if (cfi->cfiq->NumEraseRegions == 1) { /* No need to muck about with multiple erase sizes */ mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; } else { unsigned long offset = 0; int i,j; 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 1 // 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 } switch (CFIDEV_BUSWIDTH) { case 1: case 2: case 4:#if 1 if (mtd->numeraseregions > 1) mtd->erase = cfi_amdstd_erase_varsize; else#endif if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) mtd->erase = cfi_amdstd_erase_chip; else mtd->erase = cfi_amdstd_erase_onesize; mtd->read = cfi_amdstd_read;#if 1 //using the buffer write method to speed up the write process if( cfi->cfiq->BufWriteTimeoutTyp ){ printk( "Using buffer write method\n" ); mtd->write = cfi_amdstd_write_buffers; } else {#else {#endif printk( "Using word write method\n" ); mtd->write = cfi_amdstd_write_words; }#if 0 }#endif break; default: printk(KERN_WARNING "Unsupported buswidth\n"); goto setup_err; break; } if (cfi->fast_prog) { /* In cfi_amdstd_write_words() we frob the protection stuff without paying any attention to the state machine. This upsets in-progress erases. So we turn this flag off for now till the code gets fixed. */ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n"); cfi->fast_prog = 0; } /* 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; MOD_INC_USE_COUNT; return mtd; setup_err: if(mtd) { if(mtd->eraseregions) kfree(mtd->eraseregions); kfree(mtd); } kfree(cfi->cmdset_priv); kfree(cfi->cfiq); return NULL;}static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf){ DECLARE_WAITQUEUE(wait, current); unsigned long timeo = jiffies + HZ; retry: cfi_spin_lock(chip->mutex); if (chip->state != FL_READY){#if 0 printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);#endif set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait);#if 0 if(signal_pending(current)) return -EINTR;#endif timeo = jiffies + HZ; goto retry; } adr += chip->start; chip->state = FL_READY; map->copy_from(map, buf, adr, len); wake_up(&chip->wq); 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; ofs = 0; chipnum++; } return ret;}static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf){ DECLARE_WAITQUEUE(wait, current); unsigned long timeo = jiffies + HZ; struct cfi_private *cfi = map->fldrv_priv; retry: cfi_spin_lock(chip->mutex); if (chip->state != FL_READY){#if 0 printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);#endif set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait);#if 0 if(signal_pending(current)) return -EINTR;#endif timeo = jiffies + HZ; goto retry; } adr += chip->start; chip->state = FL_READY; cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); map->copy_from(map, buf, adr, len); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); return 0;}static int cfi_amdstd_secsi_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 */ /* 8 secsi bytes per chip */ chipnum=from>>3; ofs=from & 7; *retlen = 0; while (len) { unsigned long thislen; if (chipnum >= cfi->numchips) break; if ((len + ofs -1) >> 3) thislen = (1<<3) - ofs; else thislen = len; ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); if (ret) break; *retlen += thislen; len -= thislen; buf += thislen; ofs = 0; chipnum++; } return ret;}static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast){ unsigned long timeo = jiffies + HZ; unsigned int oldstatus, status; unsigned int dq6, dq5; struct cfi_private *cfi = map->fldrv_priv; DECLARE_WAITQUEUE(wait, current); int ret = 0; retry: cfi_spin_lock(chip->mutex); if (chip->state != FL_READY) {#if 0 printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state);#endif set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait);#if 0 printk(KERN_DEBUG "Wake up to write:\n"); if(signal_pending(current)) return -EINTR;#endif timeo = jiffies + HZ; goto retry; } chip->state = FL_WRITING; adr += chip->start; ENABLE_VPP(map); if (fast) { /* Unlock bypass */ cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); } else { cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); } cfi_write(map, datum, adr); cfi_spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -