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

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

内核版本:3.10.1

 

MMC

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

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

 

SD

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

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

 

SDIO

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

 

eMMC

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

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

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

 

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

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


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

 

一、SDI设备的注册

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

505static struct platform_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所有的platform device,这里,我们要关注的是s3c_device_sdi,它是Mini2440的SDI控制器。

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

1172struct platform_device s3c_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 {
 23   const char  *name;
 24   int     id;
 25   bool        id_auto;
 26   struct device   dev;
 27   u32     num_resources;
 28   struct resource *resource;
 29
 30   const struct platform_device_id *id_entry;
 31
 32    /*MFD cell pointer */
 33   struct mfd_cell *mfd_cell;
 34
 35    /*arch specific additions */
 36   struct pdev_archdata    archdata;
 37};


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

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


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

 14/*
 15 *Resources are tree-like, allowing
 16 *nesting etc..
 17*/
 18structresource {
 19   resource_size_t start;
 20   resource_size_t end;
 21   const char *name;
 22   unsigned long flags;
 23   struct resource *parent, *sibling, *child;
 24};


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

124#define DEFINE_RES_MEM(_start,_size)                   \
125   DEFINE_RES_MEM_NAMED((_start), (_size), NULL)
……
122#define DEFINE_RES_MEM_NAMED(_start,_size, _name)          \
123   DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM)
……
109#define DEFINE_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#define DEFINE_RES_IRQ(_irq)                        \
130   DEFINE_RES_IRQ_NAMED((_irq), NULL)
……
127#define DEFINE_RES_IRQ_NAMED(_irq,_name)               \
128   DEFINE_RES_NAMED((_irq), 1, (_name), IORESOURCE_IRQ)
……
109#define DEFINE_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#define S3C24XX_PA_SDI      S3C2410_PA_SDI


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

105#define S3C2410_PA_SDI     (0x5A000000)


0x5A000000是S3C2440 SDICON寄存器的地址。

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

61#define S3C24XX_SZ_SDI      SZ_1M


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

33#define SZ_1M               0x00100000


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

48#define IRQ_SDI        S3C2410_IRQ(21)
……
23#define S3C2410_IRQ(x) ((x) +S3C2410_CPUIRQ_OFFSET)
……
21#define S3C2410_CPUIRQ_OFFSET    (16)


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

622static void __init mini2440_init(void)
{
          ……
678  platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));
          ……
}


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

139/**
 140* platform_add_devices - add a numbers of platform devices
 141* @devs: array of platform devices to add
 142* @num: number of platform devices in array
 143*/
 144int platform_add_devices(structplatform_device **devs, int num)
 145{
 146   int i, ret = 0;
 147
 148   for (i = 0; i < num; i++) {
 149       ret = platform_device_register(devs[i]);
 150       if (ret) {
 151           while (--i >= 0)
 152               platform_device_unregister(devs[i]);
 153           break;
 154       }
 155    }
 156
 157   return ret;
 158}


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

 

二、SDI驱动分析

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

1980static struct platform_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文件中:

1936static struct platform_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_driver s3cmci_driver 的注册,在drivers/mmc/host/s3cmci.c文件中:

1992module_platform_driver(s3cmci_driver);


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

203/* module_platform_driver() - Helpermacro for drivers that don't do
204 * anything special in moduleinit/exit.  This eliminates a lot of
205 * boilerplate.  Each module may only use this macro once, and
206 * calling it replaces module_init() andmodule_exit()
207 */
208#definemodule_platform_driver(__platform_driver) \
209   module_driver(__platform_driver, platform_driver_register, \
210            platform_driver_unregister)


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

