在这里主要描述 v3 的 mtd ,在 linux 的 MTD 子系统当中: Linux MTD 设备的 NOR Flash 芯片驱动遵循 CFI 接口标准,其驱动程序位于 drivers/mtd/chips 子目录下。 NAND 型 Flash 的驱动程序则位于 /drivers/mtd/nand 子目录下Map 中的 bankwidth 是总线位宽, device_type 是芯片位宽;读写,要按照总线位宽读写1 :读写,要按照总线位宽读写,注意不是 FLASH 芯片位宽(例如背靠背)。2 :寻址,程序要访问的地址和 FLASH 芯片地址引脚得到的值是不一样的,例如 16 位的 FLASH 芯片,对于 CPU , 0x00 和 0x01 表示2 个不同的字节,但是到了 FLASH 引脚得到的都是 0 ,也就是都指向 FLASH 的第一个 WORD 。可以认为地址总线的 bit0 悬空,或者认为转换总线 , bit0 上实际输出的是 bit1 。这个解释了要点 1 。3 :芯片手册提到偏移量都是基于 WORD 的,而 WORD 的位宽取决于芯片的位宽,因此在下命令的时候,实际偏移 = 手册偏移*buswidth/8 。4 :芯片手册提到的变量长度(典型如 CFI 信息)例如 2 ,指的是,变量是个 16bit 数,但是读的时候,要读 2 个 WORD ,然后把每个WORD 的低 8 位拼成 1 个 16bit 数。读 WORD 再拼凑确实挺麻烦,尤其是读取大结构的时候,不过参照 cfi_util.c 的 cfi_read_pri 函数的做法就简单了。5 :背靠背,也就是比方说 2 块 16 位的芯片一起接在 32 位的总线上。带来的就是寻址的问题,很显然,首先要按 32 位读写;其次就是下命令的地址,实际偏移 = 手册偏移 *interleave*device_type/8 ,device_type=buswidth/interleave ,而 buswidth 这个时候是 32(总线位宽 ) 。另外就是背靠背的时候,命令和返回的状态码是“双份的”,例如 2 块 16 位背靠背,读命令是 0x00ff00ff 。 系统在启动的时候会在main.c 中的 init— 》 do_basic_setup- 》 do_initcalls();这里他会先执行 cfi_probe_init 函数,在 chip_drvs_list 链表中增加了 cfi 接口标准的硬件驱动:static struct mtd_chip_driver cfi_chipdrv = {
probe: cfi_probe,
name: "cfi_probe",
module: THIS_MODULE
};同样也执行 cfi_amdstd_init 函数static struct mtd_chip_driver amd_flash_chipdrv = {
probe: amd_flash_probe,
destroy: amd_flash_destroy,
name: "amd_flash",
module: THIS_MODULE
}; 以前的理解有问题,下面是两种命令集,一个是 amd/fujistu ,一个是 inter/sharp 的
static struct mtd_chip_driver cfi_intelext_chipdrv = {
probe: NULL, /* Not usable directly */
destroy: cfi_intelext_destroy,
name: "cfi_cmdset_0001",
module: THIS_MODULE
};static struct mtd_chip_driver cfi_amdstd_chipdrv = {
probe: NULL, /* Not usable directly */
destroy: cfi_amdstd_destroy,
name: "cfi_cmdset_0002",
module: THIS_MODULE
};我们可以看出,因为 cfi 标准是 intel 出的,所以在他的 probe 里面都可以通用 chip_drvs_list 。在我们的 code 中,支持了 cfi ,(要么是cfi,要么非 cfi 的 NAND 型),我自己个人认为 cfi_probe_init 设置去检测系统中用的是 cfi ,然后又要支持 flash 新片的高级选项,比如芯片位宽等等一些参数,应该是给 cfi_probe_init 的参数吧。这里会运行那些静态编译到内核中的模块,在这里是: avalanche_mtd_init ,在该函数里面:avalanche_mtd_info = do_map_probe("cfi_probe", &avalanche_map);在这里我们会找到通用的 cfi 驱动(在这里,我自己认为还有很多改进的地方)接着会调用:cfi_probe – 》mtd_do_chip_probe--- 》genprobe_ident_chips- 》 genprobe_new_chip(在这个函数里面会检查该芯片是否为 cfi 接口标准的,其中通过的方式是:向 flash 的几个端口写几个字节,然后在特定的端口去读,看是否为 qry ,如果是,就是 cfi 接口标准,函数调用如下:
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
if (!qry_present(map,base,cfi))
return 0;
)在这里会填写 struct cfi_private cfi;
map->fldrv_priv = cfi;
--- 》 check_cmd_set 根句 cfi->cfiq->P_ID 或者 cfi->cfiq->A_ID 进行调用相关的函数,这里是cfi_cmdset_0002 函数
-- 》 cfi_read_pri
-- 》 cfi_fixup(map, fixup_table); 函数
-- 》 cfi_amdstd_setup 函数