pinctrl子系统

pinctrl子系统的作用

  • 获取设备树中 pin 的信息
  • 设置 pin 的复用功能(在6ULL由IOUMXC寄存器控制)
  • 设置 pin 的电气特性,比如上/下拉、速度、驱动能力等 (在6ULL由PAD寄存器控制)

相关寄存器

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

image-20260121193259378

IMX6ULL使用IOMUXC(Input/Output Muliplexing Controller),和IOMUXC-SNVS寄存器实现引脚复用功能的设置、使用每个GPIO的PAD寄存器进行引脚电气属性的设置

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

使用方法

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表示复用为GPIO1,实际是设置了GPIO的SW_MUX_CTL寄存器的值

    • 芯片的各种复用信息定义在Linux内核源码的"arch/arm/boot/dts/imx6ull-pinfunc.h"
  • 0x10B0表示该pin的电气属性,实际是控制GPIO的SW_PAD_CTL寄存器

    • 具体是什么意思看I.MX6ULL的参考手册

image-20260121173109326image-20260121173120831

位域 字段名 功能 选项值
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进制再分析

  • PAD寄存器不能设置输出还是输入,得在驱动代码里通过GPIOx_GDIR寄存器设置

PUS、PUE、PKE这三个选项确实存在明确的依赖关系:

根据文档内容,PUS、PUE、PKE这三个选项确实存在明确的依赖关系,它们共同构成了上拉/下拉电阻的配置逻辑:

  1. PKE(Pull/Keeper Enable)- 使能控制(位12)
  • 首要开关:必须先将PKE设置为”1”(Enabled),PUS和PUE的配置才会生效
  • 禁用状态:当PKE=”0”时,无论PUS和PUE设置什么值,上拉/下拉功能都会被禁用
  1. PUE(Pull Up/Down Select)- 模式选择(位13)
  • 功能选择:在PKE=”1”的前提下,PUE决定使用哪种模式:
    • PUE=0:Keeper模式(保持器功能)
    • PUE=1:Pull模式(上拉/下拉电阻功能)
  1. PUS(Pull Up Select)- 电阻值选择(位15-14)
    • 仅在Pull模式下有效:只有当PKE=”1”且PUE=”1”时,PUS的配置才有意义
  • 只有输入模式才需要配置上下拉,输出模式不需要!输出模式得在驱动通过gpio_set_value设置电平
  • pinctrl里本质是修改PAD_CTRL和IOMUCX_CTRL这2个寄存器,而GPIO的输入输出由GPIO_CTRL寄存器控制,也就是说设备树里通过pinctrl没法直接控制输入/输出以及高低电平
  • 设备树里可以通过中断或者gpio-hog机制间接控制输入/输出以及高低电平

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属性)