⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 amd_flash.c

📁 vivi源代码专为三星公司arm处理器开发的启动代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -