高通驱动树中的GPIO详解

reference:https://blog.csdn.net/baidu_37503452/article/details/80257441

Drive Strength && tri-state相关概念

Drive Strength(也被称为:driving strength):表示“驱动强度”。这个参数用来控制信号强度,数值越大代表信号强度越高。

tri-state:三态,高电平、低电平、高阻。

实质

电路分析时高阻态可做开路理解。你可以把它看作输出(输入)电阻非常大。它的极限状态可以认为悬空(开路)。也就是说理论上高阻态不是悬空,它是对地或对电源电阻极大的状态。而实际应用上与引脚的悬空几乎是一样的。

意义

当门电路的输出上拉管导通而下拉管截止时,输出为高电平;反之就是低电平;如上拉管和下拉管都截止时,输出端就相当于浮空(没有电流流动),其电平随外部电平高低而定,即该门电路放弃对输出端电路的控制 。

表示方法

高阻态常用字母 Z 表示。

原有GPIO配置框架

之前所有的gpio操作都是通过gpiolib来实现,常用的api包括:

static inline int gpio_request(unsigned gpio, const char *label);
static inline int gpio_direction_input(unsigned gpio);
static inline int gpio_direction_output(unsigned gpio, int value);
static inline void gpio_set_value(unsigned gpio, int value);
static inline void gpio_free(unsigned gpio);

在硬件设计确定了某个设备需要使用哪些gpio之后,软件需要做的是:

以msm8916平台tp的中断为例

1)在msm8916-cdp.dsi中定义使用哪个gpio

i2c@f9924000{
goodix@5d{
compatible= "goodix,gt9xx";
reg= <0x5d>;
interrupt-parent= <&msmgpio>;
interrupts= <13 0x2>;
▲interrupt-gpios= <&msm_gpio 13 0x00>;
};
}

2)在board-8916-gpiomux.c中定义gpio的suspend和active状态

static struct gpiomux_setting ▲atmel_int_act_cfg = {
.func =GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
.pull = GPIOMUX_PULL_UP,
}; static struct gpiomux_setting ▲atmel_int_sus_cfg = {
.func =GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
.pull =GPIOMUX_PULL_NONE,
}; static struct msm_gpiomux_config ▲msm_touch_configs[] __initdata = {
▲ .gpio = 13,
.settings = {
[GPIOMUX_ACTIVE] = ▲ &atmel_int_act_cfg,
[GPIOMUX_SUSPENDED]= ▲ &atmel_int_sus_cfg,
},
},

PinControl 框架

Gpiolib方式的缺点在于:当同一套代码对应多个board设计时,需要在board--gpiomux.c文件中加宏进行区分。如在同一个分支上支持3个项目,在board-msm8974-gpiomux.c文件中添加了很多宏控。

pinctrl方式可以避免代码中的这种冗余代码:它将board--gpiomux.c文件中的配置信息移到xx-pinctrl.dtsi;这样,针对不同project的board设计,分别在各自project的xx-pinctrl.dtsi中定义各自的gpio配置信息。

Pinctrlsubsystem 分为3部分:Pinctrl core、Pinmux和Pinconf。

  • pinctrlcore是pincontrol子系统的核心,提供了和devicedriver交互的API;
  • pinmux用于实现pin的复用;
  • pinconf用于实现pin的配置,如输入/输出、pulldown/pull up、driverstrength等;另外还提供了用于debug的接口。

与gpio子系统的交互

虽然pinctrl提供了pinctrl_request_gpio()这样的API,但在代码中不可以直接调用pinctrl_request_gpio(),在该函数的定义处也有说明,如下:

/* This function should *ONLY* be used from gpiolib-based GPIO drivers,
* as part of their gpio_request() semantics, platforms and individualdrivers
* shall *NOT* request GPIO pins to be muxed in.”
*/

当设备驱动申请一个gpio时,仍然需要调用gpio_request(),这里会调用pinctrl_request_gpio()。调用过程如下:

gpio_request()
gpiod_request()
chip->request(chip,gpio_chip_hwgpio(desc));

在pinctrl_msm.c中,重新定义了chip->request()。

msm_pinctrl_probe()
msm_register_gpiochip()
gc->request= msm_pinctrl_request_gpio;

这里msm_pinctrl_request_gpio()会调pinctrl_request_gpio();

同样地,对于pinctrl_free_gpio()pinctrl_gpio_direction_input()pinctrl_gpio_direction_output()也有类似说明。

因此在clientdevice驱动中,申请和释放gpio仍然要调gpio_request()gpio_free();设置gpio为input/output仍然要调gpio_direction_input()gpio_direction_output()

Pinctrl注册

全文以msm8916平台为例进行分析。

当tlmm加载时,msm_tlmm_v4_probe()最后会调msm_pinctrl_probe(),其中会将pinctrl.dtsi中定义的pinctrlinfo解析出来,并且重新定义chip->request()chip->free()等函数。

具体的调用关系如下图所示:

postcore_initcall(msm_tlmm_v4_drv_register); //Pinctrl-msm-tlmm-v4.c
msm_tlmm_v4_probe //匹配pinctrl.dtsi定义的compatible
msm_pinctrl_probe //pinctrl_msm.c
msm_pinctrl_get_drvdata(dd,pdev); //解析pinctrl.dtsi,保存到dd
msm_pinctrl_dt_parse_pintype(node,dd);
msm_pinctrl_dt_parse_pins(node,dd);
msm_register_gpiochip(dd); //定义gpio_request()、gpio_free()
gc->request= msm_pinctrl_request_gpio;
gc->free= msm_pinctrl_free_gpio;
msm_register_pinctrl(dd);
dd->pctl_dev= pinctrl_register(ctrl_desc, dd->dev, dd);

Pinstates

一个pinstate对应对pin脚的一种配置,一个pin脚可以配置多个状态,对状态的个数也没有限制。

state的定义和电源管理关系比较紧密,例如当设备active的时候,我们需要pincontroller将相关的一组pin设定为具体的设备功能,而当设备进入sleep状态的时候,需要pincontroller将相关的一组pin设定为普通GPIO,并精确的控制GPIO状态以便节省系统的功耗。

Pinctrl-state.h中给出了常用的3种状态:

  • default:default状态表示设备处于active时的状态,一般在设备驱动的.resume中配置,另外在启动时也会配置pin脚为default状态。
  • idle:idle状态表示系统处于idle时需要配置的pin脚状态,此时系统并没有进入深度休眠。
  • sleep:sleep状态表示系统处于深度休眠时的pin脚状态,一般在设备驱动的.suspend中配置。

当然我们也可以定义任意形式的state,如“on”、“off”等。

goodix@5d{
compatible= "goodix,gt9xx";
reg= <0x5d>;
pinctrl-names= "gt9xx_int_active", "gt9xx_int_suspend";
pinctrl-0= <&gt9xx_int_active>;
pinctrl-1= <&gt9xx_int_sleep>;
interrupt-parent= <&msm_gpio>;
interrupts= <13 0x2>;
……
}

pinctrl-names定义了clientdevice用到的state列表。

  • state有两种标识,一种就是pinctrl-names定义的字符串列表,另外一种就是ID。ID从0开始,依次加一。根据例子中的定义,stateID等于0(名字是"gt9xx_int_active")的state对应pinctrl-0属性,stateID等于1(名字是"gt9xx_int_suspend")的state对应pinctrl-1属性。

pinctrl-x是一个句柄(phandle)列表,每个句柄指向一个pinconfiguration。

Boot时配置default状态

如果pin只定义了default状态,那么在设备驱动中不需要再对该pin作处理,因为在启动时会自动设为default状态。

在加载驱动模块时,如果驱动和设备匹配,最终就会调到driver定义的probe函数。在这个过程中,如果使能了pinctrl,而且定义了pin的default状态,就会配置pin脚为该状态。

