高通驱动树中的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. LabView中使用VISA设备清零时,会发送00

    最近有为小伙伴跟我说他使用串口的时候通信遇到了问题,我看到他的在程序循环中使用了VISA设备清零控件,出于好奇我就复现了一下,发现每次调用VISA设备清零控件的时候,会主动向串口中发送00数据 一.测 ...

  2. 基于FPGA的音乐蜂鸣器设计

    蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机.打印机.复印机.报警器.电子玩具.汽车电子设备.电话机.定时器等电子产品中作发声器件. 图1 :蜂鸣器实物图 蜂鸣器主要分为压电 ...

  3. C语言程序设计-笔记7-指针

    C语言程序设计-笔记7-指针 例8-1  利用指针模拟密码开锁游戏. #include<stdio.h> int main(void) { int x=5342;          //变 ...

  4. Oracle、达梦:数据库大小写不敏感,但是又要区分大小写敏感(默认敏感)

    一. 艹,这个需求就很操蛋. 实现 SELECT * FROM T1 WHERE REGEXP_LIKE(field, '.*value.*', 'c'); 在 Oracle 数据库中使用 REGEX ...

  5. java中调用exe程序和问题处理

    常规方法如下: public class RunExe { public static void main(String[] args) { try { // exe文件的完整路径 String fi ...

  6. IPv6 — 综合组网技术

    目录 文章目录 目录 前文列表 IPv4v6 综合组网技术(转换机制) 双栈策略 隧道策略 前文列表 <IPv6 - 网际协议第 6 版> <IPv6 - 地址格式与寻址模式> ...

  7. 撤销 git commit

    目录 文章目录 目录 场景1:撤回 commit,不撤销 git add .,保留代码 场景2:撤回 commit,撤销 git add .,保留代码 场景3:撤销 commit,撤销 git add ...

  8. 一键自动化博客发布工具,用过的人都说好(51cto篇)

    51cto是一个优秀的博客平台,今天给大家讲解一下blog-auto-publishing-tools如何自动发布博客到51cto上. 当然在实现过程中有可能会遇到各种困难,不过不用担心,我们一个个来 ...

  9. NODEJS通过发送json数据查询目标服务,实现服务器状态监控,发现异常发送到微信群提醒

    root@aea87fa6e6a2:/home/node# cat login2.js const request = require('request-promise'); const moment ...

  10. vue-router单页面应用的多标签页使用问题

    正常的思维 做多vue页面应用,我们的第一反应是配置多个入口点,多个vue应用,编译成多个HTML文件,由服务器来决定路由.这是正常的思维. 但谁知道单页面应用也能做到类似的效果呢.单页面不过是服务器 ...