Chinaunix首页 | 论坛 | 博客
  • 博客访问: 922079
  • 博文数量: 63
  • 博客积分: 568
  • 博客等级: 中士
  • 技术积分: 3435
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-05 11:44
文章分类
文章存档

2016年(4)

2015年(6)

2014年(3)

2013年(27)

2012年(23)

分类: LINUX

2013-11-14 07:44:04

MTD驱动

 

MTDlinux内核为了简化对Flash设备的编程而建立的一种驱动框架。当各种Flash设备需要加入到linux的内核中时,不需要编写复杂的驱动程序来在内核中建立块设备等等。而只需要遵循MTD驱动的架构,实现相应的驱动接口,就能将各种Flash设备加入到内核中去。并在内核中形成块设备,字符设备等等。

 

总而言之MTD就是将各种Flash设备的一些共有特性抽象出来,在文件系统和设备之间形成一种联系和框架。所有的Flash设备可以利用这个框架来加入内核中,而设备之需要实现框架没有的特性,就能完成开发。

 

MTD可以分为设备节点层,MTD块设备层,MTD原始设备层和硬件设备层。本节首先介绍一下硬件设备层和MTD原始设备层。

 

首先我们要介绍一下MTD中几个重量级的数据结构,一个是mtd_info,这个这数据结构主要记录Flash设备的一些关键信息和对Flash物理设备进行操作的函数。关键信息有

Flash的大小、Flash的擦除块大小、Flash的写块大小、Flashoob大小等等。

而对Flash硬件设备操作函数的指针有

Flash擦除操作、Flash写操作、Flash读取操作

一般来说,设备驱动程序需要给mtd_info结构安装硬件操作的指针,这样上层块层的操作可以调用这些函数完成具体的读写操作。但是,MTD越来越完善给开发者考虑得很全面,就是mtd_info中的操作函数都提供了NAND抽象、NOR Flash的抽象。硬件驱动只需要去关心更加底层的一些操作了。

 

然后另外一个很重要的结构体就是mtd_part,这个结构体表示一个MTD设备的一个分区,其中包含了一个mtd_info结构体(表示这个分区结构),还有一个mtd_info的指针。Mtd_info指针指向了表示整个MTD设备的数据结构。然后还有一个表示分区在整个mtd设备上偏移的数据。

 

对于硬件驱动的工作就是,初始化好mtd_infomtd_part两个结构体,然后调用mtd层的相关函数向mtd层进行注册。仅此而已。最后在mtd层中需要形成如下的数据结构联系,如下图所示。下面我们首先分析这个过程。

 

 

size = nr_sets * sizeof(*info->mtds); 
/*分配mtd_info结构体*/ 
info->mtds = kmalloc(size, GFP_KERNEL); 
if (info->mtds == NULL) 

    dev_err(&pdev->dev, "failed to allocate mtd storage\n"); 
    err = -ENOMEM; 
    goto exit_error; 

 
memset(info->mtds, 0, size); 
 
/* initialise all possible chips */ 
 
nmtd = info->mtds; 
 
for (setno = 0; setno < nr_sets; setno++, nmtd++) 

    pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info); 
    /*准备使用内核中的nand mtd框架,首先初始化一些底层的操作*/ 
    s3c2410_nand_init_chip(info, nmtd, sets); 
    /*读写chip id 并初始化一些mtd_info的域*/ 
    nmtd->scan_res = nand_scan_ident(&nmtd->mtd, 
                                     (sets) ? sets->nr_chips : 1); 
 
    if (nmtd->scan_res == 0
    { 
        s3c2410_nand_update_chip(info, nmtd); 
        /*mtd_info中的函数指针初始化*/ 
        nand_scan_tail(&nmtd->mtd); 
        s3c2410_nand_add_partition(info, nmtd, sets); 
    } 
 
    if (sets != NULL) 
        sets++; 

上面这段代码主要完成对mtd_info的初始化操作,其中还涉及到了一个数据结构nand_chip,这个数据结构中也是各种操作函数指针,上面我们说到内核中mtd越来越完善。驱动可以自己去实现mtd_info中的readwrite函数,但是也可以选择使用mtd框架中的已经实现的nand_readnand_write函数去初始化mtd_infomtd又抽象了一层,而把驱动中需要自己实现的操作包含在了nand_chip结构体中。

 

实际上上面的初始化代码中,真正硬件驱动自己的代码只有s3c2410_nand_init_chip函数,这个函数将nand_chip中一些重要的必须的函数实现并初始化一下。接下来就是调用mtd nand层来进行程序化的初始化操作。

 /* s3c2410_nand_init_chip 
 * 
 * init a single instance of an chip 
*/ 
static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, 
                                   struct s3c2410_nand_mtd *nmtd, 
                                   struct s3c2410_nand_set *set) 

    struct nand_chip *chip = &nmtd->chip; 
    void __iomem *regs = info->regs; 
    /*初始化必须实现的几个函数*/ 
    chip->write_buf    = s3c2410_nand_write_buf; 
    chip->read_buf     = s3c2410_nand_read_buf; 
    chip->select_chip  = s3c2410_nand_select_chip; 
    chip->chip_delay   = 50
    chip->priv       = nmtd; 
    chip->options       = 0
    chip->controller   = &info->controller; 
 
    switch (info->cpu_type) 
    { 
    case TYPE_S3C2410: 
        break
 
    case TYPE_S3C2440: 
        chip->IO_ADDR_W = regs + S3C2440_NFDATA; 
        info->sel_reg   = regs + S3C2440_NFCONT; 
        info->sel_bit    = S3C2440_NFCONT_nFCE; 
        /*初始化必须实现的几个函数*/ 
        chip->cmd_ctrl  = s3c2440_nand_hwcontrol; 
        chip->dev_ready = s3c2440_nand_devready; 
        chip->read_buf  = s3c2440_nand_read_buf; 
        chip->write_buf    = s3c2440_nand_write_buf; 
        break
 
    case TYPE_S3C2412: 
 
        break
    } 
 
    chip->IO_ADDR_R = chip->IO_ADDR_W; 
 
    nmtd->info       = info; 
    /*mtd 层调用mtd nand层的函数时将nand_chip数据传入*/ 
    nmtd->mtd.priv       = chip; 
    nmtd->mtd.owner    = THIS_MODULE; 
    nmtd->set       = set; 
 
    if (hardware_ecc) 
    { 
        chip->ecc.calculate = s3c2410_nand_calculate_ecc; 
        chip->ecc.correct   = s3c2410_nand_correct_data; 
        chip->ecc.mode        = NAND_ECC_HW; 
        .....
    } 
    else 
    { 
        chip->ecc.mode        = NAND_ECC_SOFT; 
    } 
 
    if (set->ecc_layout != NULL) 
        chip->ecc.layout = set->ecc_layout; 
 
    if (set->disable_ecc) 
        chip->ecc.mode    = NAND_ECC_NONE; 

 

在初始化Flash 设备时,一件最重要的事情就是确认系统中flash的类型,并初始化mtd_info

结构中Flash大小、擦写块大小、写page大小等等。这个工作在

int nand_scan_ident(struct mtd_info *mtd, int maxchips)函数中完成。这个函数已经是mtd nand模块提供的内核实现了。

 

读出了Flash相关的参数,会继续调用nand_scan_tail函数完成mtd_info结构中操作函数指针的挂接,这个过程也写非常非常重要的一个过程。

/* Fill in remaining MTD driver data */ 
mtd->type = MTD_NANDFLASH; 
mtd->flags = MTD_CAP_NANDFLASH; 
mtd->erase = nand_erase; 
mtd->point = NULL; 
mtd->unpoint = NULL; 
mtd->read = nand_read; 
mtd->write = nand_write; 
mtd->read_oob = nand_read_oob; 
mtd->write_oob = nand_write_oob; 
mtd->sync = nand_sync; 
mtd->lock = NULL; 
mtd->unlock = NULL; 
mtd->suspend = nand_suspend; 
mtd->resume = nand_resume; 
mtd->block_isbad = nand_block_isbad; 
mtd->block_markbad = nand_block_markbad; 

 

完成上上面的初始化后,调用下面函数将mtd_info注册到mtd层中,硬件驱动的工作就基本完成。而这个注册过程如下图函数调用关系所示

static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, 
                                      struct s3c2410_nand_mtd *mtd, 
                                      struct s3c2410_nand_set *set) 

    if (set == NULL) 
        return add_mtd_device(&mtd->mtd); 
 
    if (set->nr_partitions > 0 && set->partitions != NULL) 
    { 
        return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); 
    } 
 
    return add_mtd_device(&mtd->mtd); 

 

 

 

 

 

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