MTD驱动
MTD是linux内核为了简化对Flash设备的编程而建立的一种驱动框架。当各种Flash设备需要加入到linux的内核中时,不需要编写复杂的驱动程序来在内核中建立块设备等等。而只需要遵循MTD驱动的架构,实现相应的驱动接口,就能将各种Flash设备加入到内核中去。并在内核中形成块设备,字符设备等等。
总而言之MTD就是将各种Flash设备的一些共有特性抽象出来,在文件系统和设备之间形成一种联系和框架。所有的Flash设备可以利用这个框架来加入内核中,而设备之需要实现框架没有的特性,就能完成开发。
MTD可以分为设备节点层,MTD块设备层,MTD原始设备层和硬件设备层。本节首先介绍一下硬件设备层和MTD原始设备层。
首先我们要介绍一下MTD中几个重量级的数据结构,一个是mtd_info,这个这数据结构主要记录Flash设备的一些关键信息和对Flash物理设备进行操作的函数。关键信息有
Flash的大小、Flash的擦除块大小、Flash的写块大小、Flash的oob大小等等。
而对Flash硬件设备操作函数的指针有
Flash擦除操作、Flash写操作、Flash读取操作
一般来说,设备驱动程序需要给mtd_info结构安装硬件操作的指针,这样上层块层的操作可以调用这些函数完成具体的读写操作。但是,MTD越来越完善给开发者考虑得很全面,就是mtd_info中的操作函数都提供了NAND抽象、NOR Flash的抽象。硬件驱动只需要去关心更加底层的一些操作了。
然后另外一个很重要的结构体就是mtd_part,这个结构体表示一个MTD设备的一个分区,其中包含了一个mtd_info结构体(表示这个分区结构),还有一个mtd_info的指针。Mtd_info指针指向了表示整个MTD设备的数据结构。然后还有一个表示分区在整个mtd设备上偏移的数据。
对于硬件驱动的工作就是,初始化好mtd_info和mtd_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中的read、write函数,但是也可以选择使用mtd框架中的已经实现的nand_read、nand_write函数去初始化mtd_info,mtd又抽象了一层,而把驱动中需要自己实现的操作包含在了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);
}
|
阅读(770) | 评论(0) | 转发(0) |