天天看點

Pinctrl基礎簡介原gpio配置架構PinControl 架構Usecase

  1. 原gpio配置架構

之前,所有的gpio操作都是通過gpiolib來實作,常用的api包括:

staticinline int gpio_request(unsigned gpio, const char *label);

staticinline int gpio_direction_input(unsigned gpio);

staticinline int gpio_direction_output(unsigned gpio, int value);

staticinline void gpio_set_value(unsigned gpio, int value);

staticinline void gpio_free(unsigned gpio);

在硬體設計确定了某個裝置需要使用哪些gpio之後,軟體需要做的是(以msm8916平台TP的中斷為例):

1)在msm8916-cdp.dsi中定義使用哪個gpio

[email protected]{

[email protected]{

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狀态

staticstruct gpiomux_setting atmel_int_act_cfg = {

.func =GPIOMUX_FUNC_GPIO,

.drv = GPIOMUX_DRV_2MA,

.pull = GPIOMUX_PULL_UP,

};

staticstruct gpiomux_setting atmel_int_sus_cfg = {

.func =GPIOMUX_FUNC_GPIO,

.drv = GPIOMUX_DRV_2MA,

.pull =GPIOMUX_PULL_NONE,

};

staticstruct msm_gpiomux_config msm_touch_configs[] __initdata = {

.gpio = 13,

.settings = {

[GPIOMUX_ACTIVE] =&atmel_int_act_cfg,

[GPIOMUX_SUSPENDED]= &atmel_int_sus_cfg,

},

},

  1. PinControl 架構

Gpiolib方式的缺點在于:當同一套代碼對應多個board設計時,需要在board-<soc>-gpiomux.c檔案中加宏進行區分。對于不同平台項目,在board-msm8974-gpiomux.c檔案中添加了很多宏控。

pinctrl方式可以避免代碼中的這種備援代碼,它将board-<soc>-gpiomux.c檔案中的配置資訊移到<soc>-pinctrl.dtsi;這樣,針對不同project的board設計,分别在各自project的<soc>-pinctrl.dtsi中定義各自的gpio配置資訊。

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

pinctrlcore是pincontrol子系統的核心,提供了和devicedriver互動的API;

pinmux用于實作pin的複用;

pinconf用于實作pin的配置,如輸入/輸出、pulldown/pull up、driverstrength等;另外還提供了用于debug的接口。

    1. 與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()。

    1. 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);

    1. 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”等。

[email protected]{

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。

      1. 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);

if(dev->bus->probe) {

ret= dev->bus->probe(dev);

}else if (drv->probe) {

ret= drv->probe(dev);

}

pinctrl_bind_pins(dev)的調用過程如下:

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;

    1. Pingroups

SOC上需要同時配置一組gpio來支援某些功能,如I2C、SPI、UART、SDC等,在<soc>-pinctrl.dtsi中将這些pin定義為一個group。

      1. 配置統一的情況

一組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:[email protected] {

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>;

};

      1. 配置不統一的情況

如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。

[email protected]{

...

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;

};

};

};

    1. PinctrlAPI

structpinctrl *devm_pinctrl_get(struct device *dev);

擷取該device對應的pinctrlhandler。

structpinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char*name);

查找name指定的pinctrlstate。

intpinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state);

配置pin腳為指定的state。

  1. Usecase

下面舉例配置tp的中斷腳。

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

&soc{

tlmm_pinmux:[email protected]

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的節點中添加引用:

[email protected]{

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>;

……

}

3)在tp驅動中添加配置。

a.定義pinctrl_info:

#defineGOODIX_PINCTRL_STATE_SLEEP "gt9xx_int_suspend"

#defineGOODIX_PINCTRL_STATE_DEFAULT "gt9xx_int_active"

structgtp_pinctrl_info{

structpinctrl *pinctrl;

structpinctrl_state *gpio_state_active;

structpinctrl_state *gpio_state_suspend;

};

staticstruct gtp_pinctrl_info gt9xx_pctrl;

staticint 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:%dFailed 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:%dFailed to get the suspend state pinctrl handle\n",

__func__,__LINE__);

return-EINVAL;

}

return0;

}

b.在probe函數中初始化pinctrl_info,并設定state:

staticint 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:

staticint goodix_ts_suspend(struct device *dev)

{

structgoodix_ts_data *ts = dev_get_drvdata(dev);

intret = 0, i;

ret= pinctrl_select_state(gt9xx_pctrl.pinctrl,

gt9xx_pctrl.gpio_state_suspend);

if(ret)

pr_err("%s:%dcannot set pin to suspend state",

__func__,__LINE__);

……

if(ts->use_irq)

gtp_irq_disable(ts);

……

returnret;

}

staticint goodix_ts_resume(struct device *dev)

{

structgoodix_ts_data *ts = dev_get_drvdata(dev);

intret = 0;

ret= pinctrl_select_state(gt9xx_pctrl.pinctrl,

gt9xx_pctrl.gpio_state_active);

if(ret)

pr_err("%s:%dcannot set pin to suspend state",

__func__,__LINE__);

……

if(ts->use_irq)

gtp_irq_enable(ts);

……

returnret;

}

繼續閱讀