首先说明一下,笔者使用的linux源码的版本是2.6.30。
Map.h中定义了一个结构体:
struct mtd_chip_driver {
struct mtd_info *(*probe)(struct map_info *map);//探测函数
void (*destroy)(struct mtd_info *);//销毁
struct module *module;
char *name;//芯片驱动的类型,如CFI,JEDEC,ROM,RAM等
struct list_head list;//将所有结构实体链接起来
};
mtd_chip_driver为所有芯片驱动程序提供了分类:
(1)jedec_probe.c中定义了JEDEC标准的FLASH驱动;
static struct mtd_chip_driver jedec_chipdrv = {
.probe = jedec_probe,
.name = "jedec_probe",
.module = THIS_MODULE
};
(2)cfi_probe.c中定义CFI标准的FLASH驱动;
static struct mtd_chip_driver cfi_chipdrv = {
.probe = cfi_probe,
.name = "cfi_probe",
.module = THIS_MODULE
};
(3)Map_ram.c定义了以RAM作为MTD存储介质的驱动;
static struct mtd_chip_driver mapram_chipdrv = {
.probe = map_ram_probe,
.name = "map_ram",
.module = THIS_MODULE
};
(4)Map_rom.c定义了以ROM作为MTD存储介质的驱动;
static struct mtd_chip_driver maprom_chipdrv = {
.probe = map_rom_probe,
.name = "map_rom",
.module = THIS_MODULE
};
(A)JEDEC Flash的探测
它的代码在drivers/mtd/chips/Jedec_probe.c。从上面(1)中设置的jedec_chipdrv.probe函数开始执行:
static struct mtd_info *jedec_probe(struct map_info *map)
{
/*
* Just use the generic probe stuff to call our CFI-specific
* chip_probe routine in all the possible permutations, etc.
*/
return mtd_do_chip_probe(map, &jedec_chip_probe);
}
调用Gen_probe.c(这个文件是CFI和JEDEC的公共代码,两者都用到其中的代码)中的mtd_do_chip_probe:
struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
{
struct mtd_info *mtd = NULL;
struct cfi_private *cfi;
/* First probe the map to see if we have CFI stuff there. */
cfi = genprobe_ident_chips(map, cp);
if (!cfi)
return NULL;
map->fldrv_priv = cfi;
/* OK we liked it. Now find a driver for the command set it talks */
mtd = check_cmd_set(map, 1); /* First the primary cmdset */
if (!mtd)
mtd = check_cmd_set(map, 0); /* Then the secondary */
if (mtd) {
if (mtd->size > map->size) {
printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n",
(unsigned long)mtd->size >> 10,
(unsigned long)map->size >> 10);
mtd->size = map->size;
}
return mtd;
}
printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n");
kfree(cfi->cfiq);
kfree(cfi);
map->fldrv_priv = NULL;
return NULL;
}
先调用genprobe_ident_chips得到CFI结构,再判断是哪种指令集。FLASH的指令集有Intel、AMD、STAA指令,根据不同的指令,需要设置不同的操作。
1)cfi_cmdset_0001:Intel指令集,源文件drivers/mtd/chips/cfi_cmdset_0001.c
2)cfi_cmdset_0002:AMD指令集,源文件drivers/mtd/chips/cfi_cmdset_0002.c
3)cfi_cmdset_0020:STAA指令集,源文件drivers/mtd/chips/cfi_cmdset_0020.c
在这三个函数执行后,mtd_info实体被建立和初始化。
genprobe_ident_chips:
static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
{
struct cfi_private cfi;
struct cfi_private *retcfi;
unsigned long *chip_map;
int i, j, mapsize;
int max_chips;
memset(&cfi, 0, sizeof(cfi));
/* Call the probetype-specific code with all permutations of
interleave and device type, etc. */
if (!genprobe_new_chip(map, cp, &cfi)) {
/* The probe didn't like it */
pr_debug("%s: Found no %s device at location zero\n",
cp->name, map->name);
return NULL;
}
#if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD
probe routines won't ever return a broken CFI structure anyway,
because they make them up themselves.
*/
if (cfi.cfiq->NumEraseRegions == 0) {
printk(KERN_WARNING "Number of erase regions is zero\n");
kfree(cfi.cfiq);
return NULL;
}
#endif
cfi.chipshift = cfi.cfiq->DevSize;
if (cfi_interleave_is_1(&cfi)) {
;
} else if (cfi_interleave_is_2(&cfi)) {
cfi.chipshift++;
} else if (cfi_interleave_is_4((&cfi))) {
cfi.chipshift += 2;
} else if (cfi_interleave_is_8(&cfi)) {
cfi.chipshift += 3;
} else {
BUG();
}
cfi.numchips = 1;
/*
* Allocate memory for bitmap of valid chips.
* Align bitmap storage size to full byte.
*/
max_chips = map->size >> cfi.chipshift;
if (!max_chips) {
printk(KERN_WARNING "NOR chip too large to fit in mapping. Attempting to cope...\n");
max_chips = 1;
}
mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG);
chip_map = kzalloc(mapsize, GFP_KERNEL);
if (!chip_map) {
printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
kfree(cfi.cfiq);
return NULL;
}
set_bit(0, chip_map); /* Mark first chip valid */
/*
* Now probe for other chips, checking sensibly for aliases while
* we're at it. The new_chip probe above should have let the first
* chip in read mode.
*/
for (i = 1; i < max_chips; i++) {
cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
}
...代码略
return retcfi;
}
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi)
{
int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */
int max_chips = map_bankwidth(map); /* And minimum 1 */
int nr_chips, type;
for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {
if (!cfi_interleave_supported(nr_chips))
continue;
cfi->interleave = nr_chips;
/* Minimum device size. Don't look for one 8-bit device
in a 16-bit bus, etc. */
type = map_bankwidth(map) / nr_chips;
for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
cfi->device_type = type;
if (cp->probe_chip(map, 0, NULL, cfi))//调用probe_chip,JEDEC和CFI调用各自的实现函数
return 1;
}
}
return 0;
}
genprobe_ident_chips和genprobe_new_chip这两个函数的代码在Gen_probe.c中,这个文件的代码是JEDEC和CFI驱动共用的代码,为区分两者,使用了struct chip_probe作为参数传递。在Jedec_probe.c中定义的这个结构体是:
static struct chip_probe jedec_chip_probe = {
.name = "JEDEC",
.probe_chip = jedec_probe_chip
};
所以当执行到cp->probe_chip时调用的是jedec_probe_chip函数。
jedec_probe_chip函数的原型是
int jedec_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi)
读者可能会奇怪,为何JEDEC flash的代码中会有个有关CFI的结构体struct cfi_private。这是因为代码的设计者把JEDEC模拟成了CFI设备,当cfi_private.cfi_mode==CFI_MODE_CFI时,它是一个真实的CFI设备,当cfi_private.cfi_mode==CFI_MODE_JEDEC时,它是JECEC模拟的CFI设备。
cfi_private结构:
struct cfi_private {
uint16_t cmdset;
void *cmdset_priv;
int interleave;
int device_type;
int cfi_mode; /* Are we a JEDEC device pretending to be CFI? */
int addr_unlock1;
int addr_unlock2;
struct mtd_info *(*cmdset_setup)(struct map_info *);
struct cfi_ident *cfiq; /* For now only one. We insist that all devs
must be of the same type. */
int mfr, id;
int numchips;
unsigned long chipshift; /* Because they're of the same type */
const char *im_name; /* inter_module name for cmdset_setup */
struct flchip chips[0]; /* per-chip data structure for each chip */
};
系统可以有多个FLASH设备,numchips决定FLASH设备的个数。一个MTD设备也可以由多个FLASH设备组成,它用interleave表示,device_type表示它的位宽。例如一个位宽为32位的MTD设备由4个8位的FLASH设备组成,那么interleave=4。struct flchip chips用于描述每个FLASH设备的信息。
再回到Jedec_probe.c中的jedec_probe_chip函数:
jedec_probe_chip函数的主要作用是获取FLASH的信息并将它设置在cfi_private结构中。在此函数中,调用jedec_read_mfr和jedec_read_id获取生产厂商ID和设备ID,再调用jedec_match查找全局jedec_table表,找到相应的FLASH信息。最后调用cfi_jedec_setup完成cfi_private结构的设置。
全局jedec_table表的作用是列举出一些JEDEC标准的FLASH芯片的信息。它的结构结构如下:
struct amd_flash_info {
const char *name;//设备名称
const uint16_t mfr_id;//厂商ID
const uint16_t dev_id;//设备ID
const uint8_t dev_size;//容量大小
const uint8_t nr_regions;//按擦除大小的分类,分类的个数
const uint16_t cmd_set;//指令集类型
const uint32_t regions[6];//区域
const uint8_t devtypes; /* Bitmask for x8, x16 etc. 指令类型*/
const uint8_t uaddr; /* unlock addrs for 8, 16, 32, 64 */
};
如果FLASH设备的信息不在此表表,需要自定义加入。例如:
{
.mfr_id = MANUFACTURER_SST,
.dev_id = SST39LF040,
.name = "SST 39LF040",
.devtypes = CFI_DEVICETYPE_X8,
.uaddr = MTD_UADDR_0x5555_0x2AAA,
.dev_size = SIZE_512KiB,
.cmd_set = P_ID_AMD_STD,
.nr_regions = 1,
.regions = {
ERASEINFO(0x01000,128),
}
(B)CFI Flash的探测
CFI Flash的探测和JEDEC的探测有很多相似处,它们都调用了Gen_probe.c中的公共代码。CFI Flash的驱动代码在deivers/mtd/Cfi_probe.c中。
mtd_chip_driver的结构:
static struct mtd_chip_driver cfi_chipdrv = {
.probe = cfi_probe,
.name = "cfi_probe",
.module = THIS_MODULE
};
入口是cfi_probe函数:
static struct chip_probe cfi_chip_probe = {
.name = "CFI",
.probe_chip = cfi_probe_chip
};
struct mtd_info *cfi_probe(struct map_info *map)
{
/*
* Just use the generic probe stuff to call our CFI-specific
* chip_probe routine in all the possible permutations, etc.
*/
return mtd_do_chip_probe(map, &cfi_chip_probe);
}
和JEDEC Flash的实现函数一样,这里也调用Gen_probe.c中的mtd_do_chip_probe函数,只是传递的参数不同。
上面已经讲过mtd_do_chip_probe函数,它的内部调用genprobe_ident_chips,再调用genprobe_new_chip,并最终调用cp->probe_chip。这里它对应的是Cfi_probe.c中的cfi_probe_chip函数。和JEDEC不同,CFI直接通过命令读取FLASH的信息,drivers/mtd/chips/cfi_util.c提供了这些代码。
总结
无论是哪种类型的FLASH的probe,都是为了将信息生成到mtd_info结构中。先通过FLASH驱动读ID或读CFI结构的方法填充一个cfi_private结构,然后将这个结构体赋给map_info中的fldrv_priv,最后将map_info结构的指针赋给mtd_info->priv。这样,所有的信息都被保存在mtd_info。