从总线模型谈SD/MMC架构

来源:互联网 发布:网络与新媒体就业 编辑:程序博客网 时间:2024/05/17 22:15

mmc总线:mmc_bus_type

mmc驱动结构体注册:mmc_register_driver函数。

mmc设备结构体注册:mmc_alloc_card和mmc_add_card函数。

1、匹配问题:

由于总线的匹配函数mmc_bus_match直接返回1:

static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}

根据总线模型:

driver_register

bus_add_driver

driver_attach(drv)

driver_probe_device

really_probe(dev, drv);

ret = dev->bus->probe(dev);

也就是:

static int mmc_bus_probe(struct device *dev)
{
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = dev_to_mmc_card(dev);


return drv->probe(card);  //调用驱动的probe
}

结论:就是把驱动和设备的匹配任务扔给了驱动来完成:really_probe函数调用总线的probe函数,返回0就会调用driver_bound(dev)函数吧驱动和设备进行绑定了。


这里很容易可以推导出mmc_card和mmc_driver 两者第一必须存在,第二mmc_card在drv->probe(card)调用返回值必须为0,这样就算mmc总线的设备和驱动绑定成功。



2、mmc_register_driver驱动注册:


static int __init mmc_blk_init(void)  //属于块设备:

res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); //将相应的块设备添加到数组中,主设备号、名称、指向下一个指针

res = mmc_register_driver(&mmc_driver);//这里会使用第一点的匹配,调用probe函数,这个函数就是块设备的核心函数mmc_blk_probe


调用probe函数:

mmc_blk_probe

md = mmc_blk_alloc(card); //mmc_blk_data结构体

md->disk = alloc_disk(1 << MMC_SHIFT);//分配gendisk结构

       ret = mmc_init_queue(&md->queue, card, &md->lock);

 mq->queue = blk_init_queue(mmc_request, lock); //请求队列,实现块读写操作

        mq->thread = kthread_run (mmc_queue_thread, mq, "mmcqd"); //线程,负责处理请求队列的请求

 mmc_set_drvdata(card, md);

add_disk(md->disk);   //注册,到这里就可以通过队列来实现块设备的读写操作


对于上面mmc_blk_probe的传入参数是mmc_card,基于这个结构体进行,所以得看看这个结构体如何产生的


3、mmc设备结构体注册:mmc_alloc_card和mmc_add_card函数:

SD/MMC卡设备注册这一方比较麻烦,根据协议要注册设备前需要经过一些列初始化和检测之类的操作,之后这些满足了才有条件注册,我们把这个整个过程称为设备探测函数。对于6410开发板而言就是s3cmci_probe函数,实现部分放在host目录下。


s3cmci_probe函数的调用又要使用到另一个驱动模型:平台总线。

通过平台设备把6410关于MMC控制器的硬件寄存器、中断号注册入内核,所以这部分是和硬件息息相关。

s3cmci_probe函数任务:1、获取平台设备硬件信息、2、通过mmc_alloc_host函数实现把mmc_card注册到(核心层)内核,这样又会触动MMC总线模型的匹配和MMC驱动的probe函数调用,也就是前面的内容。

其中对于硬件资源获取和普通平台总线模型一样,就是获取寄存器地址、中断号、注册中断、时钟、DMA通道等。

现在看看mmc_alloc_host函数实现:

mmc_alloc_host

mmc_rescan

     if (host->ops->get_cd && host->ops->get_cd(host) == 0)

           return;//当前卡是否有插入,没有返回

mmc_go_idle(host); //放CM0,进入IDEL状态

     err = mmc_send_io_op_cond(host, 0, &ocr);  //判断卡类型

if (!err) {

//SDIO卡

  }

err = mmc_send_app_op_cond(host, 0, &ocr);

if (!err) {

//SD卡

   }

   err = mmc_send_op_cond(host, 0, &ocr);

if (!err) {\

//MMC卡

   }

判断卡类型之后就调用mmc_attach_sXX(host, ocr)函数来初始化对应的卡。MMC卡对应于mmc_attach_mmc函数。

下面根据协议来初始化卡:

