目录

1. 扫描mmc硬件总线

1.1 扫描mmc硬件总线的时机

1.1.1 启动一个host的时候而调用_mmc_detect_change

1.1.2 底层硬件发现card插入状态发生变化而调用mmc_detect_change的时候(sd card插入状态监控)

1.1.3 host要求轮询sd card插入状态的情况下,所进行的轮询操作(sd card插入状态监控)

1.2 如何扫描mmc硬件总线

2. sd card插入状态的获取

2.1 获取sd card当前的插入状态

2.1.1 通过GPIO获取当前sd card的插入状态

2.1.2 通过host寄存器获取当前card插入状态

2.2 mmc_host_ops->get_cd方法的实现

3. sd card热插拔实现

3.1 中断监控

3.1.1 cd-gpio的解析

3.1.2 注册cd-gpio中断引脚

3.1.3 mmc_gpio_request_cd实现

3.1.4 中断监控的中断处理函数mmc_gpio_cd_irqt

3.2 轮询监控


代码:

drivers\mmc\core\core.c

drivers\mmc\core\slot-gpio.c

1. 扫描mmc硬件总线

      扫描mmc硬件总线,也就是检测mmc硬件总线上是否有挂载card。更加通俗的,就是卡槽上是否有插入card。先参考前面一篇文章。

1.1 扫描mmc硬件总线的时机

mmc core在如下情况下会去扫描mmc硬件总线:

1)启动一个host的时候而调用mmc_detect_change

2)底层硬件发现card插入状态发生变化而调用mmc_detect_change的时候(sd card插入状态监控)

3)host要求轮询sd card插入状态的情况下,所进行的轮询操作(sd card插入状态监控)

1.1.1 启动一个host的时候而调用_mmc_detect_change

当启动一个host的时候,并不知道当前是否有card插入,此时需要调用mmc_detect_change来假设card插入状态发生了变化,
      并且进行第一次扫描mmc硬件总线。
      代码如下,过滤掉无关代码:

void mmc_start_host(struct mmc_host *host)
{host->f_init = max(freqs[0], host->f_min);host->rescan_disable = 0;host->ios.power_mode = MMC_POWER_UNDEFINED;if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {mmc_claim_host(host);mmc_power_up(host, host->ocr_avail);mmc_release_host(host);}mmc_gpiod_request_cd_irq(host);_mmc_detect_change(host, 0, false);
}

1.1.2 底层硬件发现card插入状态发生变化而调用mmc_detect_change的时候(sd card插入状态监控)

当底层硬件发现card插入状态发生变化,例如sd card的插入或者拔出可以触发某个GPIO产生中断。
      此时,可以在中断处理中调用mmc_detect_change来进行扫描mmc硬件总线,并且根据总线上的card状态变化进行处理。
      这种情况的主要目的是为了实现sd card的热插拔。
      代码如下,过滤掉无关代码:drivers\mmc\core\slot-gpio.c

static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{/* Schedule a card detection after a debounce timeout */struct mmc_host *host = dev_id;host->trigger_card_event = true;mmc_detect_change(host, msecs_to_jiffies(200));return IRQ_HANDLED;
}

1.1.3 host要求轮询sd card插入状态的情况下,所进行的轮询操作(sd card插入状态监控)

一般来说,在host无法根据硬件来及时获取card插入状态发生变化的情况下,会要求mmc_core每隔一段时间(一般是HZ,一秒)扫描一次mmc硬件总线。
      在这种情况下,mmc_host的MMC_CAP_NEEDS_POLL属性会被设置。
      这种情况的主要目的也是为了实现sd card的热插拔。
      代码如下,过滤掉无关代码:

void mmc_rescan(struct work_struct *work)
{...............if (host->caps & MMC_CAP_NEEDS_POLL)mmc_schedule_delayed_work(&host->detect, HZ);
}

1.2 如何扫描mmc硬件总线

从上述,我们知道了可以通过调用mmc_detect_change和执行host->detect工作来发起mmc硬件总线的扫描。
      而mmc_detect_change最终也是执行mmc_host->detect工作来发起mmc硬件总线扫描的。
      参考前面一篇文章。

2. sd card插入状态的获取

通过第一节中,我们知道了mmc_rescan是通过调用mmc_host->mmc_host_ops->get_cd来获取当前card的插入状态的。
      那么这个方法是怎么实现的呢?如何获取card当前的插入状态呢?以下来说明一下这两个问题。

2.1 获取sd card当前的插入状态

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

2.1.1 通过GPIO获取当前sd card的插入状态

(1)DECECT引脚

TF卡参考这篇文章。

可以观察到,虽然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的电平情况取决于使用的卡座的实现。

(2)注册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,unsigned int debounce)
{struct mmc_gpio *ctx = host->slot.handler_priv;int ret;ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN,ctx->cd_label);if (ret < 0)/** don't bother freeing memory. It might still get used by other* slot functions, in any case it will be freed, when the device* is destroyed.*/return ret;if (debounce) {ret = gpio_set_debounce(gpio, debounce);if (ret < 0)return ret;}ctx->override_cd_active_level = true;ctx->cd_gpio = gpio_to_desc(gpio);return 0;
}

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