具体代码流程如下:

driver_probe_device(structdevice_driver *drv, struct device *dev)
{
really_probe(dev,drv);
pinctrl_bind_pins(dev); // 函数调用过程如下:
dev->pins->p= devm_pinctrl_get(dev);
dev->pins->default_state= pinctrl_lookup_state(dev->pins->p,
PINCTRL_STATE_DEFAULT);
pinctrl_select_state(dev->pins->p,dev->pins->default_state);
对于不使用pinctrl的平台,pinctrl_bind_pins(dev)直接返回0;
if(dev->bus->probe) {
ret= dev->bus->probe(dev);
}else if (drv->probe) {
ret= drv->probe(dev);
}
}

Pingroups

SOC上需要同时配置一组gpio来支持某些功能,如I2C、SPI、UART、SDC等,在-pinctrl.dtsi中将这些pin定义为一个group。

配置统一的情况

一组gpio在各个状态下的配置都相同,这种配置比较常见也比较简单。如i2c,在active和suspend状态下,SDA和SCL的配置都相同:active状态下都是配置为8mA上拉,suspend状态下都配置为2mA和nopull。

首先在msm8916_pinctrl.c中定义suspend和active状态下的pin脚配置信息,如下:

pmx_i2c_0{
qcom,pins= <&gp 7>, <&gp 6>; //使用gpio_6和gpio_7
qcom,num-grp-pins= <2>; //共两个gpio
qcom,pin-func= <3>; //复用功能为i2c
label = "pmx_i2c_0"; //表示同一组
i2c_0_active:i2c_0_active {
drive-strength= <8>;
bias-pull-up;
}; i2c_0_sleep:i2c_0_sleep {
drive-strength= <2>;
bias-disable;
};
};

然后在msm8916.dtsi中增加pinctrlinfo的引用:

i2c_0:i2c@78b6000 {
compatible= "qcom,i2c-msm-v2";
reg-names= "qup_phys_addr", "bam_phys_addr";
reg= <0x78b6000 0x600>,
<0x7884000 0x23000>;
interrupt-names= "qup_irq", "bam_irq";
interrupts= <0 96 0>, <0 238 0>;
clocks= <&clock_gcc clk_gcc_blsp1_ahb_clk>,
<&clock_gcc clk_gcc_blsp1_qup2_i2c_apps_clk>;
clock-names= "iface_clk", "core_clk";
qcom,clk-freq-out= <100000>;
qcom,clk-freq-in = <19200000>;
pinctrl-names= "i2c_active", "i2c_sleep";
pinctrl-0= <&i2c_0_active>;
pinctrl-1= <&i2c_0_sleep>;
qcom,master-id= <86>;
};

配置不统一的情况

如SDC,每个pin脚的active和sleep状态配置各不相同,需要分开设置。

1)在msm8916_pinctrl.c中分别定义各个pin的suspend和active状态下的配置信息,如下:

sdc:sdc {
qcom,pin-type-sdc;
qcom,num-pins= <6>;
#qcom,pin-cells= <1>;
}; pmx_sdc1_clk{
qcom,pins= <&sdc 0>;
qcom,num-grp-pins= <1>;
label= "sdc1-clk";
sdc1_clk_on:clk_on {
bias-disable;
drive-strength= <16>;
};
sdc1_clk_off:clk_off {
bias-disable;
drive-strength= <2>;
};
}; pmx_sdc1_cmd{
qcom,pins= <&sdc 1>;
qcom,num-grp-pins= <1>;
label= "sdc1-cmd";
sdc1_cmd_on:cmd_on {
bias-pull-up;
drive-strength= <10>;
};
sdc1_cmd_off:cmd_off {
bias-pull-up;
drive-strength= <2>;
};
}; pmx_sdc1_data{
qcom,pins= <&sdc 2>;
qcom,num-grp-pins= <1>;
label= "sdc1-data";
sdc1_data_on:data_on {
bias-pull-up;
drive-strength= <10>;
};
sdc1_data_off:data_off {
bias-pull-up;
drive-strength= <2>;
};
};

