s5pv210-Linux驱动之SD卡主机控制器
2017-11-29 09:56
686 查看
一、开发环境
硬件平台:我用的是TQ210核心板,板载S5PV210芯片软件平台:开发板移植的是Linux3.10.46内核,UBOOT移植的是2014.12版本
二、资源简介
内核自带S5PV210芯片的SD卡驱动,drivers/mmc/host/sdhci-s3c.c三、驱动分析
1、在mach-s5pv210.c中添加HSMMC主机控制器设备static struct platform_device *smdkv210_devices[] __initdata = { &s3c_device_adc, &s3c_device_cfcon, &s3c_device_fb, &s3c_device_hsmmc0, &s3c_device_hsmmc1, &s3c_device_hsmmc2, &s3c_device_hsmmc3,
然后,由此函数添加到platform_device
platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
s3c_device_hsmmc0在文件arch/arm/plat-samsung.c中定义
#ifdef CONFIG_S3C_DEV_HSMMC static struct resource s3c_hsmmc_resource[] = { [0] = DEFINE_RES_MEM(S3C_PA_HSMMC0, SZ_4K), [1] = DEFINE_RES_IRQ(IRQ_HSMMC0), }; struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = { .max_width = 4, .host_caps = (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED), }; struct platform_device s3c_device_hsmmc0 = { .name = "s3c-sdhci", .id = 0, .num_resources = ARRAY_SIZE(s3c_hsmmc_resource), .resource = s3c_hsmmc_resource, .dev = { .dma_mask = &samsung_device_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &s3c_hsmmc0_def_platdata, }, }; void s3c_sdhci0_set_platdata(struct s3c_sdhci_platdata *pd) { s3c_sdhci_set_platdata(pd, &s3c_hsmmc0_def_platdata); } #endif /* CONFIG_S3C_DEV_HSMMC */当platform_driver的.name = "s3c-sdhci"时,就会和此平台设备匹配
2、注册s5pv210芯片SD卡的平台driver,driver/mmc/host/sdhci-s3c.c
static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, .remove = sdhci_s3c_remove, .id_table = sdhci_s3c_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-sdhci", .of_match_table = of_match_ptr(sdhci_s3c_dt_match), .pm = SDHCI_S3C_PMOPS, }, }; module_platform_driver(sdhci_s3c_driver);在对sdhci_s3c_driver进行注册的过程中,系统会在platform_bus
总线上寻找同名字的platform_dvice(这个过程称之为“探测”),如果探测成功一次,就会调用sdhci_s3c_driver.probe函数---sdhci_s3c_probe一次。
3、下面来分析一下sdhci_s3c_probe函数
在分析代码之前,先简要的概括一下这个函数的功能:
1)、既然是讲host的注册,那么首先必须构造出一个host,这个host就是通过sdhci_alloc_host函数来构造出来的,它是一个struct sdhci_host类型的结构体。同时,也通过mmc_alloc_host函数构造了一个struct mmc_host的结构体变量mmc。
2)、初始化host的时钟,设置host的gpio等等其他一些“乱七八糟”的参数初始化(需要的时候再详细分析)。
3)、通过sdhci_add_host函数来注册host。
irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq specified\n"); return irq; }获取中断资源,即在devs.c中定义的平台资源,如下
static struct resource s3c_hsmmc_resource[] = { [0] = DEFINE_RES_MEM(S3C_PA_HSMMC0, SZ_4K), [1] = DEFINE_RES_IRQ(IRQ_HSMMC0), };
host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); if (IS_ERR(host)) { dev_err(dev, "sdhci_alloc_host() failed\n"); return PTR_ERR(host); }分配一个sdhci_host的控制器,sdhci_alloc_host定义如下:
struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size) { struct mmc_host *mmc; struct sdhci_host *host; WARN_ON(dev == NULL); mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev); if (!mmc) return ERR_PTR(-ENOMEM); host = mmc_priv(mmc); host->mmc = mmc; return host; }分配了一个mmc_host结构体,同时struct sdhci_s3c 结构作为一个私有数据类型添加到structmmc_host的private域
sc = sdhci_priv(host);得到sdhci_s3c结构体指针
sc->host = host; sc->pdev = pdev; sc->pdata = pdata;这个私有结构的配置,对于core或block层见到的只有struct sdhci_host
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->ioaddr = devm_ioremap_resource(&pdev->dev, res);获取IO资源
host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; host->irq = irq;分配操作函数集等信息
ret = sdhci_add_host(host);
最后通过sdhci_add_host(host),添加到MMC系统中,此时dev与host已经完成了挂接的操作
在sdhci_add_host函数中,会注册为断程序,用来处理SD卡插拨事件,如下:
tasklet_init(&host->card_tasklet,
sdhci_tasklet_card, (unsigned long)host);
tasklet_init(&host->finish_tasklet,
sdhci_tasklet_finish, (unsigned long)host);
... ... ...
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
mmc_hostname(mmc), host);中断注册函_irq的第一个参数中断号就取自于s3c_device_hsmmc3.resource里面的irq参数,sdhci_irq就是中断服务程序,该中断函数一般在插卡、拔卡或者从设备反馈给host信息时会被调用数request,中断服务程序定义如下:
static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
... ... ... ...
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
... ... ... ...
sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
SDHCI_INT_CARD_REMOVE);
sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
SDHCI_INT_CARD_INSERT);
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
tasklet_schedule(&host->card_tasklet);
}
... ... ... ...
}程序首先读取寄存器NORINTSTSn的值,该寄存器中有两个bit分别来表示卡的插入与拔出过程(注意,必须是动态变化过程,才会让相应的两个bit置1),那么接下来的if语句就是从该寄存器的那两个bit来判断是否有卡的插入或拔出,并同时清除这两个bit,准备下一次的检测,紧接着就调用中断的下半部分函数
sdhci_tasklet_card,其实这个函数也没做什么事情,就是判读如果此时有卡的话就通过mmc_detect_chang函数调用mmc_rescan函数。
然后在此函数sdhci_add_host中构造了一个struct
mmc_host的结构体变量mmc,并添加到系统中:mmc_add_host(mmc),定义如下:
int mmc_add_host(struct mmc_host *host) { int err; WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && !host->ops->enable_sdio_irq); err = device_add(&host->class_dev); if (err) return err; led_trigger_register_simple(dev_name(&host->class_dev), &host->led); #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); #endif mmc_host_clk_sysfs_init(host); mmc_start_host(host); register_pm_notifier(&host->pm_notify); return 0; }通过device_add函数将设备注册进linux设备模型,最终的结果就是在sys/bus/platform/devices目录下能见到s3c-sdhci.1,s3c-sdhci.2,s3c-sdhci.3设备节点。mmc_start_host定义如下:
void mmc_start_host(struct mmc_host *host) { host->f_init = max(freqs[0], host->f_min); host->rescan_disable = 0; if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else mmc_power_up(host); mmc_detect_change(host, 0); }mmc_power_off函数里,关心最多的就是host->ios当中的内容,前段的赋值真正作用在硬件上是调用host层向上提供的struct
mmc_host_ops接口。后面最重要的函数mmc_detect_change(host, 0),定义如下:
void mmc_detect_change(struct mmc_host *host, unsigned long delay) { #ifdef CONFIG_MMC_DEBUG unsigned long flags; spin_lock_irqsave(&host->lock, flags); WARN_ON(host->removed); spin_unlock_irqrestore(&host->lock, flags); #endif host->detect_change = 1; mmc_schedule_delayed_work(&host->detect, delay); }只关心最后一句,这就要用到内核的延时工作队列,在mmc_alloc_host函数中调用了这一句
INIT_DELAYED_WORK(&host->detect, mmc_rescan);所以,延时delay 时间后就会去调用mmc_rescan了。前面我们传递的delay=0,那么这里就没有延时了直接mmc_rescan。它的功能就是扫描所插入的卡。
四、小结
sdhci-s3c.c作为S5PV210芯片的SD卡主机控制器的platform_driver驱动,在其probe函数中分配一个sdhci_host的控制器,并通过sdhci_add_host函数注册,并且在此函数中分配了一个struct mmc_host结构体,注册了中断服务程序,用来发送命令、数据和检测SD卡插拨等操作。相关文章推荐
- s5pv210-Linux驱动之USB-HOST主机控制器之EHCI
- s5pv210-Linux驱动之USB-HOST主机控制器之OHCI
- linux设备驱动之USB主机控制器驱动分析 (一)
- Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作
- linux设备驱动之USB主机控制器驱动分析
- s5pv210-Linux驱动之SD卡插拨识别
- linux设备驱动之USB主机控制器驱动分析
- Linux eMMC子系统之主机控制器驱动(host conntroller driver)
- Linux驱动直接访问控制器的操作方法
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- s5pv210-Linux驱动之USB鼠标
- linux-3.4.2 s5pv210 LCD驱动移植(一)
- spi 主机控制器驱动(spi_master)
- linux-3.8.0 S5PV210 LCD显示屏驱动移植
- spi 主机控制器驱动(spi_master)
- S5PV210开发系列五 sd卡驱动实现
- MT7688 SPI 数据移位问题 SPI控制器驱动更改 openwrt(linux-3.18.29)
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
- [嵌入式Linux驱动]S5PV210的步进电机Linux驱动程序
- Linux主机驱动与外设驱动分离思想