Linux驱动开发实战(七):pinctrl引脚管理入门结合数据手册
文章目录
一、什么是 Pinctrl
Pinctrl(引脚控制)子系统主要负责:
- 引脚复用(Pin Multiplexing/Muxing)
- 引脚配置(Pin Configuration)
- 比如设置引脚用作GPIO、UART、I2C等功能,以及设置引脚的上拉下拉、驱动强度等电气特性。
二、为什么要使用 Pinctrl 子系统
2.1 硬件资源共享的需求
现代 SoC 设计面临一个基本挑战:引脚数量有限,但需要支持的功能越来越多。例如,一个只有100个引脚的芯片可能需要支持多种通信协议(UART、I2C、SPI、SDIO 等)、存储接口和各种控制信号。通过引脚复用技术,单个物理引脚可以支持多种功能,大大提高了芯片的集成度和灵活性。
2.2 软件管理复杂性
没有 Pinctrl 子系统之前,Linux 内核中的引脚配置存在以下问题:
- 代码重复:每个驱动都需要单独配置引脚,导致大量重复代码
- 资源冲突:不同驱动可能会尝试将同一引脚配置为不同功能
- 维护困难:引脚配置分散在各个驱动中,难以统一管理
- 板级适配繁琐:每更换一块板子,可能需要修改多个驱动的引脚配置
2.3 Pinctrl 子系统的优势
- 集中管理:统一管理所有引脚配置,避免冲突
- 解耦合设计:将引脚配置与具体功能驱动分离
- 状态管理:支持不同状态下(如正常运行、低功耗)切换不同的引脚配置
- 动态配置:支持运行时动态改变引脚功能和配置
- 设备树支持:通过设备树描述引脚配置,提高可读性和可维护性
三、基本概念
3.1 引脚复用
现代 SOC 的引脚通常可以复用为多种功能。例如:
- 一个引脚可能既可以用作 GPIO
- 也可以用作 UART 的 TX
- 或者 I2C 的 SCL
我们需要通过配置相应的寄存器来选择所需的功能。
3.2 引脚配置
除了功能选择,还需要配置引脚的电气特性:
- 上拉/下拉电阻
- 驱动强度
- 压摆率
- 开漏模式等
四、实例分析
pinctrl写在设备树下面
先通过下面这两段pinctrl 设备树代码来分析一下特性
1. 分组管理
从示例中可以看到,pinctrl 将引脚配置分为多个命名组(group):
- pinctrl_hog_1
- pinctrl_enet1
- pinctrl_enet2
- pinctrl_uart1
- pinctrl_hog_2
- pinctrl_spi4
这种分组方式允许将功能相关的引脚配置放在一起,便于管理和引用。pinctrl是以组来管理的
2. 引脚控制器多样性
示例中展示了两种不同的引脚控制器:
- &iomuxc:标准的 I/O 复用控制器
- &iomuxc_snvs:安全非易失性存储区域的 I/O 复用控制器
这反映了现代 SoC 通常有多个引脚控制器,每个控制器负责不同区域或不同功能的引脚。
3. 默认配置与状态管理
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
}
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
}
这里展示了 pinctrl 的状态管理特性:
- pinctrl-names 定义了状态名称
pinctrl-names = “default”,“init”,“sleep”;都是状态 - pinctrl-0 对应第一个状态的配置
- 系统启动时会应用 “default” 状态的配置
除了 “default” 状态外,pinctrl 子系统还支持其他状态,如 “sleep”(休眠)、“idle”(空闲)等,这些状态可以根据系统运行状态动态切换。但 “default” 状态是最基础的,也是系统初始化时使用的状态。
4. HOG 组特性
“Hog” 组(pinctrl_hog_1 和 pinctrl_hog_2)是一种特殊的引脚组,用于:
- 直接在控制器级别配置引脚,而不是通过某个特定设备
- 通常用于配置系统启动时就需要设置的引脚,如复位线、CD 检测等
- 被系统默认加载,不需要特定驱动来激活
5. 硬件复用能力
注意到 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 这样的定义,这表明:
物理引脚 UART1_RTS_B 被复用为 GPIO1_IO19
同一个物理引脚可以有多种功能选择
6.配置(看数据手册)
以 i.MX6ULL 的 UART1 为例:
pinctrl_uart1: uart1grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>;
};
这里的配置可以分解为:
引脚复用配置
宏 MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 展开为 5 个值:
在imx6ul-pinfunc.h里面定义的
#define MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x0084 0x0310 0x0000 0 0
这些值的含义是:
- 0x0084: MUX寄存器的偏移地址
- 0x0310: PAD寄存器的偏移地址
- 0x0000: MUX模式值(选择 ALT0 功能)
- 0: SELECT_INPUT寄存器偏移(如无则为0)
- 0: SELECT_INPUT值
为什么这么定义可以看下面我传的数据手册(重要)
PAD 控制值
0x1b0b1 是 PAD 控制值,用于配置引脚的电气特性:
这是在设备树上配置的
pinctrl_uart1: uart1grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>;
};
0x1b0b1 = 0001 1011 0000 1011 0001
这些位的含义如下(从右到左):
HYS (bit 16): 0 - 禁用磁滞
PUS (bits 14-15): 00 - 100K欧姆下拉 (00=100K_OHM_PD, 01=47K_OHM_PU, 10=100K_OHM_PU, 11=22K_OHM_PU)
PUE (bit 13): 0 - Keeper
PKE (bit 12): 1 - 使能上拉/下拉
ODE (bit 11): 1 - 开漏输出使能
SPEED (bits 6-7): 10 - 中速率 (00=low(50MHz) 01=medium(100MHz) 10=medium(100MHz) 11=max(200MHz))
DSE (bits 3-5): 110 - 驱动能力为R0/6
SRE (bit 0): 1 - 慢转换率
0x1b0b1 是一个完整的PAD配置值,而不是地址。这个值会被写入到PAD控制寄存器中(比如地址0x20E_0310h)下面的数据手册会看到。
从上面的图片示例中可以看到其他不同的 PAD 配置值:
- 0x17059:用于 SD 卡和 GPIO 配置
- 0x1b0b0:用于以太网接口配置
- 0x1b0b1:用于 UART 接口配置
- 0x4001b031:用于以太网时钟引脚特殊配置
- 0x80000000:用于 SNVS 区域的特殊配置
这些值反映了不同引脚功能对电气特性的不同需求。
数据手册与配置值的对应
数据手册中显示:
SW_MUX_CTL_PAD_UART1_TX_DATA
Address: 20E_0000h base + 84h offset = 20E_0084h
基地址加偏移地址=寄存器地址
对应设备树中:
iomuxc: iomuxc@20e0000 { // 基地址: 0x20E0000
compatible = "fsl,imx6ul-iomuxc";
reg = <0x20e0000 0x4000>; // 寄存器范围
};
从数据手册(最下面的大框)可以看到:
MUX_MODE字段(位2-0)设置为000,选择ALT0模式作为UART1_TX
SION位(位4)设置为0,表示由功能决定输入路径
这正好对应了宏定义中的mux_mode = 0,选择UART1_TX功能。
这里正好对应的是上面我讲过的
imx6ul-pinfunc.h里面
#define MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x0084 0x0310 0x0000 0 0
总结
Pinctrl 子系统是现代嵌入式 Linux 系统的关键组件,通过提供统一、灵活的引脚管理机制,它解决了引脚资源有限与功能需求多样之间的矛盾,是设备驱动开发中不可或缺的基础设施。下一篇我会更深入的讲解Pinctrl 子系统的内容