pinctrl 子系統和 gpio 子系統雖然難度不大,但在內核里的使用率非常高,本文爭取一次性把相關內容介紹一遍。
pinctrl
數據結構
使用 struct pinctrl_desc 抽象一個 pin controller,該結構的定義如下:
pins
變量 pins 和 npins 把系統中所有的 pin 描述出來,并建立索引。驅動為了和具體的 pin 對應上,再將這些描述的這些 pin 組織成一個 struct pinctrl_pin_desc 類型的數組,該類型的定義為:
SoC中,有時需要將很多 pin 組合在一起,以實現特定的功能,例如 uart 接口、i2c 接口等。因此 pin controller 需要以 group 為單位,訪問、控制多個 pin,這就是 pin groups。
pinctrl core在struct pinctrl_ops中抽象出三個回調函數,用來獲取pin groups相關信息,如下:
group 的組織方式是由驅動決定的。
pin configuration
除了上面的 pin 和 pin group,有些管腳可以配置,比如上拉,下拉,高阻等。pin configuration 來封裝這些功能,具體體現在 struct pinconf_ops 數據結構中,如下:
pin mux
為了兼容不同的應用場景,有很多管腳可以配置為不同的功能,例如A和B兩個管腳,既可以當作普通的GPIO使用,又可以配置為I2C的的SCL和SDA,也可以配置為UART的TX和RX,這稱作管腳的復用(簡稱 pin mux)。使用 struct pinmux_ops 來抽象 pin mux 有關的操作,如下:
pin state
根據前面的描述,pinctrl driver 抽象出來了一些離散的對象:pin(pin group)、function、configuration,并實現了這些對象的控制和配置方式。然后我們回到某一個具體的 device 上(如 lpuart,usdhc)。一個設備在某一狀態(tài)下(如工作狀態(tài)、休眠狀態(tài)、等等),所使用的pin(pin group)、pin(pin group)的 function 和 configuration,是唯一確定的。所以固定的組合可以確定固定的狀態(tài),在設備樹里用 pinctrl-names 指明狀態(tài)名字,pinctrl-x 指明狀態(tài)引腳。
pin map
pin state 有關的信息是通過 pin map 收集,相關的數據結構如下:
pinctrl driver 確定了 pin map 各個字段的格式之后,就可以在 dts 文件中維護 pin state 以及相應的 mapping table。pinctrl core 在初始化的時候,會讀取并解析 dts,并生成 pin map。
而各個 consumer,可以在自己的 dts node 中,直接引用 pinctrl driver 定義的 pin state,并在設備驅動的相應的位置,調用 pinctrl subsystem 提供的 API(pinctrl_lookup_state,pinctrl_select_state),active 或者 deactive 這些 state。
pin controller 驅動初始化
pin 控制器描述符中包括了三類操作函數:pctlops 是一些全局的控制函數;pmxops 是復用引腳相關的操作函數;confops操作函數是用來配置引腳的特性。pin 控制器驅動的初始化主要是注冊這三類函數的回調。
struct pinctrl_ops *pctlops
c
pinctrl subsystem 的整體流程
pinctrl driver 根據 pin controller 的實際情況,定義 struct pinctrl_desc(包括pin/pin group 的抽象,function 的抽象,pinconf、pinmux 的 operation API 實現,dt_node_to_map 的實現,等等),并注冊到 kernel 中。
pinctrl driver 在 pin controller的 dts node 中,根據自己定義的格式,描述每個 device 的所有 pin state。如下所示:
相應的 consumer driver 可以在自己的 dts node 中,引用 pinctrl driver 所定義的 pin state,如下所示:
consumer driver 在需要的時候,可以調用 pinctrl_get/devm_pinctrl_get 接口,獲得一個 pinctrl handle(struct pinctrl類型的指針)。在 pinctrl get 的過程中,解析 consumer device 的 dts node,找到相應的 pin state,進行調用 pinctrl driver 提供的 dt_node_to_map 接口,解析 pin state 并轉換為 pin map。
例子
上圖中,左邊是 pin controller 節(jié)點,右邊是 client device 節(jié)點 。
pin state
對于一個“client device”來說,比如對于一個 UART 設備,它有多個“狀態(tài)”:default、sleep 等,那對應的引腳也有這些狀態(tài)。比如當這個設備處于 default 狀態(tài)時,pinctrl 子系統會自動根據上述信息把所用引腳復用為 uart0 功能。當這這個設備處于 sleep 狀態(tài)時,pinctrl 子系統會自動根據上述信息把所用引腳配置為高電平。
groups 和 function
一個設備會用到一個或多個引腳,這些引腳就可以歸為一組 group。這些引腳可以復用為某個功能 function。當然,一個設備可以用到多組多功能引腳,比如A1、A2兩組引腳,A1組復用為F1功能,A2組復用為F2功能。
sysfs 訪問方法
gpio
數據結構
每個 GPIO 控制器用一個 gpio_device 來表示:
用 gpio_chip 來定義控制引腳和中斷相關的函數:
設備樹
GPIO一般都分為幾組,每組中有若干個引腳。所以在使用GPIO子系統之前,就要先確定它所在的組以及在組中的哪一個。在設備樹中,“GPIO組” 就是一個 GPIO Controller,這通常都由芯片廠家設置好。我們要做的是找到它的名字,比如“gpio1”,然后指定要用它里面的哪個引腳,比如 <&gpio1 0>。
“gpio-controller”表示這個節(jié)點是一個GPIO Controller,它下面有很多引腳。
“#gpio-cells = <2>”表示這個控制器下每一個引腳要用2個32位的數(cell)來描述。用第1個cell來表示哪一個引腳,用第2個cell來表示有效電平:GPIO_ACTIVE_HIGH(高電平有效),GPIO_ACTIVE_LOW(低電平有效)。
怎么引用某個引腳呢?在自己的設備節(jié)點中使用屬性"[name]-gpios",示例如下:
gpio controller 驅動
gpio client 驅動
GPIO 子系統有兩套接口:基于描述符的(descriptor-based)、老的(legacy)。前者的函數都有前綴 “gpiod_”,它使用 gpio_desc 結構體來表示一個引腳;后者的函數都有前綴 “gpio_”,它使用一個整數來表示一個引腳。
要操作一個引腳,首先要 get 引腳,然后設置方向,讀值、寫值。
建議使用“devm_”版本的相關函數。有前綴“devm_”的含義是“設備資源管理”(Managed Device Resource),這是一種自動釋放資源的機制。它的思想是“資源是屬于設備的,設備不存在時資源就可以自動釋放”。
比如在 Linux 開發(fā)過程中,先申請了GPIO,再申請內存;如果內存申請失敗,那么在返回之前就需要先釋放GPIO資源。如果使用devm的相關函數,在內存申請失敗時可以直接返回:設備的銷毀函數會自動地釋放已經申請了的GPIO資源。
以上面的設備 max9286_mipi 為例,它的驅動實現如下:
sysfs 訪問方法
先確定某個GPIO Controller的基準引腳號(base number),再計算出某個引腳的號碼。
然后進入某個gpiochip目錄,查看文件label的內容,根據 label 的內容對比設備樹,就可以知道這對應哪一個 GPIO Controller。比如用上面的例子,通過對比設備樹可知 gpiochip448 對應 gpio1。
因為 pin number = base + offset,所以 GPIO1_27 的號碼是 448 + 27 = 475,那么通過 sys 可以做如下操作。
pinctrl subsystem 和 gpio subsysem 之間的耦合
pinctrl subsystem 管理系統的所有管腳,GPIO 是這些管腳的用途之一,因此 gpio subsystem 應該是 pinctrl subsystem 的 backend。在使用 GPIO 的時候,都需要向系統的 pinctrl subsystem 申請管腳,并將管腳配置為 GPIO 功能。
內核也提供了通過 pinctrl 控制 gpio 的接口:
pinctrl subsystem會維護一個gpio number到pin number的map(gpio range),將gpio subsystem傳來的gpio number轉換為pin number之后,調用struct pinmux_ops中有關的回調函數即可:
gpio ranges
當 gpio driver 需要使用某一個 gpio 的時候,可以在 struct gpio_chip 的 request 函數中,調用 pinctrl core 提供的 pinctrl_request_gpio 接口(參數是gpio編號),然后 pinctrl core 會查尋 gpio ranges 鏈表,將 gpio 編號轉換成 pin 編號,然后調用 pinctrl 的相應接口(參數是pin編號),申請該 pin 的使用。
更多信息可以來這里獲取==>>電子技術應用-AET<<