1108/**
1109 * module_driver() - Helper macro fordrivers that don't do anything
1110 * special in module init/exit. Thiseliminates a lot of boilerplate.
1111 * Each module may only use this macroonce, and calling it replaces
1112 * module_init() and module_exit().
1113 *
1114 * @__driver: driver name
1115 * @__register: register function forthis driver type
1116 * @__unregister: unregister functionfor this driver type
1117 * @...: Additional arguments to bepassed to __register and __unregister.
1118 *
1119 * Use this macro to construct busspecific macros for registering
1120 * drivers, and do not use it on itsown.
1121 */
1122#define module_driver(__driver,__register, __unregister, ...) \
1123static int __init __driver##_init(void)\
1124{ \
1125   return __register(&(__driver) , ##__VA_ARGS__); \
1126} \
1127module_init(__driver##_init); \
1128static void __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文件中,其内容如下:

1622static int s3cmci_probe(structplatform_device *pdev)
1623{
1624   struct s3cmci_host *host;
1625   struct mmc_host *mmc;
1626   int ret;
1627   int is2440;
1628   int i;
1629
1630   is2440 = platform_get_device_id(pdev)->driver_data;
1631
1632   mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
1633   if (!mmc) {
1634       ret = -ENOMEM;
1635       goto probe_out;
1636   }
1637
1638   for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {
1639       ret = gpio_request(i, dev_name(&pdev->dev));
1640       if (ret) {
1641            dev_err(&pdev->dev,"failed to get gpio %d\n", i);
1642
1643            for (i--; i >= S3C2410_GPE(5);i--)
1644                gpio_free(i);
1645
1646            goto probe_free_host;
1647       }
1648   }
1649
1650   host = mmc_priv(mmc);
1651   host->mmc   = mmc;
1652   host->pdev  = pdev;
1653   host->is2440    = is2440;
1654
1655   host->pdata = pdev->dev.platform_data;
1656   if (!host->pdata) {
1657       pdev->dev.platform_data = &s3cmci_def_pdata;
1658       host->pdata = &s3cmci_def_pdata;
1659   }
1660
1661   spin_lock_init(&host->complete_lock);
1662   tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long)host);
1663
1664   if (is2440) {
1665       host->sdiimsk   =S3C2440_SDIIMSK;
1666       host->sdidata   =S3C2440_SDIDATA;
1667       host->clk_div   = 1;
1668   } else {
1669       host->sdiimsk   =S3C2410_SDIIMSK;
1670       host->sdidata   =S3C2410_SDIDATA;
1671       host->clk_div   = 2;
1672   }
1673
1674   host->complete_what     =COMPLETION_NONE;
1675   host->pio_active    =XFER_NONE;
1676
1677#ifdef CONFIG_MMC_S3C_PIODMA
1678   host->dodma     =host->pdata->use_dma;
1679#endif
1680
1681   host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1682   if (!host->mem) {
1683       dev_err(&pdev->dev,
1684           "failed to get io memoryregion resource.\n");
1685
1686       ret = -ENOENT;
1687       goto probe_free_gpio;
1688   }
1689
1690   host->mem = request_mem_region(host->mem->start,
1691                      resource_size(host->mem), pdev->name);
1692
1693   if (!host->mem) {
1694       dev_err(&pdev->dev, "failed to request io memoryregion.\n");
1695       ret = -ENOENT;
1696       goto probe_free_gpio;
1697   }
1698
1699   host->base = ioremap(host->mem->start,resource_size(host->mem));
1700   if (!host->base) {
1701       dev_err(&pdev->dev, "failed to ioremap() io memoryregion.\n");
1702       ret = -EINVAL;
1703       goto probe_free_mem_region;
1704   }
1705
1706   host->irq = platform_get_irq(pdev, 0);
1707   if (host->irq == 0) {
1708       dev_err(&pdev->dev, "failed to get interruptresource.\n");
1709       ret = -EINVAL;
1710       goto probe_iounmap;
1711   }
1712
1713   if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
1714       dev_err(&pdev->dev, "failed to request mciinterrupt.\n");
1715       ret = -ENOENT;
1716       goto probe_iounmap;
1717   }
1718
1719   /* We get spurious interrupts even when we have set the IMSK
1720    * register to ignore everything, so use disable_irq() to make
1721    * ensure we don't lock the system with un-serviceable requests. */
1722
1723   disable_irq(host->irq);
1724   host->irq_state = false;
1725
1726   if (!host->pdata->no_detect) {
1727       ret = gpio_request(host->pdata->gpio_detect, "s3cmcidetect");
1728       if (ret) {
1729            dev_err(&pdev->dev,"failed to get detect gpio\n");
1730            goto probe_free_irq;
1731       }
1732
1733       host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);
1734
1735       if (host->irq_cd >= 0) {
1736            if (request_irq(host->irq_cd,s3cmci_irq_cd,
1737                    IRQF_TRIGGER_RISING |
1738                    IRQF_TRIGGER_FALLING,
1739                    DRIVER_NAME, host)) {
1740                dev_err(&pdev->dev,
1741                    "can't get card detectirq.\n");
1742                ret = -ENOENT;
1743                goto probe_free_gpio_cd;
1744            }
1745       } else {
1746            dev_warn(&pdev->dev,
1747                 "host detect has no irqavailable\n");
1748           gpio_direction_input(host->pdata->gpio_detect);
1749       }
1750   } else
1751       host->irq_cd = -1;
1752
1753   if (!host->pdata->no_wprotect) {
1754       ret = gpio_request(host->pdata->gpio_wprotect, "s3cmciwp");
1755       if (ret) {
1756            dev_err(&pdev->dev,"failed to get writeprotect\n");
1757            goto probe_free_irq_cd;
1758       }
1759
1760       gpio_direction_input(host->pdata->gpio_wprotect);
1761   }
1762
1763   /* depending on the dma state, get a dma channel to use. */
1764
1765   if (s3cmci_host_usedma(host)) {
1766       host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,
1767                        host);
1768       if (host->dma < 0) {
1769            dev_err(&pdev->dev,"cannot get DMA channel.\n");
1770            if (!s3cmci_host_canpio()) {
1771                ret = -EBUSY;
1772                goto probe_free_gpio_wp;
1773            } else {
1774                dev_warn(&pdev->dev,"falling back to PIO.\n");
1775                host->dodma = 0;
1776            }
1777       }
1778   }
1779
1780   host->clk = clk_get(&pdev->dev, "sdi");
1781   if (IS_ERR(host->clk)) {
1782       dev_err(&pdev->dev, "failed to find clock source.\n");
1783       ret = PTR_ERR(host->clk);
1784       host->clk = NULL;
1785       goto probe_free_dma;
1786   }
1787
1788   ret = clk_enable(host->clk);
1789   if (ret) {
1790       dev_err(&pdev->dev, "failed to enable clocksource.\n");
1791       goto clk_free;
1792   }
1793
1794   host->clk_rate = clk_get_rate(host->clk);
1795
1796   mmc->ops    = &s3cmci_ops;
1797   mmc->ocr_avail  = MMC_VDD_32_33| MMC_VDD_33_34;
1798#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
1799   mmc->caps   =MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
1800#else
1801   mmc->caps   =MMC_CAP_4_BIT_DATA;
1802#endif
1803   mmc->f_min  = host->clk_rate/ (host->clk_div * 256);
1804   mmc->f_max  = host->clk_rate/ host->clk_div;
1805
1806   if (host->pdata->ocr_avail)
1807       mmc->ocr_avail = host->pdata->ocr_avail;
1808
1809   mmc->max_blk_count  = 4095;
1810   mmc->max_blk_size   = 4095;
1811   mmc->max_req_size   = 4095 *512;
1812   mmc->max_seg_size   =mmc->max_req_size;
1813
1814   mmc->max_segs       = 128;
1815
1816   dbg(host, dbg_debug,
1817       "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%udma:%u.\n",
1818       (host->is2440?"2440":""),
1819       host->base, host->irq, host->irq_cd, host->dma);
1820
1821   ret = s3cmci_cpufreq_register(host);
1822   if (ret) {
1823       dev_err(&pdev->dev, "failed to register cpufreq\n");
1824       goto free_dmabuf;
1825   }
1826
1827   ret = mmc_add_host(mmc);
1828   if (ret) {
1829       dev_err(&pdev->dev, "failed to add mmc host.\n");
1830       goto free_cpufreq;
1831   }
1832
1833   s3cmci_debugfs_attach(host);
1834
1835   platform_set_drvdata(pdev, mmc);
1836   dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n",mmc_hostname(mmc),
1837        s3cmci_host_usedma(host) ? "dma" : "pio",
1838        mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");
1839
1840   return 0;
1841
1842 free_cpufreq:
1843   s3cmci_cpufreq_deregister(host);
1844
1845 free_dmabuf:
1846   clk_disable(host->clk);
1847
1848 clk_free:
1849   clk_put(host->clk);
1850
1851 probe_free_dma:
1852   if (s3cmci_host_usedma(host))
1853       s3c2410_dma_free(host->dma, &s3cmci_dma_client);
1854
1855 probe_free_gpio_wp:
1856   if (!host->pdata->no_wprotect)
1857       gpio_free(host->pdata->gpio_wprotect);
1858
1859 probe_free_gpio_cd:
1860   if (!host->pdata->no_detect)
1861       gpio_free(host->pdata->gpio_detect);
1862
1863 probe_free_irq_cd:
1864   if (host->irq_cd >= 0)
1865       free_irq(host->irq_cd, host);
1866
1867 probe_free_irq:
1868   free_irq(host->irq, host);
1869
1870 probe_iounmap:
1871   iounmap(host->base);
1872
1873 probe_free_mem_region:
1874   release_mem_region(host->mem->start, resource_size(host->mem));
1875
1876 probe_free_gpio:
1877   for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)
1878       gpio_free(i);
1879
1880 probe_free_host:
1881   mmc_free_host(mmc);
1882
1883 probe_out:
1884   return ret;
1885}


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

