3. pinctrl 子系统:pinctrl 概念、pin controller、pin state、pin function
在嵌入式 Linux 系统中,芯片的引脚(Pin)往往承载着多种功能:可以是 GPIO、I2C 数据线、SPI 时钟、UART 收发、PWM 输出等。pinctrl 子系统就是负责管理这些引脚的多路复用(Muxing)与电气配置(Drive Strength、Pull-up/down)的核心框架。对于 V4L2 驱动而言,摄像头接口的时钟线(MCLK/XCLK)、数据线(D0-D9)、同步信号(HSYNC/VSYNC)以及 I2C 控制总线,全部依赖 pinctrl 完成正确配置。
3.1 pinctrl 核心概念
pinctrl 子系统由三个基本概念构成,理解它们之间的关系是读懂设备树引脚配置的前提。
| 概念 | 英文 | 职责描述 | 设备树中的体现 |
|---|---|---|---|
| 引脚控制器 | Pin Controller | 硬件抽象层,管理一组引脚的多路复用与电气特性 | pinctrl-0 引用的 phandle 节点 |
| 引脚状态 | Pin State | 设备在特定工作模式下的引脚配置集合 | pinctrl-0、pinctrl-1 等属性 |
| 引脚功能 | Pin Function | 一组引脚被配置为某个特定外设接口(如 I2C、UART) | pin controller 子节点中的 function 属性 |
3.2 Pin Controller:引脚管理的硬件抽象
Pin Controller 是 SoC 中负责引脚管理的硬件模块的软件抽象。它通常对应芯片中的 Pinctrl/Pad Mux 寄存器组。在设备树中,pin controller 是一个独立的节点,提供引脚复用和电气配置的底层接口。
一个典型的 pin controller 节点示例(以 i.MX6ULL 为例):
iomuxc: iomuxc@20e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x020e0000 0x4000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
};
pin controller 内部通过 子节点 来组织不同的引脚功能组。每个子节点定义一个 function 和对应的 pins 列表。
pinctrl-0 混淆。pin controller 是提供服务的“资源提供方”,而设备节点是“资源使用方”。设备通过 pinctrl-0 引用 pin controller 中预定义的配置组。
3.3 Pin State:设备的工作模式切换
Pin State 定义了设备在 不同运行阶段 的引脚配置。Linux 内核支持多个预定义状态,驱动通过 pinctrl_select_state() 在运行时切换。
| 状态名称 | 设备树属性 | 典型用途 | V4L2 场景 |
|---|---|---|---|
| default | pinctrl-0 |
设备正常工作时的引脚配置 | 摄像头 sensor 上电后的数据/时钟引脚 |
| sleep | pinctrl-1 |
设备休眠或低功耗时的引脚配置 | 摄像头断电后,引脚设为高阻或下拉省电 |
| init | pinctrl-2 |
设备初始化阶段的临时配置 | sensor 复位时序中特殊引脚电平 |
| idle | pinctrl-3 |
设备空闲但不休眠时的配置 | 摄像头流关闭但电源未断时的引脚状态 |
设备树中通过 pinctrl-names 属性为每个状态命名,驱动通过名称查找对应的配置索引:
camera_sensor: camera@3c {
compatible = "ov5640";
reg = <0x3c>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&camera_pins_default>;
pinctrl-1 = <&camera_pins_sleep>;
};
3.4 Pin Function:引脚功能的逻辑分组
Pin Function 是 pin controller 内部定义的 逻辑功能组。它将多个物理引脚绑定到一个特定的外设接口功能上。例如,将 6 个引脚分组为“UART3 功能组”,包含 TX、RX、RTS、CTS 等信号。
在设备树中,pin function 通常定义在 pin controller 的子节点中,使用 function 和 pins 属性:
&iomuxc {
camera_pins_default: camera-default {
fsl,pins = <
MX6UL_PAD_CSI_DATA00__CSI_DATA00 0x1b0b1
MX6UL_PAD_CSI_DATA01__CSI_DATA01 0x1b0b1
MX6UL_PAD_CSI_DATA02__CSI_DATA02 0x1b0b1
MX6UL_PAD_CSI_DATA03__CSI_DATA03 0x1b0b1
MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b0b1
MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b0b1
>;
};
};
上述代码中,MX6UL_PAD_CSI_MCLK__CSI_MCLK 表示将物理引脚 CSI_MCLK 复用为 CSI_MCLK 功能(摄像头主时钟)。0x1b0b1 是电气配置值,包含驱动能力、上下拉、速度等参数。
3.5 pinctrl 与 V4L2 驱动的交互流程
下面通过一张 SVG 流程图展示 V4L2 驱动中 pinctrl 的完整工作链路:
3.6 设备树中 pinctrl 的完整配置模式
一个完整的 V4L2 摄像头设备节点,通常包含以下 pinctrl 配置模式:
/* 1. Pin Controller 内部定义功能组 */
&iomuxc {
pinctrl_camera_default: cam_default {
fsl,pins = <
MX6UL_PAD_CSI_DATA00__CSI_DATA00 0x1b0b1
MX6UL_PAD_CSI_DATA01__CSI_DATA01 0x1b0b1
MX6UL_PAD_CSI_DATA02__CSI_DATA02 0x1b0b1
MX6UL_PAD_CSI_DATA03__CSI_DATA03 0x1b0b1
MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b0b1
MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b0b1
MX6UL_PAD_CSI_HSYNC__CSI_HSYNC 0x1b0b1
MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b0b1
>;
};
pinctrl_camera_sleep: cam_sleep {
fsl,pins = <
MX6UL_PAD_CSI_DATA00__GPIO4_IO21 0x10059
MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x10059
MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x10059
MX6UL_PAD_CSI_DATA03__GPIO4_IO24 0x10059
MX6UL_PAD_CSI_MCLK__GPIO4_IO17 0x10059
MX6UL_PAD_CSI_PIXCLK__GPIO4_IO18 0x10059
MX6UL_PAD_CSI_HSYNC__GPIO4_IO19 0x10059
MX6UL_PAD_CSI_VSYNC__GPIO4_IO20 0x10059
>;
};
};
/* 2. 设备节点引用状态 */
&i2c2 {
camera_sensor: camera@3c {
compatible = "ov5640";
reg = <0x3c>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_camera_default>;
pinctrl-1 = <&pinctrl_camera_sleep>;
clocks = <&clks IMX6UL_CLK_CSI>;
clock-names = "csi_mclk";
...
};
};
3.7 pinctrl 与时钟的协同关系
在 V4L2 摄像头场景中,pinctrl 配置的 MCLK 引脚功能 必须与 时钟子系统 配合。pinctrl 负责将物理引脚连接到 SoC 内部的时钟输出模块,而时钟子系统负责提供实际的时钟频率。
| 配置层面 | 负责内容 | 设备树属性 | 典型值 |
|---|---|---|---|
| pinctrl | 引脚复用为时钟输出功能 | fsl,pins 中的 MCLK 宏 |
MX6UL_PAD_CSI_MCLK__CSI_MCLK |
| 时钟子系统 | 提供时钟源并设置频率 | clocks / assigned-clock-rates |
24000000 (24MHz) |
| sensor 驱动 | 接收时钟并配置内部 PLL | 通过 clk_get_rate() 获取 |
24MHz 输入 → 内部倍频 |
如果 pinctrl 将 MCLK 引脚配置为 GPIO 功能,即使时钟子系统使能了 CSI 时钟,时钟信号也无法到达 sensor 的 MCLK 引脚。反之,如果 pinctrl 配置正确但时钟子系统未使能,sensor 同样收不到时钟。
/sys/kernel/debug/pinctrl/pinctrl-handles),再检查时钟是否使能(clk_summary)。
3.8 调试与验证方法
在开发板上验证 pinctrl 配置是否正确,可以使用以下方法:
- 查看 pinctrl 句柄:
cat /sys/kernel/debug/pinctrl/pinctrl-handles列出所有设备的 pinctrl 状态 - 查看引脚复用状态:
cat /sys/kernel/debug/pinctrl/<pinctrl-name>/pinmux-pins - 查看引脚电气配置:
cat /sys/kernel/debug/pinctrl/<pinctrl-name>/pinconf-pins - 查看当前状态:
cat /sys/kernel/debug/pinctrl/<pinctrl-name>/pinctrl-devices - 手动切换状态测试:通过
echo操作/sys/devices/.../power/control触发休眠/唤醒
以下是一个典型的调试输出示例:
# cat /sys/kernel/debug/pinctrl/pinctrl-handles
Device: camera@3c
state: default type: 0
device pinctrl handle: 0
claim: pinctrl-camera-default
state: sleep type: 1
device pinctrl handle: 1
claim: pinctrl-camera-sleep
如果 camera@3c 没有出现在列表中,说明设备树中 pinctrl-0 属性缺失或 phandle 引用错误。
devm_pinctrl_get_select_default(&client->dev) 调用,确保 pinctrl 在驱动加载时立即生效。如果返回 EPROBE_DEFER,说明 pin controller 尚未就绪,驱动应延迟加载。