LED/GPIO/pinctrl子系统(3):pinctrl子系统概述
关键词:pinctrl、pinmux、pinconf等。
pinctrl可以归结为两类设置:一类是功能选择,比如是普通的GPIO或者专用功能引脚;另一类是上拉、下拉、驱动能力、速率等配置。
pinctrl子系统主要完成如下工作:
- 初始化pin contrroller,并为其每个pin命名和枚举。
- 处理pinctrl的引脚复用功能。
- 处理引脚的配置。
下面首先介绍Linux下pinctrl框架和配置;然后分别介绍重要数据结构和API、pinctrl相关DTS、Pin Controller驱动、Client Device驱动;最后介绍pinctrl相关调试接口。
1 pinctrl框架
kernel中pinctrl子系统可以分为三部分:
- pinctrl core负责pin controller注册以及pin数据维护,并为client device提供pin设置功能。
- pin controller是pinctrl子系统的provider,包括pin controller的初始化、注册、提供pin数据。
- client device是使用到pin的设备,是pinctrol子系统的consumer。需要设置不同状态及其对应的pin设置。
1.1 pinctrl debugfs初始化
在启动启动早起创建了pinctrl相关debugfs:
core_initcall(pinctrl_init)
pinctrl_init_debugfs--创建pinctrl-devices、pinctrl-maps、pinctrl-handles。
当使能pinctrl设备时,创建特定设备的debugfs:
pinctrl_enable
pinctrl_init_device_debugfs--创建pins/pingroups/gpio-ranges/pinmux/pinconf等调试文件。
2 pinctrl配置
内核支持pinctrl需要如下配置:
Device Drivers
->Pin controllers
->Debug PINCTRL calls--对pinctrl调用检查和诊断。 ->STMicroelectronics STMFX GPIO expander pinctrl driver--STM32 pinctrl驱动。
涉及到的文件如下:
drivers/pinctrl/
├── core.c--pinctrl子系统核心。 ├── devicetree.c--处理pinctrl相关的device tree。 ├── pinconf.c--pinctrl子系统core关于pinconf处理。 ├── pinconf-generic.c--从device tree中解析pin通用属性。 ├── pinctrl-utils.c ├── pinmux.c--pinctrl子系统core中关于pinmux处理。 ├── stm32 │ ├── pinctrl-stm32.c--STM32probe函数,以及关于pinconf/pinctrl/pinmux的函数设置。 │ ├── pinctrl-stm32mp157.c--STM32MP157的Pin Controller驱动入口,以及各个pin的名称和功能定义。
3 pinctrl 数据结构和API
3.1 pinctrl数据结构
struct pinctrl_dev是内核中对pin control一类设备的抽象。
struct pinctrl_dev { struct list_head node; struct pinctrl_desc *desc;--对应的pin controller描述信息。 struct radix_tree_root pin_desc_tree; #ifdef CONFIG_GENERIC_PINCTRL_GROUPS struct radix_tree_root pin_group_tree; unsigned int num_groups; #endif #ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS struct radix_tree_root pin_function_tree; unsigned int num_functions; #endif struct list_head gpio_ranges; struct device *dev; struct module *owner; void *driver_data; struct pinctrl *p; struct pinctrl_state *hog_default; struct pinctrl_state *hog_sleep; struct mutex mutex; #ifdef CONFIG_DEBUG_FS struct dentry *device_root; #endif };
struct pinctrl_desc用于描述一个pin controller,包括:包含所有引脚描述信息、pinmux操作函数、pin引脚或group配置接口。
struct pinctrl_desc { const char *name;--pin controller名称。 const struct pinctrl_pin_desc *pins;--此pin controller下所有引脚描述符数组。 unsigned int npins;--上面pins数组大小。 const struct pinctrl_ops *pctlops;--pin control操作函数集。 const struct pinmux_ops *pmxops;--pinmux操作函数集。 const struct pinconf_ops *confops;--pin config操作函数集。 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 bool link_consumers;--表示此pinctrl是否被consumer使用,并创建连接。 };
struct pinctrl_pin_desc用于描述一个pin:
struct pinctrl_pin_desc { unsigned number;--全局唯一的pin编号。 const char *name;--pin名称。 void *drv_data; };
一个设备会用到一个或多个引脚,这些引脚可以归为一组group。这些引脚可以复用为某种特定功能:function。
pinmux_ops用来描述那组group引脚复用为哪个function功能。
pinconf_ops可以用来描述配置信息:上来、下拉等等。
struct pinctrl_ops { int (*get_groups_count) (struct pinctrl_dev *pctldev);--获取pin controller的group个数。 const char *(*get_group_name) (struct pinctrl_dev *pctldev, unsigned selector);--获取selector(group序号)对应的名称。 int (*get_group_pins) (struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins);--获取group下pin列表。 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); }; 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);--选择mux的一个功能。 int (*gpio_request_enable) (struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);--选择mux中的GPIO功能。 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);--设置GPIO的input/output。 bool strict; }; 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);--配置单个pin的configuration。 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);--配置一个group的pin的configuration。 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); };
struct pinctrl维护每个设备pinctrl的各种状态;struct pinctrl_state表示设备的一个pinctrl状态。
struct pinctrl { struct list_head node; struct device *dev; struct list_head states; struct pinctrl_state *state; struct list_head dt_maps; struct kref users; }; struct pinctrl_state { struct list_head node; const char *name; struct list_head settings; };
3.2 pinctrl API
pinctrl的注册函数需要提供一个初始化好的struct pinctrl_desc结构体,返回一个struct pinctrl_dev。并将struct pinctrl_dev加入到pinctrldev_list全剧链表中。
extern int pinctrl_register_and_init(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data, struct pinctrl_dev **pctldev); extern int pinctrl_enable(struct pinctrl_dev *pctldev); extern struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev, void *driver_data); extern void pinctrl_unregister(struct pinctrl_dev *pctldev); extern int devm_pinctrl_register_and_init(struct device *dev, struct pinctrl_desc *pctldesc, void *driver_data, struct pinctrl_dev **pctldev); extern struct pinctrl_dev *devm_pinctrl_register(struct device *dev, struct pinctrl_desc *pctldesc, void *driver_data); extern void devm_pinctrl_unregister(struct device *dev, struct pinctrl_dev *pctldev);
pinctrl的注册函数简要流程如下:
devm_pinctrl_register_and_init--带资源管理的版本。
pinctrl_register_and_init--注册pin controller到pinctrl子系统,并初始化。
pinctrl_init_controller
pinctrl_register_pins
pinctrl_register_one_pin
pin_desc_get
radix_tree_insert
pinctrl_get()用于从struce device中获取pinctrl状态列表struct pinctrl。然后通过pinctrl_pinctrl_state()函数设置使用某种状态。对于低功耗通过pinctrl_pm_select_default_state()/pinctrl_pm_select_sleep_state()/pinctrl_pm_select_idle_state()根据低功耗状态切换pinctrl状态。
extern struct pinctrl * __must_check pinctrl_get(struct device *dev); extern void pinctrl_put(struct pinctrl *p); extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev); extern void devm_pinctrl_put(struct pinctrl *p); extern struct pinctrl_state * __must_check pinctrl_lookup_state( struct pinctrl *p, const char *name); extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); extern int pinctrl_pm_select_default_state(struct device *dev); extern int pinctrl_pm_select_sleep_state(struct device *dev); extern int pinctrl_pm_select_idle_state(struct device *dev);
4 pinctrl DTS(Pin Controller & Client Device)
关于pin的详细配置更多参考《pinctrl-bindings.txt》。
pinctrl节点存放pin-controller配置信息,以及相关pin配置:
pinctrl: pin-controller@50002000 { #address-cells = <1>; #size-cells = <1>; compatible = "st,stm32mp157-pinctrl"; ranges = <0 0x50002000 0xa400>;--所有pin的寄存器起始地址和范围。 interrupt-parent = <&exti>;--父中断为exti。 st,syscfg = <&exti 0x60 0xff>; hwlocks = <&hsem 0 1>; pins-are-numbered; gpioa: gpio@50002000 { gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; reg = <0x0 0x400>;--根据address-cells/size-cells可知,第一个是寄存器偏移地址,第二个是寄存器地址大小。 clocks = <&rcc GPIOA>; st,bank-name = "GPIOA"; status = "disabled"; }; gpiob: gpio@50003000 { gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; reg = <0x1000 0x400>; clocks = <&rcc GPIOB>; st,bank-name = "GPIOB"; status = "disabled"; }; };
以UART为例,涉及到不同状态的pin配置如下:
&pinctrl { uart4_pins_a: uart4-0 { pins1 {--一个或一系列pin的配置,下面可以是pins/group/pinmux的一种。 pinmux = <STM32_PINMUX('G', 11, AF6)>; /* UART4_TX */ bias-disable;--关闭pin bias。 drive-push-pull; slew-rate = <0>;--设置slew rate。 }; pins2 { pinmux = <STM32_PINMUX('B', 2, AF8)>; /* UART4_RX */ bias-disable; }; }; uart4_idle_pins_a: uart4-idle-0 { pins1 { pinmux = <STM32_PINMUX('G', 11, ANALOG)>; /* UART4_TX */ }; pins2 { pinmux = <STM32_PINMUX('B', 2, AF8)>; /* UART4_RX */ bias-disable; }; }; uart4_sleep_pins_a: uart4-sleep-0 { pins { pinmux = <STM32_PINMUX('G', 11, ANALOG)>, /* UART4_TX */ <STM32_PINMUX('B', 2, ANALOG)>; /* UART4_RX */ }; }; };
以uart4为例,其dts配置如下:
uart4: serial@40010000 { compatible = "st,stm32h7-uart"; reg = <0x40010000 0x400>; interrupts-extended = <&exti 30 IRQ_TYPE_LEVEL_HIGH>; clocks = <&rcc UART4_K>; resets = <&rcc UART4_R>; wakeup-source; power-domains = <&pd_core>; dmas = <&dmamux1 63 0x400 0x5>, <&dmamux1 64 0x400 0x1>; dma-names = "rx", "tx"; status = "disabled"; };
对uart4使用的pin进行如下配置:
&uart4 { pinctrl-names = "default", "sleep", "idle", "no_console_suspend";--指定状态名称列表,依次对应pinctrl-0、pinctrl-1等。 pinctrl-0 = <&uart4_pins_a>;--phandle句柄列表,每一个指向一个pin配置节点。 pinctrl-1 = <&uart4_sleep_pins_a>; pinctrl-2 = <&uart4_idle_pins_a>; pinctrl-3 = <&uart4_pins_a>; /delete-property/dmas;--删除dmas节点。 /delete-property/dma-names;--删除dma-names节点。 status = "okay"; };
可以将pinctrl配置分为两部分:provider和consumer。
pinctrl节点作为consumer,各外设作为consumer使用pinctrl配置。
5 Pin Controller驱动(STM32MP157D)
pinctrl驱动初始化级别为arch_initcall():
- 根据stm32mp157_match_data为每个pin创建struct pinctrl_pin_desc。
- 初始化struct pinctrl_desc成员。
- 根据struct pinctrl_desc将pin controller注册到pinctrl子系统中。
- 注册gpio。
stm32mp157_pinctrl_driver
->stm32_pctl_probe--当和stm32mp157_pctrl_match中compatible匹配之后,调用probe函数。
->devm_kcalloc--根据传入的数据stm32mp157_match_data为每个pin创建数组。
->stm32_pctrl_create_pins_tab--初始化struct stm32_desc_pin。
->devm_kcalloc--为每个pin创建struct pinctrl_pin_desc数组。
->初始化struct pinctrl_desc:confops/pctlops/pmxops函数集;创建的struct pinctrl_pin_desc数组和个数。
->devm_pinctrl_register--将代表当前pinctrl controller的struct pinctrl_desc注册到pinctrl子系统中。
->stm32_gpiolib_register_bank--pinctrl子节点如果是gpio-controller,则注册到GPIO子系统。
->pinctrl_add_gpio_range--为当前pinctrl注册一组GPIO。
->gpiochip_add_data
使用stm32mp157_match_data作为驱动的私有数据传入probe函数:
static struct stm32_pinctrl_match_data stm32mp157_match_data = { .pins = stm32mp157_pins, .npins = ARRAY_SIZE(stm32mp157_pins), }; static const struct of_device_id stm32mp157_pctrl_match[] = { { .compatible = "st,stm32mp157-pinctrl", .data = &stm32mp157_match_data, }, { } };
Pin Controller驱动编写有两大部分:一是各个Pin功能定义;另一个是pinctrl_ops/pinmux_ops/pinconf的函数集。
6 Client Device对pin配置
每一个struct device都有一个pins成员,指向设备的pinctrl:
struct device {
...
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif...
};
使用某个具体pinctrl配置,是在外设驱动probe之前通过pinctrl_bind_pins做如下工作:
- 从设备节点读取pinctrl信息。
- 查找default/init状态。
- 将当前pin状态配置为default/init。
bus_for_each_drv--遍历bus下所有driver。
->__device_attach_driver
->driver_probe_device--driver和device匹配后,开始probe。
->really_probe/really_probe_debug
->pinctrl_bind_pins--在正式probe之前,处理设备的pinctrl节点。
->devm_pinctrl_get--获取当前设备的struct pinctrl。
->pinctrl_get
->find_pinctrl
->create_pinctrl
->pinctrl_dt_to_map
->add_setting
->pinctrl_lookup_state--从pinctrl句柄中分别查找pinctrl-names为default(默认配置)/init(probe时配置)/idle(runtime suspend时配置)/sleep(suspend时配置)的pinctrl状态。
->pinctrl_select_state--此时处于probe阶段,如果有init状态配置则进行配置;否则使用default状态配置。
->pinctrl_commit_state
->pinctrl_commit_state
->pinmux_enable_setting--PIN_MAP_TYPE_MUX_GROUP
->pin_request
->ops->set_mux
->pinconf_apply_setting--PIN_MAP_TYPE_CONFIGS_PIN/PIN_MAP_TYPE_CONFIGS_GROUP
->ops->pin_config_set--PIN_MAP_TYPE_CONFIGS_PIN
->ops->pin_config_group_set--PIN_MAP_TYPE_CONFIGS_GROUP
->dev->bus->probe/drv->probe--调用bus或者driver的probe函数。
->pinctrl_init_done--将pinctrl状态切换到default状态。
以stm32串口驱动为例:初始化时设置为default_state;睡眠时设置为sleep_state;退出睡眠设置为default_state。
stm32_serial_driver
stm32_serial_pm_ops
stm32_serial_suspend
pinctrl_pm_select_sleep_state
pinctrl_pm_select_state--设置为sleep_state。
stm32_serial_resume
pinctrl_pm_select_default_state
pinctrl_pm_select_state--设置为default_state。
7 pinctrl相关sysfs
/sys/kernel/debug/pinctrl/ |-- pinctrl-devices--当前使能的Pin Controller名称,以及是否支持pinmux/pinconf。 |-- pinctrl-handles--当前被外设使用的pinctrl信息。按照不同外设不同状态显示详细信息。 |-- pinctrl-maps--遍历pinctrl_maps,显示每个pin所属的设备、状态、Pin Controller、Group、Function。 |-- soc:pin-controller-z@54004000 | |-- gpio-ranges--不同GPIO组范围及其对应的pin。 | |-- pinconf-groups--每个group的configuration显示。 | |-- pinconf-pins--每个pin的configuration信息显示。 | |-- pingroups--显示所有注册的pin group及其属下pin。 | |-- pinmux-functions--不同functions下的group列表。 | |-- pinmux-pins--当前pin选择的mux功能和对应的group号。 | `-- pins--所有注册的pin列表。 `-- soc:pin-controller@50002000 |-- gpio-ranges |-- pinconf-groups |-- pinconf-pins |-- pingroups |-- pinmux-functions |-- pinmux-pins `-- pins
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-01-07 ARMv8-A QEMU运行OP-TEE/ATF环境搭建
2020-01-07 一个std::thread()线程创建失败问题分析过程