19.3 Nand Flash驱动
如图 19.5 所示,Linux 内核在 MTD的下层实现通用的 NAND 驱动(主要通过 drivers/mtd/nand/
nand_base.c 文件实现),因此芯片级的 NAND 驱动不再实现mtd_info 中的 read()、write()、read_oob()、write_oob()等成员函数,而主体转移到 nand_chip 数据结构。
MTD 使用 nand_chip 数据结构表示一个 NAND Flash 芯片,这个结构体中包含了关于 NAND Flash 的地址信息、读写方法、ECC 模式、硬件控制等一系列底层机制,其定义如代码清单 19.10所示。
代码清单 19.10 nand_chip 结构体
include/linux/mtd/nand.h
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);
int chip_delay;
unsigned int options;
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;
nand_state_t state;
uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
struct mtd_oob_ops ops;
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
void *priv;
};
与 NOR Flash 类似,由于有了 MTD 层,完成一个 NAND Flash 驱动在 Linux 中的工作量也很小,如图 19.6 所示,主要的工作如下。
图19.6 NAND Flash 驱动
(1)如果 Flash 要分区,则定义 mtd_partition 数组,将实际电路板中 Flash 分区信息记录于其中。
(2)在模块加载时分配和 nand_chip 的内存,根据目标板 NAND 控制器的情况初始化nand_chip 中的 cmd_ctrl ()、dev_ready()、read_byte()、read_buf()、write_buf()、select_chip()、block_bad()、
block_markbad()等成员函数(如果不赋值会使用 nand_base.c 中的默认函数,利用面向对象的继承和重载的思想),注意将 mtd_info 的 priv 置为 nand_chip。
(3)以 mtd_info 为参数调用 nand_scan()函数探测 NAND Flash 的存在。
nand_scan()函数的原型为:
include/linux/mtd/nand.h
int nand_scan (struct mtd_info *mtd, int max_chips);
drivers/mtd/nand/nand_base.c
/**
* nand_scan - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
*
* This fills out all the uninitialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values.
* The mtd->owner field must be set to the module of the caller
*
*/
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
/* Many callers got this wrong, so check for it for a while... */
if (!mtd->owner && caller_is_module()) {
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
BUG();
}
ret = nand_scan_ident(mtd, maxchips);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
nand_scan()函数会读取 NAND 芯片 ID,并根据 mtd→priv 即 nand_chip 中的成员初始化 mtd_info。
(4)如果要分区,则以 mtd_info 和 mtd_partition 为参数调用 add_mtd_partitions(),添加分区信息。