本文主要是介绍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
- 33 struct mfd_cell *mfd_cell;
- 34
- 35
- 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
-
-
-
- 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
-
-
-
-
- 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
-
-
-
-
- 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
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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
-
-
- 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
- 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
-
-
-
-
-
-
-
-
-
-
-
-
- 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
- 729 if (of_driver_match_device(dev, drv))
- 730 return 1;
- 731
- 732
- 733 if (acpi_driver_match_device(dev, drv))
- 734 return 1;
- 735
- 736
- 737 if (pdrv->id_table)
- 738 return platform_match_id(pdrv->id_table, pdev) != NULL;
- 739
- 740
- 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
- 66
- 67 iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));
- 68
- 69
- 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
-
-
-
-
-
-
- 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
- 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
-
-
-
- 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
- 33#define IORESOURCE_MEM 0x00000200
- 34#define IORESOURCE_REG 0x00000300
- 35#define IORESOURCE_IRQ 0x00000400
- 36#define IORESOURCE_DMA 0x00000800
- 37#define IORESOURCE_BUS 0x00001000
platform_get_resource函数定义在drivers/base/platform.c文件中:
- 59
-
-
-
-
-
- 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
-
-
-
-
-
-
-
- 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
- 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
-
-
-
-
- 85int platform_get_irq(struct platform_device *dev, unsigned int num)
- 86{
- 87#ifdef CONFIG_SPARC
- 88
- 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函数我们就分析完了。
这篇关于Linux设备驱动程序架构分析之MMC/SD(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!