9. [mmc subsystem] host(第三章)——sdhci-pltfm说明
一、sdhci-pltfm说明
sdhci-pltfm并不是实际某个host的driver。
sdhci-pltfm是指在sdhci core的基础上,提供了统一对sdhci_host的必要属性进行解析和设置的方法。
但是,对于sdhci类的host driver来说,使用sdhci-pltfm并不是必须的,host driver也可以自己来实现对应的操作。
通过《host(第二章)——sdhci》,我们知道了host driver调用sdhci_add_host注册sdhci_host的之前需要设置的信息如下:
- sdhci的寄存器的映射过后的基地址(sdhci_host->ioaddr)
- sdhci的癖好quirks、quirks2(sdhci_host->quirks,sdhci_host->quirks2)
- sdhci的中断号(sdhci_host->irq)
- host提供给sdhci core用来操作硬件的操作集(sdhci_host->ops)
因此,sdhci-pltfm实现了两个方法来统一设置这些信息,方便host driver对于sdhci driver的使用。
后续继续说明。
二、数据结构说明
1、sdhci_pltfm_data
首先看一下sdhci-pltfm要设置的sdhci_host的成员的来源信息:
- sdhci的寄存器的映射过后的基地址(sdhci_host->ioaddr)
由DTS节点中的地址属性解析出来寄存器的物理地址之后,进行映射得到
- sdhci的癖好quirks、quirks2(sdhci_host->quirks,sdhci_host->quirks2)
由平台host驱动(host driver)提供最基本的值,后续会进行调整
- sdhci的中断号(sdhci_host->irq)
由DTS节点中的中断属性解析出来
- host提供给sdhci core用来操作硬件的操作集(sdhci_host->ops)
由平台host驱动(host driver)提供
综上,ops、quirks和quirks2这几个的值是必须由平台host驱动(host driver)提供,而ioaddr和irq可以通过解析属性得到。
因此,sdhci-pltfm把ops、quirks和quirks2的值封装到sdhci_pltfm_data中,由底层host驱动提供。
其内容如下:
struct sdhci_pltfm_data {
const struct sdhci_ops *ops; // host提供给sdhci core用来操作硬件的操作集
unsigned int quirks; // sdhci的癖好quirks
unsigned int quirks2; // sdhci的癖好quirks2
};
2、sdhci_pltfm_host
sdhci_pltfm也为host抽象出一个host结构体sdhci_pltfm_host来作为sdhci_host和平台定制的host的中间层。
struct sdhci_pltfm_host {
struct clk *clk;
/* migrate from sdhci_of_host */
unsigned int clock;
u16 xfer_mode_shadow;
unsigned long private[0] ____cacheline_aligned;
};
以高通定制的host结构体sdhci_msm_host为例,三者之间的关系如下:
sdhci_host->private = sdhci_pltfm_host
sdhci_pltfm_host->priv = sdhci_msm_host
platform_get_drvdata(sdhci_msm_host->struct platform_device) = sdhci_host
sdhci_pltfm_host = sdhci_priv(sdhci_host);
sdhci_msm_host= sdhci_pltfm_priv(sdhci_pltfm_host);
三、API总览
1、sdhci_pltfm分配和释放相关
- sdhci_pltfm_init & sdhci_pltfm_free
由底层host driver调用。
sdhci_pltfm_init 用于分配sdhci_pltfm_host和sdhci_host的部分成员进行设置。
sdhci_pltfm_free用于释放sdhci_pltfm_host和sdhci_host。
原型:struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata)
参数说明:struct platform_device *pdev——》host对应的平台设备的device
struct sdhci_pltfm_data *pdata——》需要host driver提供给sdhci_host的一些信息,前面说过了
使用案例:sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata);
原型:void sdhci_pltfm_free(struct platform_device *pdev)
2、属性解析相关
- sdhci_get_of_property
由底层host driver调用。
用来解析host的dtsi节点的部分属性,前提是要求这部分属性必须按照一定的规范来。
原型:void sdhci_get_of_property(struct platform_device *pdev)
3、sdhci_host注册相关
- sdhci_pltfm_register & sdhci_pltfm_unregister
由底层host driver调用。
sdhci_pltfm_register 直接根据sdhci_pltfm_data来注册一个sdhci_host,会调用上述的sdhci_pltfm_init 和sdhci_get_of_property操作。
注意,但是一般用得比较少,因为host driver得到sdhci_host可能需要根据自己的需求来设置sdhci_host,而不是马上注册sdhci_host。
原型:int sdhci_pltfm_register(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata)
参数说明:struct platform_device *pdev——》host对应的平台设备的device
struct sdhci_pltfm_data *pdata——》需要host driver提供给sdhci_host的一些信息,前面说过了
四、接口代码说明
1、sdhci_pltfm_init
主要工作
- 调用sdhci_alloc_host分配一个sdhci_host
- 根据sdhci_pltfm_data设置sdhci_host->ops
- 根据sdhci_pltfm_data设置sdhci_host->quirks
- 根据sdhci_pltfm_data设置sdhci_host->quirks2
- 获取dtsi节点中的第一个中断属性并申请,设置sdhci_host->irq
- 获取dtsi节点中的第一个寄存器属性并映射,设置sdhci_host->ioaddr
- 调用平台的初始化操作
具体代码如下
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata)
{
// struct platform_device:sdhci host的平台设备
// const struct sdhci_pltfm_data:sdhci host的平台数据结构体,包含了对应host的sdhci_ops操作集
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct device_node *np = pdev->dev.of_node;
struct resource *iomem;
int ret;
/* 获取sdhci内存资源区域 */
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 所以在dtsi节点中,sdhci的内存资源必须作为内存列表的第一个属性!!!!
/* 调用sdhci_alloc_host获取一个标准的struct sdhci_host结构体 */
host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
/* 将struct sdhci_host的私有数据和struct sdhci_pltfm_host关联 */
pltfm_host = sdhci_priv(host);
/* 根据传进来的sdhci host的平台数据来初始化sdhci_host的ops、quirks */
host->hw_name = dev_name(&pdev->dev);
host->ops = pdata->ops;
host->quirks = pdata->quirks;
/* 获取中断 */
host->irq = platform_get_irq(pdev, 0); // 所以在dtsi节点中,sdhci的中断属性必须作为中断列表的第一个属性!!!!!
/* 申请sdhci的内存资源并且进行映射 */
if (!request_mem_region(iomem->start, resource_size(iomem),
mmc_hostname(host->mmc))) {
dev_err(&pdev->dev, "cannot request region\n");
ret = -EBUSY;
goto err_request;
}
host->ioaddr = ioremap(iomem->start, resource_size(iomem));
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
ret = -ENOMEM;
goto err_remap;
}
/* 调用host->ops->platform_init进行初始化 */
if (host->ops && host->ops->platform_init)
host->ops->platform_init(host);
/* 将struct sdhci_host作为对应host的私有数据 */
platform_set_drvdata(pdev, host);
return host;
}
注意,通过上述代码,sdhci-pltfm要求必须把sdhci的寄存器属性放在host的dtsi的寄存器属性的第一个,同时,也要把sdhci的中断属性放在host的dtsi的中断属性的第一个。简单dtsi的例子如下图所示:
sdhc_1: sdhci@07824000 {
reg = <0x07824900 0x11c>, <0x07824000 0x800>;
reg-names = "hc_mem", "core_mem"; // 其中,hc_mem表示sdhci的寄存器属性,放在了第一个
interrupts = <0 123 0>, <0 138 0>;
interrupt-names = "hc_irq", "pwr_irq"; // 其中,hc_irq表示sdhci的中断,放在了第一个
2、sdhci_get_of_property
代码如下:
void sdhci_get_of_property(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host = platform_get_drvdata(pdev); // 从平台设备结构体中获取私有数据,对应就是sdhci_host
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); // sdhci_host的私有护具就是struct sdhci_pltfm_host
const __be32 *clk;
u32 bus_width;
int size;
if (of_device_is_available(np)) {
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
// 解析"sdhci,auto-cmd12"属性,设置quirks的SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12标识
// sdhci,auto-cmd12————》SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12
// Controller uses Auto CMD12 command to stop the transfer,控制器使用CMD12自动结束传输
if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
(of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
bus_width == 1))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
// 解析"sdhci,1-bit-only"属性,设置quirks的SDHCI_QUIRK_FORCE_1_BIT_DATA标识
// sdhci,1-bit-only————》SDHCI_QUIRK_FORCE_1_BIT_DATA
// Controller can only handle 1-bit data transfers,该sdhci controller只支持1bit位宽传输
if (sdhci_of_wp_inverted(np))
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
// sdhci,wp-inverted | wp-inverted————》SDHCI_QUIRK_INVERTED_WRITE_PROTECT
if (of_get_property(np, "broken-cd", NULL))
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
// broken-cd————》SDHCI_QUIRK_BROKEN_CARD_DETECTION
// Controller has unreliable card detection,sdhci controller没有实现card检测
if (of_get_property(np, "no-1-8-v", NULL))
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
// no-1-8-v————》SDHCI_QUIRK2_NO_1_8_V
// The system physically doesn't support 1.8v, even if the host does,不支持1.8V
clk = of_get_property(np, "clock-frequency", &size);
if (clk && size == sizeof(*clk) && *clk)
pltfm_host->clock = be32_to_cpup(clk);
// clock-frequency————》pltfm_host->clock
// 获取时钟频率
if (of_find_property(np, "keep-power-in-suspend", NULL))
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", NULL))
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
}
3、sdhci_pltfm_register
使用得比较少,简单了解下即可。
int sdhci_pltfm_register(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata)
{
struct sdhci_host *host;
int ret = 0;
/* 调用sdhci_pltfm_init分配并初始化一个sdhci_host */
host = sdhci_pltfm_init(pdev, pdata);
if (IS_ERR(host))
return PTR_ERR(host);
/* 调用sdhci_get_of_property解析dtsi属性并设置sdhci_host的部分成员 */
sdhci_get_of_property(pdev);
/* 调用sdhci_add_host将该sdhci_host注册到sdhci core中 */
ret = sdhci_add_host(host);
if (ret)
sdhci_pltfm_free(pdev);
return ret;
}
9. [mmc subsystem] host(第三章)——sdhci-pltfm说明的更多相关文章
- 8. [mmc subsystem] host(第二章)——sdhci
一.sdhci core说明 1.sdhci说明 具体参考<host(第一章)--概述> SDHC:Secure Digital(SD) Host Controller,是指一套sd ho ...
- 7. [mmc subsystem] host(第一章)——概述
一.host简单说明 host,也可以理解为host controller,是指mmc总线上的主机端,mmc总线的控制器,每个host controller对应一条mmc总线. host contro ...
- 10. [mmc subsystem] host(第四章)——host实例(sdhci-msm说明)
一.说明 sdhci-msm是指高通的mmc host,其使用了标准SDHC标准.故可以使用前面说的<host(第二章)--sdhci>和<host(第三章)--sdhci-pltf ...
- 3. [mmc subsystem] mmc core(第三章)——bus模块说明
零.说明 对应代码drivers/mmc/core/bus.c. 抽象出虚拟mmc bus,实现mmc bus的操作. 一.API总览 1.mmc bus相关 mmc_register_bus &am ...
- 4. [mmc subsystem] mmc core(第四章)——host模块说明
零.说明 对应代码drivers/mmc/core/host.c,drivers/mmc/core/host.h. 为底层host controller driver实现mmc host的申请以及注册 ...
- 6. [mmc subsystem] mmc core(第六章)——mmc core主模块
一.说明 1.mmc core概述 mmc core主模块是mmc core的实现核心.也是本章的重点内容. 对应代码位置drivers/mmc/core/core.c. 其主要负责如下功能: mmc ...
- 5. [mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)
零.说明(重要,需要先搞清楚概念有助于后面的理解) 1.mmc core card相关模块为对应card实现相应的操作,包括初始化操作.以及对应的总线操作集合.负责和对应card协议层相关的东西. 这 ...
- 1. [mmc subsystem] 概念与框架
一.概念 1.mmc的概念 mmc有很多种意义,具体如下: mmc MultiMedia Card,多媒体存储卡, 但后续泛指一个接口协定(一种卡式),能符合这接口的内存器都可称作mmc储存体. 主要 ...
- 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...
随机推荐
- C学习笔记(10)--- 强制类型转换,错误处理,递归
1.强制类型转换: 强制类型转换是把变量从一种类型转换为另一种数据类型.例如,如果您想存储一个 long 类型的值到一个简单的整型中,您需要把 long 类型强制转换为 int 类型. 您可以使用强制 ...
- pyqt5环境变量踩坑记
之前用一个cmd脚本 wmic ENVIRONMENT create name="QT_QPA_PLATFORM_PLUGIN_PATH",username="<s ...
- A1101 Quick Sort (25 分)
一.技术总结 这里的一个关键就是理解调换位置排序是时,如果是元主,那么它要确保的条件就只有两个一个是,自己的位置不变,还有就是前面的元素不能有比自己大的. 二.参考代码 #include<ios ...
- 物联网架构成长之路(39)-Bladex开发框架环境搭建
0.前言 上一篇博客已经介绍了,阶段性小结.目前第一版的物联网平台已经趋于完成.框架基本不变了,剩下就是调整一些UI,还有配合硬件和市场那边,看看怎么推广这个平台.能不能挣点外快.第一版系统虽然简陋, ...
- (二十三)golang--内置函数
1.用于求长度,占多少个字节 2.内置函数new:分配内存,主要用来分配值类型,比如int.float等,其第一个实参为类型,而非值,其返回值为指向该类型的新分配的零值的指针: 3.make:用来分配 ...
- pip 设置阿里云源
在~/.pip/pip.conf文件中添加或修改 mkdir ~/.pip [global] index-url = http://mirrors.aliyun.com/pypi/simple/ [i ...
- nginx二级域名反向代理
nginx二级域名反向代理 添加两个开发测试的域名 test.xxx.com :8088 testmobile.xxx.com: 8089 内网地址:127.0.0.1 外网地址:127.0.0.1 ...
- 此贴告诉你:为啥shell脚本人,不建议学python
py很强大,我承认.但在运维方面,py不但不强大,还有硬伤.正因为有下述硬伤,所以我们运维,还是用shell多,用py极少.我看到用shell的人很多,你建议人用python,人说py是很好,但下一秒 ...
- 配置每次git push 不需要输入账号密码
配置每次git push 不需要输入账号密码 .gitconfig文件地址 C:\Users\Admin
- 初探云原生应用管理(二): 为什么你必须尽快转向 Helm v3
系列介绍:这个系列是介绍如何用云原生技术来构建.测试.部署.和管理应用的内容专辑.做这个系列的初衷是为了推广云原生应用管理的最佳实践,以及传播开源标准和知识.在这个系列文章的开篇初探云原生应用管理(一 ...