(3)通过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;if (!ctx || !ctx->cd_gpio)return -ENOSYS;if (ctx->override_cd_active_level)return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);return gpiod_get_value_cansleep(ctx->cd_gpio);
}

2.1.2 通过host寄存器获取当前card插入状态

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

(1)寄存器介绍

以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插入状态判断方式。**

(2)软件介绍

以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.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 gpio_cd = mmc_gpio_get_cd(mmc);if (host->flags & SDHCI_DEVICE_DEAD)return 0;/* If nonremovable, assume that the card is always present. */if (!mmc_card_is_removable(host->mmc))return 1;/** Try slot gpio detect, if defined it take precedence* over build in controller functionality*/if (gpio_cd >= 0)return !!gpio_cd;/* If polling, assume that the card is always present. */if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)return 1;/* Host native card detect */return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
}

3. sd card热插拔实现

sd card热插拔实现,就是对sd card的插入状态的监控。通常来说有两种方式中断监控和轮询监控。
      mmc core会选择其中的一种方式来监控sd card的插入状态。

3.1 中断监控

通过sd card的cd引脚的电平变化来触发中断,从而告知cpu说sd card的插入状态已经发生了变化。
      实现方式如下:

3.1.1 cd-gpio的解析

在dtsi中可以定义如下:

&sdhc_2 {
cd-gpios = <&gpio_ctrl 99 0x1>;
}
// cd-gpios这个属性名的定义取决于host driver将cd gpio定义成了什么名字
// gpio_ctrl,也就是要使用的GPIO所使用的gpio controller
// 99:sd card的cd引脚连接到GPIO99上
// 0x01:取决于host driver如何解释这个flag的,一般来说,0x01表示低电平有card插入,0x00则表示高电平有card插入

在host driver的解析如下:

status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);// 解析cd-gpios属性,提取对应GPIO号到status_gpio中,提取flags
if (gpio_is_valid(status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW))pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;// 这里会解析flags,如前面说的// 0x01表示低电平有card插入,0x00则表示高电平有card插入// OF_GPIO_ACTIVE_LOW=1// 所以当flags=0的时候,会设置MMC_CAP2_CD_ACTIVE_HIGH,告诉mmc core,这个host现在是高电平表示有card插入

3.1.2 注册cd-gpio中断引脚

前面说过了可以通过mmc_gpio_request_cd来注册一个gpio检测卡插入状态的引脚。
      同样也是通过这个函数来注册这个GPIO对应的中断。

mmc_gpio_request_cd(mmc_host, status_gpio);

3.1.3 mmc_gpio_request_cd实现

/*** mmc_gpiod_request_cd - request a gpio descriptor for card-detection* @host: mmc host* @con_id: function within the GPIO consumer* @idx: index of the GPIO to obtain in the consumer* @override_active_level: ignore %GPIO_ACTIVE_LOW flag* @debounce: debounce time in microseconds* @gpio_invert: will return whether the GPIO line is inverted or not, set* to NULL to ignore** Use this function in place of mmc_gpio_request_cd() to use the GPIO* descriptor API.  Note that it must be called prior to mmc_add_host()* otherwise the caller must also call mmc_gpiod_request_cd_irq().** Returns zero on success, else an error.*/
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,unsigned int idx, bool override_active_level,unsigned int debounce, bool *gpio_invert)
{struct mmc_gpio *ctx = host->slot.handler_priv;struct gpio_desc *desc;int ret;desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);if (IS_ERR(desc))return PTR_ERR(desc);if (debounce) {ret = gpiod_set_debounce(desc, debounce);if (ret < 0)return ret;}if (gpio_invert)*gpio_invert = !gpiod_is_active_low(desc);ctx->override_cd_active_level = override_active_level;ctx->cd_gpio = desc;return 0;
}

3.1.4 中断监控的中断处理函数mmc_gpio_cd_irqt

static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
{/* Schedule a card detection after a debounce timeout */struct mmc_host *host = dev_id;host->trigger_card_event = true;mmc_detect_change(host, msecs_to_jiffies(200));return IRQ_HANDLED;
}

3.2 轮询监控

主要实现为每隔一段时间(一般是HZ,1s)扫描一下mmc硬件总线。

  • 对应需要设置的属性标识
    需要设置mmc_host的MMC_CAP_NEEDS_POLL属性标识

  • 实现方式
    每个1s中调用一次mmc_host->detect工作。

void mmc_rescan(struct work_struct *work)
{
if (host->caps & MMC_CAP_NEEDS_POLL)mmc_schedule_delayed_work(&host->detect, HZ);// 这里先说明下// 在《host模块说明》已经知道了在mmc_alloc_host中默认将host->detect工作设置为mmc_rescan(card扫描)函数, // INIT_DELAYED_WORK(&host->detect, mmc_rescan)// 这样,通过mmc_schedule_delayed_work(&host->detect, HZ)就会每隔HZ时间就会执行一次mmc_rescan
}

在host启动的时候,会执行一次mmc_host->detect工作,这时就开始进行轮询了。
      以上就实现了每隔1s执行一次mmc_rescan来扫描mmc硬件总线的变化情况。

Linux内核4.14版本——mmc框架——mmc硬件总线扫描流程(以sd card为例)相关推荐

  1. Linux内核4.14版本——alsa框架分析(1)—alsa简介

    目录 一,ALSA声音编程介绍 二,ALSA历史 三,数字音频基础 四,ALSA基础 五,ALSA体系结构 六,设备命名 七,声音缓存和数据传输 八,Over and Under Run 九,一个典型 ...

  2. Linux内核4.14版本——drm框架分析(1)——drm简介

    目录 1. DRM简介(Direct Rendering Manager) 1.1 DRM发展历史 1.2 DRM架构对比FB架构优势 1.3 DRM图形显示框架 1.4 DRM图形显示框架涉及元素 ...

  3. Linux内核4.14版本——alsa框架分析(8)-ASoC(Codec)

    1. 概述 ASOC的出现是为了让Codec独立于CPU,减少和CPU之间的耦合,这样同一个Codec驱动无需修 改就可以适用任何一款平台.还是以下图做参考例子: 在Machine中已经知道,snd_ ...

  4. Linux内核4.14版本——alsa框架分析(11)——DAPM(2)——widget、route和path的概念

    目录 1. DAPM的基本单元:widget(struct  snd_soc_dapm_widget) 2. widget的种类 3. widget之间的连接器:path(struct snd_soc ...

  5. Linux内核4.14版本:ARM64的内核启动过程(二)——start_kernel

    目录 1. rest_init 2. init 进程(kernel_init) 2.1 kernel_init_freeable 2.1.1 do_basic_setup 2.1.2 prepare_ ...

  6. Linux内核4.14版本——mmc框架_软件总体架构

    目录 1. 前言 2. 软件架构 3. 工作流程 4. mmc设备 4.1 mmc type card 4.2 sd type card 4.3 sdio type card 5. mmc协议 5.1 ...

  7. Linux内核4.14版本——mmc core(3)——host模块

    1. 前言 2. MMC host驱动介绍 3. 主要数据结构 3.1 struct mmc_host 3.2 struct mmc_host_ops 3.2.1 数据传输有关的函数 3.2.2 总线 ...

  8. Linux内核4.14版本——mmc core(4)——card相关模块(mmc type card)

    目录 1. 简介 1.1 mmc card的几种类型 1.2 mmc子系统如何区分使用哪种card的?(mmc_rescan) 2. mmc type card协议相关操作 3. 一些重要的API函数 ...

  9. Linux内核4.14版本——watchdog看门狗框架分析

    目录 0 简介 1. 设备的注册 1.1 dw_wdt_drv_probe 1.2 watchdog_register_device 1.3 __watchdog_register_device 1. ...

  10. Linux内核4.14版本——DMA Engine框架分析(6)-实战(测试dma驱动)

    1. dw-axi-dmac驱动 2. dma的测试程序 2.1 内核程序 2.2 用户测试程序 1. dw-axi-dmac驱动 dw-axi-dmac驱动4.14版本没有,是从5.4版本移植的,基 ...

最新文章

  1. codeforces #310 div1 C
  2. php http请求xml数据,php获取通过http协议post提交过来xml数据及解析xml
  3. struts2中报错404 No result defined for action com.jcrj.ahsfjd.ajgl.JdshAction and result input解决方法...
  4. java中有几种内部类,Java中的四种内部类
  5. vim E492: Not an editor command: ^M
  6. UVa439 Knight Move 骑士的移动(bfs)
  7. Shell简介:什么是Shell,Shell命令的两种执行方式
  8. 为SQLMap配置WebUI界面
  9. 《从零开始学Swift》学习笔记(Day 40)——析构函数
  10. 最新5G标准必要专利声明量排名:中兴通讯位列全球前三
  11. Android零碎要点---eclipse两个小技巧
  12. 变通实现微服务的per request以提高IO效率(三)
  13. leetcode python3 简单题70. Climbing Stairs
  14. windows知识点
  15. Linux 操作系统启动流程以及trouble shooting
  16. (原创)windows10cmd装逼命令
  17. matlab 一阶微分方程求解,一阶微分方程的matlab数值解法.doc
  18. 怎么创建自己的网站?创建自己网站的步骤
  19. 【GNN】时空图网络 tensorflow 实现
  20. 迷途emlog模板全站好看的变色模板源码(内含搭建教程)

热门文章

  1. LOL或迈入科技时代?多位主播遭实锤脚本,官方不作为疑似默许!
  2. 键盘鼠标录制哪个好用_美商海盗船Scimitar RGB Elite鼠标体验:再多技能也怕这把弯刀...
  3. kd树的构造和搜索(超详细)
  4. 鸿蒙应用开发 | 时间选择器(TimePicker)的功能和用法
  5. 金蝶kis专业版 服务器系统,金蝶kis专业版服务器设置
  6. 计算机网络二进制计算题
  7. js移动端文字提示框
  8. lol服务器维修2019,lol服务器是不是炸了 2019年3月23出现预料之外的错误
  9. 《个人信息安全规范》会让我们的信息更安全吗?
  10. 面试常见逻辑题小整理