pinctrl(1)——pinctrl子系統的使用


一、pinctrl子系統設備樹配置

  有了pinctrl子系統以后,驅動就可以操作pinctrl子系統的接口函數完成I/O操作了,而不需要自己去配置了。一般pinctrl子系統驅動是由芯片原廠的BSP工程師實現好的。驅動工程師通過配置設備樹去使用pinctrl子系統。有些I/O口具有不同的狀態(state),比如在正常工作的時候這一組I/O口被配置成uart接口,休眠時配置成GPIO接口且輸出為高電平。pinctrl子系統的設備樹配置也是遵守service和client結構。

舉個例子:這里的device節點成為pinctrl子系統中的一個client設備,因為其使用了pinctrl子系統里面提供出來的接口。pinctrl就是pincontroller的縮寫。

//client節點
device {
    pinctrl-names = "default", "sleep"; //使用pinctrl-names來表示設備的狀態(state),這里有2個,分別為默認狀態和休眠狀態。
    pinctrl-0 = <&state_0_node_a>; //第0個狀態對應於"default"狀態,對應的引腳在pinctrl-0里面定義。
    pinctrl-0 = <&state_1_node_a>; //第1個狀態對應於"sleep"狀態,對應的引腳在pinctrl-1里面定義。
};

//service節點
pincontroller {
    state_0_node_a {
        function = "uart0";
        groups = "u0rxtx", "u0rtscts";
    };
    state_1_node_a {
        function = "gpio";
        groups = "u0rxtx", "u0rtscts";
    };
};

  上面的是對一組引腳的復用,在不同狀態下復用為uart引腳或gpio引腳,稱為“Generic pin multiplexing node”(復用節點)。還有一種配置叫做“Generic pin configuration node”(配置節點)是對引腳功能的配置,不同的狀態(default、idle、sleep...)配置成不同的功能。

一個配置節點的例子如下:

//client節點
device {
    pinctrl-names = "default", "sleep"; //使用pinctrl-names來表示設備的狀態(state),這里有2個,分別為默認狀態和休眠狀態。
    pinctrl-0 = <&state_0_node_a>; //第0個狀態對應於"default"狀態,對應的引腳在pinctrl-0里面定義。
    pinctrl-0 = <&state_1_node_a>; //第1個狀態對應於"sleep"狀態,對應的引腳在pinctrl-1里面定義。
};

//service節點,controller來提供服務
pincontroller {
    state_0_node_a { //復用節點
        function = "uart0";
        groups = "u0rxtx", "u0rtscts";
    };
    state_1_node_a { //配置節點
        groups = "u0rxtx", "u0rtscts";
        output-high; //輸出高電平
    };
};

  不論是復用節點還是配置節點,都是來操作這些引腳。對於一個client它可以指定多個狀態(state),在每一個狀態下都可以指定對應的子節點來描述它的狀態。子節點在controller服務側實現,子節點可以是一個復用節點(把對應的引腳復用成某個功能)也可以是一個配置節點(把對應的引腳配置成某個狀態)。

  對於client節點,其設備樹書寫格式基本上是一致的,統一的。但是對於服務側的controller節點的寫法就五法八門了,有些根本就沒有function和group,可以說的上是毫無格式。

舉幾個實際使用的例子:

1.imx6ull的

//client端:
@uart1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart1>;
    status = "okay";
};

//pincontroller服務端
pinctrl_uart1: uartlgrp {
    fsl.pins = <MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX, //名字為UART1_TX的引腳被復用為UART1_DCE_TX功能。
        MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX>; //這里寫的是兩個宏,所以沒有加"&"
};

2.rk3288平台的

//client端
@uart0 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; //它使用三個節點來表示三組引腳。
    status = "okay";
};

//pincontroller服務端
gpio4_uart0 {
    uart0_xfer: uart0-xfer {
        rockchip,pins = <UART0BT_SIN>, <UART0BT_SOUT>; //使用rockchip,pins來指定使用哪些引腳,就等效於groups
        rockchip,pull = <VALUE_PULL_DISABLE>; //這兩個字段來配置這些引腳的參數
        rockchip,drive = <VALUE_DRV_DEFAULT>;
    };
    uart0_cts: uart0-cts {
        rockchip,pins = <UART0BT_CTSN>; //這里寫的是兩個宏,所以沒有加"&",這里其實是指定了兩個引腳。
        rockchip,pull = <VALUE_PULL_DISABLE>;
        rockchip,drive = <VALUE_DRV_DEFAULT>;
    };
    uart0_rts: uart0-rts {
        rockchip,pins = <UART0BT_RTSN>;
        rockchip,pull = <VALUE_PULL_DISABLE>;
        rockchip,drive = <VALUE_DRV_DEFAULT>;
    };
    uart0_rts_gpio: uart0-rts-gpio {
        rockchip,pins = <FUNC_TO_GPIO(UART0BT_RTSN)>;
        rockchip,drive = <VALUE_DRV_DEFAULT>;
    };
};

雖然pincontroller服務端沒有統一的格式,但是其里面的概念還是一樣的,使用到哪些引腳,這些引腳會被歸為一組一組,這些引腳會被復用為某一個功能。

3. 一個設備使用多個gpio時的設備樹的設置方法

pcie0: qcom,pcie@xxx {
    compatible = "qcom,pci-msm";
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&pcie0_clkreq_default &pcie0_perst_default &pcie0_wake_default>; //gpio80 gpio79 gpio81
    pinctrl-1 = <&pcie0_clkreq_sleep &pcie0_perst_default &pcie0_wake_default>; //gpio80 gpio79 gpio81
}    

