sdmmc卡驱动学习日记.docx
- 文档编号:28521954
- 上传时间:2023-07-18
- 格式:DOCX
- 页数:13
- 大小:22.90KB
sdmmc卡驱动学习日记.docx
《sdmmc卡驱动学习日记.docx》由会员分享,可在线阅读,更多相关《sdmmc卡驱动学习日记.docx(13页珍藏版)》请在冰豆网上搜索。
sdmmc卡驱动学习日记
sd-mmc卡驱动学习日记
首先,我们来看Makefile文件吧,Makefile中文件的目标文件的顺序是很重要的,因为这个会涉及到模块的依赖关系,比如说,如果这些源文件中有module_init(),则这些module_init就按在Makefile中的顺序链接进内核,之后也按照链接的顺序进行调用。
根据我们的内核配置选项,将要编译进内核的文件就只有
mmc.c,mmc_sysfs.c,mmc_block.c,mmc_queue.c,s3cmci.c这几个文件。
其中mmc.c和mmc_queue.c主要是定义了一些其他文件中将要使用的函数,我们暂时不管它。
接下来,我们来分析mmc_sysfs.c
我们先来看mmc_init(),这是系统启动后将要调用的,在mmc_init函数中,主要完成3项工作:
workqueue=create_singlethread_workqueue("kmmcd");//创见一个单线程的工作队列
bus_register(&mmc_bus_type);//注册总线
class_register(&mmc_host_class);//注册mmc_host_class
mmc_bus_type的定义为:
staticstructbus_typemmc_bus_type={
.name="mmc",
.dev_attrs=mmc_dev_attrs,
.match=mmc_bus_match,
.uevent=mmc_bus_uevent,
.probe=mmc_bus_probe,
.remove=mmc_bus_remove,
.suspend=mmc_bus_suspend,
.resume=mmc_bus_resume,
};
这里,我们主要关注mmc_bus_match和mmc_bus_probe,此两函数将要在device_register和driver_register向总线注册设备的时候被调用。
staticintmmc_bus_match(structdevice*dev,structdevice_driver*drv)
{
structmmc_card*card=dev_to_mmc_card(dev);
return!
mmc_card_bad(card);
}
#definemmc_card_bad(c)((c)->state&(MMC_STATE_BAD)
可见,对于mmc总线,mmc_bus_match是通过返回structmmc_card中的状态标示state位来实现的。
staticintmmc_bus_probe(structdevice*dev)
{
structmmc_driver*drv=to_mmc_driver(dev->driver);
structmmc_card*card=dev_to_mmc_card(dev);
returndrv->probe(card);
}
在使用bus_register之后,我们可以在sysfs的/sys/bus目录里看到它
staticstructclassmmc_host_class={
.name ="mmc_host",
.dev_release =mmc_host_classdev_release,
};
class_register之后,在/sys/class目录下将出现mmc_host目录
接下来,我们看mmc_block.c,还是从初始化函数mmc_blk_init开始分析
staticint__initmmc_blk_init(void)
{
intres=-ENOMEM;
res=register_blkdev(major,"mmc");
if(res<0){
printk(KERN_WARNING"Unabletogetmajor%dforMMCmedia:
%d\n",
major,res);
gotoout;
}
if(major==0)
major=res;
returnmmc_register_driver(&mmc_driver);
out:
returnres;
}
mmc_blk_init中,register_blkdev(major,"mmc")的作用是注册一个块设备。
如果传递的major为0,这内核将分派一个新的主设备号给设备。
register_blkdev的功能比较少,一是动态分配设备号,二是在/proc/devices中创建一个入口项。
故通过它之后,系统还是不能使用块设备的。
接着我们看
returnmmc_register_driver(&mmc_driver);
mmc_register_driver在mmc_sysfs.c中定义:
staticstructmmc_drivermmc_driver={
.drv ={
.name ="mmcblk",
},
.probe =mmc_blk_probe,
.remove =mmc_blk_remove,
.suspend =mmc_blk_suspend,
.resume =mmc_blk_resume,
};
intmmc_register_driver(structmmc_driver*drv)
{
drv->drv.bus=&mmc_bus_type;
returndriver_register(&drv->drv);
}
这两句代码比较好理解吧。
在注册一个structdriver之前,都要先设置它的bus,类似的,在platform_driver_register中我们也可所以看到:
drv->driver.bus=&platform_bus_type
driver_register将到相应总线mmc_bus_type上去搜索相应设备。
找到设备后就设置dev->driver=drv,并调用mmc_bus_type总线的probe函数被调用,即mmmc_bus_probe函数.
我们跟踪driver_register(&drv->drv),它会调应bus_add_driver。
intbus_add_driver(structdevice_driver*drv)
{
...
...
error=kobject_set_name(&drv->kobj,"%s",drv->name);
if(error)
gotoout_put_bus;
drv->kobj.kset=&bus->drivers;
if((error=kobject_register(&drv->kobj)))
gotoout_put_bus;
error=driver_attach(drv);
...
...
}
在调用kobject_register之后,我们可以在/sys/bus/mmc/driver目录下看到mmcblk文件driver_attach函数会遍历相应总线(mmc_bus_type)上的dev,对这些dev执行总线的match函数(mmc_bus_match)。
如果match成功,它会调用mmc_bus_type总线的probe函数mmc_bus_probe(如果总线的probe存在的话).我们在以上走过的流程中可以看到,我们并没有向总线添加任何设备,故mmc_bus_probe是不会调用的。
但相应的driver已经注册到系统了。
那么,现在mmc_block.c中的分析就先暂时到此为止。
。
。
不过,等会儿我们还会继续回到这个文件的。
。
我们接着来到s3cmci.c文件
s3cmci_init----->platform_driver_register(&s3cmci_driver_2440)------------>s3cmci_probe_2440----->s3cmci_probe
在s3cmci_probe中主要是分配及初始化
structmmc_host*mmc;
structs3cmci_host*host;
这两个结构体。
分配DMA通道,注册irq中断。
以下对个别函数的作用进行说明:
1:
clk_get
系统初始化的时候外围总线上的设备不是都给时钟的,主要是为了省电。
在2.6.20中,我们在文件arch/arm/mach-s3c2410/s3c2410-clock中可以看到nand,sdi,adc,i2c,iis这些是不给时钟的lcd,gpio,usb,uart这些是给时钟的
clk_get在arch/arm/mach-s3c2410/clock.c中定义
2:
mmc_alloc_host
mmc_alloc_host分配sizeof(structmmc_host)+extra这么大的空间,并做以下初始化
host=mmc_alloc_host_sysfs(extra,dev);
host->parent=dev;
host->class_dev.parent=dev;
host->class_dev.class=&mmc_host_class;
device_initialize(&host->class_dev);
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_LIST_HEAD(&host->cards);
INIT_DELAYED_WORK(&host->detect,mmc_rescan);
*Bydefault,hostsdonotsupportSGIOorlargerequests.
*Theyhavetosettheseaccordingtotheirabilities.
host->max_hw_segs=1;
host->max_phys_segs=1;
host->max_sectors=1<<(PAGE_CACHE_SHIFT-9);
host->max_seg_size=PAGE_CACHE_SIZE;
3:
mmc_add_host
调用mmc_add_host的最终结果是device_add(&host->class->dev)(所以在/sys/class/mmc/目录下出现mmc0文件)。
并会调用mmc_power_off(host);mmc_detect_change(host,0);
mmc_power_off函数比较简单,顾名思义,它是让SD/MMC卡停止工作的,相应的,此函数就会配置相应的IO口以及时钟等。
我们来看看mmc_detect_change函数吧。
voidmmc_detect_change(structmmc_host*host,unsignedlongdelay)
{
mmc_schedule_delayed_work(&host->detect,delay);
}
intmmc_schedule_delayed_work(structdelayed_work*work,unsignedlongdelay)
{
returnqueue_delayed_work(workqueue,work,delay);
}
queue_delayed_work把host->detect提交到workqueue工作对列中。
相应定义:
INIT_DELAYED_WORK(&host->detect,mmc_rescan);
workqueue=create_singlethread_workqueue("kmmcd");
所以,当此delayed_work执行的时候,mmc_rescan将会被调用
staticvoidmmc_rescan(structwork_struct*work)
{
structmmc_host*host=
container_of(work,structmmc_host,detect.work);//返回指向s3cmci_probe中分配的mmc_host结构的指针
structlist_head*l,*n;
unsignedcharpower_mode;
mmc_claim_host(host);
/*
驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么host->claimed=1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权
*/
/*
*Checkforremovedcardsandnewlyinsertedones.Wecheckfor
*removedcardsfirstsowecanintelligentlyre-selecttheVDD.
*/
power_mode=host->ios.power_mode;
if(power_mode==MMC_POWER_ON)
mmc_check_cards(host);
mmc_setup(host);
/*
*SomebrokencardsprocessCMD1eveninstand-bystate.Thereis
*noreply,butanILLEGAL_COMMANDerroriscachedandreturned
*afternextcommand.Wepollforcardstatusheretoclearany
*possiblypendingerror.
*/
if(power_mode==MMC_POWER_ON)
mmc_check_cards(host);
if(!
list_empty(&host->cards)){
/*
*(Re-)calculatethefastestclockratewhichthe
*attachedcardsandthehostsupport.
*/
host->ios.clock=mmc_calculate_clock(host);
mmc_set_ios(host);
}
mmc_release_host(host);
list_for_each_safe(l,n,&host->cards){
structmmc_card*card=mmc_list_to_card(l);
/*
*Ifthisisanewandgoodcard,registerit.
*/
if(!
mmc_card_present(card)&&!
mmc_card_dead(card)){
if(mmc_register_card(card))
mmc_card_set_dead(card);
else
mmc_card_set_present(card);
}
/*s
*Ifthiscardisdead,destroyit.
*/
if(mmc_card_dead(card)){
list_del(&card->node);
mmc_remove_card(card);
}
}
/*
*Ifwediscoverthattherearenocardsonthe
*bus,turnofftheclockandpowerdown.
*/
if(list_empty(&host->cards))
mmc_power_off(host);
}
起初,power_mode=MMC_POWER_OFF,故第一个mmc_check_cards(host)是不会执行的。
但mmc_setup函数中设置了power_mode=MMC_POWER_ON。
故第二个mmc_check_cards(host)是会执行的。
在mmc_setup中,会调用mmc_discover_cards.如果发现有卡,则addnewcardtolist.
因为现在没有发现卡,host->card=NULL,故mmc_check_cards和mmc_rescan中的list_for_each_safe循环体中的内容也是不会执行的。
staticvoidmmc_check_cards(structmmc_host*host)
{
structlist_head*l,*n;
mmc_deselect_cards(host);//Ensurethatnocardisselected.
list_for_each_safe(l,n,&host->cards){
structmmc_card*card=mmc_list_to_card(l);
structmmc_commandcmd;
interr;
cmd.opcode=MMC_SEND_STATUS;
cmd.arg=card->rca<<16;
cmd.flags=MMC_RSP_R1|MMC_CMD_AC;
err=mmc_wait_for_cmd(host,&cmd,CMD_RETRIES);
if(err==MMC_ERR_NONE)
continue;
mmc_card_set_dead(card);
}
}
s3c2410-sdis3c2410-sdi:
powereddown.
s3c2410-sdis3c2410-sdi:
initialisationdone.
s3c2410-sdis3c2410-sdi:
runningat0kHz(requested:
0kHz).
s3c2410-sdis3c2410-sdi:
runningat198kHz(requested:
197kHz).
s3c2410-sdis3c2410-sdi:
runningat198kHz(requested:
197kHz).
s3c2410-sdis3c2410-sdi:
runningat198kHz(requested:
197kHz).
s3c2410-sdis3c2410-sdi:
CMD[TIMEOUT]#2op:
APP_CMD(55)arg:
0x00000000flags:
0xe
s3c2410-sdis3c2410-sdi:
CMD[TIMEOUT]#3op:
APP_CMD(55)arg:
0x00000000flags:
0xe
s3c2410-sdis3c2410-sdi:
CMD[TIMEOUT]#4op:
APP_CMD(55)arg:
0x00000000flags:
0xe
s3c2410-sdis3c2410-sdi:
CMD[TIMEOUT]#5op:
APP_CMD(55)arg:
0x00000000flags:
0xe
s3c2410-sdis3c2410-sdi:
CMD[TIMEOUT]#6op:
ALL_SEND_OCR
(1)arg:
0x00000000flage
s3c2410-sdis3c2410-sdi:
powereddown.
至此,我们已经跟踪了mmc/sd卡驱动的注册。
。
我们接着来看插入拔除卡的中断处理函数:
staticirqreturn_ts3cmci_irq_cd(intirq,void*dev_id)
{
structs3cmci_host*host=(structs3cmci_host*)dev_id;
dbg(host,dbg_irq,"carddetect\n");
mmc_detect_change(host->mmc,500);
returnIRQ_HANDLED;
}
可见,这里也会调用mmc_detect_change。
。
。
我们跟着前面的分析来到mmc_setup这里,此时mmc_setup调用mmc_discover_cards。
Createammc_cardentryforeachdiscoveredcard,addnewcardtolist.同时还会调用mmc_read_switch_caps或者mmc_process_ext_csds来实现对大容量卡的支持(>4G)
跟着程序的流程我们来到
if(!
mmc_card_present(card)&&!
mmc_card_dead(card)){
if(mmc_register_card(card))来看
intmmc_register_card(structmmc_card*card)
{
intret;
snprintf(card->dev.bus_i
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- sdmmc 驱动 学习 日记