一、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