s5pv210-Linux驱动之SD卡主机控制器
来源:互联网 发布:贫贱夫妻百事哀 知乎 编辑:程序博客网 时间:2024/06/01 08:46
一、开发环境
硬件平台:我用的是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_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
s3c_device_hsmmc0在文件arch/arm/plat-samsung.c中定义
#ifdef CONFIG_S3C_DEV_HSMMCstatic 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_FSmmc_add_host_debugfs(host);#endifmmc_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);elsemmc_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_DEBUGunsigned long flags;spin_lock_irqsave(&host->lock, flags);WARN_ON(host->removed);spin_unlock_irqrestore(&host->lock, flags);#endifhost->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卡插拨等操作。阅读全文
0 0
- s5pv210-Linux驱动之SD卡主机控制器
- s5pv210-Linux驱动之USB-HOST主机控制器之EHCI
- s5pv210-Linux驱动之USB-HOST主机控制器之OHCI
- s5pv210-Linux驱动之SD卡理论篇
- s5pv210-Linux驱动之SD卡插拨识别
- s5pv210-Linux驱动之MMC/SD/SDIO区别
- Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作
- Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作
- linux设备驱动之USB主机控制器驱动分析
- linux设备驱动之USB主机控制器驱动分析
- linux设备驱动之USB主机控制器驱动分析
- linux设备驱动之USB主机控制器驱动分析
- linux设备驱动之USB主机控制器驱动分析
- Linux eMMC子系统之主机控制器驱动(host conntroller driver)
- s5pv210-裸机之SD卡
- USB驱动之主机控制器驱动
- s5pv210-Linux驱动之ME3760
- linux设备驱动之USB主机控制器驱动分析 (一)
- 从浏览器输入URL到显示页面发生了什么?
- tmux软件应用
- python enumerate用法总结
- Ethernet/IP 学习笔记六
- Error:Conflict with dependency 'com.google.code.findbugs:jsr305'解决
- s5pv210-Linux驱动之SD卡主机控制器
- php选择排序,插入排序
- 传统的创建线程的两种方式
- ubuntu16.04 opencv3.3 python2.7 caffe GPU环境搭建
- Struts
- 终结者模式
- 归并排序学习总结,递归法&&插入排序&&冒泡排序&&选择排序&&快速排序
- Python小Tips
- Spark Hadoop集群部署与Spark操作HDFS运行详解---Spark学习笔记10