Chinaunix首页 | 论坛 | 博客
  • 博客访问: 808146
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: LINUX

2015-12-14 16:15:45

NAND FLASH设备驱动:平台-设备-驱动模型
platform: linux-2.6.22.6


0 概述

FLASH在嵌入式系统中是必不可少的,它是bootloaderlinux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR FLASHNAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。

在引入MTDLinux系统中FLASH设备驱动可分为四层,如图:

 



1. 硬件驱动层

FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。

2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.cmtdpart.c),另一部分是各个特定的FLASH的数据,例如分区。

3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。

4. 设备节点:通过mknod/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。

也可通过下图理解:




工作流程:
   
当应用层要求对FLASH进行读写时:它会向MTD设备层发出请求,设备层的读写函数会调用MTD原始设备层中的读写函数,即mtd_info结构体mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数会调用nand_chipnand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以当我们写一个flash硬件驱动程序时,有以下步骤:

1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。

2. 在模块加载时为每一个chip(主分区)分配mtd_infonand_chip的内存,根据目标板nand 控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()dev_ready()read_byte()write_byte()等。填充mtd_info,并将其priv成员指向nand_chip

3. mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。

4. 如果要分区,则以mtd_infomtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区(不包含主分区)分配一个mtd_info结构体,填充,并注册。







1. NAND flash设备注册过程:
1.1 NAND FLASH设备的定义:\arch\arm\plat-s3c24xx\devs.c

    static struct resource s3c_nand_resource[] = {
        [0] = {
            .start = S3C2410_PA_NAND,
            .end   = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
            .flags = IORESOURCE_MEM,
        }
    };

    struct platform_device s3c_device_nand = {
        .name          = "s3c2410-nand",
        .id          = -1,
        .num_resources      = ARRAY_SIZE(s3c_nand_resource),
        .resource      = s3c_nand_resource,
    };

1.2 平台设备数组定义:\arch\arm\plat-s3c24xx\common-smdk.c
    static struct platform_device __initdata *smdk_devs[] = {
        &s3c_device_nand,
        &smdk_led4,
        &smdk_led5,
        &smdk_led6,
        &smdk_led7,
    };
    
static void __init smdk2410_init(void)
    void __init smdk_machine_init(void)
        platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));    
            platform_device_register(devs[i]);  // 注册设备:s3c_device_nand
    
2. NAND FLASH设备驱动注册过程:

2.1 设备驱动定义:
    static struct platform_driver s3c2410_nand_driver = {
        .probe        = s3c2410_nand_probe,
        .remove        = s3c2410_nand_remove,
        .suspend    = s3c24xx_nand_suspend,
        .resume        = s3c24xx_nand_resume,
        .driver        = {
            .name    = "s3c2410-nand",   // 看名字是不是和设备定义一致的
            .owner    = THIS_MODULE,
        },
    };
    
2.2 NAND FLASH设备驱动注册流程
module_init(s3c2410_nand_init);
    static int __init s3c2410_nand_init(void)
        platform_driver_register(&s3c2410_nand_driver)  // 完成s3c2410_nand_driver的注册
    
3. probe函数过程:
前面分析过,在总线-设备-驱动平台中,比较.name,如果相等,则调用驱动下面的probe函数,
即调用函数:s3c2410_nand_probe

