?? amd_flash.c
字號:
/* * MTD map driver for AMD compatible flash chips (non-CFI) * * Author: Jonas Holmberg <jonas.holmberg@axis.com> * * $Id: amd_flash.c,v 1.5 2002/10/08 01:46:34 nandy Exp $ * * Copyright (c) 2001 Axis Communications AB * * This file is under GPL. * * * Author: Janghon Lyu <nandy@mizi.com> * * And then, I translated this file to the vivi. * */#include <config.h>#include <printk.h>#include <heap.h>#include <time.h>#include <mtd/map.h>#include <mtd/mtd.h>#include <mtd/flashchip.h>#include <mtd/amd_flash.h>#include <command.h>#include <types.h>#include <io.h>#include <sizes.h>#include <vivi_string.h>#include <errno.h>#include <string.h>/* debugging macros */#undef AMDFLASH_DEBUG#ifdef AMDFLASH_DEBUG#define DPRINTK(args...) printk(##args)#else#define DPRINTK(args...)#endifstatic const char im_name[] = "amd_flash";static inline __u32 wide_read(struct map_info *map, __u32 addr){ if (map->buswidth == 1) { return map->read8(map, addr); } else if (map->buswidth == 2) { return map->read16(map, addr); } else if (map->buswidth == 4) { return map->read32(map, addr); } return 0;}static inline void wide_write(struct map_info *map, __u32 val, __u32 addr){ if (map->buswidth == 1) { map->write8(map, val, addr); } else if (map->buswidth == 2) { map->write16(map, val, addr); } else if (map->buswidth == 4) { map->write32(map, val, addr); }}static inline __u32 make_cmd(struct map_info *map, __u32 cmd){ const struct amd_flash_private *private = map->fldrv_priv; if ((private->interleave == 2) && (private->device_type == DEVICE_TYPE_X16)) { cmd |= (cmd << 16); } return cmd;}static inline void send_unlock(struct map_info *map, unsigned long base){ wide_write(map, (CMD_UNLOCK_DATA_1 << 16) | CMD_UNLOCK_DATA_1, base + (map->buswidth * ADDR_UNLOCK_1)); wide_write(map, (CMD_UNLOCK_DATA_2 << 16) | CMD_UNLOCK_DATA_2, base + (map->buswidth * ADDR_UNLOCK_2));}static inline void send_cmd(struct map_info *map, unsigned long base, __u32 cmd){ send_unlock(map, base); wide_write(map, make_cmd(map, cmd), base + (map->buswidth * ADDR_UNLOCK_1));}static inline void send_cmd_to_addr(struct map_info *map, unsigned long base, __u32 cmd, unsigned long addr){ send_unlock(map, base); wide_write(map, make_cmd(map, cmd), addr);}static inline int flash_is_busy(struct map_info *map, unsigned long addr, int interleave){ if ((interleave == 2) && (map->buswidth == 4)) { __u32 read1, read2; read1 = wide_read(map, addr); read2 = wide_read(map, addr); return (((read1 >> 16) & D6_MASK) != ((read2 >> 16) & D6_MASK)) || (((read1 & 0xffff) & D6_MASK) != ((read2 & 0xffff) & D6_MASK)); } return ((wide_read(map, addr) & D6_MASK) != (wide_read(map, addr) & D6_MASK));}static inline void unlock_sector(struct map_info *map, unsigned long sect_addr, int unlock){ /* Sector lock address. A6 = 1 for unlock, A6 = 0 for lock */ int SLA = unlock ? (sect_addr | (0x40 * map->buswidth)) : (sect_addr & ~(0x40 * map->buswidth)) ; __u32 cmd = make_cmd(map, CMD_UNLOCK_SECTOR); wide_write(map, make_cmd(map, CMD_RESET_DATA), 0); wide_write(map, cmd, SLA); /* 1st cycle: write cmd to any address */ wide_write(map, cmd, SLA); /* 2nd cycle: write cmd to any address */ wide_write(map, cmd, SLA); /* 3rd cycle: write cmd to SLA */}static inline int is_sector_locked(struct map_info *map, unsigned long sect_addr){ int status; wide_write(map, CMD_RESET_DATA, 0); send_cmd(map, sect_addr, CMD_MANUFACTURER_UNLOCK_DATA); /* status is 0x0000 for unlocked and 0x0001 for locked */ status = wide_read(map, sect_addr + (map->buswidth * ADDR_SECTOR_LOCK)); wide_write(map, CMD_RESET_DATA, 0); return status;}static int amd_flash_do_unlock(struct mtd_info *mtd, loff_t ofs, size_t len, int is_unlock){ struct map_info *map; struct mtd_erase_region_info *merip; int eraseoffset, erasesize, eraseblocks; int i; int retval = 0; int lock_status; map = mtd->priv; /* Pass the whole chip through sector by sector and check for each sector if the sector and the given interval overlap */ for(i = 0; i < mtd->numeraseregions; i++) { merip = &mtd->eraseregions[i]; eraseoffset = merip->offset; erasesize = merip->erasesize; eraseblocks = merip->numblocks; if (ofs > eraseoffset + erasesize) continue; while (eraseblocks > 0) { if (ofs < eraseoffset + erasesize && ofs + len > eraseoffset) { unlock_sector(map, eraseoffset, is_unlock); lock_status = is_sector_locked(map, eraseoffset); if (is_unlock && lock_status) { printk("Cannot unlock sector at address %x length %xx\n", eraseoffset, merip->erasesize); retval = -1; } else if (!is_unlock && !lock_status) { printk("Cannot lock sector at address %x length %x\n", eraseoffset, merip->erasesize); retval = -1; } } eraseoffset += erasesize; eraseblocks --; } } return retval;}static int amd_flash_unlock(struct mtd_info *mtd, loff_t ofs, size_t len){ return amd_flash_do_unlock(mtd, ofs, len, 1);}static int amd_flash_lock(struct mtd_info *mtd, loff_t ofs, size_t len){ return amd_flash_do_unlock(mtd, ofs, len, 0);}/* * Reads JEDEC manufacturer ID and device ID and returns the index of the first * matching table entry (-1 if not found or alias for already found chip). */ static int probe_new_chip(struct mtd_info *mtd, __u32 base, struct flchip *chips, struct amd_flash_private *private, const struct amd_flash_info *table, int table_size){ __u32 mfr_id; __u32 dev_id; struct map_info *map = mtd->priv; struct amd_flash_private temp; int i; temp.device_type = DEVICE_TYPE_X16; // Assume X16 (FIXME) temp.interleave = 2; map->fldrv_priv = &temp; /* Enter autoselect mode. */ send_cmd(map, base, CMD_RESET_DATA); send_cmd(map, base, CMD_MANUFACTURER_UNLOCK_DATA); mfr_id = wide_read(map, base + (map->buswidth * ADDR_MANUFACTURER)); dev_id = wide_read(map, base + (map->buswidth * ADDR_DEVICE_ID)); if ((map->buswidth == 4) && ((mfr_id >> 16) == (mfr_id & 0xffff)) && ((dev_id >> 16) == (dev_id & 0xffff))) { mfr_id &= 0xffff; dev_id &= 0xffff; } else { temp.interleave = 1; } for (i = 0; i < table_size; i++) { if ((mfr_id == table[i].mfr_id) && (dev_id == table[i].dev_id)) { if (chips) { int j; /* Is this an alias for an already found chip? * In that case that chip should be in * autoselect mode now. */ for (j = 0; j < private->numchips; j++) { __u32 mfr_id_other; __u32 dev_id_other; mfr_id_other = wide_read(map, chips[j].start + (map->buswidth * ADDR_MANUFACTURER )); dev_id_other = wide_read(map, chips[j].start + (map->buswidth * ADDR_DEVICE_ID)); if (temp.interleave == 2) { mfr_id_other &= 0xffff; dev_id_other &= 0xffff; } if ((mfr_id_other == mfr_id) && (dev_id_other == dev_id)) { /* Exit autoselect mode. */ send_cmd(map, base, CMD_RESET_DATA); return -1; } } if (private->numchips == MAX_AMD_CHIPS) { printk("%s: Too many flash chips " "detected. Increase " "MAX_AMD_CHIPS from %d.\n", map->name, MAX_AMD_CHIPS); return -1; } chips[private->numchips].start = base; private->numchips++; } printk("%s: Found %d x %ldMiB %s at 0x%x\n", map->name, temp.interleave, (table[i].size)/(1024*1024), table[i].name, base); mtd->size += table[i].size * temp.interleave; mtd->numeraseregions += table[i].numeraseregions; break; } } /* Exit autoselect mode. */ send_cmd(map, base, CMD_RESET_DATA); if (i == table_size) { printk("%s: unknown flash device at 0x%x, " "mfr id 0x%x, dev id 0x%x\n", map->name, base, mfr_id, dev_id); map->fldrv_priv = NULL; return -1; } private->device_type = temp.device_type; private->interleave = temp.interleave; return i;}struct mtd_info *amd_flash_probe(struct map_info *map){ /* Keep this table on the stack so that it gets deallocated after the * probe is done. */ const struct amd_flash_info table[] = { { mfr_id: MANUFACTURER_AMD, dev_id: AM29LV160DT, name: "AMD AM29LV160DT", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29LV160DB, name: "AMD AM29LV160DB", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } } }, { mfr_id: MANUFACTURER_TOSHIBA, dev_id: TC58FVT160, name: "Toshiba TC58FVT160", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_FUJITSU, dev_id: MBM29LV160TE, name: "Fujitsu MBM29LV160TE", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_TOSHIBA, dev_id: TC58FVB160, name: "Toshiba TC58FVB160", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } } }, { mfr_id: MANUFACTURER_FUJITSU, dev_id: MBM29LV160BE, name: "Fujitsu MBM29LV160BE", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29LV800BB, name: "AMD AM29LV800BB", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29F800BB, name: "AMD AM29F800BB", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29LV800BT, name: "AMD AM29LV800BT", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29F800BT, name: "AMD AM29F800BT", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29LV800BB, name: "AMD AM29LV800BB", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_ST, dev_id: M29W800T, name: "ST M29W800T", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_ST, dev_id: M29W160DT, name: "ST M29W160DT", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } } }, { mfr_id: MANUFACTURER_ST, dev_id: M29W160DB, name: "ST M29W160DB", size: 0x00200000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29BDS323D, name: "AMD AM29BDS323D", size: 0x00400000, numeraseregions: 3, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 48 }, { offset: 0x300000, erasesize: 0x10000, numblocks: 15 }, { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 }, } }, { mfr_id: MANUFACTURER_AMD, dev_id: AM29BDS643D, name: "AMD AM29BDS643D", size: 0x00800000, numeraseregions: 3, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 96 }, { offset: 0x600000, erasesize: 0x10000, numblocks: 31 }, { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 }, } }, { mfr_id: MANUFACTURER_ATMEL, dev_id: AT49xV16x, name: "Atmel AT49xV16x", size: 0x00200000, numeraseregions: 2, regions: { { offset: 0x000000, erasesize: 0x02000, numblocks: 8 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } } }, { mfr_id: MANUFACTURER_ATMEL, dev_id: AT49xV16xT, name: "Atmel AT49xV16xT", size: 0x00200000, numeraseregions: 2, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 } } }, { mfr_id: MANUFACTURER_HYNIX, dev_id: HY29LV800B, name: "Hynix HY29LV800B", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } } }, { mfr_id: MANUFACTURER_HYNIX, dev_id: HY29LV800T, name: "Hynix HY29LV800T", size: 0x00100000, numeraseregions: 4, regions: { { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } } } }; struct mtd_info *mtd; struct flchip chips[MAX_AMD_CHIPS]; int table_pos[MAX_AMD_CHIPS]; struct amd_flash_private temp; struct amd_flash_private *private; u_long size; unsigned long base; int i; int reg_idx; int offset; mtd = (struct mtd_info*)mmalloc(sizeof(*mtd)); if (!mtd) { printk("%s: malloc failed for info structure\n", map->name); return NULL; } memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; memset(&temp, 0, sizeof(temp)); printk("%s: Probing for AMD compatible flash...\n", map->name); if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table, sizeof(table)/sizeof(table[0]))) == -1) { printk("%s: Found no AMD compatible device at location zero\n", map->name); mfree(mtd); return NULL;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -