日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux内核中的GPIO系统之(4):pinctrl驱动的理解和总结

發布時間:2025/3/21 linux 67 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核中的GPIO系统之(4):pinctrl驱动的理解和总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 前言

本站之前的三篇文章[1][2][3]介紹了pin controller(對應的pin controller subsystem)、gpio controller(對應的GPIO subsystem)有關的基本概念,包括pin multiplexing、pin configuration等等。本文將基于這些文章,單純地從pin controller driver的角度(屏蔽掉pinctrl core的實現細節),理解pinctrl subsystem的設計思想,并掌握pinctrl驅動的移植和實現方法。

2. pin controller的概念和軟件抽象

相信每一個嵌入式從業人員,都知道“pin(管腳)”是什么東西(就不贅述了)。由于SoC系統越來越復雜、集成度越來越高,SoC中pin的數量也越來越多、功能也越來越復雜,這就對如何管理、使用這些pins提出了挑戰。因此,用于管理這些pins的硬件模塊(pin controller)就出現了。相應地,linux kernel也出現了對應的驅動(pin controller driver)。

Kernel pinctrl core使用struct pinctrl_desc抽象一個pin controller,該結構的定義如下(先貼在這里,后面會圍繞這個抽象一步步展開):

/* include/linux/pinctrl/pinctrl.h */?
struct pinctrl_desc {?
??????? const char *name;?
??????? const struct pinctrl_pin_desc *pins;?
??????? unsigned int npins;?
??????? const struct pinctrl_ops *pctlops;?
??????? const struct pinmux_ops *pmxops;?
??????? const struct pinconf_ops *confops;?
??????? struct module *owner;?
#ifdef CONFIG_GENERIC_PINCONF?
??????? unsigned int num_custom_params;?
??????? const struct pinconf_generic_params *custom_params;?
??????? const struct pin_config_item *custom_conf_items;?
#endif?
};

注1:本文后續的描述基于本站“X Project”所使用的kernel版本[4]。?
注2:本文很多的表述(特別是例子),都是引用kernel的document[5](寫的很好,可以耐心看看)。

2.1 Pin

kernel的pin controller子系統要想管理好系統的pin資源,第一個要搞明白的問題就是:系統中到底有多少個pin?用軟件語言來表述就是:要把系統中所有的pin描述出來,并建立索引。這由上面struct pinctrl_desc結構中pins和npins來完成。

對pinctrl core來說,它只關心系統中有多少個pin,并使用自然數為這些pin編號,后續的操作,都是以這些編號為操作對象。至于編號怎樣和具體的pin對應上,完全是pinctrl driver自己的事情。

因此,pinctrl driver需要根據實際情況,將系統中所有的pin組織成一個struct pinctrl_pin_desc類型的數組,該類型的定義為:

/**?
* struct pinctrl_pin_desc - boards/machines provide information on their?
? * pins, pads or other muxable units in this struct?
? * @number: unique pin number from the global pin number space?
? * @name: a name for this pin?
? * @drv_data: driver-defined per-pin data. pinctrl core does not touch this?
? */?
struct pinctrl_pin_desc {?
??????? unsigned number;?
??????? const char *name;?
??????? void *drv_data;?
};

number和name完全由driver自己決定,不過要遵循有利于代碼編寫、有利于理解等原則。另外,為了便于driver的編寫,可以在drv_data中保存driver的私有數據結構(可以包含相關的寄存器偏移等信息)。

注3:[5]中有個例子,大家可以參考理解。

2.2 Pin groups

在SoC系統中,有時需要將很多pin組合在一起,以實現特定的功能,例如SPI接口、I2C接口等。因此pin controller需要以group為單位,訪問、控制多個pin,這就是pin groups。相應地,pin controller subsystem需要提供一些機制,來獲取系統中到底有多少groups、每個groups包含哪些pins、等等。

因此,pinctrl core在struct pinctrl_ops中抽象出三個回調函數,用來獲取pin groups相關信息,如下:

struct pinctrl_ops {?
??????? int (*get_groups_count) (struct pinctrl_dev *pctldev);?
??????? const char *(*get_group_name) (struct pinctrl_dev *pctldev,?
??????????????????????????????????????? unsigned selector);?
??????? int (*get_group_pins) (struct pinctrl_dev *pctldev,?
?????????????????????????????? unsigned selector,?
?????????????????????????????? const unsigned **pins,?
?????????????????????????????? unsigned *num_pins);?
??????? void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,?
????????????????????????? unsigned offset);?
??????? int (*dt_node_to_map) (struct pinctrl_dev *pctldev,?
?????????????????????????????? struct device_node *np_config,?
?????????????????????????????? struct pinctrl_map **map, unsigned *num_maps);?
??????? void (*dt_free_map) (struct pinctrl_dev *pctldev,?
???????????????????????????? struct pinctrl_map *map, unsigned num_maps);?
};

get_groups_count,獲取系統中pin groups的個數,后續的操作,將以相應的索引為單位(類似數組的下標,個數為數組的大小)。

get_group_name,獲取指定group(由索引selector指定)的名稱。

get_group_pins,獲取指定group的所有pins(由索引selector指定),結果保存在pins(指針數組)和num_pins(指針)中。

注4:dt_node_to_map用于將device tree中的pin state信息轉換為pin map,具體可參考后面第3章、第4章的介紹。

當然,最終的group信息是由pinctrl driver提供的,至于driver怎么組織這些group,那是driver自己的事情了,[4]中有一個例子,大家可以參考(在編寫pinctrl driver的時候直接copy然后rename即可)。??

2.3. Pin configuration(對象是pin或者pin group)

2.1和2.2中介紹了pinctrl subsystem中的操作對象(pin or pin group)以及抽象方法。嵌入式系統的工程師都知道,SoC中的管腳有些屬性可以配置,例如上拉、下拉、高阻、驅動能力等。pinctrl subsystem使用pin configuration來封裝這些功能,具體體現在struct pinconf_ops數據結構中,如下:

struct pinconf_ops {?
#ifdef CONFIG_GENERIC_PINCONF?
???????? bool is_generic;?
#endif?
??????? int (*pin_config_get) (struct pinctrl_dev *pctldev,?
?????????????????????????????? unsigned pin,?
?????????????????????????????? unsigned long *config);?
??????? int (*pin_config_set) (struct pinctrl_dev *pctldev,?
?????????????????????????????? unsigned pin,?
??????????????????????????????? unsigned long *configs,?
??????????????????????????????? unsigned num_configs);?
??????? int (*pin_config_group_get) (struct pinctrl_dev *pctldev,?
????????????????????????????????????? unsigned selector,?
????????????????????????????????????? unsigned long *config);?
??????? int (*pin_config_group_set) (struct pinctrl_dev *pctldev,?
????????????????????????????????????? unsigned selector,?
????????????????????????????????????? unsigned long *configs,?
???????????????????????????????????? unsigned num_configs);?
??????? int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev,?
??????????????????????????????????????????? const char *arg,?
??????????????????????????????????????????? unsigned long *config);?
??????? void (*pin_config_dbg_show) (struct pinctrl_dev *pctldev,?
????????????????????????????????????? struct seq_file *s,?
????????????????????????????????????? unsigned offset);?
??????? void (*pin_config_group_dbg_show) (struct pinctrl_dev *pctldev,?
??????????????????????????????????????????? struct seq_file *s,?
??????????????????????????????????????????? unsigned selector);?
??????? void (*pin_config_config_dbg_show) (struct pinctrl_dev *pctldev,?
???????????????????????????????????????????? struct seq_file *s,?
???????????????????????????????????????????? unsigned long config);?
};?

pin_config_get,獲取指定pin(管腳的編號,由2.1中pin的注冊信息獲得)當前配置,保存在config指針中(配置的具體含義,只有pinctrl driver自己知道,下同)。

pin_config_set,設置指定pin的配置(可以同時配置多個config,具體意義要由相應pinctrl driver解釋)。

pin_config_group_get、pin_config_group_set,獲取或者設置指定pin group的配置項。

剩下的是一些debug用的api,不再說明(用得著的時候,再研究也不遲)。

關于pinconf,有一點一定要想明白:

kernel pinctrl subsystem并不關心configuration的具體內容是什么,它只提供pin configuration get/set的通用機制,至于get到的東西,以及set的東西,到底是什么,是pinctrl driver自己的事情。后面結合pin map和pin state的介紹(3.2小節),就能更好地理解這種設計了。

2.4. Pin multiplexing(對象是pin或者pin group)

為了照顧不同類型的產品、不同的應用場景,SoC中的很多管腳可以配置為不同的功能,例如A2和B5兩個管腳,既可以當作普通的GPIO使用,又可以配置為I2C0的的SCL和SDA,也可以配置為UART5的TX和RX,這稱作管腳的復用(pin multiplexing,簡稱為pinmux)。kernel pinctrl subsystem使用struct pinmux_ops來抽象pinmux有關的操作,如下:

struct pinmux_ops {?
???????? int (*request) (struct pinctrl_dev *pctldev, unsigned offset);?
??????? int (*free) (struct pinctrl_dev *pctldev, unsigned offset);?
??????? int (*get_functions_count) (struct pinctrl_dev *pctldev);?
??????? const char *(*get_function_name) (struct pinctrl_dev *pctldev,?
?????????????????????????????????????????? unsigned selector);?
??????? int (*get_function_groups) (struct pinctrl_dev *pctldev,?
????????????????????????????????? unsigned selector,?
????????????????????????????????? const char * const **groups,?
????????????????????????????????? unsigned *num_groups);?
??????? int (*set_mux) (struct pinctrl_dev *pctldev, unsigned func_selector,?
??????????????????????? unsigned group_selector);?
??????? int (*gpio_request_enable) (struct pinctrl_dev *pctldev,?
??????????????????????????????????? struct pinctrl_gpio_range *range,?
???????????????????????????????????? unsigned offset);?
??????? void (*gpio_disable_free) (struct pinctrl_dev *pctldev,?
?????????????????????????????????? struct pinctrl_gpio_range *range,?
??????????????????????????????????? unsigned offset);?
??????? int (*gpio_set_direction) (struct pinctrl_dev *pctldev,?
?????????????????????????????????? struct pinctrl_gpio_range *range,?
??????????????????????????????????? unsigned offset,?
?????????????????????????????????? bool input);?
??????? bool strict;?
};

注5:function的概念:

  • 為了管理SoC的管腳復用,pinctrl subsystem抽象出function的概念,用來表示I2C0、UART5等功能。pin(或者pin group)所對應的function一經確定,它(們)的管腳復用狀態也就確定了
  • 和2.2中描述的pin group類似,pinctrl core不關心function的具體形態,只要求pinctrl driver將SoC的所有可能的function枚舉出來(格式自行定義,不過需要有編號、名稱等內容,可參考[5]中的例子),并注冊給pinctrl core。后續pinctrl core將會通過function的索引,訪問、控制相應的function
  • 另外,有一點大家應該很熟悉:在SoC的設計中,同一個function(如I2C0),可能可以map到不同的pin(或者pin group)上

理解了function的概念之后,struct pinmux_ops中的API就簡單了:

get_functions_count,獲取系統中function的個數。

get_function_name,獲取指定function的名稱。

get_function_groups,獲取指定function所占用的pin group(可以有多個)。

set_mux,將指定的pin group(group_selector)設置為指定的function(func_selector)。

request,檢查某個pin是否已作它用,用于管腳復用時的互斥(避免多個功能同時使用某個pin而不知道,導致奇怪的錯誤)。

free,request的反操作。

gpio_request_enable、gpio_disable_free、gpio_set_direction,gpio有關的操作,等到gpio有關的文章中再說明。

strict,為true時,說明該pin controller不允許某個pin作為gpio和其它功能同時使用。

3. pinctrl subsystem的控制邏輯

第2章以struct pinctrl_desc為引子,介紹了pinctrl subsystem中有關pin controller的概念抽象,包括pin、pin group、pinconf、pinmux、pinmux function、等等,相當于從provider的角度理解pinctrl subsystem。那么,問題來了,怎么使用pinctrl subsystem提供的功能控制管腳的配置以及功能復用呢?這看似需要由consumer(例如各個外設的驅動)自行處理,實際上卻不盡然:

1)前面講了,由于pinctrl subsystem的特殊性,對于pin configuration以及pin multiplexing而言,要怎么配置、怎么復用,只有pinctrl driver自己知道。同理,各個consumer也是云里霧里,啥都搞不清楚(想象各位編寫設備驅動需要用到pinctrl的時候的心情吧!)。

2)那這樣的配置有道理嗎?有!記得我們在[6]中提到過,對一個確定的產品來說,某個設備所使用的pinctrl功能(function)、以及所對應的pin(或者pin group)、還有這些pin(或者pin group)的屬性配置,基本上在產品設計的時候就確定好了,consumer沒必要(也不想)關心技術細節。因此pinctrl driver就要多做一些事情,幫助consumer厘清pin有關資源的使用情況,并在這些設備需要使用的時候(例如probe時),一聲令下,將資源準備好。

3)因此,pinctrl subsystem的設計理念就是:不需要consumer關心pin controller的技術細節,只需要在特定的時候向pinctrl driver發出一些簡單的指令,告訴pinctrl driver自己的需求即可(例如我在運行時需要使用這樣一組配置,在休眠時使用那樣一組配置)。

4)最后,需求的細節(例如需要使用哪些pin、配置為什么功能、等等),要怎么確定呢?一般是通過machine的配置文件、具體版型的device tree等,告訴pinctrl subsystem,以便在需要的時候使用。

下面小節我們將會根據這些思路,進行更為詳細的分析。

3.1 pin state

根據前面的描述,pinctrl driver抽象出來了一些離散的對象:pin(pin group)、function、configuration,并實現了這些對象的控制和配置方式。然后我們回到某一個具體的device上(如SPI2):

該device在某一狀態下(如工作狀態、休眠狀態、等等),所使用的pin(pin group)、pin(pin group)的function和configuration,是唯一確定的。

把上面的話顛倒過來說,就是:

pin(pin group)以及相應的function和configuration的組合,可以確定一個設備的一個“狀態”。

這個狀態在pinctrl subsystem中就稱作pin state。而pinctrl driver和具體板型有關的部分,需要負責枚舉該板型下所有device(當然,特指那些需要pin資源的device)的所有可能的狀態,并詳細定義這些狀態需要使用的pin(或pin group),以及這些pin(或pin group)需要配置為哪種function、哪種配置項。這些狀態確定之后,consumer(device driver)就好辦了,直接發號施令就行了:

喂,pinctrl subsystem,幫忙將我的xxx state激活。

pinctrl subsystem接收到指令后,找到該state的相關信息(pin、function和configuration),并調用pinctrl driver提供的相應API(參考第2章中struct pinctrl_desc有關的內容),控制pin controller即可。

3.2 pin map

在pinctrl subsystem中,pin state有關的信息是通過pin map收集,相關的數據結構如下:

/* include/linux/pinctrl/machine.h */

struct pinctrl_map {?
??????? const char *dev_name;?
??????? const char *name;?
??????? enum pinctrl_map_type type;?
??????? const char *ctrl_dev_name;?
??????? union {?
??????????????? struct pinctrl_map_mux mux;?
???????????????? struct pinctrl_map_configs configs;?
??????? } data;?
};

dev_name,device的名稱。

name,pin state的名稱。

ctrl_dev_name,pin controller device的名字。

type,該map的類型,包括PIN_MAP_TYPE_MUX_GROUP(配置管腳復用)、PIN_MAP_TYPE_CONFIGS_PIN(配置pin)、PIN_MAP_TYPE_CONFIGS_GROUP(配置pin group)、PIN_MAP_TYPE_DUMMY_STATE(不需要任何配置,僅僅為了表示state的存在。

data,該map需要用到的數據項,是一個聯合體,如果map的類型是PIN_MAP_TYPE_CONFIGS_GROUP,則為struct pinctrl_map_mux類型的變量;如果map的類型是PIN_MAP_TYPE_CONFIGS_PIN或者PIN_MAP_TYPE_CONFIGS_GROUP,則為struct pinctrl_map_configs類型的變量。

struct pinctrl_map_mux的定義如下:

struct pinctrl_map_mux {?
???????? const char *group;?
??????? const char *function;?
};

group,group的名字,指明該map所涉及的pin group。

function,function的名字,表示該map需要將group配置為哪種function。

struct pinctrl_map_configs的定義如下:

struct pinctrl_map_configs {?
??????? const char *group_or_pin;?
??????? unsigned long *configs;?
??????? unsigned num_configs;?
};????

group_or_pin,pin或者pin group的名字。

configs,configuration數組,指明要將該group_or_pin配置成“神馬樣子”。

num_configs,配置項的個數。

注6:講到這里,應該理解為什么2.3小結中struct pinconf_ops中的api,都不知道configuration到底是什么東西了吧?因為都是pinctrl driver自己安排好的,自產自銷,外人(pinctrl subsystem以及consumers)沒必要理解!

最后,某一個device的某一種pin state,可以由多個不同類型的map entry組合而成,舉例如下[5]

static struct pinctrl_map mapping[] __initdata = {?
??????? PIN_MAP_MUX_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "i2c0"),?
??????? PIN_MAP_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs),?
??????? PIN_MAP_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs),?
??????? PIN_MAP_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs),?
};

