📄 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.1.1.1 2004/02/04 06:22:25 laputa 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...)
#endif
static 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 }
}
}
};
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;
}
chips[0].start = 0;
temp.numchips = 1;
for (size = mtd->size; size > 1; size >>= 1) {
temp.chipshift++;
}
switch (temp.interleave) {
case 2:
temp.chipshift += 1;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -