现在的位置: 首页 > 综合 > 正文

基于MTD的NANDFLASH设备驱动底层实现原理分析(四)

2013年05月19日 ⁄ 综合 ⁄ 共 12342字 ⁄ 字号 评论关闭

进过前面3篇文章对NANDFLASH的一些硬件特性以及MTD的上层操作已经有了一个大体概念,这些东西的重要性就像你要吃饭那么你首先得学会拿筷子道理一样吧,应该一样的。

五、MTD原始设备层和硬件驱动层的桥梁:

    

熟悉这几个重要的结构体:linux/mtd/mtd.h

struct mtd_info {
    u_char type;               /**内存技术类型(包括MTD_RAM,MTD_ROM,MTD_NANDFLASH等)**/
    uint32_t flags;           /*MTD设备属性标志*/
    uint64_t size;     // Total size of the MTD

    uint32_t erasesize;//MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小
    
    uint32_t writesize;//最小的可写单元的字节数

    uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
    uint32_t oobavail;  // Available OOB bytes per block

    unsigned int erasesize_shift;
    unsigned int writesize_shift;
    /* Masks based on erasesize_shift and writesize_shift */
    unsigned int erasesize_mask;
    unsigned int writesize_mask;

    // Kernel-only stuff starts here.
    const char *name;
    int index;

    /* ecc layout structure pointer - read only ! */
    struct nand_ecclayout *ecclayout;

    /* Data for variable erase regions. If numeraseregions is zero,
     * it means that the whole device has erasesize as given above.

     *一般为1吧

     */
    int numeraseregions;

    //擦除区域的指针

    struct mtd_erase_region_info *eraseregions;
    //擦除函数将一个erase_info结构放入擦除队列中
    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

    /* This stuff for eXecute-In-Place */
    /* phys is optional and may be set to NULL */
    int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
            size_t *retlen, void **virt, resource_size_t *phys);

    /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
    void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

    /* Allow NOMMU mmap() to directly map the device (if not NULL)
     * - return the address to which the offset maps
     * - return -ENOSYS to indicate refusal to do the mapping
     */
    unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
                        unsigned long len,
                        unsigned long offset,
                        unsigned long flags);

    /* Backing device capabilities for this device
     * - provides mmap capabilities
     */
    struct backing_dev_info *backing_dev_info;

  //read和write分别用于MTD设备的读和写
    int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

    int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    //读写MTD设备的OOB区域的数据
    int (*read_oob) (struct mtd_info *mtd, loff_t from,
             struct mtd_oob_ops *ops);
    int (*write_oob) (struct mtd_info *mtd, loff_t to,
             struct mtd_oob_ops *ops);
     //访问一些受保护的寄存器  
    int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

    /* kvec-based read/write methods.
       NB: The 'count' parameter is the number of _vectors_, each of
       which contains an (ofs, len) tuple.
    */
    int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

    /* Sync *//*同步*/
    void (*sync) (struct mtd_info *mtd);

    /* Chip-supported device locking */
    int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);

    /* Power Management functions */
    int (*suspend) (struct mtd_info *mtd);
    void (*resume) (struct mtd_info *mtd);

    /* Bad block management functions */
    int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

    struct notifier_block reboot_notifier;  /* default mode before reboot */

    /* ECC status information */
    struct mtd_ecc_stats ecc_stats;
    /* Subpage shift (NAND) */
    int subpage_sft;
   //私有数据 指向map_info结构
    void *priv;

    struct module *owner;
    struct device dev;
    int usecount;

   //设备驱动回调函数

    int (*get_device) (struct mtd_info *mtd);
    void (*put_device) (struct mtd_info *mtd);
};

上面的read()、write()、read_oob()、等都是MTD设备驱动要实现的主要函数,不过这些函数都是透明的不需要我们自己去实现,因为Linux在MTD的下层实现了针对NORFLASH和NANDFLASH的通用mtd_info成员函数。

感觉没什么可写的了,因为这些都不是我要关注的东西,但是又不能不知道有这么回事

这些结构体还是得了解了解

driver/mtd/mtdpart.c

/* Our partition node structure */
struct mtd_part {
    struct mtd_info mtd;    //分区信息
    struct mtd_info *master; //该分区的主分区
    uint64_t offset;              //该分区的偏移量
    struct list_head list;
};

mtd_partition会在MTD原始设备调用add_mtd_partitions()的时候传递分区参数/linux/mtd/partition.h

struct mtd_partition {
    char *name;            /* identifier string */
    uint64_t size;            /* partition size */
    uint64_t offset;        /* offset within the master MTD space */
    uint32_t mask_flags;        /* master MTD flags to mask out for this partition */
    struct nand_ecclayout *ecclayout;    /* out of band layout for this partition (NAND only)*/
};

一个MTD原始设备可以通过mtd_part分割成数个MTD原始设备注册进mtd_table,mtd_table中的每个MTD原始设备都可以被注册成一个MTD设备,有两个函数可以完成这个工作,即add_mtd_device函数和add_mtd_partitions函数。

其中add_mtd_device函数是把整个NAND FLASH注册进MTD Core,而add_mtd_partitions函数则是把NAND FLASH的各个分区分别注册进MTD Core。

int add_mtd_device(struct mtd_info *mtd)

int del_mtd_device(struct mtd_info *mtd)

int add_mtd_partitions(struct mtd_info *master,structmtd_partitions *parts,int
nbparts
)

int del_mtd_partitions(struct mtd_info *master)

当MTD原始设备调用add_mtd_partitions()的时候它会对每一个新建分区建立一个struct mtd_part 结构将其加入mtd_partitions中并调用add_mtd_device()将此分区做为MTD设备注册进MTD
Core
,成功的时返回0

重点关注一下add_mtd_partitions(struct mtd_info *master,struct mtd_partitions*parts,int nbparts)其中master就是这个MTD原始设备,parts即NAND的分区信息,nbparts指有几个分区。那么parts和nbparts怎么来的呢,其实上面有一句话已经说了。。举个例子在我们移植Linux内核到我们的开发板的时候我们会对NANDFLASH进行分区
eilian240_default_nand_part就是起到上面这两个参数的作用如下:

static struct mtd_partition eilian240_default_nand_part[] = {
    [0] = {
        .name    = "bootloader",/*uboot存放的地址对应dev/mtdblock0*/
        .size    = 0x00040000,  /*大小256KB=((D)(0x00040000))/1024*/
        .offset    = 0,
    },
    [1] = {
        .name    = "param",
        .offset = 0x00040000,/***如果UBOOT比较大就放在这个区域可以将前面的覆盖掉**//**0x00040000是偏移量**/
        .size    = 0x00020000,  /**128KB**/
    },
    [2] = {
        .name    = "Linux Kernel",/********用于存放Linux内核镜像dev/mtdblock3*/
        .offset = 0x00060000,
        .size    = 0x00500000,        /*5M*/
    },
    [3] = {
        .name    = "rootfs",
        .offset = 0x00560000,
        .size    = 1024 * 1024 * 1024, //1G因为该移植同时支持大容量的NAND
    },
    [4] = {
        .name    = "nand",
        .offset = 0x00000000,
        .size    = 1024 * 1024 * 1024, //
    }
};
/*arch/arm/plat-samsung/include/plat/nand.h **/

进过前面3篇文章对NANDFLASH的一些硬件特性以及MTD的上层操作已经有了一个大体概念,这些东西的重要性就像你要吃饭那么你首先得学会拿筷子道理一样吧,应该一样的。

五、MTD原始设备层和硬件驱动层的桥梁:

    

熟悉这几个重要的结构体:linux/mtd/mtd.h

struct mtd_info {
    u_char type;               /**内存技术类型(包括MTD_RAM,MTD_ROM,MTD_NANDFLASH等)**/
    uint32_t flags;           /*MTD设备属性标志*/
    uint64_t size;     // Total size of the MTD

    uint32_t erasesize;//MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小
    
    uint32_t writesize;//最小的可写单元的字节数

    uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
    uint32_t oobavail;  // Available OOB bytes per block

    unsigned int erasesize_shift;
    unsigned int writesize_shift;
    /* Masks based on erasesize_shift and writesize_shift */
    unsigned int erasesize_mask;
    unsigned int writesize_mask;

    // Kernel-only stuff starts here.
    const char *name;
    int index;

    /* ecc layout structure pointer - read only ! */
    struct nand_ecclayout *ecclayout;

    /* Data for variable erase regions. If numeraseregions is zero,
     * it means that the whole device has erasesize as given above.

     *一般为1吧

     */
    int numeraseregions;

    //擦除区域的指针

    struct mtd_erase_region_info *eraseregions;
    //擦除函数将一个erase_info结构放入擦除队列中
    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