20struct s3cmci_host {
21   struct platform_device  *pdev;
22   struct s3c24xx_mci_pdata *pdata;
23   struct mmc_host     *mmc;
24   struct resource     *mem;
25   struct clk      *clk;
26   void __iomem        *base;
27   int         irq;
28   int         irq_cd;
29   int         dma;
30
31   unsigned long       clk_rate;
32   unsigned long       clk_div;
33   unsigned long       real_rate;
34   u8          prescaler;
35
36   int         is2440;
37   unsigned        sdiimsk;
38   unsigned        sdidata;
39   int         dodma;
40   int         dmatogo;
41
42   bool            irq_disabled;
43   bool            irq_enabled;
44   bool            irq_state;
45   int         sdio_irqen;
46
47   struct mmc_request  *mrq;
48   int         cmd_is_stop;
49
50   spinlock_t      complete_lock;
51   enum s3cmci_waitfor complete_what;
52
53   int         dma_complete;
54
55   u32         pio_sgptr;
56   u32         pio_bytes;
57   u32         pio_count;
58   u32         *pio_ptr;
59#define XFER_NONE 0
60#define XFER_READ 1
61#define XFER_WRITE 2
62   u32         pio_active;
63
64   int         bus_width;
65
66   char            dbgmsg_cmd[301];
67   char            dbgmsg_dat[301];
68   char            *status;
69
70   unsigned int        ccnt, dcnt;
71   struct tasklet_struct  pio_tasklet;
72
73#ifdef CONFIG_DEBUG_FS
74   struct dentry       *debug_root;
75   struct dentry       *debug_state;
76   struct dentry       *debug_regs;
77#endif
78
79#ifdef CONFIG_CPU_FREQ
80   struct notifier_block  freq_transition;
81#endif
82};


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

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

