您的位置:首页 > 运维架构 > 网站架构

Linux设备驱动程序架构分析之MMC/SD(一)

2013-12-19 13:31 246 查看
作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz

内核版本:3.10.1


MMC

MMC全称MultiMediaCard,由西门子公司和SanDisk公司1997年推出的多媒体记忆卡标准。MMC卡尺寸为32mmx24mmx1.4mm,它将存贮单元和控制器一同做到了卡上,智能的控制器使得MMC保证兼容性和灵活性。

MMC卡具有MMC和SPI两种工作模式,MMC模式是默认工作模式,具有MMC的全部特性。而SPI模式则是MMC协议的一个子集,主要用于低速系统。



SD

SD卡全称SecureDigitalMemoryCard,由松下、东芝和SanDisk公司于1999年8月共同开发的新一代记忆卡标准,已完全兼容MMC标准。SD卡比MMC卡多了一个进行数据著作权保护的暗号认证功能,读写速度比MMC卡快4倍。

SD卡尺寸为32mmx24mmx2.1mm,长宽和MMC卡一样,只是比MMC卡厚了0.7mm,以容纳更大容量的存贮单元。SD卡与MMC卡保持向上兼容,也就是说,MMC卡可以被新的设有SD卡插槽的设备存取,但是SD卡却不可以被设有MMC插槽的设备存取。



SDIO

SDIO全称SecureDigitalInputandOutputCard,SDIO是在SD标准上定义了一种外设接口,它使用SD的I/O接口来连接外围设备,并通过SD上的I/O数据接口与这些外围设备传输数据。现在已经有很多手持设备支持SDIO功能,而且许多SDIO外设也被开发出来,目前常见的SDIO外设有:WIFICard、GPSCard、BluetoothCard等等。



eMMC

eMMC全称EmbeddedMultiMediaCard,是MMC协会所制定的内嵌式存储器标准规格,主要应用于智能手机和移动嵌入式产品等。eMMC是一种嵌入式非易失性存储系统,由闪存和闪存控制器两部分组成,它的一个明显优势是在封装中集成了一个闪存控制器,它采用JEDEC标准BGA封装,并采用统一闪存接口管理闪存。

eMMC结构由一个嵌入式存储解决方案组成,带有MMC接口、快闪存储设备及主控制器,所有这些由一个小型BGA封装。由于采用标准封装,eMMC也很容易升级,并不用改变硬件结构。

eMMC的这种将NandFlash芯片和控制芯片封装在一起的设计概念,就是为了简化产品内存储器的使用,客户只需要采购eMMC芯片放进产品中,不需要处理其它复杂的NandFlash兼容性和管理问题,减少研发成本和研发周期。



下面我们以Mini2440为例,分析其SDI驱动程序。

Mini2440MMC/SD硬件接口电路原理图如下:



从Mini2440原理图可以看出,Mini2440SDI使用的GPE7-GPE10作为4根数据信号线,使用GPE6作为命令信号线,使用GPE5作为时钟信号线。另外,使用GPG8的外部中断功能来作SD卡的插拨检测,使用GPH8来判断SD卡是否有写保护。



一、SDI设备的注册

先来看device注册过程,在arch/arm/mach-s3c24xx/mach-mini2440.c文件中,有如下内容:

505staticstructplatform_device*mini2440_devices[]__initdata={
506&s3c_device_ohci,
507&s3c_device_wdt,
508&s3c_device_i2c0,
509&s3c_device_rtc,
510&s3c_device_usbgadget,
511&mini2440_device_eth,
512&mini2440_led1,
513&mini2440_led2,
514&mini2440_led3,
515&mini2440_led4,
516&mini2440_button_device,
517&s3c_device_nand,
518&s3c_device_sdi,
519&s3c_device_iis,
520&uda1340_codec,
521&mini2440_audio,
522};


这里定义了Mini2440所有的platformdevice,这里,我们要关注的是s3c_device_sdi,它是Mini2440的SDI控制器。

s3c_device_sdi定义在arch/arm/plat-samsung/devs.c文件中:

1172structplatform_devices3c_device_sdi={
1173.name="s3c2410-sdi",
1174.id=-1,
1175.num_resources=ARRAY_SIZE(s3c_sdi_resource),
1176.resource=s3c_sdi_resource,
1177};


回忆一下platform_device定义在include/linux/platform_device.h文件中:

22structplatform_device{
23constchar*name;
24intid;
25boolid_auto;
26structdevicedev;
27u32num_resources;
28structresource*resource;
29
30conststructplatform_device_id*id_entry;
31
32/*MFDcellpointer*/
33structmfd_cell*mfd_cell;
34
35/*archspecificadditions*/
36structpdev_archdataarchdata;
37};


其中,s3c_sdi_resource定义在arch/arm/plat-samsung/devs.c文件中:

1167staticstructresources3c_sdi_resource[]={
1168[0]=DEFINE_RES_MEM(S3C24XX_PA_SDI,S3C24XX_SZ_SDI),
1169[1]=DEFINE_RES_IRQ(IRQ_SDI),
1170};


structresource定义在include/linux/ioport.h文件中:

14/*
15*Resourcesaretree-like,allowing
16*nestingetc..
17*/
18structresource{
19resource_size_tstart;
20resource_size_tend;
21constchar*name;
22unsignedlongflags;
23structresource*parent,*sibling,*child;
24};


宏DEFINE_RES_MEM定义在include/linux/ioport.h文件中:

124#defineDEFINE_RES_MEM(_start,_size)\
125DEFINE_RES_MEM_NAMED((_start),(_size),NULL)
……
122#defineDEFINE_RES_MEM_NAMED(_start,_size,_name)\
123DEFINE_RES_NAMED((_start),(_size),(_name),IORESOURCE_MEM)
……
109#defineDEFINE_RES_NAMED(_start,_size,_name,_flags)\
110{\
111.start=(_start),\
112.end=(_start)+(_size)-1,\
113.name=(_name),\
114.flags=(_flags),\
115}


宏DEFINE_RES_IRQ宏定义在include/linux/ioport.h文件中:

129#defineDEFINE_RES_IRQ(_irq)\
130DEFINE_RES_IRQ_NAMED((_irq),NULL)
……
127#defineDEFINE_RES_IRQ_NAMED(_irq,_name)\
128DEFINE_RES_NAMED((_irq),1,(_name),IORESOURCE_IRQ)
……
109#defineDEFINE_RES_NAMED(_start,_size,_name,_flags)\
110{\
111.start=(_start),\
112.end=(_start)+(_size)-1,\
113.name=(_name),\
114.flags=(_flags),\
115}


宏S3C24XX_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

155#defineS3C24XX_PA_SDIS3C2410_PA_SDI


宏S3C2410_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

105#defineS3C2410_PA_SDI(0x5A000000)


0x5A000000是S3C2440SDICON寄存器的地址。

宏S3C24XX_SZ_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

61#defineS3C24XX_SZ_SDISZ_1M


宏SZ_1M定义在include/linux/sizes.h文件中:

33#defineSZ_1M0x00100000


宏IRQ_SDI定义在arch/arm/mach-s3c24xx/include/mach/irqs.h文件中:

48#defineIRQ_SDIS3C2410_IRQ(21)
……
23#defineS3C2410_IRQ(x)((x)+S3C2410_CPUIRQ_OFFSET)
……
21#defineS3C2410_CPUIRQ_OFFSET(16)


至此,我们知道了Mini2440的platform_devices3c_device_sdi的定义,下面就是要注册这个平台设备,在arch/arm/mach-s3c24xx/mach-mini2440.c文件中:

622staticvoid__initmini2440_init(void)
{
……
678platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));
……
}


platform_add_devices定义在drivers/base/platform.c文件中:

139/**
140*platform_add_devices-addanumbersofplatformdevices
141*@devs:arrayofplatformdevicestoadd
142*@num:numberofplatformdevicesinarray
143*/
144intplatform_add_devices(structplatform_device**devs,intnum)
145{
146inti,ret=0;
147
148for(i=0;i<num;i++){
149ret=platform_device_register(devs[i]);
150if(ret){
151while(--i>=0)
152platform_device_unregister(devs[i]);
153break;
154}
155}
156
157returnret;
158}


149行,通过调用platform_device_register完成对平台设备的注册,其中包括s3c_device_sdi。



二、SDI驱动分析

Mini2440的SDI驱动定义在drivers/mmc/host/s3cmci.c文件中:

1980staticstructplatform_drivers3cmci_driver={
1981.driver={
1982.name="s3c-sdi",
1983.owner=THIS_MODULE,
1984.pm=s3cmci_pm_ops,
1985},
1986.id_table=s3cmci_driver_ids,
1987.probe=s3cmci_probe,
1988.remove=s3cmci_remove,
1989.shutdown=s3cmci_shutdown,
1990};


s3cmci_driver_ids定义在drivers/mmc/host/s3cmci.c文件中:

1936staticstructplatform_device_ids3cmci_driver_ids[]={
1937{
1938.name="s3c2410-sdi",
1939.driver_data=0,
1940},{
1941.name="s3c2412-sdi",
1942.driver_data=1,
1943},{
1944.name="s3c2440-sdi",
1945.driver_data=1,
1946},
1947{}
1948};


我们来看platform_drivers3cmci_driver的注册,在drivers/mmc/host/s3cmci.c文件中:

1992module_platform_driver(s3cmci_driver);


module_platform_driver是一个宏,定义在include/linux/platform_device.h文件中:

203/*module_platform_driver()-Helpermacrofordriversthatdon'tdo
204*anythingspecialinmoduleinit/exit.Thiseliminatesalotof
205*boilerplate.Eachmodulemayonlyusethismacroonce,and
206*callingitreplacesmodule_init()andmodule_exit()
207*/
208#definemodule_platform_driver(__platform_driver)\
209module_driver(__platform_driver,platform_driver_register,\
210platform_driver_unregister)


宏module_driver定义在include/linux/device.h文件中,其内容如下:

