Pinctrl基础简介

    xiaoxiao2021-03-25  111

    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

    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中定义gpiosuspendactive状态

    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, /*TOUCH IRQ */

    .settings = {

    [GPIOMUX_ACTIVE] =&atmel_int_act_cfg,

    [GPIOMUX_SUSPENDED]= &atmel_int_sus_cfg,

    },

    },

    PinControl 框架

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

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

    Pinctrlsubsystem 分为3部分:Pinctrl corePinmuxPinconf

    pinctrlcorepincontrol子系统的核心,提供了和devicedriver交互的API

    pinmux用于实现pin的复用;

    pinconf用于实现pin的配置,如输入/输出、pulldown/pull updriverstrength等;另外还提供了用于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();设置gpioinput/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= <>9xx_int_active>;

    pinctrl-1= <>9xx_int_sleep>;

    interrupt-parent= <&msm_gpio>;

    interrupts= <13 0x2>;

    ……

    }

    pinctrl-names定义了clientdevice用到的state列表。state有两种标识,一种就是pinctrl-names定义的字符串列表,另外一种就是IDID0开始,依次加一。根据例子中的定义,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,而且定义了pindefault状态,就会配置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

    Pingroups

    SOC上需要同时配置一组gpio来支持某些功能,如I2CSPIUARTSDC等,在<soc>-pinctrl.dtsi中将这些pin定义为一个group

    配置统一的情况

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

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

    pmx_i2c_0{

    /*CLK, DATA */

    qcom,pins= <&gp 7>, <&gp 6>; //使用gpio_6gpio_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>; /*8 MA */

    bias-pull-up; /* PULL UP */

    };

    i2c_0_sleep:i2c_0_sleep {

    drive-strength= <2>; /* 2 MA */

    bias-disable; /* No PULL */

    };

    };

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

    i2c_0:i2c@78b6000 { /* BLSP1 QUP2 */

    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脚的activesleep状态配置各不相同,需要分开设置。

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

    /*SDC pin type */

    sdc:sdc {

    qcom,pin-type-sdc;

    /*0-2 for sdc1 4-6 for sdc2 */

    qcom,num-pins= <6>;

    /*Order of pins */

    /*SDC1: CLK -> 0, CMD -> 1, DATA -> 2 */

    /*SDC2: CLK -> 4, CMD -> 5, DATA -> 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;/* NO pull */

    drive-strength= <16>; /* 16 MA */

    };

    sdc1_clk_off:clk_off {

    bias-disable;/* NO pull */

    drive-strength= <2>; /* 2 MA */

    };

    };

    pmx_sdc1_cmd{

    qcom,pins= <&sdc 1>;

    qcom,num-grp-pins= <1>;

    label= "sdc1-cmd";

    sdc1_cmd_on:cmd_on {

    bias-pull-up;/* pull up */

    drive-strength= <10>; /* 10 MA */

    };

    sdc1_cmd_off:cmd_off {

    bias-pull-up;/* pull up */

    drive-strength= <2>; /* 2 MA */

    };

    };

    pmx_sdc1_data{

    qcom,pins= <&sdc 2>;

    qcom,num-grp-pins= <1>;

    label= "sdc1-data";

    sdc1_data_on:data_on {

    bias-pull-up;/* pull up */

    drive-strength= <10>; /* 10 MA */

    };

    sdc1_data_off:data_off {

    bias-pull-up;/* pull up */

    drive-strength= <2>; /* 2 MA */

    };

    };

    2msm8916-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>; /* no-pull, pull-up, pull-up */

    //qcom,pad-pull-off= <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */

    //qcom,pad-drv-on= <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */

    //qcom,pad-drv-off= <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */

    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,在不同statepin_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

    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

    Usecase

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

    1msm8916-pinctrl.dtsi中定义pinctrlinfo

    &soc{

    tlmm_pinmux:pinctrl@1000000

    gt9xx_int_pin{

    qcom,pins= <&gp 13>;

    qcom,num-grp-pins= <1>; /*num_pins */

    qcom,pin-func= <0>; /*功能选择*/

    label= "gt9xx_int_pin"; /*group name */

    /*active state */

    gt9xx_int_active:active {

    drive-strength= <2>; /*2 MA */

    bias-pull-up;

    };

    /*suspendedstate */

    gt9xx_int_sleep:sleep {

    drive-strength= <2>; /*2 MA */

    bias-disable;

    };

    };

    }

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

    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

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

    转载请注明原文地址: https://ju.6miu.com/read-12676.html

    最新回复(0)