📚 技术平台:嵌入式Jerry(B站)
一、引言
在嵌入式系统中,SoC 芯片的引脚通常具有多种功能,如 GPIO、UART、I2C、SPI 等。为了在不同的应用场景中灵活配置引脚功能,Linux 内核引入了 pinctrl(Pin Control)子系统。该子系统提供了一种统一的机制,用于管理引脚的复用(pin multiplexing)、配置(pin configuration)以及状态管理。
二、pinctrl 子系统概述
2.1 设计目标
pinctrl 子系统的主要目标包括:
- 引脚复用管理:允许将同一引脚配置为不同的功能,如 GPIO、UART 等。
- 引脚配置管理:设置引脚的电气属性,如上拉/下拉、电平驱动能力等。
- 状态管理 :支持根据设备的不同工作状态(如正常、休眠)切换引脚配置。
2.2 核心组件
pinctrl 子系统的核心组件包括:
- pinctrl_dev:表示一个 pin 控制器设备。
- pinctrl_desc:描述 pin 控制器的结构体,定义了操作接口。
- pinctrl_ops:定义了 pin 控制器的操作函数集。
- pinmux_ops:定义了引脚复用的操作函数集。
- pinconf_ops:定义了引脚配置的操作函数集。
三、设备树中的 pinctrl 配置
在设备树中,pinctrl 的配置通常包括以下几个部分:
3.1 pin 控制器节点
dts
pinctrl: pinctrl@30330000 {
compatible = "fsl,imx8mp-iomuxc";
reg = <0x30330000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
};
3.2 引脚配置节点
dts
pinctrl_uart1: uart1grp {
fsl,pins = <
MX8MP_IOMUXC_UART1_TXD__UART1_DCE_TX 0x1c4
MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 0x1c4
>;
};
3.3 设备节点引用
dts
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
四、pinctrl 驱动的实现
以 i.MX8MP 平台为例,其 pinctrl 驱动位于 drivers/pinctrl/freescale/pinctrl-imx8mp.c
。该驱动主要完成以下任务:
4.1 定义引脚描述
c
static const struct pinctrl_pin_desc imx8mp_pinctrl_pins[] = {
PINCTRL_PIN(0, "GPIO0_IO00"),
PINCTRL_PIN(1, "GPIO0_IO01"),
// ...
};
4.2 实现操作函数集
c
static const struct pinctrl_ops imx8mp_pinctrl_ops = {
.get_groups_count = imx_pinctrl_get_groups_count,
.get_group_name = imx_pinctrl_get_group_name,
.get_group_pins = imx_pinctrl_get_group_pins,
.dt_node_to_map = imx_pinctrl_dt_node_to_map,
.dt_free_map = imx_pinctrl_dt_free_map,
};
4.3 注册 pin 控制器
c
static int imx8mp_pinctrl_probe(struct platform_device *pdev)
{
return imx_pinctrl_probe(pdev, &imx8mp_pinctrl_data);
}
五、驱动中使用 pinctrl API
在设备驱动中,可以使用以下 API 来获取和设置引脚状态:
c
struct pinctrl *pinctrl;
struct pinctrl_state *state;
pinctrl = devm_pinctrl_get(&pdev->dev);
state = pinctrl_lookup_state(pinctrl, "default");
pinctrl_select_state(pinctrl, state);
这些 API 允许驱动在运行时切换引脚的配置状态,例如在设备进入休眠或唤醒时。
六、实战案例:配置 UART 引脚
以配置 UART1 的引脚为例,设备树中的配置如下:
dts
pinctrl_uart1: uart1grp {
fsl,pins = <
MX8MP_IOMUXC_UART1_TXD__UART1_DCE_TX 0x1c4
MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 0x1c4
>;
};
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
在驱动中,可以使用上述的 pinctrl API 来设置引脚状态。
七、pinctrl 与 GPIO 子系统的关系
pinctrl 子系统与 GPIO 子系统密切相关。通常,GPIO 控制器是 pin 控制器的一个子集。pinctrl 子系统负责引脚的复用和配置,而 GPIO 子系统负责引脚的输入输出操作。
在某些平台上,pinctrl 驱动需要注册 GPIO 范围,以便 GPIO 子系统能够正确地映射引脚。例如:
c
static struct pinctrl_gpio_range imx8mp_gpio_ranges[] = {
{
.name = "gpio1",
.id = 0,
.base = 0,
.pin_base = 0,
.npins = 32,
.gc = &gpio1_chip,
},
// ...
};
八、总结
pinctrl 子系统为 Linux 内核提供了一个统一的引脚管理机制,使得驱动开发者可以方便地配置引脚的复用和电气属性。通过设备树的配置和驱动中的 API 调用,pinctrl 子系统简化了引脚管理的复杂性,提高了驱动的可移植性和可维护性。
技术平台:嵌入式Jerry