pinctrl子系统

pinctrl子系统的作用

  • 获取设备树中 pin 的信息
  • 设置 pin 的复用功能
  • 设置 pin 的电气特性,比如上/下拉、速度、驱动能力等

相关硬件

对于引脚的配置,不同平台通常不一样:

IMX6ULL这款SoC中有2个专门的引脚配置的硬件模块IOMUXC(Input/Output Muliplexing Controller),和IOMUXC-SNVS。通过设置该控制器的某些寄存器,就可以实现对指定引脚的配置。

  • IOMUXC和IOMUXC-SNVS的区别主要在于配置的引脚所属的电源域,前者控制主电源域的引脚,而后者控制SNVS这个独立的电源域的引脚(该电源域用于在系统休眠时维持部分功能)
  • 同一个引脚只能由一个引脚配置模块来配置

而STM32就没有独立的IOMUXC,其引脚的电气属性及复用的配置全由GPIO控制器来做

使用方法

pinctrl子系统的使用通常遵循以下步骤:

1.定义引脚控制组:节点的名字一般以grp结尾

1
2
3
4
5
6
7
&iomuxc {
// LED的引脚控制组
pinctrl_led: ledgrp {
// 复用功能 + 电气属性
fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0>;
};
};

注意:

  • pinctrl节点需要写在对应的引脚控制器(iomux)节点下
  • LED的MX6UL_PAD_GPIO1_IO03__GPIO1_IO03表示该pin==复用==为GPIO1的03(芯片的各种复用信息定义在Linux内核源码的"arch/arm/boot/dts/imx6ull-pinfunc.h"中),实际是控制GPIO的SW_MUX_CTL寄存器
  • 0x10B0表示该pin的==电气==属性,具体是什么意思由芯片手册(开发板光盘->7、I.MX6UL 芯片资料->2、I.MX6ULL 芯片资料->IMX6ULL 数据手册(商用级).pdf)决定,实际是控制GPIO的SW_PAD_CTL 寄存器
image-20240804183808154
位域 字段名 功能 选项值
16 HYS 施密特触发使能 0: 禁用,1: 使能(增强抗噪)
15-14 PUS 上下拉电阻配置 00: 100K下拉,01: 47K上拉,10: 100K上拉,11: 22K上拉
13 PUE 上下拉/保持器选择 0: Keeper功能,1: 上下拉功能
12 PKE 上下拉/保持器使能 0: 禁用,1: 使能
11 ODE 开漏输出使能 0: 推挽输出,1: 开漏输出
10-8 保留 未使用 -
7-6 SPEED 信号带宽 00: 50MHz,01/10: 100MHz,11: 200MHz
5-3 DSE 驱动强度 000: 禁用,001: R0(260Ω@3.3V),010: R0/2,… 111: R0/7
2-1 保留 未使用 -
0 SRE 压摆率控制 0: 慢压摆率(低EMI),1: 快压摆率(高速信号

设备树里的pinctrl写的都是16进制,要先转成2进制再分析

常用值

  • 0x10B0:推挽输出、无上下拉
  • 0xF080:上拉输入

2.在设备节点中引用引脚控制组:通过pinctrl-namespinctrl-* 关联引脚组:

1
2
3
4
5
6
7
8
9
10
&usdhc2 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc2_8bit>;
pinctrl-1 = <&pinctrl_usdhc2_8bit_100mhz>;
pinctrl-2 = <&pinctrl_usdhc2_8bit_200mhz>;
bus-width = <8>;
non-removable;
no-1-8-v;
status = "okay";
};
  • pinctrl-names 的长度需要和和 pinctrl-* 一致
  • pinctrl-*并不代表该设备用到了几个不同的引脚,而是该设备用到的引脚可以配置成几种不同的状态。比如上面这个例子,usb可以配置成3个速度,所以为这3个状态各创建了一个pinctrl节点
  • 驱动层有API可以根据pinctrl-names切换引脚到不同的状态(pinctrl-*)

注意事项

1.如果一个设备确实用到了多个不同的引脚,要么把这些引脚全部写在一个引脚控制组里面,比如这样

1
2
3
4
5
6
pinctrl_uart1_sleep: uart1_sleep_grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__GPIO1_IO01 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__GPIO1_IO02 0x1b0b1
>;
};

要么就写多个引脚控制组,但在使用的时候,写到设备节点的一个pinctrl-*里面

1
2
3
4
5
&my_device {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1_tx>, <&pinctrl_led>; // 合并两组
status = "okay";
};

2.写完设备树后要进行冲突检查:

  • 同一个引脚不能同时配置成多个状态
  • 多个设备不能同时使用一个引脚,需要分时复用(设置status属性)