在 Linux 内核中,pinctrl 和 GPIO 子系统 是与引脚控制和 GPIO 管理密切相关的两个子系统。它们主要用于嵌入式系统中,帮助驱动程序或应用程序与硬件的引脚和 GPIO 进行交互。这两个子系统常见于 SoC(System on Chip)平台上,用于处理引脚复用、多功能引脚配置、GPIO 方向设置和电平控制等任务。
一、pinctrl 子系统
1. pinctrl 的作用
pinctrl
(Pin Control,管脚控制)子系统用于管理 SoC 上的管脚(引脚)和引脚复用。SoC 上的引脚往往可以连接到多个外设,pinctrl
系统通过配置管脚的复用功能,使得不同的设备和驱动能够使用这些引脚。
pinctrl
系统的主要作用:
- 引脚复用(pin muxing):设置引脚连接到哪一个外设(例如 UART、SPI、I2C 等)。
- 引脚配置:设置引脚的电气特性(例如上拉、下拉、电平方向等)。
- 引脚组配置:对一组相关的引脚进行联合配置。
- 引脚状态管理:定义和管理引脚在不同设备状态下的配置,例如活动状态、休眠状态等。
2. pinctrl 的基本概念
- Pin:单个引脚,位于芯片的外围,负责输入或输出信号。
- Pin Muxing(引脚复用):一个引脚可以与多个外围设备相连,pinctrl 子系统用于选择引脚要连接到的具体设备。
- Pin Configuration(引脚配置):定义引脚的电气特性,例如上拉、下拉、驱动强度等。
- Pin Group(引脚组):多个引脚的集合,通常多个引脚作为一个设备的接口使用。
- State(状态):pinctrl 支持为引脚定义不同的状态,例如设备激活时和进入低功耗模式时的引脚配置。
3. 设备树中的 pinctrl 配置
设备树是嵌入式系统中常用的描述硬件配置的方式。pinctrl 通常在设备树中定义。
示例设备树配置:
&uart0 {pinctrl-names = "default";pinctrl-0 = <&uart0_pins>;status = "okay";
};pinctrl@44e10800 {uart0_pins: uart0_pins {pinmux = <PIN_UART0_TX | MUX_MODE0>, <PIN_UART0_RX | MUX_MODE0>;bias-pull-up;drive-strength = <4>;};
};
- pinctrl-names:设备的引脚控制状态名称。
- pinctrl-0:表示默认状态时的引脚配置。
- pinmux:定义引脚与设备的复用关系。
- bias-pull-up:表示引脚的上拉配置。
- drive-strength:定义驱动强度。
4. pinctrl 的主要内核接口
驱动程序通常会通过 pinctrl
API 进行引脚配置。
pinctrl_get()
:获取pinctrl
句柄。pinctrl_lookup_state()
:获取特定状态的配置(如“active”、“idle”状态)。pinctrl_select_state()
:将引脚配置切换到某个状态。
5. pinctrl 使用步骤
- 在设备树中定义 pinctrl 配置。
- 在驱动的
probe()
函数中调用pinctrl_get()
来获取 pinctrl 句柄。 - 调用
pinctrl_lookup_state()
来获取特定状态的引脚配置。 - 调用
pinctrl_select_state()
来设置引脚的状态(如活动或休眠)。
二、GPIO 子系统
1. GPIO 的作用
GPIO(General Purpose Input/Output,通用输入输出)是系统中最基本的数字接口,用于控制引脚的电平高低,或读取引脚的电平状态。GPIO 通常用于控制简单的硬件组件(如 LED、按键、继电器等)。
GPIO 子系统提供了以下功能:
- GPIO 方向控制:设置 GPIO 引脚的方向(输入或输出)。
- GPIO 电平控制:设置输出 GPIO 的电平高低。
- GPIO 电平读取:读取输入 GPIO 的电平状态。
- 中断支持:支持 GPIO 引脚触发中断,通常用于处理按键等输入信号。
2. GPIO 相关 API
内核提供了一些标准的 GPIO 操作函数供驱动使用。
gpio_request()
:申请一个 GPIO 引脚。gpio_free()
:释放一个 GPIO 引脚。gpio_direction_input()
:设置 GPIO 为输入模式。gpio_direction_output()
:设置 GPIO 为输出模式,并可以指定初始电平。gpio_set_value()
:设置 GPIO 输出电平(高/低)。gpio_get_value()
:读取 GPIO 输入电平。gpio_to_irq()
:将 GPIO 引脚转换为中断号。request_irq()
:为 GPIO 引脚的中断号注册中断处理函数。
3. GPIO 与设备树的关系
设备树也支持对 GPIO 进行配置和管理,通过设备树可以描述 GPIO 引脚和其相关属性。
示例设备树配置:
leds {compatible = "gpio-leds";pinctrl-names = "default";pinctrl-0 = <&led_pins>;led0: led_0 {label = "user_led0";gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>;default-state = "off";};led1: led_1 {label = "user_led1";gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>;default-state = "on";};
};
在上面的示例中,gpios
属性定义了 GPIO1_18
和 GPIO1_19
分别作为 LED0 和 LED1 的控制引脚。驱动可以通过这些信息来获取 GPIO 并进行控制。
4. GPIO 驱动程序中的使用
在驱动程序中使用 GPIO 通常遵循以下步骤:
- 从设备树中解析 GPIO 信息,使用
of_get_named_gpio()
获取 GPIO。 - 调用
gpio_request()
申请 GPIO 使用权。 - 调用
gpio_direction_input()
或gpio_direction_output()
设置 GPIO 方向。 - 调用
gpio_set_value()
控制 GPIO 电平,或调用gpio_get_value()
读取 GPIO 输入状态。
5. GPIO 子系统中的中断
GPIO 引脚通常支持中断模式,按键、传感器等设备会使用 GPIO 中断来通知 CPU 某个事件发生。GPIO 子系