首先说明一下,笔者使用的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。