1108/**
1109*module_driver()-Helpermacrofordriversthatdon'tdoanything
1110*specialinmoduleinit/exit.Thiseliminatesalotofboilerplate.
1111*Eachmodulemayonlyusethismacroonce,andcallingitreplaces
1112*module_init()andmodule_exit().
1113*
1114*@__driver:drivername
1115*@__register:registerfunctionforthisdrivertype
1116*@__unregister:unregisterfunctionforthisdrivertype
1117*@...:Additionalargumentstobepassedto__registerand__unregister.
1118*
1119*Usethismacrotoconstructbusspecificmacrosforregistering
1120*drivers,anddonotuseitonitsown.
1121*/
1122#definemodule_driver(__driver,__register,__unregister,...)\
1123staticint__init__driver##_init(void)\
1124{\
1125return__register(&(__driver),##__VA_ARGS__);\
1126}\
1127module_init(__driver##_init);\
1128staticvoid__exit__driver##_exit(void)\
1129{\
1130__unregister(&(__driver),##__VA_ARGS__);\
1131}\
1132module_exit(__driver##_exit);


我们知道,注册s3cmci_driver的过程中,会触发s3cmci_probe函数的执行,所以先来看s3cmci_probe函数,它定义在drivers/mmc/host/s3cmci.c文件中,其内容如下:

1622staticints3cmci_probe(structplatform_device*pdev)
1623{
1624structs3cmci_host*host;
1625structmmc_host*mmc;
1626intret;
1627intis2440;
1628inti;
1629
1630is2440=platform_get_device_id(pdev)->driver_data;
1631
1632mmc=mmc_alloc_host(sizeof(structs3cmci_host),&pdev->dev);
1633if(!mmc){
1634ret=-ENOMEM;
1635gotoprobe_out;
1636}
1637
1638for(i=S3C2410_GPE(5);i<=S3C2410_GPE(10);i++){
1639ret=gpio_request(i,dev_name(&pdev->dev));
1640if(ret){
1641dev_err(&pdev->dev,"failedtogetgpio%d\n",i);
1642
1643for(i--;i>=S3C2410_GPE(5);i--)
1644gpio_free(i);
1645
1646gotoprobe_free_host;
1647}
1648}
1649
1650host=mmc_priv(mmc);
1651host->mmc=mmc;
1652host->pdev=pdev;
1653host->is2440=is2440;
1654
1655host->pdata=pdev->dev.platform_data;
1656if(!host->pdata){
1657pdev->dev.platform_data=&s3cmci_def_pdata;
1658host->pdata=&s3cmci_def_pdata;
1659}
1660
1661spin_lock_init(&host->complete_lock);
1662tasklet_init(&host->pio_tasklet,pio_tasklet,(unsignedlong)host);
1663
1664if(is2440){
1665host->sdiimsk=S3C2440_SDIIMSK;
1666host->sdidata=S3C2440_SDIDATA;
1667host->clk_div=1;
1668}else{
1669host->sdiimsk=S3C2410_SDIIMSK;
1670host->sdidata=S3C2410_SDIDATA;
1671host->clk_div=2;
1672}
1673
1674host->complete_what=COMPLETION_NONE;
1675host->pio_active=XFER_NONE;
1676
1677#ifdefCONFIG_MMC_S3C_PIODMA
1678host->dodma=host->pdata->use_dma;
1679#endif
1680
1681host->mem=platform_get_resource(pdev,IORESOURCE_MEM,0);
1682if(!host->mem){
1683dev_err(&pdev->dev,
1684"failedtogetiomemoryregionresource.\n");
1685
1686ret=-ENOENT;
1687gotoprobe_free_gpio;
1688}
1689
1690host->mem=request_mem_region(host->mem->start,
1691resource_size(host->mem),pdev->name);
1692
1693if(!host->mem){
1694dev_err(&pdev->dev,"failedtorequestiomemoryregion.\n");
1695ret=-ENOENT;
1696gotoprobe_free_gpio;
1697}
1698
1699host->base=ioremap(host->mem->start,resource_size(host->mem));
1700if(!host->base){
1701dev_err(&pdev->dev,"failedtoioremap()iomemoryregion.\n");
1702ret=-EINVAL;
1703gotoprobe_free_mem_region;
1704}
1705
1706host->irq=platform_get_irq(pdev,0);
1707if(host->irq==0){
1708dev_err(&pdev->dev,"failedtogetinterruptresource.\n");
1709ret=-EINVAL;
1710gotoprobe_iounmap;
1711}
1712
1713if(request_irq(host->irq,s3cmci_irq,0,DRIVER_NAME,host)){
1714dev_err(&pdev->dev,"failedtorequestmciinterrupt.\n");
1715ret=-ENOENT;
1716gotoprobe_iounmap;
1717}
1718
1719/*WegetspuriousinterruptsevenwhenwehavesettheIMSK
1720*registertoignoreeverything,sousedisable_irq()tomake
1721*ensurewedon'tlockthesystemwithun-serviceablerequests.*/
1722
1723disable_irq(host->irq);
1724host->irq_state=false;
1725
1726if(!host->pdata->no_detect){
1727ret=gpio_request(host->pdata->gpio_detect,"s3cmcidetect");
1728if(ret){
1729dev_err(&pdev->dev,"failedtogetdetectgpio\n");
1730gotoprobe_free_irq;
1731}
1732
1733host->irq_cd=gpio_to_irq(host->pdata->gpio_detect);
1734
1735if(host->irq_cd>=0){
1736if(request_irq(host->irq_cd,s3cmci_irq_cd,
1737IRQF_TRIGGER_RISING|
1738IRQF_TRIGGER_FALLING,
1739DRIVER_NAME,host)){
1740dev_err(&pdev->dev,
1741"can'tgetcarddetectirq.\n");
1742ret=-ENOENT;
1743gotoprobe_free_gpio_cd;
1744}
1745}else{
1746dev_warn(&pdev->dev,
1747"hostdetecthasnoirqavailable\n");
1748gpio_direction_input(host->pdata->gpio_detect);
1749}
1750}else
1751host->irq_cd=-1;
1752
1753if(!host->pdata->no_wprotect){
1754ret=gpio_request(host->pdata->gpio_wprotect,"s3cmciwp");
1755if(ret){
1756dev_err(&pdev->dev,"failedtogetwriteprotect\n");
1757gotoprobe_free_irq_cd;
1758}
1759
1760gpio_direction_input(host->pdata->gpio_wprotect);
1761}
1762
1763/*dependingonthedmastate,getadmachanneltouse.*/
1764
1765if(s3cmci_host_usedma(host)){
1766host->dma=s3c2410_dma_request(DMACH_SDI,&s3cmci_dma_client,
1767host);
1768if(host->dma<0){
1769dev_err(&pdev->dev,"cannotgetDMAchannel.\n");
1770if(!s3cmci_host_canpio()){
1771ret=-EBUSY;
1772gotoprobe_free_gpio_wp;
1773}else{
1774dev_warn(&pdev->dev,"fallingbacktoPIO.\n");
1775host->dodma=0;
1776}
1777}
1778}
1779
1780host->clk=clk_get(&pdev->dev,"sdi");
1781if(IS_ERR(host->clk)){
1782dev_err(&pdev->dev,"failedtofindclocksource.\n");
1783ret=PTR_ERR(host->clk);
1784host->clk=NULL;
1785gotoprobe_free_dma;
1786}
1787
1788ret=clk_enable(host->clk);
1789if(ret){
1790dev_err(&pdev->dev,"failedtoenableclocksource.\n");
1791gotoclk_free;
1792}
1793
1794host->clk_rate=clk_get_rate(host->clk);
1795
1796mmc->ops=&s3cmci_ops;
1797mmc->ocr_avail=MMC_VDD_32_33|MMC_VDD_33_34;
1798#ifdefCONFIG_MMC_S3C_HW_SDIO_IRQ
1799mmc->caps=MMC_CAP_4_BIT_DATA|MMC_CAP_SDIO_IRQ;
1800#else
1801mmc->caps=MMC_CAP_4_BIT_DATA;
1802#endif
1803mmc->f_min=host->clk_rate/(host->clk_div*256);
1804mmc->f_max=host->clk_rate/host->clk_div;
1805
1806if(host->pdata->ocr_avail)
1807mmc->ocr_avail=host->pdata->ocr_avail;
1808
1809mmc->max_blk_count=4095;
1810mmc->max_blk_size=4095;
1811mmc->max_req_size=4095*512;
1812mmc->max_seg_size=mmc->max_req_size;
1813
1814mmc->max_segs=128;
1815
1816dbg(host,dbg_debug,
1817"probe:mode:%smappedmci_base:%pirq:%uirq_cd:%udma:%u.\n",
1818(host->is2440?"2440":""),
1819host->base,host->irq,host->irq_cd,host->dma);
1820
1821ret=s3cmci_cpufreq_register(host);
1822if(ret){
1823dev_err(&pdev->dev,"failedtoregistercpufreq\n");
1824gotofree_dmabuf;
1825}
1826
1827ret=mmc_add_host(mmc);
1828if(ret){
1829dev_err(&pdev->dev,"failedtoaddmmchost.\n");
1830gotofree_cpufreq;
1831}
1832
1833s3cmci_debugfs_attach(host);
1834
1835platform_set_drvdata(pdev,mmc);
1836dev_info(&pdev->dev,"%s-using%s,%sSDIOIRQ\n",mmc_hostname(mmc),
1837s3cmci_host_usedma(host)?"dma":"pio",
1838mmc->caps&MMC_CAP_SDIO_IRQ?"hw":"sw");
1839
1840return0;
1841
1842free_cpufreq:
1843s3cmci_cpufreq_deregister(host);
1844
1845free_dmabuf:
1846clk_disable(host->clk);
1847
1848clk_free:
1849clk_put(host->clk);
1850
1851probe_free_dma:
1852if(s3cmci_host_usedma(host))
1853s3c2410_dma_free(host->dma,&s3cmci_dma_client);
1854
1855probe_free_gpio_wp:
1856if(!host->pdata->no_wprotect)
1857gpio_free(host->pdata->gpio_wprotect);
1858
1859probe_free_gpio_cd:
1860if(!host->pdata->no_detect)
1861gpio_free(host->pdata->gpio_detect);
1862
1863probe_free_irq_cd:
1864if(host->irq_cd>=0)
1865free_irq(host->irq_cd,host);
1866
1867probe_free_irq:
1868free_irq(host->irq,host);
1869
1870probe_iounmap:
1871iounmap(host->base);
1872
1873probe_free_mem_region:
1874release_mem_region(host->mem->start,resource_size(host->mem));
1875
1876probe_free_gpio:
1877for(i=S3C2410_GPE(5);i<=S3C2410_GPE(10);i++)
1878gpio_free(i);
1879
1880probe_free_host:
1881mmc_free_host(mmc);
1882
1883probe_out:
1884returnret;
1885}


1624行,定义structs3cmci_host指针变量host,structs3cmci_host定义在drivers/mmc/host/s3cmci.h文件中,其内容如下:

20structs3cmci_host{
21structplatform_device*pdev;
22structs3c24xx_mci_pdata*pdata;
23structmmc_host*mmc;
24structresource*mem;
25structclk*clk;
26void__iomem*base;
27intirq;
28intirq_cd;
29intdma;
30
31unsignedlongclk_rate;
32unsignedlongclk_div;
33unsignedlongreal_rate;
34u8prescaler;
35
36intis2440;
37unsignedsdiimsk;
38unsignedsdidata;
39intdodma;
40intdmatogo;
41
42boolirq_disabled;
43boolirq_enabled;
44boolirq_state;
45intsdio_irqen;
46
47structmmc_request*mrq;
48intcmd_is_stop;
49
50spinlock_tcomplete_lock;
51enums3cmci_waitforcomplete_what;
52
53intdma_complete;
54
55u32pio_sgptr;
56u32pio_bytes;
57u32pio_count;
58u32*pio_ptr;
59#defineXFER_NONE0
60#defineXFER_READ1
61#defineXFER_WRITE2
62u32pio_active;
63
64intbus_width;
65
66chardbgmsg_cmd[301];
67chardbgmsg_dat[301];
68char*status;
69
70unsignedintccnt,dcnt;
71structtasklet_structpio_tasklet;
72
73#ifdefCONFIG_DEBUG_FS
74structdentry*debug_root;
75structdentry*debug_state;
76structdentry*debug_regs;
77#endif
78
79#ifdefCONFIG_CPU_FREQ
80structnotifier_blockfreq_transition;
81#endif
82};


可以看到,structs3cmci_host描述了整个SDI控制器。

1625行,定义了structmmc_host指针变量mmc,structmmc_host定义在include/linux/mmc/host.h文件中,其内容如下:

198structmmc_host{
199structdevice*parent;
200structdeviceclass_dev;
201intindex;
202conststructmmc_host_ops*ops;
203unsignedintf_min;
204unsignedintf_max;
205unsignedintf_init;
206u32ocr_avail;
207u32ocr_avail_sdio;/*SDIO-specificOCR*/
208u32ocr_avail_sd;/*SD-specificOCR*/
209u32ocr_avail_mmc;/*MMC-specificOCR*/
210structnotifier_blockpm_notify;
211u32max_current_330;
212u32max_current_300;
213u32max_current_180;
214
215#defineMMC_VDD_165_1950x00000080/*VDDvoltage1.65-1.95*/
216#defineMMC_VDD_20_210x00000100/*VDDvoltage2.0~2.1*/
217#defineMMC_VDD_21_220x00000200/*VDDvoltage2.1~2.2*/
218#defineMMC_VDD_22_230x00000400/*VDDvoltage2.2~2.3*/
219#defineMMC_VDD_23_240x00000800/*VDDvoltage2.3~2.4*/
220#defineMMC_VDD_24_250x00001000/*VDDvoltage2.4~2.5*/
221#defineMMC_VDD_25_260x00002000/*VDDvoltage2.5~2.6*/
222#defineMMC_VDD_26_270x00004000/*VDDvoltage2.6~2.7*/
223#defineMMC_VDD_27_280x00008000/*VDDvoltage2.7~2.8*/
224#defineMMC_VDD_28_290x00010000/*VDDvoltage2.8~2.9*/
225#defineMMC_VDD_29_300x00020000/*VDDvoltage2.9~3.0*/
226#defineMMC_VDD_30_310x00040000/*VDDvoltage3.0~3.1*/
227#defineMMC_VDD_31_320x00080000/*VDDvoltage3.1~3.2*/
228#defineMMC_VDD_32_330x00100000/*VDDvoltage3.2~3.3*/
229#defineMMC_VDD_33_340x00200000/*VDDvoltage3.3~3.4*/
230#defineMMC_VDD_34_350x00400000/*VDDvoltage3.4~3.5*/
231#defineMMC_VDD_35_360x00800000/*VDDvoltage3.5~3.6*/
232
233u32caps;/*Hostcapabilities*/
234
235#defineMMC_CAP_4_BIT_DATA(1<<0)/*Canthehostdo4bittransfers*/
236#defineMMC_CAP_MMC_HIGHSPEED(1<<1)/*CandoMMChigh-speedtiming*/
237#defineMMC_CAP_SD_HIGHSPEED(1<<2)/*CandoSDhigh-speedtiming*/
238#defineMMC_CAP_SDIO_IRQ(1<<3)/*CansignalpendingSDIOIRQs*/
239#defineMMC_CAP_SPI(1<<4)/*TalksonlySPIprotocols*/
240#defineMMC_CAP_NEEDS_POLL(1<<5)/*Needspollingforcard-detection*/
241#defineMMC_CAP_8_BIT_DATA(1<<6)/*Canthehostdo8bittransfers*/
242
243#defineMMC_CAP_NONREMOVABLE(1<<8)/*Nonremovablee.g.eMMC*/
244#defineMMC_CAP_WAIT_WHILE_BUSY(1<<9)/*Waitswhilecardisbusy*/
245#defineMMC_CAP_ERASE(1<<10)/*Allowerase/trimcommands*/
246#defineMMC_CAP_1_8V_DDR(1<<11)/*cansupport*/
247/*DDRmodeat1.8V*/
248#defineMMC_CAP_1_2V_DDR(1<<12)/*cansupport*/
249/*DDRmodeat1.2V*/
250#defineMMC_CAP_POWER_OFF_CARD(1<<13)/*Canpoweroffafterboot*/
251#defineMMC_CAP_BUS_WIDTH_TEST(1<<14)/*CMD14/CMD19buswidthok*/
252#defineMMC_CAP_UHS_SDR12(1<<15)/*HostsupportsUHSSDR12mode*/
253#defineMMC_CAP_UHS_SDR25(1<<16)/*HostsupportsUHSSDR25mode*/
254#defineMMC_CAP_UHS_SDR50(1<<17)/*HostsupportsUHSSDR50mode*/
255#defineMMC_CAP_UHS_SDR104(1<<18)/*HostsupportsUHSSDR104mode*/
256#defineMMC_CAP_UHS_DDR50(1<<19)/*HostsupportsUHSDDR50mode*/
257#defineMMC_CAP_DRIVER_TYPE_A(1<<23)/*HostsupportsDriverTypeA*/
258#defineMMC_CAP_DRIVER_TYPE_C(1<<24)/*HostsupportsDriverTypeC*/
259#defineMMC_CAP_DRIVER_TYPE_D(1<<25)/*HostsupportsDriverTypeD*/
260#defineMMC_CAP_CMD23(1<<30)/*CMD23supported.*/
261#defineMMC_CAP_HW_RESET(1<<31)/*Hardwarereset*/
262
263u32caps2;/*Morehostcapabilities*/
264
265#defineMMC_CAP2_BOOTPART_NOACC(1<<0)/*Bootpartitionnoaccess*/
266#defineMMC_CAP2_CACHE_CTRL(1<<1)/*Allowcachecontrol*/
267#defineMMC_CAP2_POWEROFF_NOTIFY(1<<2)/*Notifypoweroffsupported*/
268#defineMMC_CAP2_NO_MULTI_READ(1<<3)/*Multiblockreadsdon'twork*/
269#defineMMC_CAP2_NO_SLEEP_CMD(1<<4)/*Don'tallowsleepcommand*/
270#defineMMC_CAP2_HS200_1_8V_SDR(1<<5)/*cansupport*/
271#defineMMC_CAP2_HS200_1_2V_SDR(1<<6)/*cansupport*/
272#defineMMC_CAP2_HS200(MMC_CAP2_HS200_1_8V_SDR|\
273MMC_CAP2_HS200_1_2V_SDR)
274#defineMMC_CAP2_BROKEN_VOLTAGE(1<<7)/*Usethebrokenvoltage*/
275#defineMMC_CAP2_DETECT_ON_ERR(1<<8)/*OnI/Oerrcheckcardremoval*/
276#defineMMC_CAP2_HC_ERASE_SZ(1<<9)/*High-capacityerasesize*/
277#defineMMC_CAP2_CD_ACTIVE_HIGH(1<<10)/*Card-detectsignalactivehigh*/
278#defineMMC_CAP2_RO_ACTIVE_HIGH(1<<11)/*Write-protectsignalactivehigh*/
279#defineMMC_CAP2_PACKED_RD(1<<12)/*Allowpackedread*/
280#defineMMC_CAP2_PACKED_WR(1<<13)/*Allowpackedwrite*/
281#defineMMC_CAP2_PACKED_CMD(MMC_CAP2_PACKED_RD|\
282MMC_CAP2_PACKED_WR)
283#defineMMC_CAP2_NO_PRESCAN_POWERUP(1<<14)/*Don'tpowerupbeforescan*/
284
285mmc_pm_flag_tpm_caps;/*supportedpmfeatures*/
286
287#ifdefCONFIG_MMC_CLKGATE
288intclk_requests;/*internalreferencecounter*/
289unsignedintclk_delay;/*numberofMCIclkholdcycles*/
290boolclk_gated;/*clockgated*/
291structdelayed_workclk_gate_work;/*delayedclockgate*/
292unsignedintclk_old;/*oldclockvaluecache*/
293spinlock_tclk_lock;/*lockforclkfields*/
294structmutexclk_gate_mutex;/*mutexforclockgating*/
295structdevice_attributeclkgate_delay_attr;
296unsignedlongclkgate_delay;
297#endif
298
299/*hostspecificblockdata*/
300unsignedintmax_seg_size;/*seeblk_queue_max_segment_size*/
301unsignedshortmax_segs;/*seeblk_queue_max_segments*/
302unsignedshortunused;
303unsignedintmax_req_size;/*maximumnumberofbytesinonereq*/
304unsignedintmax_blk_size;/*maximumsizeofonemmcblock*/
305unsignedintmax_blk_count;/*maximumnumberofblocksinonereq*/
306unsignedintmax_discard_to;/*max.discardtimeoutinms*/
307
308/*privatedata*/
309spinlock_tlock;/*lockforclaimandbusops*/
310
311structmmc_iosios;/*currentiobussettings*/
312u32ocr;/*thecurrentOCRsetting*/
313
314/*groupbitfieldstogethertominimizepadding*/
315unsignedintuse_spi_crc:1;
316unsignedintclaimed:1;/*hostexclusivelyclaimed*/
317unsignedintbus_dead:1;/*bushasbeenreleased*/
318#ifdefCONFIG_MMC_DEBUG
319unsignedintremoved:1;/*hostisbeingremoved*/
320#endif
321
322intrescan_disable;/*disablecarddetection*/
323intrescan_entered;/*usedwithnonremovabledevices*/
324
325structmmc_card*card;/*deviceattachedtothishost*/
326
327wait_queue_head_twq;
328structtask_struct*claimer;/*taskthathashostclaimed*/
329intclaim_cnt;/*"claim"nestingcount*/
330
331structdelayed_workdetect;
332intdetect_change;/*carddetectflag*/
333structmmc_slotslot;
334
335conststructmmc_bus_ops*bus_ops;/*currentbusdriver*/
336unsignedintbus_refs;/*referencecounter*/
337
338unsignedintsdio_irqs;
339structtask_struct*sdio_irq_thread;
340boolsdio_irq_pending;
341atomic_tsdio_irq_thread_abort;
342
343mmc_pm_flag_tpm_flags;/*requestedpmfeatures*/
344
345structled_trigger*led;/*activityled*/
346
347#ifdefCONFIG_REGULATOR
348boolregulator_enabled;/*regulatorstate*/
349#endif
350structmmc_supplysupply;
351
352structdentry*debugfs_root;
353
354structmmc_async_req*areq;/*activeasyncreq*/
355structmmc_context_infocontext_info;/*asyncsynchronizationinfo*/
356
357#ifdefCONFIG_FAIL_MMC_REQUEST
358structfault_attrfail_mmc_request;
359#endif
360
361unsignedintactual_clock;/*ActualHCclockrate*/
362
363unsignedintslotno;/*usedforsdioacpibinding*/
364
365unsignedlongprivate[0]____cacheline_aligned;
366};


1630行,调用platform_get_device_id宏,取得处理器类型,该宏定义在include/linux/platform_device.h文件中:

39#defineplatform_get_device_id(pdev)((pdev)->id_entry)


但是,回忆一下我们注册的platform_devices3c_device_sdi,我们并没有初始化platform_device.id_entry成员,那么这里的platform_get_device_id宏返回值是NULL吗?如果不是NULL,应该是什么值呢?

答案是platform_get_device_id(pdev)->driver_data返回值为1。

原因是s3cmci_driver.id_table被设置为s3cmci_driver_ids。s3cmci_driver_ids定义在drivers/mmc/host/s3cmci.c文件中:

1936staticstructplatform_device_ids3cmci_driver_ids[]={
1937{
1938.name="s3c2410-sdi",
1939.driver_data=0,
1940},{
1941.name="s3c2412-sdi",
1942.driver_data=1,
1943},{
1944.name="s3c2440-sdi",
1945.driver_data=1,
1946},
1947{}
1948};


structplatform_device_id定义在include/linux/mod_devicetable.h文件中:

482structplatform_device_id{
483charname[PLATFORM_NAME_SIZE];
484kernel_ulong_tdriver_data;
485};


platform_driver.id_table是platform_driver所支持的设备列表。可以看到,s3cmci_driver支持3种设备,分别是"s3c2410-sdi"、"s3c2412-sdi"和"s3c2440-sdi"。对于"s3c2410-sdi",其driver_data成员值为0,对于其它两种设备,它们的driver_data成员值为1。

当调用platform_driver_register函数注册s3cmci_driver时,s3cmci_driver.driver.bus被设置为platform_bus_type,structbus_typeplatform_bus_type定义在drivers/base/platform.c文件中:



895structbus_typeplatform_bus_type={
896.name="platform",
897.dev_attrs=platform_dev_attrs,
898.match=platform_match,
899.uevent=platform_uevent,
900.pm=&platform_dev_pm_ops,
901};


根据《Linux设备模型分析之device_driver(基于3.10.1内核)》一文对Linux设备模型的分析,在s3cmci_driver.probe函数被调用执行之前,platform_bus_type.match即platform_match首先会被调用执行。platform_match函数定义在drivers/base/platform.c文件中:

710/**
711*platform_match-bindplatformdevicetoplatformdriver.
712*@dev:device.
713*@drv:driver.
714*
715*PlatformdeviceIDsareassumedtobeencodedlikethis:
716*"<name><instance>",where<name>isashortdescriptionofthetypeof
717*device,like"pci"or"floppy",and<instance>istheenumerated
718*instanceofthedevice,like'0'or'42'.DriverIDsaresimply
719*"<name>".So,extractthe<name>fromtheplatform_devicestructure,
720*andcompareitagainstthenameofthedriver.Returnwhethertheymatch
721*ornot.
722*/
723staticintplatform_match(structdevice*dev,structdevice_driver*drv)
724{
725structplatform_device*pdev=to_platform_device(dev);
726structplatform_driver*pdrv=to_platform_driver(drv);
727
728/*AttemptanOFstylematchfirst*/
729if(of_driver_match_device(dev,drv))
730return1;
731
732/*ThentryACPIstylematch*/
733if(acpi_driver_match_device(dev,drv))
734return1;
735
736/*Thentrytomatchagainsttheidtable*/
737if(pdrv->id_table)
738returnplatform_match_id(pdrv->id_table,pdev)!=NULL;
739
740/*fall-backtodrivernamematch*/
741return(strcmp(pdev->name,drv->name)==0);
742}


737行,如果pdrv->id_table不为空,则调用platform_match_id函数。而我们的platform_drivers3cmci_driver.id_table被设置为s3cmci_driver_ids,所以platform_match_id函数会被执行。

platform_match_id函数定义在drivers/base/platform.c文件中:

696staticconststructplatform_device_id*platform_match_id(
697conststructplatform_device_id*id,
698structplatform_device*pdev)
699{
700while(id->name[0]){
701if(strcmp(pdev->name,id->name)==0){
702pdev->id_entry=id;
703returnid;
704}
705id++;
706}
707returnNULL;
708}


可以看到,在701行,如果platform_device.name与platform_device_id.name相同,则将id赋值给pdev->id_entry。

回到我们的platform_drivers3cmci_driver和platform_devices3c_device_sdi,s3c_device_sdi.name被初始化为"s3c2410-sdi",但是因为我们基于的平台是Mini2440,处理器是S3C2440,所以s3c244x_map_io函数会被调用,至于什么时候该函数会被调用我还没有搞清楚,呵呵!该函数定义在arch/arm/mach-s3c24xx/s3c244x.c文件中:

63void__inits3c244x_map_io(void)
64{
65/*registerourio-tables*/
66
67iotable_init(s3c244x_iodesc,ARRAY_SIZE(s3c244x_iodesc));
68
69/*renameanyperipheralsuseddifferingfromthes3c2410*/
70
71s3c_device_sdi.name="s3c2440-sdi";
72s3c_device_i2c0.name="s3c2440-i2c";
73s3c_nand_setname("s3c2440-nand");
74s3c_device_ts.name="s3c2440-ts";
75s3c_device_usbgadget.name="s3c2440-usbgadget";
76}


71行,将s3c_device_sdi.name设置为"s3c2440-sdi"

而s3cmci_driver.id_table被设置为s3cmci_driver_ids,s3cmci_driver_ids[2].name同样为"s3c2440-sdi",所以,虽然s3c_device_sdi.id_entry在初始化时没有赋值,但是在platform_match_id函数中,它会被赋值为s3cmci_driver_ids[2]。

现在我们可以回到s3cmci_probe函数了:

1630行,经过前面的分析,我们知道platform_get_device_id(pdev)->driver_data的值其实就是s3cmci_driver_ids[2].driver_data,其值为1。所以,对于Mini2440平台,is2440变量被赋值为1。

1632行,调用mmc_alloc_host函数为structmmc_host指针变量mmc分配内存空间并初始化。注意mmc_alloc_host的第一个参数表示除了mmc_host结构体外,还要额外多分配的内存空间大小,所以这里分配的内存大小是structmmc_host加上structs3cmci_host。

mmc_alloc_host函数定义在drivers/mmc/core/host.c文件中,其内容如下:

420/**
421*mmc_alloc_host-initialisetheper-hoststructure.
422*@extra:sizeofprivatedatastructure
423*@dev:pointertohostdevicemodelstructure
424*
425*Initialisetheper-hoststructure.
426*/
427structmmc_host*mmc_alloc_host(intextra,structdevice*dev)
428{
429interr;
430structmmc_host*host;
431
432host=kzalloc(sizeof(structmmc_host)+extra,GFP_KERNEL);
433if(!host)
434returnNULL;
435
436/*scanningwillbeenabledwhenwe'reready*/
437host->rescan_disable=1;
438idr_preload(GFP_KERNEL);
439spin_lock(&mmc_host_lock);
440err=idr_alloc(&mmc_host_idr,host,0,0,GFP_NOWAIT);
441if(err>=0)
442host->index=err;
443spin_unlock(&mmc_host_lock);
444idr_preload_end();
445if(err<0)
446gotofree;
447
448dev_set_name(&host->class_dev,"mmc%d",host->index);
449
450host->parent=dev;
451host->class_dev.parent=dev;
452host->class_dev.class=&mmc_host_class;
453device_initialize(&host->class_dev);
454
455mmc_host_clk_init(host);
456
457mutex_init(&host->slot.lock);
458host->slot.cd_irq=-EINVAL;
459
460spin_lock_init(&host->lock);
461init_waitqueue_head(&host->wq);
462INIT_DELAYED_WORK(&host->detect,mmc_rescan);
463#ifdefCONFIG_PM
464host->pm_notify.notifier_call=mmc_pm_notify;
465#endif
466
467/*
468*Bydefault,hostsdonotsupportSGIOorlargerequests.
469*Theyhavetosettheseaccordingtotheirabilities.
470*/
471host->max_segs=1;
472host->max_seg_size=PAGE_CACHE_SIZE;
473
474host->max_req_size=PAGE_CACHE_SIZE;
475host->max_blk_size=512;
476host->max_blk_count=PAGE_CACHE_SIZE/512;
477
478returnhost;
479
480free:
481kfree(host);
482returnNULL;
483}


回到s3cmci_probe函数:

1638-1648行,通过gpio_request函数申请获取GPE5-GPE10。从Mini2440原理图可以看出,Mini2440SDI使用的GPE7-GPE10作为4根数据信号线,使用GPE6作为命令信号线,使用GPE5作为时钟信号线。另外,使用GPG8的外部中断功能来作SD卡的插拨检测,使用GPH8来判断SD卡是否有写保护。

1650行,通过调用mmc_priv(mmc)取得s3cmci_host指针变量host。

下面的内容就是初始化host的各个成员变量。

1681行,调用platform_get_resource(pdev,IORESOURCE_MEM,0)取得IORESOURCE_MEM类型资源。IORESOURCE_MEM宏定义在include/linux/ioport.h文件中:

32#defineIORESOURCE_IO0x00000100/*PCI/ISAI/Oports*/
33#defineIORESOURCE_MEM0x00000200
34#defineIORESOURCE_REG0x00000300/*Registeroffsets*/
35#defineIORESOURCE_IRQ0x00000400
36#defineIORESOURCE_DMA0x00000800
37#defineIORESOURCE_BUS0x00001000


platform_get_resource函数定义在drivers/base/platform.c文件中:

59/**
60*platform_get_resource-getaresourceforadevice
61*@dev:platformdevice
62*@type:resourcetype
63*@num:resourceindex
64*/
65structresource*platform_get_resource(structplatform_device*dev,
66unsignedinttype,unsignedintnum)
67{
68inti;
69
70for(i=0;i<dev->num_resources;i++){
71structresource*r=&dev->resource[i];
72
73if(type==resource_type(r)&&num--==0)
74returnr;
75}
76returnNULL;
77}


resource_type定义在include/linux/ioport.h文件中:

168staticinlineunsignedlongresource_type(conststructresource*res)
169{
170returnres->flags&IORESOURCE_TYPE_BITS;
171}


回忆一下,Mini2440的资源文件s3c_sdi_resource定义在arch/arm/plat-samsung/devs.c文件中:

1167staticstructresources3c_sdi_resource[]={
1168[0]=DEFINE_RES_MEM(S3C24XX_PA_SDI,S3C24XX_SZ_SDI),
1169[1]=DEFINE_RES_IRQ(IRQ_SDI),
1170};


宏S3C24XX_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

155#defineS3C24XX_PA_SDIS3C2410_PA_SDI


宏S3C2410_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

105#defineS3C2410_PA_SDI(0x5A000000)


0x5A000000是S3C2440SDICON寄存器的地址。

宏S3C24XX_SZ_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

61#defineS3C24XX_SZ_SDISZ_1M


宏SZ_1M定义在include/linux/sizes.h文件中:

33#defineSZ_1M0x00100000


所以s3cmci_probe函数1681行,platform_get_resource(pdev,IORESOURCE_MEM,0)函数返回的就是s3c_sdi_resource[0]。

1690-1691行,调用request_mem_region(host->mem->start,resource_size(host->mem),pdev->name)函数,该函数用于获取参数指定的内存空间。request_mem_region函数定义在include/linux/ioport.h文件中:

177#definerequest_mem_region(start,n,name)__request_region(&iomem_resource,(start),(n),(name),0)

__request_region定义在kernel/resource.c文件中:

931/**
932*__request_region-createanewbusyresourceregion
933*@parent:parentresourcedescriptor
934*@start:resourcestartaddress
935*@n:resourceregionsize
936*@name:reservingcaller'sIDstring
937*@flags:IOresourceflags
938*/
939structresource*__request_region(structresource*parent,
940resource_size_tstart,resource_size_tn,
941constchar*name,intflags)
942{
943DECLARE_WAITQUEUE(wait,current);
944structresource*res=alloc_resource(GFP_KERNEL);
945
946if(!res)
947returnNULL;
948
949res->name=name;
950res->start=start;
951res->end=start+n-1;
952res->flags=IORESOURCE_BUSY;
953res->flags|=flags;
954
955write_lock(&resource_lock);
956
957for(;;){
958structresource*conflict;
959
960conflict=__request_resource(parent,res);
961if(!conflict)
962break;
963if(conflict!=parent){
964parent=conflict;
965if(!(conflict->flags&IORESOURCE_BUSY))
966continue;
967}
968if(conflict->flags&flags&IORESOURCE_MUXED){
969add_wait_queue(&muxed_resource_wait,&wait);
970write_unlock(&resource_lock);
971set_current_state(TASK_UNINTERRUPTIBLE);
972schedule();
973remove_wait_queue(&muxed_resource_wait,&wait);
974write_lock(&resource_lock);
975continue;
976}
977/*Uhhuh,thatdidn'tworkout..*/
978free_resource(res);
979res=NULL;
980break;
981}
982write_unlock(&resource_lock);
983returnres;
984}


1699行,调用ioremap(host->mem->start,resource_size(host->mem))宏,该宏完成物理内存到虚拟内存的映射。ioremap宏定义在arch/arm/include/asm/io.h文件中:

328#defineioremap(cookie,size)__arm_ioremap((cookie),(size),MT_DEVICE)


__arm_ioremap函数定义在arch/arm/mm/ioremap.c文件中:

374void__iomem*
375__arm_ioremap(unsignedlongphys_addr,size_tsize,unsignedintmtype)
376{
377returnarch_ioremap_caller(phys_addr,size,mtype,
378__builtin_return_address(0));
379}


1706行,调用platform_get_irq函数获取设备中断。platform_get_irq函数定义在drivers/base/platform.c文件中:



80/**
81*platform_get_irq-getanIRQforadevice
82*@dev:platformdevice
83*@num:IRQnumberindex
84*/
85intplatform_get_irq(structplatform_device*dev,unsignedintnum)
86{
87#ifdefCONFIG_SPARC
88/*sparcdoesnothaveirqsrepresentedasIORESOURCE_IRQresources*/
89if(!dev||num>=dev->archdata.num_irqs)
90return-ENXIO;
91returndev->archdata.irqs[num];
92#else
93structresource*r=platform_get_resource(dev,IORESOURCE_IRQ,num);
94
95returnr?r->start:-ENXIO;
96#endif
97}


1713行,调用request_irq(host->irq,s3cmci_irq,0,DRIVER_NAME,host)申请中断,中断处理函数是s3cmci_irq。

1723行,调用disable_irq禁用中断。

1726-1751行,处理SD卡探测相关内容。

1753-1761行,处理SD卡写保护相关内容。

1765-1778行,处理DMA相关内容。

1780-1794行,处理时钟相关内容。

1796-1814行,初始化mmc。

需要注意的是1796行,设置mmc->ops为s3cmci_ops,s3cmci_ops定义在drivers/mmc/host/s3cmci.c文件中:

1427staticstructmmc_host_opss3cmci_ops={
1428.request=s3cmci_request,
1429.set_ios=s3cmci_set_ios,
1430.get_ro=s3cmci_get_ro,
1431.get_cd=s3cmci_card_present,
1432.enable_sdio_irq=s3cmci_enable_sdio_irq,
1433};


1821行,调用s3cmci_cpufreq_register(host),提供对变频的支持。

1827行,调用mmc_add_host(mmc),向core层注册mmc_host。

1833行,调用s3cmci_debugfs_attach(host)创建debugfs相关节点。

至此,s3cmci_probe函数我们就分析完了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: