19.2 Nor Flash驱动
在 Linux 系统中,实现了针对 CFI(公共 Flash 接口)、JEDEC(电子元件工业联合会)等接口的通用 NOR 驱动,这一层的驱动直接面向 mtd_info 的成员函数,这使得 NOR 的芯片级驱动变得十分简单,只需要定义具体的内存映射情况结构体 map_info 并使用指定接口类型调用 do_map_ probe()。
图19.3 MTD、通用 NOR Flash驱动与 map_info
NOR Flash 驱动的核心是定义 map_info 结构体,指定NOR Flash 的基址、位宽、大小等信息以及 Flash 的读写函数,该结构体对于 NOR Flash 驱动非常关键,甚至NOR Flash 驱动的代码本质上可以被认作是根据 map_info 探测芯片的过程,其定义如代码清单 19.7 所示。
代码清单 19.7 map_info 结构体
include/linux/mtd/map.h
struct map_info {
char *name;
unsigned long size;
resource_size_t phys;
#define NO_XIP (-1UL)
void __iomem *virt;/* 虚拟地址 */
void *cached;
int bankwidth; /* 总线宽度 */
#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
map_word (*read)(struct map_info *, unsigned long);
void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
void (*write)(struct map_info *, const map_word, unsigned long);
void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
#endif
/* 缓存的虚拟地址 */
void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
void (*set_vpp)(struct map_info *, int);
unsigned long map_priv_1;
unsigned long map_priv_2;
void *fldrv_priv;
struct mtd_chip_driver *fldrv;
};
NOR Flash 驱动在 Linux 中的实现简单,如图 19.4 所示,主要的工作如下。
图19.4 NOR Flash 驱动
(1)定义 map_info 的实例,初始化其中的成员,根据目标板的情况为 name、size、bankwidth和 phys 赋值。
(2)如果 Flash 要分区,则定义 mtd_partition 数组,将实际电路板中 Flash 分区信息记录于其中。
(3)以map_info 和探测的接口类型(如"cfi_probe"、"jedec_probe"等)为参数调用do_map_ probe(),探测 Flash 得到 mtd_info。
do_map_probe()的函数原型为:
struct mtd_info *do_map_probe(const char *name, struct map_info *map);
第一个参数为探测的接口类型,常见的调用方法如下。
do_map_probe("cfi_probe",&xxx_map_info);
do_map_probe("jedec_probe",&xxx_map_info);
do_map_probe("map_rom",&xxx_ma _info);
do_map_probe()会根据传入的参数 name 通过 get_mtd_chip_driver()得到具体的 MTD 驱动,调用与接口对应的 probe()函数探测设备,如代码清单 19.8 所示。
代码清单 19.8 do_map_probe()函数
struct mtd _ info *do _ map _ probe(const char *name, struct map _ info *map)
{
struct mtd _ chip _ driver *drv;
struct mtd _ info *ret;
drv = get_mtd_chip_driver(name);/* 通过名称获得驱动 */
if (!drv && !request _ module("%s", name))
drv = get_mtd_chip_driver(name);
if (!drv)
return NULL;
ret = drv->probe(map);/* 调用驱动的探测函数 */
module _ put(drv!module);
if (ret)
return ret;
return NULL;
}
分析:
利用 map_info 中的配置,do_map_probe()可以自动识别支持 CFI 或 JEDEC 接口的 Flash 芯片,MTD 以后会自动采用适当的命令参数对 Flash 进行读写或擦除。
(4)在模块初始化时以 mtd_info 为参数调用 add_mtd_device()或以 mtd_info、mtd_partition 数组及分区数为参数调用add_mtd_partitions()注册设备或分区。当然,在这之前可以调用parse_mtd_partitions()查看Flash 上是否已有分区信息,并将查看出的分区信息通过add_mtd_partitions()注册。
(5)在模块卸载时删除设备或分区。