SDIO SDCard热插拔支持
MMC - SDCard热插拔
热插拔的定义
热插拔一般是指某个部件在插入或拔出主体时,能够让主体感知到。在sdcard热插拔这块,也就是指sd card插入和拔出时,linux内核都能检测卡的状态,收到通知并做出响应的反馈。
热插拔的实现方式
热插拔的实现方式一般有两种:
- 轮询
- 中断
Linux内核中具体的实现方式
linux内核实现了MMC Core,MMC Core在如下情况下会去扫描mmc硬件总线:
- Probe host时,调用_mmc_detect_change,唤醒host->detect;
- cd-gpio中断检测到card状态发送变化,调用mmc_detect_change,唤醒host->detect;
- host要求轮询sd card插入状态的情况下,所进行的轮询操作;
Probe host时
probe host时就已经会解析设备树中指定的cd-gpios了,
内核已经实现了mmc_gpio_cd_irqt回调,gpio中断触发时调用mmc_detect_change函数,该函数负责唤醒detect延时任务,detect在初始化时被绑定为:
1 | dw_mci_init_slot(host); |
mmc_rescan中负责检测mmc card
cd-gpio中断触发时
底层硬件发现card插入状态发生变化而调用mmc_detect_change的时候(sd card插入状态监控)。
当底层硬件发现card插入状态发生变化,例如sd card的插入或者拔出可以触发某个GPIO产生中断。此时,可以在中断处理中调用mmc_detect_change来进行扫描mmc硬件总线,并且根据总线上的card状态变化进行处理。
这种情况的主要目的是为了实现sd card的热插拔。
1 | static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) |
轮询检测
host要求轮询sd card插入状态的情况下,所进行的轮询操作(sd card插入状态监控)。一般来说,在host无法根据硬件来及时获取card插入状态发生变化的情况下,会要求mmc_core每隔一段时间(一般是HZ,一秒)扫描一次mmc硬件总线。
在这种情况下,mmc_host的MMC_CAP_NEEDS_POLL属性会被设置。
1 | void mmc_rescan(struct work_struct *work) |
如何扫描mmc硬件总线
从上述,我们知道了可以通过调用mmc_detect_change和执行host->detect工作来发起mmc硬件总线的扫描。而mmc_detect_change最终也是执行mmc_host->detect工作来发起mmc硬件总线扫描的。
下面描述detect任务的初始化过程和唤醒时机
detect task的创建
1 | struct mmc_host *mmc_alloc_host(int extra, struct device *dev) |
detect task的唤醒
1 | void mmc_detect_change(struct mmc_host *host, unsigned long delay) |
detect task(mmc_rescan)的实现
当mmc core检测到一张card并且初始化该card完成时,会将该mmc_bus于该card绑定,相应的mmc_bus的操作mmc_bus_ops就会被设置成对应card类型的操作集。
例如emmc的话就是mmc_ops,sd card的话就是mmc_sd_ops,sdio card就是mmc_sdio_ops。
mmc_rescan主要是进行以下两种操作:
当mmc_host的mmc_bus_ops没有被设置的时候
这种情况下,说明mmc_bus并没有和card绑定。也就是之前并没有card插入或者识别初始化card的过程中失败了。
所以只需要去判断当前是否有card插入,如果有的话,进行card的识别和初始化操作(并且会设置mmc_bus_ops)。否则,说明都不做。当mmc_host的mmc_bus_ops被设置的时候
这种情况下,说明mmc_bus已经和card绑定了。也就是之前已经有card插入并且初始化成功了。
那么此时,需要调用mmc_bus_ops中的detect方法(对于sd card来说就是mmc_sd_detect)来判断card是否被移除,并且进行相应的动作。
1 | void mmc_rescan(struct work_struct *work) |
sd card热插拔状态的获取
获取方式主要有两种:
- gpio中断
- host本身具有检测卡状态的寄存器
gpio中断实现热插拔
mmc core解析gpio中断,在Probe host一节的代码路径里可以看到,这依赖dts中描述cd-gpios节点:
一个配置案例:
注意,这里必须要引用gpio控制器(带有gpio-controller描述的节点)
host本身具有检测卡状态的寄存器
忽略
一次sd card热插拔适配异常问题的定位和解决经历
内核启动时,sd card初始化成功,但是热插拔时,sd card初始化失败, 所以将drivers\mmc\core\core.c中定义DEBUG宏,对比启动时的初始化log和热插拔时的log
左 - 内核启动时log
右 - 热插拔时的log
为了理解上面的指令序列:
查询spec sdio-spce-v3:
An SDIO aware host sends CMD5 prior to the CMD55/ACMD41 pair, and thus would receive a valid OCR in
the R4 response to CMD5 and continue to initialize the card. Figure 3-2 shows the operation of an SDIO
aware host operating in the SD modes and Figure 3-3 shows the same operation for a host that operates in
the SPI mode.
在初始化正常的log里,CMD55/CMD41指令对协商工作电压,sd card返回的R4类型的回复,bit24为0,而热插拔的log中CMD41的R3返回的bit24为1
R4类型的回复中,bit24为S18R,
看样子是sdhci开始切换1.8v了,所以右边的log里会在CMD41获取到OCR bit24为1后开始执行CMD11。目前我们的原型平台还不支持1.8v工作。
正常的初始化序列是:
为了屏蔽此处的电压切换,走读代码寻找CMD11的发送位置:
临时让mmc_set_uhs_voltage在入口处就返回:
再次启动内核,热插拔sd card,初始化可以成功!