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 数据结构。

第19章Flash设备驱动之Nand Flash驱动

    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章Flash设备驱动之Nand Flash驱动

图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(),添加分区信息。


相关文章: