Linux内核4.14版本——mmc框架——mmc硬件总线扫描流程(以sd card为例)
目录
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为例)相关推荐
- Linux内核4.14版本——alsa框架分析(1)—alsa简介
目录 一,ALSA声音编程介绍 二,ALSA历史 三,数字音频基础 四,ALSA基础 五,ALSA体系结构 六,设备命名 七,声音缓存和数据传输 八,Over and Under Run 九,一个典型 ...
- Linux内核4.14版本——drm框架分析(1)——drm简介
目录 1. DRM简介(Direct Rendering Manager) 1.1 DRM发展历史 1.2 DRM架构对比FB架构优势 1.3 DRM图形显示框架 1.4 DRM图形显示框架涉及元素 ...
- Linux内核4.14版本——alsa框架分析(8)-ASoC(Codec)
1. 概述 ASOC的出现是为了让Codec独立于CPU,减少和CPU之间的耦合,这样同一个Codec驱动无需修 改就可以适用任何一款平台.还是以下图做参考例子: 在Machine中已经知道,snd_ ...
- 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 ...
- 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_ ...
- 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 ...
- 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 总线 ...
- 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函数 ...
- Linux内核4.14版本——watchdog看门狗框架分析
目录 0 简介 1. 设备的注册 1.1 dw_wdt_drv_probe 1.2 watchdog_register_device 1.3 __watchdog_register_device 1. ...
- 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版本移植的,基 ...
最新文章
- codeforces #310 div1 C
- php http请求xml数据,php获取通过http协议post提交过来xml数据及解析xml
- struts2中报错404 No result defined for action com.jcrj.ahsfjd.ajgl.JdshAction and result input解决方法...
- java中有几种内部类,Java中的四种内部类
- vim E492: Not an editor command: ^M
- UVa439 Knight Move 骑士的移动(bfs)
- Shell简介:什么是Shell,Shell命令的两种执行方式
- 为SQLMap配置WebUI界面
- 《从零开始学Swift》学习笔记(Day 40)——析构函数
- 最新5G标准必要专利声明量排名:中兴通讯位列全球前三
- Android零碎要点---eclipse两个小技巧
- 变通实现微服务的per request以提高IO效率(三)
- leetcode python3 简单题70. Climbing Stairs
- windows知识点
- Linux 操作系统启动流程以及trouble shooting
- (原创)windows10cmd装逼命令
- matlab 一阶微分方程求解,一阶微分方程的matlab数值解法.doc
- 怎么创建自己的网站?创建自己网站的步骤
- 【GNN】时空图网络 tensorflow 实现
- 迷途emlog模板全站好看的变色模板源码(内含搭建教程)
热门文章
- LOL或迈入科技时代?多位主播遭实锤脚本,官方不作为疑似默许!
- 键盘鼠标录制哪个好用_美商海盗船Scimitar RGB Elite鼠标体验:再多技能也怕这把弯刀...
- kd树的构造和搜索(超详细)
- 鸿蒙应用开发 | 时间选择器(TimePicker)的功能和用法
- 金蝶kis专业版 服务器系统,金蝶kis专业版服务器设置
- 计算机网络二进制计算题
- js移动端文字提示框
- lol服务器维修2019,lol服务器是不是炸了 2019年3月23出现预料之外的错误
- 《个人信息安全规范》会让我们的信息更安全吗?
- 面试常见逻辑题小整理