198struct mmc_host {
199   struct device       *parent;
200   struct device       class_dev;
201   int         index;
202   const struct mmc_host_ops *ops;
203   unsigned int        f_min;
204   unsigned int        f_max;
205   unsigned int        f_init;
206   u32         ocr_avail;
207   u32         ocr_avail_sdio; /*SDIO-specific OCR */
208   u32         ocr_avail_sd;   /* SD-specific OCR */
209   u32         ocr_avail_mmc;  /* MMC-specific OCR */
210   struct notifier_block   pm_notify;
211   u32         max_current_330;
212   u32         max_current_300;
213   u32         max_current_180;
214
215#define MMC_VDD_165_195     0x00000080 /* VDD voltage 1.65 - 1.95 */
216#define MMC_VDD_20_21       0x00000100  /* VDD voltage 2.0 ~ 2.1 */
217#define MMC_VDD_21_22       0x00000200  /* VDD voltage 2.1 ~ 2.2 */
218#define MMC_VDD_22_23       0x00000400  /* VDD voltage 2.2 ~ 2.3 */
219#define MMC_VDD_23_24       0x00000800  /* VDD voltage 2.3 ~ 2.4 */
220#define MMC_VDD_24_25       0x00001000  /* VDD voltage 2.4 ~ 2.5 */
221#define MMC_VDD_25_26       0x00002000  /* VDD voltage 2.5 ~ 2.6 */
222#define MMC_VDD_26_27       0x00004000  /* VDD voltage 2.6 ~ 2.7 */
223#define MMC_VDD_27_28       0x00008000  /* VDD voltage 2.7 ~ 2.8 */
224#define MMC_VDD_28_29       0x00010000  /* VDD voltage 2.8 ~ 2.9 */
225#define MMC_VDD_29_30       0x00020000  /* VDD voltage 2.9 ~ 3.0 */
226#define MMC_VDD_30_31       0x00040000  /* VDD voltage 3.0 ~ 3.1 */
227#define MMC_VDD_31_32       0x00080000  /* VDD voltage 3.1 ~ 3.2 */
228#define MMC_VDD_32_33       0x00100000  /* VDD voltage 3.2 ~ 3.3 */
229#define MMC_VDD_33_34       0x00200000  /* VDD voltage 3.3 ~ 3.4 */
230#define MMC_VDD_34_35       0x00400000  /* VDD voltage 3.4 ~ 3.5 */
231#define MMC_VDD_35_36       0x00800000  /* VDD voltage 3.5 ~ 3.6 */
232
233   u32         caps;       /* Host capabilities */
234
235#define MMC_CAP_4_BIT_DATA  (1 << 0)    /* Can the host do 4 bit transfers */
236#define MMC_CAP_MMC_HIGHSPEED   (1 << 1)    /* Can do MMC high-speed timing */
237#define MMC_CAP_SD_HIGHSPEED    (1 << 2)    /*Can do SD high-speed timing */
238#define MMC_CAP_SDIO_IRQ    (1 << 3)    /* Can signal pending SDIO IRQs */
239#define MMC_CAP_SPI     (1 << 4)    /* Talks only SPI protocols */
240#define MMC_CAP_NEEDS_POLL  (1 << 5)    /* Needs polling for card-detection */
241#define MMC_CAP_8_BIT_DATA  (1 << 6)    /* Can the host do 8 bit transfers */
242
243#define MMC_CAP_NONREMOVABLE    (1 << 8)    /* Nonremovable e.g. eMMC */
244#define MMC_CAP_WAIT_WHILE_BUSY (1<< 9)    /* Waits while card isbusy */
245#define MMC_CAP_ERASE       (1 << 10)   /* Allow erase/trim commands */
246#define MMC_CAP_1_8V_DDR    (1 << 11)   /* can support */
247                        /* DDR mode at 1.8V */
248#define MMC_CAP_1_2V_DDR    (1 << 12)   /* can support */
249                        /* DDR mode at 1.2V */
250#define MMC_CAP_POWER_OFF_CARD  (1 << 13)   /* Can power off after boot */
251#define MMC_CAP_BUS_WIDTH_TEST  (1 << 14)   /* CMD14/CMD19 bus width ok */
252#define MMC_CAP_UHS_SDR12   (1 << 15)   /* Host supports UHS SDR12 mode */
253#define MMC_CAP_UHS_SDR25   (1 << 16)   /* Host supports UHS SDR25 mode */
254#define MMC_CAP_UHS_SDR50   (1 << 17)   /* Host supports UHS SDR50 mode */
255#define MMC_CAP_UHS_SDR104  (1 << 18)   /* Host supports UHS SDR104 mode */
256#define MMC_CAP_UHS_DDR50   (1 << 19)   /* Host supports UHS DDR50 mode */
257#define MMC_CAP_DRIVER_TYPE_A   (1 << 23)   /* Host supports Driver Type A */
258#define MMC_CAP_DRIVER_TYPE_C   (1 << 24)   /* Host supports Driver Type C */
259#define MMC_CAP_DRIVER_TYPE_D   (1 << 25)   /* Host supports Driver Type D */
260#define MMC_CAP_CMD23       (1 << 30)   /* CMD23 supported. */
261#define MMC_CAP_HW_RESET    (1 << 31)   /* Hardware reset */
262
263   u32         caps2;      /* More host capabilities */
264
265#define MMC_CAP2_BOOTPART_NOACC (1<< 0)    /* Boot partition noaccess */
266#define MMC_CAP2_CACHE_CTRL (1 <<1)    /* Allow cache control */
267#define MMC_CAP2_POWEROFF_NOTIFY (1<< 2)   /* Notify poweroffsupported */
268#define MMC_CAP2_NO_MULTI_READ  (1 << 3)    /* Multiblock reads don't work */
269#define MMC_CAP2_NO_SLEEP_CMD   (1 << 4)    /* Don't allow sleep command */
270#define MMC_CAP2_HS200_1_8V_SDR (1<< 5)        /* can support */
271#define MMC_CAP2_HS200_1_2V_SDR (1<< 6)        /* can support */
272#define MMC_CAP2_HS200      (MMC_CAP2_HS200_1_8V_SDR | \
273                 MMC_CAP2_HS200_1_2V_SDR)
274#define MMC_CAP2_BROKEN_VOLTAGE (1<< 7)    /* Use the broken voltage*/
275#define MMC_CAP2_DETECT_ON_ERR  (1 << 8)    /* On I/O err check card removal */
276#define MMC_CAP2_HC_ERASE_SZ    (1 << 9)    /* High-capacity erase size */
277#define MMC_CAP2_CD_ACTIVE_HIGH (1<< 10)   /* Card-detect signalactive high */
278#define MMC_CAP2_RO_ACTIVE_HIGH (1<< 11)   /* Write-protect signalactive high */
279#define MMC_CAP2_PACKED_RD  (1 << 12)   /* Allow packed read */
280#define MMC_CAP2_PACKED_WR  (1 << 13)   /* Allow packed write */
281#define MMC_CAP2_PACKED_CMD(MMC_CAP2_PACKED_RD | \
282                 MMC_CAP2_PACKED_WR)
283#define MMC_CAP2_NO_PRESCAN_POWERUP (1<< 14)   /* Don't power up beforescan */
284
285   mmc_pm_flag_t       pm_caps;    /* supported pm features */
286
287#ifdef CONFIG_MMC_CLKGATE
288   int         clk_requests;   /* internal reference counter */
289   unsigned int       clk_delay;  /* number of MCI clkhold cycles */
290   bool            clk_gated;  /* clock gated */
291   struct delayed_work clk_gate_work; /* delayed clock gate */
292   unsigned int        clk_old;    /* old clock value cache */
293   spinlock_t      clk_lock;   /* lock for clk fields */
294   struct mutex       clk_gate_mutex; /* mutex for clock gating */
295   struct device_attribute clkgate_delay_attr;
296   unsigned long          clkgate_delay;
297#endif
298
299   /* host specific block data */
300   unsigned int       max_seg_size;   /* seeblk_queue_max_segment_size */
301   unsigned short      max_segs;   /* see blk_queue_max_segments */
302   unsigned short      unused;
303   unsigned int        max_req_size;   /* maximum number of bytes in one req */
304   unsigned int       max_blk_size;   /* maximum size ofone mmc block */
305   unsigned int       max_blk_count;  /* maximum numberof blocks in one req */
306   unsigned int       max_discard_to; /* max. discard timeout in ms */
307
308   /* private data */
309   spinlock_t      lock;       /* lock for claim and bus ops */
310
311   struct mmc_ios      ios;        /* current io bus settings */
312   u32         ocr;        /* the current OCR setting */
313
314   /* group bitfields together to minimize padding */
315   unsigned int        use_spi_crc:1;
316   unsigned int       claimed:1;  /* host exclusivelyclaimed */
317   unsigned int        bus_dead:1; /* bus has been released */
318#ifdef CONFIG_MMC_DEBUG
319   unsigned int       removed:1;  /* host is beingremoved */
320#endif
321
322   int         rescan_disable; /*disable card detection */
323   int         rescan_entered; /* usedwith nonremovable devices */
324
325   struct mmc_card     *card;      /* device attached to this host */
326
327   wait_queue_head_t   wq;
328   struct task_struct  *claimer;   /* task that has host claimed */
329   int         claim_cnt;  /* "claim" nesting count */
330
331   struct delayed_work detect;
332   int         detect_change;  /* card detect flag */
333   struct mmc_slot     slot;
334
335   const struct mmc_bus_ops *bus_ops; /* current bus driver */
336   unsigned int        bus_refs;   /* reference counter */
337
338   unsigned int        sdio_irqs;
339   struct task_struct *sdio_irq_thread;
340   bool            sdio_irq_pending;
341   atomic_t       sdio_irq_thread_abort;
342
343   mmc_pm_flag_t       pm_flags;   /* requested pm features */
344
345   struct led_trigger  *led;       /* activity led */
346
347#ifdef CONFIG_REGULATOR
348   bool            regulator_enabled;/* regulator state */
349#endif
350   struct mmc_supply   supply;
351
352   struct dentry       *debugfs_root;
353
354   struct mmc_async_req   *areq;      /* active async req */
355   struct mmc_context_info context_info;  /* async synchronization info */
356
357#ifdef CONFIG_FAIL_MMC_REQUEST
358   struct fault_attr  fail_mmc_request;
359#endif
360
361   unsigned int       actual_clock;   /* Actual HC clockrate */
362
363   unsigned int        slotno; /*used for sdio acpi binding */
364
365   unsigned long       private[0]____cacheline_aligned;
366};


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

