reference:https://blog.csdn.net/baidu_37503452/article/details/80257441
Drive Strength(也被称为:driving strength):表示“驱动强度”。这个参数用来控制信号强度,数值越大代表信号强度越高。
tri-state:三态,高电平、低电平、高阻。
电路分析时高阻态可做开路理解。你可以把它看作输出(输入)电阻非常大。它的极限状态可以认为悬空(开路)。也就是说理论上高阻态不是悬空,它是对地或对电源电阻极大的状态。而实际应用上与引脚的悬空几乎是一样的。
当门电路的输出上拉管导通而下拉管截止时,输出为高电平;反之就是低电平;如上拉管和下拉管都截止时,输出端就相当于浮空(没有电流流动),其电平随外部电平高低而定,即该门电路放弃对输出端电路的控制 。
高阻态常用字母 Z 表示。
之前所有的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@5 d{
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, }, },
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的接口。
虽然pinctrl提供了pinctrl_request_gpio()
这样的API,但在代码中不可以直接调用pinctrl_request_gpio()
,在该函数的定义处也有说明,如下:
当设备驱动申请一个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()
。
全文以msm8916平台为例进行分析。
当tlmm加载时,msm_tlmm_v4_probe()
最后会调msm_pinctrl_probe()
,其中会将pinctrl.dtsi
中定义的pinctrlinfo解析出来,并且重新定义chip->request()
、chip->free()
等函数。 具体的调用关系如下图所示:
postcore_initcall(msm_tlmm_v4_drv_register);
msm_tlmm_v4_probe
msm_pinctrl_probe
msm_pinctrl_get_drvdata(dd,pdev);
msm_pinctrl_dt_parse_pintype(node,dd);
msm_pinctrl_dt_parse_pins(node,dd);
msm_register_gpiochip(dd);
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);
一个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@5 d{
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 >;
……
}
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。
如果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);
}
}
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 >;
qcom,num-grp-pins= <2 >;
qcom,pin-func= <3 >;
label = "pmx_i2c_0" ;
i2c_0_active:i2c_0_active {
drive-strength= <8 >;
bias-pull-up;
};
i2c_0_sleep:i2c_0_sleep {
drive-strength= <<span class="hljs-number"><span class="hljs-number">2</span></span>>;
bias-disable;
};
};
然后在msm8916.dtsi中增加pinctrlinfo的引用:
i2c_0:i2c@78b 6000 {
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 >;
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-5 wire-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-5 wire-active:wcnss-active {
drive-strength= <6 >; / * 6 MA */
bias-pull-up;
};
};
pmx-wcnss-5 wire-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-5 wire-sleep:wcnss-sleep {
drive-strength= <6 >; / * 6 MA */
bias-pull-down;
};
};
};
struct pinctrl * devm_pinctrl_get (struct device *dev) ;
struct pinctrl_state * pinctrl_lookup_state (struct pinctrl *p, const char *name) ;
int pinctrl_select_state (struct pinctrl *p, struct pinctrl_state *state) ;
下面举例配置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= <<span class="hljs-number"><span class="hljs-number">2</span></span>>;
bias-pull-up;
};
▲gt9xx_int_sleep:sleep {
drive-strength= <<span class="hljs-number"><span class="hljs-number">2</span></span>>;
bias-disable;
};
};
}
2)在msm8916-cdp.dtsi中tp的节点中添加引用:
goodix@5 d{
compatible= "goodix,gt9xx" ;
reg= <0x5d >;
▲ pinctrl-names= "gt9xx_int_active" , "gt9xx_int_suspend" ;
▲ pinctrl-0 = <>9 xx_int_active>;
▲ pinctrl-1 = <>9 xx_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;
}