    /* This stuff for eXecute-In-Place */
    /* phys is optional and may be set to NULL */
    int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
            size_t *retlen, void **virt, resource_size_t *phys);

    /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
    void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

    /* Allow NOMMU mmap() to directly map the device (if not NULL)
     * - return the address to which the offset maps
     * - return -ENOSYS to indicate refusal to do the mapping
     */
    unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
                        unsigned long len,
                        unsigned long offset,
                        unsigned long flags);

    /* Backing device capabilities for this device
     * - provides mmap capabilities
     */
    struct backing_dev_info *backing_dev_info;

  //read和write分别用于MTD设备的读和写
    int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

    int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    //读写MTD设备的OOB区域的数据
    int (*read_oob) (struct mtd_info *mtd, loff_t from,
             struct mtd_oob_ops *ops);
    int (*write_oob) (struct mtd_info *mtd, loff_t to,
             struct mtd_oob_ops *ops);
     //访问一些受保护的寄存器  
    int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

    /* kvec-based read/write methods.
       NB: The 'count' parameter is the number of _vectors_, each of
       which contains an (ofs, len) tuple.
    */
    int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

    /* Sync *//*同步*/
    void (*sync) (struct mtd_info *mtd);

    /* Chip-supported device locking */
    int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);

    /* Power Management functions */
    int (*suspend) (struct mtd_info *mtd);
    void (*resume) (struct mtd_info *mtd);

    /* Bad block management functions */
    int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

    struct notifier_block reboot_notifier;  /* default mode before reboot */

    /* ECC status information */
    struct mtd_ecc_stats ecc_stats;
    /* Subpage shift (NAND) */
    int subpage_sft;
   //私有数据 指向map_info结构
    void *priv;

    struct module *owner;
    struct device dev;
    int usecount;

   //设备驱动回调函数

    int (*get_device) (struct mtd_info *mtd);
    void (*put_device) (struct mtd_info *mtd);
};

上面的read()、write()、read_oob()、等都是MTD设备驱动要实现的主要函数,不过这些函数都是透明的不需要我们自己去实现,因为Linux在MTD的下层实现了针对NORFLASH和NANDFLASH的通用mtd_info成员函数。

感觉没什么可写的了,因为这些都不是我要关注的东西,但是又不能不知道有这么回事

这些结构体还是得了解了解

driver/mtd/mtdpart.c

/* Our partition node structure */
struct mtd_part {
    struct mtd_info mtd;    //分区信息
    struct mtd_info *master; //该分区的主分区
    uint64_t offset;              //该分区的偏移量
    struct list_head list;
};

mtd_partition会在MTD原始设备调用add_mtd_partitions()的时候传递分区参数/linux/mtd/partition.h

struct mtd_partition {
    char *name;            /* identifier string */
    uint64_t size;            /* partition size */
    uint64_t offset;        /* offset within the master MTD space */
    uint32_t mask_flags;        /* master MTD flags to mask out for this partition */
    struct nand_ecclayout *ecclayout;    /* out of band layout for this partition (NAND only)*/
};

一个MTD原始设备可以通过mtd_part分割成数个MTD原始设备注册进mtd_table,mtd_table中的每个MTD原始设备都可以被注册成一个MTD设备,有两个函数可以完成这个工作,即add_mtd_device函数和add_mtd_partitions函数。

其中add_mtd_device函数是把整个NAND FLASH注册进MTD Core,而add_mtd_partitions函数则是把NAND FLASH的各个分区分别注册进MTD Core。

int add_mtd_device(struct mtd_info *mtd)

int del_mtd_device(struct mtd_info *mtd)

int add_mtd_partitions(struct mtd_info *master,structmtd_partitions *parts,int
nbparts
)

int del_mtd_partitions(struct mtd_info *master)

当MTD原始设备调用add_mtd_partitions()的时候它会对每一个新建分区建立一个struct mtd_part 结构将其加入mtd_partitions中并调用add_mtd_device()将此分区做为MTD设备注册进MTD
Core
,成功的时返回0

重点关注一下add_mtd_partitions(struct mtd_info *master,struct mtd_partitions*parts,int nbparts)其中master就是这个MTD原始设备,parts即NAND的分区信息,nbparts指有几个分区。那么parts和nbparts怎么来的呢,其实上面有一句话已经说了。。举个例子在我们移植Linux内核到我们的开发板的时候我们会对NANDFLASH进行分区
eilian240_default_nand_part就是起到上面这两个参数的作用如下:

static struct mtd_partition eilian240_default_nand_part[] = {
    [0] = {
        .name    = "bootloader",/*uboot存放的地址对应dev/mtdblock0*/
        .size    = 0x00040000,  /*大小256KB=((D)(0x00040000))/1024*/
        .offset    = 0,
    },
    [1] = {
        .name    = "param",
        .offset = 0x00040000,/***如果UBOOT比较大就放在这个区域可以将前面的覆盖掉**//**0x00040000是偏移量**/
        .size    = 0x00020000,  /**128KB**/
    },
    [2] = {
        .name    = "Linux Kernel",/********用于存放Linux内核镜像dev/mtdblock3*/
        .offset = 0x00060000,
        .size    = 0x00500000,        /*5M*/
    },
    [3] = {
        .name    = "rootfs",
        .offset = 0x00560000,
        .size    = 1024 * 1024 * 1024, //1G因为该移植同时支持大容量的NAND
    },
    [4] = {
        .name    = "nand",
        .offset = 0x00000000,
        .size    = 1024 * 1024 * 1024, //
    }
};
/*arch/arm/plat-samsung/include/plat/nand.h **/

抱歉!评论已关闭.