static int s3c2410_nand_probe(struct platform_device *dev)
    s3c24xx_nand_probe(dev, TYPE_S3C2410);
        struct s3c2410_nand_info *info;
        info = kmalloc(sizeof(*info), GFP_KERNEL);
        
        // 设置时钟
        info->clk = clk_get(&pdev->dev, "nand");
        clk_enable(info->clk);
        
        // 获取资源变量
        info->regs       = ioremap(res->start, size);
        
        // 初始化硬件
        err = s3c2410_nand_inithw(info, pdev);
        
        // 分配mtd
        info->mtds = kmalloc(size, GFP_KERNEL);
        
        // 初始化MTD原始设备对应的芯片
        s3c2410_nand_init_chip(info, nmtd, sets);
        
        // 扫描NAND设备
        nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
            nand_scan_ident(mtd, maxchips);
            nand_scan_tail(mtd);
            
        // 添加分区信息
        s3c2410_nand_add_partition(info, nmtd, sets);
            add_mtd_device(&mtd->mtd);
                list_for_each(this, &mtd_notifiers) {
                    struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
                    not->add(mtd);
                    
4. 相关数据结构体
4.1 struct s3c2410_nand_info:
        表示一个NAND FLASH控制器的相关信息,包括:控制器的硬件信息,MTD原始设备信息,分区信息,
    所使用的资源,以及工作的时钟频率等。
    
    struct s3c2410_nand_info {
        /* mtd info */
        struct nand_hw_control        controller;
        struct s3c2410_nand_mtd        *mtds;
        struct s3c2410_platform_nand    *platform;

        /* device info */
        struct device            *device;
        struct resource            *area;
        struct clk            *clk;
        void __iomem            *regs;
        void __iomem            *sel_reg;
        int                sel_bit;
        int                mtd_count;

        enum s3c_cpu_type        cpu_type;
    };

4.2 struct s3c2410_nand_mtd:
        主要定义了该NAND FLASH芯片对应的MTD原始设备,物理NAND FLASH芯片以及对应的NAND FLASH控制器。
            
    struct s3c2410_nand_mtd {
        struct mtd_info            mtd;                    // 表示MTD原始设备
        struct nand_chip        chip;                      // 表示硬件驱动层:对应的物理NAND FLASH芯片
        struct s3c2410_nand_set        *set;               // 分区信息表
        struct s3c2410_nand_info    *info;                 // NADN FLASH控制器
        int                scan_res;
    };

4.3 struct mtd_info:

    定义了MTD原始设备层相关的参数和函数接口。

        struct mtd_info {

    u_char type;
    u_int32_t flags;
    u_int32_t size;     // Total size of the MTD

    /* "Major" erase size for the device. Na茂ve users may take this
     * to be the only erase size available, or may use the more detailed
     * information below if they desire
     */
    u_int32_t erasesize;
    /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
     * though individual bits can be cleared), in case of NAND flash it is
     * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
     * it is of ECC block size, etc. It is illegal to have writesize = 0.
     * Any driver registering a struct mtd_info must ensure a writesize of
     * 1 or larger.
     */
    u_int32_t writesize;

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

    // Kernel-only stuff starts here.
    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.
     */
    int numeraseregions;
    struct mtd_erase_region_info *eraseregions;

    int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

    /* This stuff for eXecute-In-Place */
    int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);

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


    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 (*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);

    /*
     * Methods to access the protection register area, present in some
     * flash devices. The user data is one time programmable but the
     * factory data is read only.
     */
    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, size_t len);
    int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_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;

    void *priv;

    struct module *owner;
    int usecount;

    /* If the driver is something smart, like UBI, it may need to maintain
     * its own reference counting. The below functions are only for driver.
     * The driver may register its callbacks. These callbacks are not
     * supposed to be called by MTD users */
    int (*get_device) (struct mtd_info *mtd);
    void (*put_device) (struct mtd_info *mtd);
};



4.4 struct nand_chip:
        定义了具体的物理NAND FLASH芯片最底层的硬件接口操作函数及硬件相关信息。
        主要包括:NAND FLASH芯片所在的读写I/O地址,读写函数,NAND FLASH芯片容量以及坏块的模式等。
        
    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;
    };

4.5 struct s3c2410_nand_set:定义了NAND FLASH芯片的分区信息和名字。

    /* struct s3c2410_nand_set
     *
     * define an set of one or more nand chips registered with an unique mtd
     *
     * nr_chips     = number of chips in this set
     * nr_partitions = number of partitions pointed to be partitoons (or zero)
     * name         = name of set (optional)
     * nr_map     = map for low-layer logical to physical chip numbers (option)
     * partitions     = mtd partition list
    */

    struct s3c2410_nand_set {
        int            nr_chips;
        int            nr_partitions;
        char            *name;
        int            *nr_map;
        struct mtd_partition    *partitions;
    };
    
    比如在:\arch\arm\plat-s3c24xx\common-smdk.c 定义了数组,每一个数组元素表示一个NAND FLASH芯片的分区信息。
    static struct s3c2410_nand_set smdk_nand_sets[] = {
        [0] = {
            .name        = "NAND",                                   // 芯片集合的名字
            .nr_chips    = 1,                                        // 芯片集合的数量 = 1
            .nr_partitions    = ARRAY_SIZE(smdk_default_nand_part),   // 分区个数
            .partitions    = smdk_default_nand_part,                   // 分区信息表
        },
    };
    
    // 具体的分区信息表
    static struct mtd_partition smdk_default_nand_part[] = {
        [0] = {
            .name    = "Boot Agent",
            .size    = SZ_16K,
            .offset    = 0,
        },
        [1] = {
            .name    = "S3C2410 flash partition 1",
            .offset = 0,
            .size    = SZ_2M,
        },
        [2] = {
            .name    = "S3C2410 flash partition 2",
            .offset = SZ_4M,
            .size    = SZ_4M,
        },
        [3] = {
            .name    = "S3C2410 flash partition 3",
            .offset    = SZ_8M,
            .size    = SZ_2M,
        },
        [4] = {
            .name    = "S3C2410 flash partition 4",
            .offset = SZ_1M * 10,
            .size    = SZ_4M,
        },
        [5] = {
            .name    = "S3C2410 flash partition 5",
            .offset    = SZ_1M * 14,
            .size    = SZ_1M * 10,
        },
        [6] = {
            .name    = "S3C2410 flash partition 6",
            .offset    = SZ_1M * 24,
            .size    = SZ_1M * 24,
        },
        [7] = {
            .name    = "S3C2410 flash partition 7",
            .offset = SZ_1M * 48,
            .size    = SZ_16M,
        }
    };

参考博客:
1. Linux NAND FLASH驱动框架分析---mtd
2. Linux NAND FLASH驱动框架分析--实例

阅读(1056) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~