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

📄 amd_flash.c

📁 老版本的mtd-snap
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * MTD map driver for AMD compatible flash chips (non-CFI) * * Author: Jonas Holmberg <jonas.holmberg@axis.com> * * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $ * * Copyright (c) 2001 Axis Communications AB * * This file is under GPL. * */#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/mtd/map.h>#include <linux/mtd/mtd.h>#include <linux/mtd/flashchip.h>/* There's no limit. It exists only to avoid realloc. */#define MAX_AMD_CHIPS 8#define DEVICE_TYPE_X8	(8 / 8)#define DEVICE_TYPE_X16	(16 / 8)#define DEVICE_TYPE_X32	(32 / 8)/* Addresses */#define ADDR_MANUFACTURER		0x0000#define ADDR_DEVICE_ID			0x0001#define ADDR_SECTOR_LOCK		0x0002#define ADDR_HANDSHAKE			0x0003#define ADDR_UNLOCK_1			0x0555#define ADDR_UNLOCK_2			0x02AA/* Commands */#define CMD_UNLOCK_DATA_1		0x00AA#define CMD_UNLOCK_DATA_2		0x0055#define CMD_MANUFACTURER_UNLOCK_DATA	0x0090#define CMD_UNLOCK_BYPASS_MODE		0x0020#define CMD_PROGRAM_UNLOCK_DATA		0x00A0#define CMD_RESET_DATA			0x00F0#define CMD_SECTOR_ERASE_UNLOCK_DATA	0x0080#define CMD_SECTOR_ERASE_UNLOCK_DATA_2	0x0030#define CMD_UNLOCK_SECTOR		0x0060/* Manufacturers */#define MANUFACTURER_AMD	0x0001#define MANUFACTURER_ATMEL	0x001F#define MANUFACTURER_FUJITSU	0x0004#define MANUFACTURER_ST		0x0020#define MANUFACTURER_SST	0x00BF#define MANUFACTURER_TOSHIBA	0x0098/* AMD */#define AM29F800BB	0x2258#define AM29F800BT	0x22D6#define AM29LV800BB	0x225B#define AM29LV800BT	0x22DA#define AM29LV160DT	0x22C4#define AM29LV160DB	0x2249#define AM29BDS323D     0x22D1/* Atmel */#define AT49xV16x	0x00C0#define AT49xV16xT	0x00C2/* Fujitsu */#define MBM29LV160TE	0x22C4#define MBM29LV160BE	0x2249#define MBM29LV800BB	0x225B/* ST - www.st.com */#define M29W800T	0x00D7#define M29W160DT	0x22C4#define M29W160DB	0x2249/* SST */#define SST39LF800	0x2781#define SST39LF160	0x2782/* Toshiba */#define TC58FVT160	0x00C2#define TC58FVB160	0x0043#define D6_MASK	0x40struct amd_flash_private {	int device_type;		int interleave;		int numchips;		unsigned long chipshift;//	const char *im_name;	struct flchip chips[0];};struct amd_flash_info {	const __u16 mfr_id;	const __u16 dev_id;	const char *name;	const u_long size;	const int numeraseregions;	const struct mtd_erase_region_info regions[4];};static int amd_flash_read(struct mtd_info *, loff_t, size_t, size_t *,			  u_char *);static int amd_flash_write(struct mtd_info *, loff_t, size_t, size_t *,			   const u_char *);static int amd_flash_erase(struct mtd_info *, struct erase_info *);static void amd_flash_sync(struct mtd_info *);static int amd_flash_suspend(struct mtd_info *);static void amd_flash_resume(struct mtd_info *);static void amd_flash_destroy(struct mtd_info *);static struct mtd_info *amd_flash_probe(struct map_info *map);static struct mtd_chip_driver amd_flash_chipdrv = {	.probe = amd_flash_probe,	.destroy = amd_flash_destroy,	.name = "amd_flash",	.module = THIS_MODULE};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(KERN_WARNING					       "%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;				chips[private->numchips].state = FL_READY;				chips[private->numchips].mutex =					&chips[private->numchips]._spinlock;				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(KERN_DEBUG "%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;}static struct mtd_info *amd_flash_probe(struct map_info *map){	static 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,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -