一、SDHCI与控制器驱动
SDHC:Secure Digital(SD) Host Controller,是指一套sd host控制器的设计标准,其寄存器偏移以及意义都有一定的规范,并且提供了对应的驱动程序,方便vendor进行host controller的开发。
厂商按照这套标准设计host controller之后,可以直接使用sdhci driver来实现host controller的使用,(qcom和samsung都使用了这套标准)。而vendor只需要实现平台相关的部分、如clock、pinctrl、power等等的部分即可。
关于这个标准,可以参考《SDHC_Ver3.00_Final_110225》。
注意,强调一下,这是一种mmc host controller的设计标准,其本质上还是属于mmc host。并且,其兼容mmc type card,而不是说只能使用于sd type card。
SDHCI:Secure Digital(SD) Host Controller Interface,是针对SDHC标准的驱动接口。
其常见接口如:
sdhci_pltfm_init:平台设备SDHCI初始化,主要是分配、设置sdhci_host,最终关联到platform_device的device
sdhci_alloc_host:分配sdhci_host
mmc_alloc_host:分配、设置mmc_host(卡检测的扫描工作队列)
sdhci_add_host:设置sdhci_host,关联到mmc_host,并注册mmc_host
sdhci_setup_host:设置sdhci_host
二、卡检测的初始化:
自定义SDHC驱动初始化调用platform_driver_register平台注册用户platform_driver。
其中自定义SDHC驱动的probe会分配sdhci_host、sdhci_pltfm_host内存,并对sdhci_host进行设置
然后进行关联mmc_host、sdhci_host、sdhci_pltfm_host。
然后调用SDHCI接口sdhci_add_host,将得到的sdhci_host注册到sdhci core中。
SDHCI接口sdhci_add_host会设置sdhci_host,并调用下级__sdhci_add_host。
SDHCI接口__sdhci_add_host:
设置请求处理完成时调用的任务队列处理函数sdhci_tasklet_finish
设置当前请求命令的响应定时处理函数sdhci_timeout_timer
设置当前数据交互的响应定时sdhci_timeout_data_timer
设置等待队列的缓冲区读准备中断
设置sdhci_host
设置外部中断
注册LE灯
调用mmc_add_host
使能卡检测sdhci_enable_card_detection
其中mmc_add_host会添加设备类,调用mmc_start_host开启主机,并注册电源管理通知。
其中mmc_claim_host声明独占主机,设置电源,使用host->slot.handler_priv->cd_gpio注册线程化中断,执行一次卡检测_mmc_detect_change。
注意:其中SD插拔经常出现一个打印问题“mmcblk1: error -110 sending status command, aborting ”
其实问题原因:
二、卡检测的执行:
因为在启动主机时会执行卡检测,但是这里也不一定会检测成功,因为有可能没插卡。
除了SDHC驱动以外,还会编写一份控制器驱动,比如海思的himci。
自定义控制器驱动初始化调用platform_driver_register平台注册用户platform_driver。
其中自定义SDHC驱动的probe会分配、设置自定义主机属性,其中卡检测函数有三种使用情况:
1)自定义主机属性的mmc_host_ops的get_cd函数
2)自定义主机属性的card_status函数
3)自定义主机属性的定时器
其中通常插卡检测是由定时器的处理函数检测到的,定时器处理函数的流程为:
1)通过卡检测寄存器或者IO,连续检查5次SD卡状态,相同则继续,否则重复100次。(次数均为自定义)
2)如果5次相同则判断其值是否为插入,如果是插入则软复位,初始化自定义主机属性,调用MMC子系统API的mmc_detect_change。
mmc_detect_change调用_mmc_detect_change,两者的区别为电源管理是否唤醒事件,默认为有
_mmc_detect_change会判断如果设备被配置为唤醒,我们将防止新的休眠5秒,以便为用户提供使用事件的空间。
然后设置mmc_host->detect_change为1,检测更改
然后调用mmc_schedule_delayed_work调度工作队列
mmc_schedule_delayed_work会调用queue_delayed_work执行system_freezable_wq的工作队列,其中使用system_freezable_wq的原因有2个:
1)它允许同时执行多个工作(不是相同的工作项)。
2)当用户空间在系统PM期间冻结时,队列将冻结。
而此处的工作任务为mmc_alloc_host中设置的INIT_DELAYED_WORK(&host->detect, mmc_rescan);
所以,这里跳转到mmc_rescan
mmc_rescan:
根据mmc_host->rescan_disable判断是否允许扫描,如果为真则不允许直接退出。
根据mmc_host->cap判断是否为不可移动的已注册卡只扫描一次还是可以继续扫描。
设置mmc_host->rescan_entered=1,表示进入扫描
调用mmc_bus_get递增总线操作计数
调用mmc_rescan_try_freq,以四种频率进行初始化SDIO SD EMMC
mmc_rescan_try_freq:
设置mmc_host->f_init,根据mmc_recan函数传进来的频率参数,一般mmc/sd/sdio的初始化时钟采用的是400kHZ.
调用mmc_power_up,进行上电。在mmc_add_host时,会调用mmc_start_host,而那里首先是将host掉电的,所以这里上电。
调用mmc_hw_reset_for_init,有些emmc (VCCQ总是开着的)可能在通电后无法复位,所以如果可能的话,可以进行硬件复位。
根据不同卡做不同的操作:
1)纯SD卡,则目标卡不会应答,一般主机host的寄存器会报错,但是这个无关紧要,可以不理它。
2)纯SDIO卡,那么这里就是复位SDIO卡,通过发送命令CMD52来实现的。
3)SD卡和SDIO卡的组合卡,则需要先发送CMD52来复位SDIO卡,再复位SD卡,因为CMD52要先于CMD0发送。
调用mmc_go_idle,发送CMD0,复位SD卡,进入IDLE模式
调用mmc_send_if_cond,如果是SD卡,则发送CMD8 获取支持的电压值
根据不同卡做不同的卡检测操作:
1)SDIO卡,调用mmc_attach_sdio
2)SD卡,调用mmc_attach_sd
3)MMC卡,调用mmc_attach_mmc
4)都不是,则调用mmc_power_off进行下电
此处以SD卡为例,所以只看mmc_attach_sd