2)msm8916-cdp.dtsi中作出相应修改:

&sdhc_1{
vdd-supply= <&pm8916_l8>;
qcom,vdd-voltage-level= <2900000 2900000>;
qcom,vdd-current-level= <200 400000>;
vdd-io-supply= <&pm8916_l5>;
qcom,vdd-io-always-on;
qcom,vdd-io-lpm-sup;
qcom,vdd-io-voltage-level= <1800000 1800000>;
qcom,vdd-io-current-level= <200 60000>;
//qcom,pad-pull-on= <0x0 0x3 0x3>;
//qcom,pad-pull-off= <0x0 0x3 0x3>;
//qcom,pad-drv-on= <0x4 0x4 0x4>;
//qcom,pad-drv-off= <0x0 0x0 0x0>;
pinctrl-names= "active", "sleep";
pinctrl-0= <&sdc1_clk_on &sdc1_cmd_on &sdc1_data_on>;
pinctrl-1= <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off>;
qcom,nonremovable;
status= "ok";
};

对sdhc_2也是同样的配置。

另外还有一种情况,对于一组gpio,在不同state下pin_func定义不同的情况,如wifi,在active状态设置为wifi功能,在suspend状态下设置为普通gpio。

pinctrl@fd511000{
//...
pmx-wcnss-5wire-active{
qcom,pins= <&gp 40>, <&gp 41>, <&gp 42>, <&gp43>.
<&gp44>;
qcom,pin-func= <1>;
qcom,num-grp-pins= <5>;
label= "wcnss-5wire-active";
wcnss-5wire-active:wcnss-active {
drive-strength= <6>; / * 6MA */
bias-pull-up;
};
};
pmx-wcnss-5wire-suspend{
qcom,pins= <&gp 40>, <&gp 41>, <&gp 42>, <&gp43>.
<&gp44>;
qcom,pin-func= <0>;
qcom,num-grp-pins= <5>;
label= "wcnss-5wire-suspend";
wcnss-5wire-sleep:wcnss-sleep {
drive-strength= <6>; / * 6MA */
bias-pull-down;
};
};
};

PinctrlAPI

//获取该device对应的pinctrlhandler。
struct pinctrl *devm_pinctrl_get(struct device *dev); // 查找name指定的pinctrlstate。
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char*name); // 配置pin脚为指定的state。
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state);

Usecase

下面举例配置tp的中断脚。

1)在msm8916-pinctrl.dtsi中定义pinctrlinfo:

&soc{
tlmm_pinmux:pinctrl@1000000
gt9xx_int_pin{
▲ qcom,pins= <&gp 13>;
qcom,num-grp-pins= <1>;
qcom,pin-func= <0>;
label= "gt9xx_int_pin"; ▲ gt9xx_int_active:active {
drive-strength= <2>;
bias-pull-up;
}; ▲gt9xx_int_sleep:sleep {
drive-strength= <2>;
bias-disable;
};
};
}

2)在msm8916-cdp.dtsi中tp的节点中添加引用:

goodix@5d{
compatible= "goodix,gt9xx";
reg= <0x5d>;
▲ pinctrl-names= "gt9xx_int_active", "gt9xx_int_suspend";
▲ pinctrl-0= <>9xx_int_active>;
▲ pinctrl-1= <>9xx_int_sleep>;
▲ interrupt-parent= <&msm_gpio>;
interrupts= <13 0x2>;
//……
}

3)在tp驱动中添加配置。

a.定义pinctrl_info:

#define GOODIX_PINCTRL_STATE_SLEEP "gt9xx_int_suspend"
#define GOODIX_PINCTRL_STATE_DEFAULT "gt9xx_int_active"
struct gtp_pinctrl_info{
structpinctrl *pinctrl;
structpinctrl_state *gpio_state_active;
structpinctrl_state *gpio_state_suspend;
};
static struct gtp_pinctrl_info gt9xx_pctrl;
static int **gtp_pinctrl_init**(struct device *dev)
{
gt9xx_pctrl.pinctrl= ▲ devm_pinctrl_get(dev);
if(IS_ERR_OR_NULL(gt9xx_pctrl.pinctrl)) {
pr_err("%s:%dGetting pinctrl handle failed\n",
__func__,__LINE__);
return-EINVAL;
}
▲ gt9xx_pctrl.gpio_state_active= pinctrl_lookup_state(gt9xx_pctrl.pinctrl,
GOODIX_PINCTRL_STATE_DEFAULT);
if(IS_ERR_OR_NULL(gt9xx_pctrl.gpio_state_active)) {
pr_err("%s:Failed to get the active state pinctrl handle\n",
__func__,__LINE__);
return-EINVAL;
}
gt9xx_pctrl.gpio_state_suspend= pinctrl_lookup_state(
gt9xx_pctrl.pinctrl,
GOODIX_PINCTRL_STATE_SLEEP);
if(IS_ERR_OR_NULL(gt9xx_pctrl.gpio_state_suspend)) {
pr_err("%s:Failed to get the suspend state pinctrl handle\n",
__func__,__LINE__);
return-EINVAL;
}
return0;
}

b.在probe函数中初始化pinctrl_info,并设置state:

static int goodix_ts_probe(struct i2c_client *client, const structi2c_device_id *id)
{
goodix_parse_dt(&client->dev,pdata);
gtp_request_io_port(ts);
gtp_pinctrl_init(&ts->client->dev);
pinctrl_select_state(gt9xx_pctrl.pinctrl,gt9xx_pctrl.gpio_state_active);
// ……
}

c.在suspend()和resume()中分别设置为activestate和suspendstate:

static int goodix_ts_suspend(struct device *dev)
{
struct goodix_ts_data *ts = dev_get_drvdata(dev);
int ret = 0, i;
ret= pinctrl_select_state(gt9xx_pctrl.pinctrl,
gt9xx_pctrl.gpio_state_suspend);
if(ret)
pr_err("%s:Cannot set pin to suspend state",
__func__,__LINE__);
// ……
if(ts->use_irq)
gtp_irq_disable(ts);
// ……
return ret;
}
static int goodix_ts_resume(struct device *dev)
{
struct goodix_ts_data *ts = dev_get_drvdata(dev);
int ret = 0;
ret= pinctrl_select_state(gt9xx_pctrl.pinctrl,
gt9xx_pctrl.gpio_state_active);
if(ret)
pr_err("%s:Cannot set pin to suspend state",
__func__,__LINE__);
// ……
if(ts->use_irq)
gtp_irq_enable(ts);
// ……
returnret;
}