這是一個mapping數組,包含4個map entry,定義了"foo-i2c.0"設備的一個pin state(PINCTRL_STATE_DEFAULT,"default"),該state由一個PIN_MAP_TYPE_MUX_GROUP entry、一個PIN_MAP_TYPE_CONFIGS_GROUP entry以及兩個PIN_MAP_TYPE_CONFIGS_PIN entry組成(這些entry的具體含義,大家可參考[5]以及相應的source code理解,這里不再詳細說明)。

3.3 通過dts生成pin map

在舊時代,kernel的bsp工程師需要在machine有關的代碼中,靜態的定義pin map數組(類似于3.2小節中的例子),這一個非常繁瑣且不易維護的過程。不過當kernel引入device tree之后,事情就簡單了很多:

pinctrl driver確定了pin map各個字段的格式之后,就可以在dts文件中維護pin state以及相應的mapping table。pinctrl core在初始化的時候,會讀取并解析dts,并生成pin map。

而各個consumer,可以在自己的dts node中,直接引用pinctrl driver定義的pin state,并在設備驅動的相應的位置,調用pinctrl subsystem提供的API,active或者deactive這些state。

至于dts中pin map描述的格式是什么,則完全由pinctrl driver自己決定,因為,最終的解析工作(dts to map)也是它自己做的(具體可參考后面第4章的介紹)。

4. pinctrl subsystem的整體流程

通過前面幾章的分析,我們對pinctrl subsystem有了一個比較全面的認識,這里以pinctrl整個使用流程為例,簡單的總結一下。

1)pinctrl driver根據pin controller的實際情況,實現struct pinctrl_desc(包括pin/pin group的抽象,function的抽象,pinconf、pinmux的operation API實現,dt_node_to_map的實現,等等),并注冊到kernel中。

2)pinctrl driver在pin controller的dts node中,根據自己定義的格式,描述每個device的所有pin state。大致的形式如下(具體可參考kernel中的代碼,照葫蘆總能畫出來瓢~~~):

??????? pinctrl_xxx {?????????????????????????????? /* the dts node for pin controller */?
??????????????? ...?
??????????????? xxx_state_xxx: xxx_xxx {? /* dts node for xxx device's "xxx state" */?
??????????????????????? xxx_pinmux {???????????? /* pinmux entry */?
??????????????????????????????? xxx = xxxx;?
??????????????????????????????? xxx = xxxxxxx;?
??????????????????????????????? ...?
??????????????????????? };?
??????????????????????? xxx_pinconf {??????????? /* pinconf entry */?
??????????????????????????????? xxx = xxxx;?
??????????????????????????????? xxx = xxxxxxx;?
??????????????????????????????? ...?
??????????????????????? };?
??????????????????????? xxx_pinconf {?
??????????????????????????????? xxx = xxxx;?
??????????????????????????????? xxx = xxxxxxx;?
??????????????????????????????? ...?
??????????????????????? };?
?????????????????????? ...?
?????????????? };?
?????????????? ...?
??????? };

3)相應的consumer driver可以在自己的dts node中,引用pinctrl driver所定義的pin state,例如:

?????????????? xxx_device:?xxx@xxxxxxxx?{?
??????????????????????? compatible = "xxx,xxxx";?
??????????????????????? ...?
??????????????????????? pinctrl-names = "default";?
??????????????????????? pinctrl-0 = <&xxx_state_xxx>;?
??????????????????????? ...?
??????????????? };

4)consumer driver在需要的時候,可以調用pinctrl_get/devm_pinctrl_get接口,獲得一個pinctrl handle(struct pinctrl類型的指針)。pinctrl subsystem在pinctrl get的過程中,解析consumer device的dts node,找到相應的pin state,進行調用pinctrl driver提供的dt_node_to_map API,解析pin state并轉換為pin map。以driver probe時為例,調用過程如下(大家可以自己去看代碼):

probe?
??? devm_pinctrl_get or pinctrl_get?
??????? create_pinctrl(drivers/pinctrl/core.c)?
??????????? pinctrl_dt_to_map(drivers/pinctrl/devicetree.c)?
??????????????? dt_to_map_one_config?
??????????????????? pctlops->dt_node_to_map

5)consumer獲得pinctrl handle之后,可以調用pinctrl subsystem提供的API(例如pinctrl_select_state),使自己的某個pin state生效。pinctrl subsystem進而調用pinctrl driver提供的各種回調函數,配置pin controller的硬件。?
注7:具體細節就不再描述了,后續開發pinctrl driver的時候,可以再著重說明。

總結

以上是生活随笔為你收集整理的linux内核中的GPIO系统之(4):pinctrl驱动的理解和总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。