转自:http://blog.****.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文件中,其内容如下:
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函数我们就分析完了。