mmc_attach_mmc

mmc_init_card

1、mmc_go_idle(host);\\进入IDEL

2、mmc_send_op_cond; \\ACMD41,进入ready状态

。。。//根据MMC卡初始化发命令,具体可参考http://wenku.baidu.com/view/457131c3d5bbfd0a795673bd.html

mmc_add_card //把mmc_card设备结构体注册到MMC总线,这个就回到前面,会匹配MMC驱动了。



上面都是基于SD卡已经接到开发板了,如果卡是后来才插入的呢?

那就得靠中断来实现了,但是我们可以猜想中断处理函数一定会调用到mmc_rescan函数(区分卡类型、初始化卡、注册卡设备到MMC总线)。

中断处理函数在哪里?前面说过平台驱动s3cmci_probe有说到了负责中断注册.

s3cmci_irq_cd中断处理函数:

s3cmci_irq_cd(int irq, void *dev_id)

mmc_detect_change(host->mmc, msecs_to_jiffies(500));

mmc_schedule_delayed_work(&host->detect, delay);//在mmc_alloc_host函数里INIT_DELAYED_WORK(&host->detect, mmc_rescan);所以这里调用mmc_rescan

最后一个问题,设备节点在哪里产生的?

猜想:驱动和设备匹配成功后产生,并且在负责(块设备)驱动那一方负责产生.用户空间操作设备节点就是操作块设备节点,然后块设备节点才来调用设备这边实现的操作函数。

mmc_blk_probe

add_disk(md->disk);

bdi_register_dev(bdi, disk_devt(disk));

dev = device_create_vargs(bdi_class, parent, MKDEV(0, 0), bdi, fmt, args);

总结一下:

对于MMC/SD卡而言分为了三个目录:card\Host\Core。

card目录一看就知道和块设备操作有关,所以这个目录负责SD卡的块设备驱动注册;

接着就剩下Core目录和Host目录,Core目录实现了MMC\SDIO\MMC卡的底层操作以及命令发送之类的,主要还有一个就是MMC总线设备模型也是在这个目录下的BUS文件实现,还有一个core文件实现了SD/SDIO/MMC的协议。

而Host就是我们的MMC/SD控制器了,所有的协议都是一种约定,最底层还是需要硬件来搭建起来,这方面就是交给了Host来实现。

所以我们呢可以猜想:块设备那一层会得到用户的操作请求,这个请求要经过核心层,因为核心层才知道如何根据SD/MMC协议来实现用户的请求,和核心层的要实现协议里的每一条数据或者命令都是依靠低层Host来实现。




总线模型总结:首先是MMC总线,该总线的注册在core.c文件完成,MMC的驱动注册在block.c文件,MMC的设备注册有个前提,那就是在一个平台驱动里注册s3cmci_probe,这个驱动是SD/MMC的主机控制器。所以MMC/SD的设备层实际是MMC/SD的主机控制层。

1、在平台设备上把6410芯片手册的关于MMC/SD的操作寄存器和中断号封装为平台设备注册入内核;

2、内核有个平台驱动会根据平台总线去匹配平台设备,匹配成功调用平台驱动的probe函数,例如s3cmci_probe;

3、在s3cmci_probe函数里获取主机控制器的硬件资源填充mmc_host结构体,并且通过mmc_alloc_host函数,会间接调用mmc_rescan函数来勘测设备是否存在,如果不存在:

if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out;

如果存在:调用mmc_attach_mmc函数初始化MMC卡,并会间接调用mmc_add_card函数注册MMC的设备。

4、如果注册MMC总线的设备,又会触发MMC总线匹配MMC的驱动链表,进而调用MMC的驱动来完成块设备的初始化。



当开发板没有MMC卡:

MMC设备不会被注册到MMC总线上,原因:

if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out;


当开发板有MMC卡:

如果系统刚启动:那么就会通过s3cmci_probe-->mmc_rescan-->mmc_attach_mmc-->mmc_add_card;

如果系统已经启动:产生中断s3cmci_irq_cd-->host->detect(mmc_rescan)-->mmc_attach_mmc-->mmc_add_card;


原创粉丝点击