4. 另外還有一種情況,對於一組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"; //使用label去指定function
        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"; //使用label去指定function
            wcnss-5wire-sleep:wcnss-sleep {
                drive-strength= <6>; / * 6MA */
                bias-pull-down;
            };
    };
};

 

二、驅動中怎樣使用pinctrl子系統

  對於驅動代碼中怎樣使用pinctrl子系統,這對驅動工程師來說是透明的,我們驅動中基本不用管,當設備切換狀態時(對應設備樹中pinctrl-names的狀態),對應的pinctrl就會被調用。

1. 在驅動probe之前就先獲取了pinctl的各種state

__device_attach
    bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
        __device_attach_driver 
            driver_match_device(drv, dev); //只有驅動和設備樹節點匹配上了才會繼續往下走
            driver_probe_device(struct device_driver *drv, struct device *dev)
                really_probe
                    pinctrl_bind_pins //執行到這里了,一定是驅動和設備匹配上后的。                
                        drv->probe(dev)//然后再probe我們的驅動
int pinctrl_bind_pins(struct device *dev)
{
    /*1.先get*/
    dev->pins->p = devm_pinctrl_get(dev);

    /*2.獲取各種state下的pin*/
    dev->pins->default_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_DEFAULT); /*default的必須要有,否則直接退出*/
    dev->pins->init_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_INIT);
    /*3.選中一個state進行設置*/
    if (IS_ERR(dev->pins->init_state)) {
        /*這里對clent的gpio設備樹節點進行解析,並對硬件進行了設置*/
        ret = pinctrl_select_state(dev->pins->p, dev->pins->default_state); /*如果有init state,就設置為init state,否則設置為default state, Qcom沒有定義sleep state*/
    } else {
        ret = pinctrl_select_state(dev->pins->p, dev->pins->init_state);
    }

#ifdef CONFIG_PM //R上也定義了這個值
    dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_SLEEP);
    dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, PINCTRL_STATE_IDLE);
#endif

    return 0;
}

2. 若是非要自己調用,也有函數

struct pinctrl * __must_check devm_pinctrl_get_select_default(struct device *dev) //獲取"default"狀態的pinctrl配置
struct pinctrl * __must_check devm_pinctrl_get_select(struct device *dev, const char *name); //獲取name指定的pinctrl配置
void devm_pinctrl_put(struct pinctrl *p); //釋放pinctrl配置資源,通常不需要我們調用。

3. pinctrl使用步驟

/*1.驅動獲取對應的pinctrl節點*/
struct pinctrl *devm_pinctrl_get(struct device *dev)

devm_pinctrl_get
    pinctrl_get(dev); 
        find_pinctrl(dev);//從pinctrl_list中找到屬於此dev的struct pinctrl
        create_pinctrl(dev, NULL);
            pinctrl_dt_to_map(p, pctldev); //解析設備端(client)設備樹種的配置,,
                of_find_node_by_phandle(phandle); //通過phandle找到config節點
                dt_to_map_one_config(p, pctldev, statename, np_config); //解析設備樹的config節點

/*2.獲取各種state的gpio配置*/
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) //name = "xxx_active"/"xxx_sleep"/"xxx_default"

struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)
    find_state(p, name); //通過名字返回對應的pinctrl_state

/*3.將上面獲取的指定state狀態設置到硬件中*/
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)

int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
    pinctrl_commit_state(p, state);
        pinmux_disable_setting(setting);
        p->state = NULL; //轉換過程中設置為null
        pinmux_enable_setting(setting); //對於每一個setting都調用這個函數
            struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
            pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins, &num_pins); /*獲取對應的pin和pin的數量,每一個pin都是對一個gpio引腳的寄存器描述*/
            pin_request(pctldev, pins[i], setting->dev_name, NULL); //對每一個引腳進行獲取
            ops->set_mux(pctldev, setting->data.mux.func, setting->data.mux.group); //這里進行pinmux的設置

 

三、對pmic的gpio的獲取和使用

上面描述的都是對Soc的gpio的配置,pmic的gpio的配置和使用和之前對gpio的使用方法基本一致。

1. 相關函數:

int of_get_named_gpio(struct device_node *np, const char *propname, int index)
int gpio_request(unsigned gpio, const char *label)
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
int gpio_get_value(unsigned int gpio)
void gpio_set_value(unsigned int gpio, int value)

2. 舉例

設備樹節點中gpio配置

qcom,otg_en-gpio = <&pm8150l_gpios 10 0x00>;

驅動中gpio獲取:

of_get_named_gpio(node, "qcom,otg_en-gpio", 0);
//
gpio_request(en_gpio, "qcom,otg_en-gpio");

 

四、總結

1. pinctrl節點,也即是service的節點的驅動中是沒有解析設備樹的,在設備驅動,也即client的驅動中設置的時候才會去解析設備樹,進行實際的硬件配置。若是只是service中配置了,但是沒有client使用,沒有什么影響。

2. 一般是驅動自身,在suspend回調中設置為sleep state,resume時設置為avtive state。平台設備驅動在probe()時會自動設置為default state,其它驅動在probe()內可以進行設置。

 

 

 

 

參考:

pinctrl基礎簡介:http://www.voidcn.com/article/p-vowhhncl-xp.html

gpio pinctrl的使用demo:https://dongka.github.io/2018/02/25/pinctrl/sunxi_pinctrl/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM