1、pinctrl子系統設備樹配置html
有了pinctrl子系統之後,驅動就能夠操做pinctrl子系統的接口函數完成I/O操做了,而不須要本身去配置了。通常pinctrl子系統驅動是由芯片原廠的BSP工程師實現好的。驅動工程師經過配置設備樹去使用pinctrl子系統。有些I/O口具備不一樣的狀態(state),好比在正常工做的時候這一組I/O口被配置成uart接口,休眠時配置成GPIO接口且輸出爲高電平。pinctrl子系統的設備樹配置也是遵照service和client結構。node
舉個例子:這裏的device節點成爲pinctrl子系統中的一個client設備,由於其使用了pinctrl子系統裏面提供出來的接口。pinctrl就是pincontroller的縮寫。ios
//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...)配置成不一樣的功能。git
一個配置節點的例子以下:github
//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,能夠說的上是毫無格式。spa
舉幾個實際使用的例子:code
1.imx6ull的htm
//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平臺的blog
//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; }; }; };
2、驅動中怎樣使用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的設置
3、對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");
4、總結
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/