查看: 1952|回复: 0

[技术交流] [sd card] mmc硬件总线扫描流程(二)

[复制链接]

185

主题

204

帖子

596

积分

利尔达员工

Rank: 9Rank: 9Rank: 9

积分
596
发表于 2021-8-27 12:10:49 | 显示全部楼层 |阅读模式
二、sd card插入状态的获取
通过第一节中,我们知道了mmc_rescan是通过调用mmc_host->mmc_host_ops->get_cd来获取当前card的插入状态的。
那么这个方法是怎么实现的呢?如何获取card当前的插入状态呢?以下来说明一下这两个问题。

1、获取sd card当前的插入状态
一般来说有两种方式来获取到sd card当前的插入状态
(1)GPIO获取的方法
可以通过sd card的card detect引脚来判断当前是否有sd card插入
(2)host寄存器获取的方法
某些host在硬件上有识别sd card是否插入的能力。这种情况下,可以通过读取host的寄存器来获取到当前是否有sd card插入。

1(1)、通过GPIO获取当前sd card的插入状态
DECECT引脚
这里先
以TF card为例,参考blog.csdn.net/xqhrs232/article/details/43339171。
首先以一个原理图来看一下TF card卡座的连接

(其中SD_CARD_DET_N是有外部上拉的)。
以卡座的DET_SWITCH引脚作为card检测脚。
其card和卡座引脚定义如下:

可以观察到,虽然TF CARD(micro)只有8个引脚,但是卡座另外定义了一个CD引脚(DET_SWITCH)。
我的猜想是,对于某些tf card卡座来说,当tf card插入时,会把CD引脚(DET_SWITCH)直接定义到连接到groud上去。
所以,这里可以梳理出,
●当card没有插入的情况下,由于外部上拉的关系,cpu连接到DET_SWITCH的gpio为高电平。
●当card插入时,tf card卡座会把DET_SWITCH拉低,相应的,cpu连接到DET_SWITCH的gpio为低电平。
当然,上述只是一种情况,具体cd gpio的电平情况取决于使用的卡座的实现。
●注册card插入状态检测GPIO软件介绍
mmc core提供了mmc_gpio_request_cd来为host定义自己的cd-detect引脚,也就是对应上面连接到卡座上的CD引脚的GPIO。
mmc_gpio_request_cd实现如下(虽然也会注册GPIO中断,但是这里先不关心):

int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
{
struct mmc_gpio *ctx;    // struct mmc_gpio用来表示连接卡槽的一些GPIO的状态
int ret;

ret = mmc_gpio_alloc(host);    // 调用mmc_gpio_alloc来为host分配一个struct mmc_gpio
ctx = host->slot.handler_priv;    // 获取struct mmc_gpio

ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, ctx->cd_label);   // 申请这个GPIO
ctx->cd_gpio = gpio;    // 将设置到ctx->cd_gpio中
}

所以后续就可以通过读取 mmc_host->slot.handler_priv->cd_gpio的电平状态来判断当前card的插入状态

通过GPIO获取当前sd card的插入状态
在上述通过mmc_gpio_request_cd注册完成cd gpio之后,就可以通过mmc_gpio_get_cd来获取card的插入状态。
当其返回1时,表示当前有card插入,当其返回0是,表示当前没有card插入。
其实现如下

int mmc_gpio_get_cd(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;    // 提取出host对应的struct mmc_gpio

if (!ctx || !gpio_is_valid(ctx->cd_gpio))    // 如果没有注册cd gpio的情况下,直接返回了
 return -ENOSYS;

return !gpio_get_value_cansleep(ctx->cd_gpio) ^ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
  // 因为mmc_gpio_get_cd返回1表示有card插入,返回0是表示没有card插入
  // MMC_CAP2_CD_ACTIVE_HIGH属性被设置的话,表示当有card插入时为cd gpio为高电平(主要取决于卡座)
  // 假设这里MMC_CAP2_CD_ACTIVE_HIGH没有被设置,也就是有card插入时cd gpio为低电平
  // 当cd gpio检测到低电平的时候,相当于是!0 ^ 0=1,因此会返回1,有card插入
  // 当cd gpio检测到高电平的时候,相当于是!1 ^ 0=0,因此会返回0,没有card插入
}

1(2)、通过host寄存器获取当前card插入状态
前提是某些host在硬件上有识别sd card是否插入的能力。这种情况下,可以通过读取host的寄存器来获取到当前是否有sd card插入。

●寄存器介绍
以sdhci类host为例,根据《SDHC_Ver3.00_Final_110225》 “2.2.9 Present State Register(Offset 024h)”

可以通过Present State Register(偏移地址为0x24)的bit16来判断card的插入状态。
**注意:有可能存在实际使用sdhci的host不符合上述标准的情况,对于这类host,会定义sdhci_host中的quirks的SDHCI_QUIRK_BROKEN_CARD_DETECTION。
SDHCI_QUIRK_BROKEN_CARD_DETECTION说明该sdhci host并没有通过Present State Register(偏移地址为0x24)的bit16来提供一个可靠的card插入状态判断方式。**

●软件介绍
以sdhci类host为例

static int sdhci_do_get_cd(struct sdhci_host *host)
{
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
  return 1;

/* Host native card detect */
return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
}

2、mmc_host_ops->get_cd方法的实现
通过上述我们已经知道了两种获取当前card插入状态的方法。
接下来就是利用上述的两种方法来实现mmc_host_ops->get_cd。
对于这个方法来说,返回1表示当前有card插入,返回0表示当前没有card插入。
对于sdhci类host来说,这个方法对应实现为sdhci_do_get_cd,实现方法如下:

static int sdhci_get_cd(struct mmc_host *mmc)
{
  struct sdhci_host *host = mmc_priv(mmc);
  int ret;

  sdhci_runtime_pm_get(host);
  ret = sdhci_do_get_cd(host);   // 进一步调用sdhci_do_get_cd
  sdhci_runtime_pm_put(host);
  return ret;
}
static int sdhci_do_get_cd(struct sdhci_host *host)
{
  int gpio_cd = mmc_gpio_get_cd(host->mmc);   
   // 通过调用mmc_gpio_get_cd,通过cd gpio获取当前的card插入状态,对应上述“通过GPIO获取当前sd card的插入状态”

  /* If polling/nonremovable, assume that the card is always present. */
  if (host->mmc->caps & MMC_CAP_NONREMOVABLE)   // 对于不可移除的设备,总是返回1
   return 1;

  if (!IS_ERR_VALUE(gpio_cd))  
   return !!gpio_cd;   // mmc_gpio_get_cd返回值有效的话,返回插入状态

// 因为host并不一定有通过mmc_gpio_request_cd注册cd gpio,也就是没有实现“通过GPIO获取当前sd card的插入状态”
// 这种情况下就尝试通过用sdhci的寄存器来获取

   // 当SDHCI_QUIRK_BROKEN_CARD_DETECTION被设置的时候,说明该host并没有提供检测card插入状态检测的能力,
   // 直接返回1,假设插入,由后续协议的工作来尝试初始化
  if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
   return 1;

   // 读取sdhci的SDHCI_PRESENT_STATE的bit SDHCI_CARD_PRESENT来获取card的插入状态。
  /* Host native card detect */
  return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
}

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表