高通驱动树中的GPIO详解的更多相关文章

  1. Linux驱动开发必看详解神秘内核(完全转载)

    Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html   IT168 技术文档]在开始步入L ...

  2. ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)

    前面的六篇文章,我们已经讨论了dapm关于动态电源管理的有关知识,包括widget的创建和初始化,widget之间的连接以及widget的上下电顺序等等.本章我们准备讨论dapm框架中的另一个机制:事 ...

  3. ALSA声卡驱动中的DAPM详解之三:如何定义各种widget

    上一节中,介绍了DAPM框架中几个重要的数据结构:snd_soc_dapm_widget,snd_soc_dapm_path,snd_soc_dapm_route.其中snd_soc_dapm_pat ...

  4. 高并发网络编程之epoll详解(转载)

    高并发网络编程之epoll详解(转载) 转载自:https://blog.csdn.net/shenya1314/article/details/73691088 在linux 没有实现epoll事件 ...

  5. ALSA声卡驱动中的DAPM详解之六:精髓所在,牵一发而动全身

    设计dapm的主要目的之一,就是希望声卡上的各种部件的电源按需分配,需要的就上电,不需要的就下电,使得整个音频系统总是处于最小的耗电状态,最主要的就是,这一切对用户空间的应用程序是透明的,也就是说,用 ...

  6. ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

    前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...

  7. App域名劫持之DNS高可用 - 开源版HttpDNS方案详解(转)

      http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=209805123&idx=1&sn=ced8d67c3e2cc3 ...

  8. NVIDIA显卡笔记本安装ubuntu驱动以及分辨率之详解

    随着对ubuntu的了解,突然想在自己的笔记本上装一个双系统.在网上查了安装方法之后,发现因为nvidia显卡的原因会出现一些问题,结果在我自己装了之后发现问题要比看到的多,再看了无数个帖子之后,最终 ...

  9. 基于SOA的高并发和高可用分布式系统架构和组件详解

    基于SOA的分布式高可用架构和微服务架构,是时下如日中天的互联网企业级系统开发架构选择方案.在核心思想上,两者都主张对系统的横向细分和扩展,按不同的业务功能模块来对系统进行分割并且使用一定的手段实现服 ...

  10. 高并发网络编程之epoll详解

    select.poll和epoll的区别 在linux没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序.在大数据.高并发.集群等一些名词 ...

随机推荐

  1. 习题8 #第8章 Verilog有限状态机设计-1 #Verilog #Quartus #modelsim

    1. 设计一个"111"串行数据检测器.要求是:当检测到连续3个或3个以上的"1"时输出为1,其他输入情况下输出为0. (1)思路分析:参照本章前文的范例,如第 ...

  2. 零侵入!试试这款Api接口文档生成器!

    大家好,我是 Java陈序员. 作为一名合格的程序员,不仅代码要写好,而且文档要写好. 虽然目前有成熟的框架可以快速生成接口文档,如大名鼎鼎的 Swagger.但是 Swagger 需要编写大量的注解 ...

  3. 这几个好用的 Google 搜索技巧,让你飞起!

    搜索能力是被绝大多数人低估一项基本素质,绝大部分做编程技术相关的朋友应该都知道如何使用 Google,但是并不知道如何利用它的潜力.其实不管是 Google 还是 百度,会搜索的人一样都可以查找到需要 ...

  4. 08. rails 创建user控制器-用户添加

    创建控制器 如果创始的数据模型是对应一张表,那么要使用复数的形式 例如给users表创建一个控制器 rails g controller users 路由 config/routes.rb里添加 re ...

  5. gorm 动态拼接查询条件

    结构体 type Mould struct { MouldId string `grom:"column:mouldID"` MouldInteriorID string `gro ...

  6. 移动端termux安装kali

    1.相关准备一部安卓手机,termux,NVAC,浏览器2.安装kali首先进入kali的官网选择文档找到Android手机上的kali找到NetHunter-Rootless找到kali安装命令:t ...

  7. ETSI GS MEC 013,UE 位置 API

    目录 文章目录 目录 版本 功能理解 Relation with OMA APIs Relation with OMA API for Zonal Presence Relation with OMA ...

  8. 自动化部署elasticsearch三节点集群

    什么是Elasticsearch? Elasticsearch 是一个开源的分布式搜索和分析引擎,构建在 Apache Lucene 的基础上.它提供了一个分布式多租户的全文搜索引擎,具有实时分析功能 ...

  9. Android 13 - Media框架(31)- ACodec(七)

    关注公众号免费阅读全文,进入音视频开发技术分享群! 之前的章节中我们解了 input buffer 是如何传递给 OMX 的,以及Output buffer 是如何分配并且注册给 OMX 的.这一节我 ...

  10. Windows库链接报错

    问题回溯 今天拿到别人已经编译好的库,发现在链接的时候出现了报错 [9/9 12.7/sec] Linking CXX shared module bin\plugins\AsensingPlugin ...