39#define platform_get_device_id(pdev)    ((pdev)->id_entry)


但是,回忆一下我们注册的platform_device s3c_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文件中:

1936static struct platform_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};


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

482struct platform_device_id {
483   char name[PLATFORM_NAME_SIZE];
484   kernel_ulong_t driver_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_type platform_bus_type定义在drivers/base/platform.c文件中:

 

895structbus_type platform_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 - bind platform device to platform driver.
 712* @dev: device.
 713* @drv: driver.
 714*
 715* Platform device IDs are assumed to be encoded like this:
 716* "<name><instance>", where <name> is a shortdescription of the type of
 717* device, like "pci" or "floppy", and <instance> isthe enumerated
 718* instance of the device, like '0' or '42'. Driver IDs are simply
 719* "<name>".  So, extractthe <name> from the platform_device structure,
 720* and compare it against the name of the driver. Return whether they match
 721* or not.
 722*/
 723static int platform_match(struct device*dev, struct device_driver *drv)
 724{
 725   struct platform_device *pdev = to_platform_device(dev);
 726   struct platform_driver *pdrv = to_platform_driver(drv);
 727
 728   /* Attempt an OF style match first */
 729   if (of_driver_match_device(dev, drv))
 730       return 1;
 731
 732   /* Then try ACPI style match */
 733   if (acpi_driver_match_device(dev, drv))
 734       return 1;
 735
 736   /* Then try to match against the id table */
 737   if (pdrv->id_table)
 738       return platform_match_id(pdrv->id_table, pdev) != NULL;
 739
 740   /* fall-back to driver name match */
 741   return (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文件中:

 696staticconst struct platform_device_id *platform_match_id(
 697           const struct platform_device_id *id,
 698           struct platform_device *pdev)
 699{
 700   while (id->name[0]) {
 701        if (strcmp(pdev->name, id->name)== 0) {
 702           pdev->id_entry = id;
 703           return id;
 704       }
 705       id++;
 706    }
 707   return NULL;
 708}


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

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

 63void__init s3c244x_map_io(void)
 64{
 65    /* register our io-tables */
 66
 67   iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));
 68
 69    /*rename any peripherals used differing from the s3c2410 */
 70
 71   s3c_device_sdi.name  ="s3c2440-sdi";
 72   s3c_device_i2c0.name  ="s3c2440-i2c";
 73   s3c_nand_setname("s3c2440-nand");
 74   s3c_device_ts.name = "s3c2440-ts";
 75   s3c_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函数为struct mmc_host指针变量mmc分配内存空间并初始化。注意mmc_alloc_host的第一个参数表示除了mmc_host结构体外,还要额外多分配的内存空间大小,所以这里分配的内存大小是struct mmc_host加上struct s3cmci_host。

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

420/**
421 * mmc_alloc_host - initialise the per-host structure.
422 * @extra: sizeof private data structure
423 * @dev: pointer to host device model structure
424 *
425 * Initialise the per-host structure.
426 */
427struct mmc_host *mmc_alloc_host(intextra, struct device *dev)
428{
429   int err;
430   struct mmc_host *host;
431
432   host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
433   if (!host)
434       return NULL;
435
436   /* scanning will be enabled when we're ready */
437   host->rescan_disable = 1;
438   idr_preload(GFP_KERNEL);
439   spin_lock(&mmc_host_lock);
440   err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
441   if (err >= 0)
442       host->index = err;
443   spin_unlock(&mmc_host_lock);
444   idr_preload_end();
445   if (err < 0)
446       goto free;
447
448   dev_set_name(&host->class_dev, "mmc%d",host->index);
449
450   host->parent = dev;
451   host->class_dev.parent = dev;
452   host->class_dev.class = &mmc_host_class;
453   device_initialize(&host->class_dev);
454
455   mmc_host_clk_init(host);
456
457   mutex_init(&host->slot.lock);
458   host->slot.cd_irq = -EINVAL;
459
460   spin_lock_init(&host->lock);
461   init_waitqueue_head(&host->wq);
462   INIT_DELAYED_WORK(&host->detect, mmc_rescan);
463#ifdef CONFIG_PM
464   host->pm_notify.notifier_call = mmc_pm_notify;
465#endif
466
467   /*
468    * By default, hosts do not support SGIO or large requests.
469    * They have to set these according to their abilities.
470    */
471   host->max_segs = 1;
472   host->max_seg_size = PAGE_CACHE_SIZE;
473
474   host->max_req_size = PAGE_CACHE_SIZE;
475   host->max_blk_size = 512;
476   host->max_blk_count = PAGE_CACHE_SIZE / 512;
477
478   return host;
479
480free:
481   kfree(host);
482   return NULL;
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#define IORESOURCE_IO       0x00000100  /* PCI/ISA I/O ports */
33#define IORESOURCE_MEM      0x00000200
34#define IORESOURCE_REG      0x00000300  /* Register offsets */
35#define IORESOURCE_IRQ      0x00000400
36#define IORESOURCE_DMA      0x00000800
37#define IORESOURCE_BUS      0x00001000


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

  59/**
  60* platform_get_resource - get a resource for a device
  61* @dev: platform device
  62* @type: resource type
  63* @num: resource index
  64*/
 65struct resource *platform_get_resource(struct platform_device *dev,
 66                       unsigned int type, unsigned int num)
  67{
 68    int i;
  69
 70    for (i = 0; i <dev->num_resources; i++) {
 71        struct resource *r =&dev->resource[i];
  72
 73        if (type ==resource_type(r) && num-- == 0)
 74            return r;
 75    }
 76    return NULL;
  77}


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

168static inline unsigned longresource_type(const struct resource *res)
169{
170   return res->flags & IORESOURCE_TYPE_BITS;
171}


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

1167static struct resources3c_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#define S3C24XX_PA_SDI      S3C2410_PA_SDI


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

105#define S3C2410_PA_SDI     (0x5A000000)


0x5A000000是S3C2440 SDICON寄存器的地址。

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

61#define S3C24XX_SZ_SDI      SZ_1M


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

33#define SZ_1M               0x00100000


所以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#define request_mem_region(start,n,name)__request_region(&iomem_resource, (start), (n), (name), 0)

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

 931/**
 932* __request_region - create a new busy resource region
 933* @parent: parent resource descriptor
 934* @start: resource start address
 935* @n: resource region size
 936* @name: reserving caller's ID string
 937* @flags: IO resource flags
 938*/
 939struct resource * __request_region(structresource *parent,
 940                   resource_size_t start,resource_size_t n,
 941                   const char *name, int flags)
 942{
 943   DECLARE_WAITQUEUE(wait, current);
 944   struct resource *res = alloc_resource(GFP_KERNEL);
 945
 946   if (!res)
 947       return NULL;
 948
 949   res->name = name;
 950   res->start = start;
 951   res->end = start + n - 1;
 952   res->flags = IORESOURCE_BUSY;
 953   res->flags |= flags;
 954
 955   write_lock(&resource_lock);
 956
 957   for (;;) {
 958       struct resource *conflict;
 959
 960       conflict = __request_resource(parent, res);
 961       if (!conflict)
 962           break;
 963       if (conflict != parent) {
 964            parent = conflict;
 965           if (!(conflict->flags &IORESOURCE_BUSY))
 966                continue;
 967       }
 968       if (conflict->flags & flags & IORESOURCE_MUXED) {
 969           add_wait_queue(&muxed_resource_wait, &wait);
 970           write_unlock(&resource_lock);
 971           set_current_state(TASK_UNINTERRUPTIBLE);
 972           schedule();
 973           remove_wait_queue(&muxed_resource_wait, &wait);
 974           write_lock(&resource_lock);
 975           continue;
 976       }
 977       /* Uhhuh, that didn't work out.. */
 978       free_resource(res);
 979       res = NULL;
 980       break;
 981    }
 982   write_unlock(&resource_lock);
 983   return res;
 984}


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

328#define ioremap(cookie,size)        __arm_ioremap((cookie), (size),MT_DEVICE)


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

374void __iomem *
375__arm_ioremap(unsigned long phys_addr,size_t size, unsigned int mtype)
376{
377   return arch_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 - get an IRQ for a device
  82* @dev: platform device
  83* @num: IRQ number index
  84*/
 85int platform_get_irq(struct platform_device *dev, unsigned int num)
  86{
 87#ifdef CONFIG_SPARC
 88    /* sparc does not have irqsrepresented as IORESOURCE_IRQ resources */
 89    if (!dev || num >=dev->archdata.num_irqs)
 90        return -ENXIO;
 91    returndev->archdata.irqs[num];
 92#else
 93    struct resource *r =platform_get_resource(dev, IORESOURCE_IRQ, num);
  94
 95    return r ? 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文件中:

1427static struct mmc_host_ops s3cmci_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函数我们就分析完了。

  • 0
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值