
大家好,我是情报小哥~
pinctrl子系统和GPIO子系统是Linux内核中两个密切相关但又职责不同的子系统,它们共同协作管理SoC的引脚,pinctrl子系统负责引脚的复用和配置,而GPIO子系统则提供了对配置为GPIO功能的引脚进行访问和控制的接口。
主要功能:引脚复用(pin multiplexing)和引脚配置(pin configuration)。
引脚复用:一个物理引脚可能有多个功能,例如可以是GPIO、串口TX、I2C SCL等。pinctrl子系统负责选择引脚的功能(即选择复用模式)。
引脚配置:例如设置引脚的上下拉电阻、驱动强度、 slew rate等电气特性。
引脚状态管理:睡眠、默认、激活等不同状态下的配置
在设备树中,pinctrl的配置通常被组织在pinctrl节点中,然后在具体设备节点中通过pinctrl-names和pinctrl-0等属性来引用。
主要功能:当引脚被配置为GPIO功能时,GPIO子系统提供了一套统一的接口来访问这些GPIO引脚,例如设置方向(输入/输出)、读写值、设置中断等,提供gpio_request()、gpio_direction_output()等接口。
GPIO子系统会为每个GPIO控制器创建一个GPIO chip,这个chip提供了一系列操作GPIO的函数(如设置方向、读写等)。然后,这些GPIO可以通过一个全局的编号(即gpio number)来访问,维护全局GPIO编号空间,或者通过设备树中的gpio属性来指定。
// 1. pinctrl配置引脚为GPIO功能&pinctrl { my_gpio_pins: my-gpio-pins { pins = "GPIO0", "GPIO1"; function = "gpio"; bias-pull-up; drive-strength = <8>; };};// 2. 设备节点使用该配置my_device { compatible = "my-device"; pinctrl-names = "default"; pinctrl-0 = <&my_gpio_pins>; // 引用pinctrl配置 // 3. 通过GPIO子系统指定具体GPIO gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, // GPIO0 <&gpio 1 GPIO_ACTIVE_HIGH>; // GPIO1};staticintmy_driver_probe(struct platform_device *pdev){// 1. pinctrl子系统自动或手动设置引脚状态 pinctrl_pm_select_default_state(&pdev->dev);// 2. 通过GPIO子系统获取并操作GPIOstructgpio_desc *gpio0, *gpio1; gpio0 = gpiod_get(&pdev->dev, "gpio", 0); // 获取第一个GPIO gpio1 = gpiod_get(&pdev->dev, "gpio", 1); // 获取第二个GPIO// 3. 设置GPIO方向并操作 gpiod_direction_output(gpio0, 1); gpiod_direction_input(gpio1);return0;}在Linux内核中,每个GPIO都有一个唯一的整数编号(即GPIO编号),用于在系统中标识该GPIO。这个编号是全局的,与具体的GPIO控制器无关。但是,硬件上GPIO是分布在多个GPIO控制器中的,每个控制器管理一组GPIO。因此,需要一种机制将全局的GPIO编号映射到具体的控制器和该控制器内部的偏移。
GPIO编号是一个整数,通常由芯片厂商在BSP中定义,或者由设备树(Device Tree)动态分配。在旧的内核版本中,GPIO编号通常是静态定义的,而在设备树中,GPIO编号是通过GPIO控制器的phandle和引脚偏移来动态计算的。
每个GPIO控制器在内核中由一个struct gpio_chip表示。它提供了该控制器管理的GPIO数量、操作方法(如设置方向、读写值)等。每个控制器管理一组连续的本地GPIO引脚(通常从0开始)。

由于系统中有多个GPIO控制器,每个控制器管理不同的GPIO引脚,因此需要将全局GPIO编号映射到具体的控制器和控制器内的偏移。这是通过struct gpio_range来描述的。

一个GPIO范围定义了:
全局GPIO编号的起始(base)
本地引脚编号的起始(pin_base)
引脚数量(npins)
对应的GPIO控制器(gc)
一个pinctrl驱动程序可以添加多个GPIO范围到pinctrl设备中。这样,当我们需要操作一个全局GPIO编号时,内核可以通过查找GPIO范围来确定使用哪个GPIO控制器以及在该控制器中的本地偏移。
在设备树中,GPIO控制器节点会定义#gpio-cells属性,指定用多少个cell来描述一个GPIO引脚。通常为2,第一个cell是引脚在该控制器中的偏移,第二个cell是标志(如有效电平等)。
当另一个设备节点引用GPIO时,会使用类似<&gpio0 5 GPIO_ACTIVE_HIGH>的描述。这里的5就是该控制器内的偏移。
在系统启动时,内核会解析设备树,为每个GPIO控制器创建gpio_chip,并注册到GPIO子系统中。同时,pinctrl子系统会添加GPIO范围,建立映射。
假设有两个GPIO控制器:gpio0和gpio1,每个控制器管理32个GPIO引脚。在设备树中,它们可能被分配全局GPIO编号如下:
gpio0: 全局编号0~31
gpio1: 全局编号32~63
那么,全局GPIO编号35就对应gpio1控制器的第3个引脚(因为35-32=3)。


小哥搜集了一些嵌入式学习资料,公众号内回复【1024】即可找到下载链接!
推荐好文点击蓝色字体即可跳转
☞专辑|Linux应用程序编程大全 ☞ 专辑|学点网络知识 ☞ 专辑|手撕C语言 ☞ 专辑|手撕C++语言
☞ 专辑|经验分享 ☞ 专辑|从单片机到Linux ☞ 专辑|电能控制技术 ☞ 专辑|嵌入式必备数学知识 ☞ MCU进阶专辑
☞ 经验分享