4.3.5. Pinctrl 调试指南

4.3.5.1. 概述

Pinctrl (Pin Control) 是 Linux 内核用于管理配置硬件引脚 (如 GPIO 引脚、复用引脚等) 的一个子系统。它主要功能系统提供硬件引脚灵活控制,涵盖引脚功能配置、方向设置、上拉/下电阻选项。Pinctrl功能通常通过配置设备树 (DTS) 文件实现,从而有效管理优化引脚使用。

4.3.5.2. 特点

芯片中 Pinctrl 的特点如下

  • 多功能引脚控制:支持 GPIO、UART、SPI、I2C、PWM 等多种功能。

  • 引脚方向电平配置:可以设置引脚输入/输出方向输出电平(高/低)。

  • 引脚复用:支持引脚功能复用(MUX_ALT),使得一个引脚可以承载多种功能。

  • 电气特性配置:设置上拉/下电阻、开漏输出电气特性。

  • 设备树支持:通过设备树(DTS)配置引脚功能电气特性。

  • 灵活引脚分配:支持根据需求动态分配引脚功能接口。

  • 可扩展性:支持根据定制硬件需求进行扩展配置。

4.3.5.3. 功能描述

Pinctrl 典型应用

pinctrl_Typical_Applications

例如,在 Linux 系统中,若我们希望使用某个引脚控制 LED 灯开关,就可以通过 Pinctrl 子系统配置引脚功能。通常,这种配置涉及引脚方向、电平以及可能复用功能。

Pinctrl 功能原理

Pinctrl 的核心原理通过设备树(DTS)来描述硬件平台引脚配置。设备树定义每个引脚功能、复用模式、电气特性等。主控设备通过解析设备树引脚控制器节点,来初始化管理引脚配置。

Pinctrl_work

主控芯片关键调用步骤 :

1. 解析设备树引脚配置:

  • 设备树的 pinctrl 配置通常定义引脚功能、复用模式、驱动配置等。主控通过解析这些设备树属性获取硬件引脚配置信息。

2. 主控初始化配置引脚控制器

  • 主控驱动通过 horizon_pinctrl_probe 函数初始化硬件引脚控制器。这一步包括分配内存、设置设备树引脚控制信息以及解析 GPIO bank(引脚组)配置。

  • horizon_pinctrl_parse_gpio_bank:解析设备树与 GPIO 引脚相关信息,为硬件控制器初始化引脚配置。

  • horizon_pinctrl_probe_dt:进一步解析设备树的 pinctrl 配置,读取引脚复用 (muxing)、电气配置 (如拉、下拉)等设置。

3. 设备初始化注册

  • 使用 devm_pinctrl_register_and_init 注册引脚控制器。该函数初始化设备,创建必要设备结构,并使主控能够使用引脚控制器

4. 引脚状态管理

  • 引脚配置状态通过 pinctrl_commit_state 函数应用。设备树状态(如默认状态、睡眠状态等)通过 pinctrl_select_state 函数进行设置。这使得主控能够控制引脚不同工作模式。

5. 引脚请求配置

  • 主控通过 pin_request 函数请求特定引脚。每个引脚使用必须被“请求”并分配某个功能(如 GPIO 或其他设备功能)。请求时会检查引脚是否占用,确保没有冲突。

6. 设备树引脚复用

  • pinctrl 通过 pinmux_enable_setting设置引脚复用模式。这些设置根据设备树定义复用配置(horizon,pins)来选择适当功能。

Pinctrl 工作方式

Pinctrl 和 GPIO 子系统

在 Linux 中,GPIO 操作通过 Pinctrl 子系统完成的。驱动使用 Pinctrl 时,会进入 Pinctrl 核心层分析引脚枚举、映射配置,从而完成引脚配置管理。Pinctrl 子系统有效避免传统直接操作硬件寄存器可能出现引脚冲突配置错误。

pinctrl_and_gpio_subsystem

传统引脚配置方式通常直接操作相应寄存器,但这种方式不仅繁琐,而且容易出错(例如引脚功能冲突)为了简化这一过程避免潜在问题,引入了pinctrl子系统,对于使用者而言,只需要重点关注工作内容如下:

  • 获取设备树引脚信息。

  • 根据获取引脚信息配置引脚复用功能。

  • 根据获取的 pin 信息设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

Pinctrl 子系统结构

pinctrl_structure

如上图所示,Pinctrl 核心层是 Linux 内核抽象层,它下层的 SoC 引脚控制器驱动(Pin Controller Driver)提供通信接口,并上层驱动提供引脚控制能力。具体功能包括引脚复用配置、电气特性设置(如上下电阻、驱动强度等),并为 GPIO 子系统提供引脚操作接口。

驱动代码

Kernel Space

kernel/drivers/pinctrl/hobot/aon.c # Pinctrl 驱动代码源文件
kernel/drivers/pinctrl/hobot/common.c # Pinctrl 驱动代码源文件
kernel/drivers/pinctrl/hobot/display.c # Pinctrl 驱动代码源文件
kernel/drivers/pinctrl/hobot/dsp.c # Pinctrl 驱动代码源文件
kernel/drivers/pinctrl/hobot/hsio.c # Pinctrl 驱动代码源文件
kernel/drivers/pinctrl/hobot/lsio.c # Pinctrl 驱动代码源文件
kernel/drivers/pinctrl/hobot/common.h # Pinctrl 驱动代码头文件

内核配置

CONFIG_PINCTRL_SINGLE

pinctrl-kernel-menuconfig

Pinctrl的DTS配置

Pinctrl 控制器设备树定义位于 BSP 源码的 kernel 文件夹arch/arm64/boot/dts/hobot/x5.dtsi文件内。

4.3.5.4. Pinctrl使用

驱动DTS配置

使用 Pinctrl 前,驱动需要设备树配置相关的 Pinctrl 配置组。设备树通过 pinctrl-names 和 pinctrl-0 属性指定引脚控制配置组。驱动程序在 probe 阶段根据设备树配置,将相应引脚配置寄存器中。

示例: 设备树文件的 UART2 配置:

/* arch/arm64/boot/dts/hobot/x5-evb.dts */
&uart2 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_uart2>;
};

引脚控制配置(pinctrl_uart2):

/* arch/arm64/boot/dts/hobot/pinmux-func.dtsi */
	pinctrl_uart2: uart2grp {
		horizon,pins = <
			LSIO_UART2_RX  LSIO_PINMUX_3 BIT_OFFSET16  MUX_ALT0 &pconf_drv_pu_mid_3v3
			LSIO_UART2_TX  LSIO_PINMUX_3 BIT_OFFSET18  MUX_ALT0 &pconf_drv_pu_mid_3v3
		>;
	};
};

配置说明:

  • &uart2:这个节点一个名为 uart2 的硬件外设,通常设备树中,& 符号表示引用一个已经其他地方定义外设。这个节点表明启用了 UART2 外设,并且状态是 “okay”,即表示外设处于启用状态。

  • status:这代码表明启用 UART2 外设。

  • pinctrl-names:定义引脚控制(pin control)名 “default” 代表引脚控制默认状态。

  • pinctrl-0: 表示引脚控制配置名为 pinctrl_uart2 的配置节点,具体配置信息如下:

    • pinctrl_uart2: uart2grp:这定义一个名为 uart2grp 的引脚组,并一个标签 pinctrl_uart2,用于其他地方引用。

    • horizon,pins = <…>:该行描述了 UART2 的两个引脚配置,包括接收引脚(LSIO_UART2_RX)和发送引脚(LSIO_UART2_TX)。每个引脚四个部分组成:

      • 引脚名称(LSIO_UART2_RX 和 LSIO_UART2_TX):这代表 UART2 接收(RX)和发送(TX)引脚,标识两个引脚硬件位置。

      • 引脚复用(LSIO_PINMUX_3):代表引脚复用组。

      • 偏移量(BIT_OFFSET16 和 BIT_OFFSET18):这些引脚相关偏移,用于定位引脚寄存器位置。

      • 引脚复用功能(MUX_ALT0):MUX_ALT0 表示引脚复用模式。在这种情况下,表示引脚配置为 UART 功能(而其他功能)。

      • 驱动配置(&pconf_drv_pu_mid_3v3):这表示引脚驱动配置,引用一个驱动配置节点 pconf_drv_pu_mid_3v3,它定义引脚电气属性,如电压域、上下拉、驱动力等。

另外提供了GPIO功能配置,在arch/arm64/boot/dts/hobot/pinmux-gpio.dtsi中,内容如下:

/* arch/arm64/boot/dts/hobot/pinmux-gpio.dtsi */
	lsio_gpio0_8: lsio_gpio0_8 {
		horizon,pins = <
			LSIO_UART2_RX	LSIO_PINMUX_3 BIT_OFFSET16	MUX_ALT2	&pconf_input_en_3v3
		>;
	};

	lsio_gpio0_9: lsio_gpio0_9 {
		horizon,pins = <
			LSIO_UART2_TX	LSIO_PINMUX_3 BIT_OFFSET18	MUX_ALT2	&pconf_input_en_3v3
		>;
	};
  • lsio_gpio0_8: lsio_gpio0_8:这是一个 GPIO 引脚配置节点,表示 gpio0_8 引脚设置。该引脚配置与 LSIO_UART2_RX(UART2 的接收引脚)相对的 GPIO 引脚。

  • horizon,pins = <…>:描述引脚复用设置。这部分内容上面的 UART2 配置类似,不同是 MUX_ALT2 代表引脚复用功能为 GPIO。

  • &pconf_input_en_3v3:引用一个名为 pconf_input_en_3v3 的引脚驱动配置,表示引脚配置输入模式并且使用 3.3V 电压。

设备树完整的 pinctrl 属性包括:

  • Pin-mux配置 :用于引脚配置某个功能,最支持4种功能。

    • MUX_ALT0 0x0

    • MUX_ALT1 0x1

    • MUX_ALT2 0x2

    • MUX_ALT3 0x3

  • drive-strength :引脚电流驱动级,详细驱动力对应如下:

IOL Low Level Output Current @VOL (max) = 0.125*VDDIO

DS (Drive Strength) Typical Current Unit
0b0000 3.8 mA
0b0001 5.7 mA
0b0010 7.5 mA
0b0011 9.4 mA
0b0100 11.3 mA
0b0101 13.2 mA
0b0110 15.0 mA
0b0111 16.9 mA
0b1000 18.8 mA
0b1001 20.7 mA
0b1010 22.6 mA
0b1011 24.4 mA
0b1100 26.3 mA
0b1101 28.2 mA
0b1110 30.0 mA
0b1111 31.9 mA

IOH High Level Output Current @VOH (min) = 0.75*VDDIO

DS (Drive Strength) Typical Current Unit
0b0000 5.3 mA
0b0001 7.9 mA
0b0010 10.6 mA
0b0011 13.2 mA
0b0100 15.8 mA
0b0101 18.5 mA
0b0110 21.1 mA
0b0111 23.7 mA
0b1000 26.3 mA
0b1001 29.0 mA
0b1010 31.6 mA
0b1011 34.2 mA
0b1100 36.9 mA
0b1101 39.5 mA
0b1110 42.1 mA
0b1111 44.7 mA
  • 上下配置 :每个Pin脚支持上下配置,在arch/arm64/boot/dts/hobot/pinmux-func.dtsi可以设置默认配置,可支持上下配置有:

    • bias-pull-up

    • bias-disable

    • bias-pull-down

  • 施密特触发配置 :施密特触发器能够有效去除输入信号噪声稳定输出,密特触发器核心特点一个上下阈值,只有输入信号穿越这些阈值时,输出改变。

每个引脚的 pinctrl 属性配置根据 《X5 PIN SW Reg.xlsx》 文档选择,如下图:

  • 第一列:0x38,代表pin reg,例如LSIO的地址为0x3418_0000,则对应 lsio_uart2_txd_pu 地址 = 基地址 + pin reg 。

  • 第二列:lsio_pin_ctrl_12,代表寄存器地址名。

  • 第三列:RW,RW 通常表示寄存器内存区域访问权限。它意味着区域内容可以读取(Read)和写入(Write)

  • 第四列:偏移比特位,代表对应寄存器区域地址。

  • 第五列:对应寄存器区域。

  • 第六列:代表寄存器功能默认值。

  • 第七列:代表不同寄存器功能描述,例如上下拉,施密特触发,电压域,驱动力。

0x38 lsio_pin_ctrl_12 RW [31] lsio_uart2_ms 0x0 SDIO mode select for PAD LSIO_UART2_TXD,LSIO_UART2_RXD, LSIO_UART3_TXD, LSIO_UART3_RXD, LSIO_UART4_TXD, LSIO_UART4_RXD, 0: 3.3v; 1: 1.8v
RSV [30:15] RSV 0x0 reserved bits
RW [14] lsio_uart2_txd_pu 0x0 lsio_uart2_txd pull up enable: 1: pull up enable; 0: pull up disable;
RW [13] lsio_uart2_txd_pd 0x0 lsio_uart2_txd pull down enable: 1: pull down enable; 0: pull down disable;
RW [12:9] lsio_uart2_txd_ds 0x3 lsio_uart2_txd driving selector;
RW [8] lsio_uart2_txd_st 0x0 lsio_uart2_txd; Schmitt trigger enable. 1: enables Schmitt trigger input function; 0: no Schmitt trigger input function;
RSV [7] RSV 0x0 reserved bits
RW [6] lsio_uart2_rxd_pu 0x0 lsio_uart2_rxd pull up enable: 1: pull up enable; 0: pull up disable;
RW [5] lsio_uart2_rxd_pd 0x0 lsio_uart2_rxd pull down enable: 1: pull down enable; 0: pull down disable;
RW [4:1] lsio_uart2_rxd_ds 0x3 lsio_uart2_rxd driving selector;
RW [0] lsio_uart2_rxd_st 0x0 lsio_uart2_rxd; Schmitt trigger enable. 1: enables Schmitt trigger input function; 0: no Schmitt trigger input function;

Pinctrl 设备树解析

Pinctrl_device_tree

驱动调用代码

驱动代码中,首先通过 devm_pinctrl_get 获取引脚控制器。然后,通过 pinctrl_lookup_state 获取切换相应引脚配置状态。

static int hobot_pinctrl_probe(struct platform_device *pdev)
{
    ...
    g_xxx_dev->pinctrl = devm_pinctrl_get(&pdev->dev);
    if (IS_ERR(g_xxx_dev->pinctrl)) {
        dev_warn(&pdev->dev, "pinctrl get none\n");
        g_xxx_dev->pins_xxxx = NULL;
    }
    ...
        /* 按照 pinctrl-names lookup state */
        g_xxx_dev->pins_xxxx = pinctrl_lookup_state(g_xxx_dev->pinctrl, "xxx_func");
    if (IS_ERR(g_xxx_dev->pins_xxxx)) {
        dev_info(&pdev->dev, "xxx_func get error %ld\n",
                PTR_ERR(g_xxx_dev->pins_xxxx));
        g_xxx_dev->pins_xxxx = NULL;
    }
    ...
}
int xxxx_pinmux_select(void)
{
    if (!g_xxx_dev->pins_xxxx)
        return -ENODEV;
    /* 切换到对应的state */
        return pinctrl_select_state(g_xxx_dev->pinctrl, g_xxx_dev->pins_xxxx);
}

用户空间调试

Uboot Space

Pinctrl 功能与 I/O 域、GPIO 紧密相关。在 IO-DOMAIN_UBOOT 阶段,可以使用相关命令调试修改电压功能。

Kernel Space

如果内核配置打开了 Linux Kernel 的 CONFIG_DEBUG_FS 选项,并且挂载了 debugfs 文件系统,内核提供了 Pinctrl 的 debugfs 接口。

首先,检查内核是否已经挂载了 debugfs,如果下列命令输出空,则代表当前挂载 debugfs:

mount | grep debugfs

如果输出为空,则执行以下命令挂载 debugfs:

mount -t debugfs none /sys/kernel/debug

确保 debugfs 已挂载后,用户可以用户空间的 debugfs 下查看 Pinctrl 的信息。 以 lsio 子系统为例,lsio 子系统的 debugfs 节点路径为:/sys/kernel/debug/pinctrl/34180000.lsio_iomuxc/

目录节点可以查看 lsio 域引脚的 Pinctrl 信息(以下命令输出仅供参考,具体节点以 Linux 内核代码为准):

root@buildroot:~$ ls /sys/kernel/debug/pinctrl/34180000.lsio_iomuxc/
gpio-ranges  pinconf-groups  pinconf-pins  pingroups  pinmux-functions  pinmux-pins  pinmux-select  pins

以“pinmux-pins”为例,这个节点可以查看哪些 Pin 脚配置配置组。(以下命令输出仅供参考,具体输出随着dts配置变化)

root@buildroot:~$ cat /sys/kernel/debug/pinctrl/34180000.lsio_iomuxc/pinmux-pins
pin 14 (lsio_spi0_sclk): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 36 (lsio_spi0_ssn): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 37 (lsio_spi0_miso): vdd08_gpu_reg@3 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio1_5
pin 38 (lsio_spi0_mosi): soc:cam:vcon@3 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio1_6
pin 15 (lsio_spi1_ssn_1): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 16 (lsio_spi1_sclk): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 17 (lsio_spi1_ssn): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 18 (lsio_spi1_miso): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 19 (lsio_spi1_mosi): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 20 (lsio_spi2_sclk): 34020000.spi (GPIO UNCLAIMED) function lsio_iomuxc group spi2grp
pin 21 (lsio_spi2_ssn): 34020000.spi (GPIO UNCLAIMED) function lsio_iomuxc group spi2grp
pin 22 (lsio_spi2_miso): 34020000.spi (GPIO UNCLAIMED) function lsio_iomuxc group spi2grp
pin 23 (lsio_spi2_mosi): 34020000.spi (GPIO UNCLAIMED) function lsio_iomuxc group spi2grp
pin 24 (lsio_spi3_sclk): 3d060000.mipi_host (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_24
pin 25 (lsio_spi3_ssn): 34110000.lpwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_lpwm1_1
pin 26 (lsio_spi3_miso): 3d080000.mipi_host (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_26
pin 27 (lsio_spi3_mosi): 3d090000.mipi_host (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_27
pin 28 (lsio_spi4_sclk): 341a0000.serial (GPIO UNCLAIMED) function lsio_iomuxc group uart5grp
pin 29 (lsio_spi4_ssn): 341a0000.serial (GPIO UNCLAIMED) function lsio_iomuxc group uart5grp
pin 30 (lsio_spi4_miso): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 31 (lsio_spi4_mosi): 35010000.gmac-tsn (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_31
pin 32 (lsio_spi5_sclk): 34100000.lpwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_lpwm0_0
pin 33 (lsio_spi5_ssn): 34100000.lpwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_lpwm0_1
pin 34 (lsio_spi5_miso): 34100000.lpwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_lpwm0_2
pin 35 (lsio_spi5_mosi): 34100000.lpwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_lpwm0_3
pin 39 (lsio_i2c0_scl): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 40 (lsio_i2c0_sda): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 41 (lsio_i2c1_scl): 34170000.pwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_pwm3_0
pin 42 (lsio_i2c1_sda): 34170000.pwm (GPIO UNCLAIMED) function lsio_iomuxc group pinctrl_pwm3_1
pin 43 (lsio_i2c2_scl): 340d0000.i2c (GPIO UNCLAIMED) function lsio_iomuxc group i2c2grp
pin 44 (lsio_i2c2_sda): 340d0000.i2c (GPIO UNCLAIMED) function lsio_iomuxc group i2c2grp
pin 45 (lsio_i2c3_scl): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 46 (lsio_i2c3_sda): (MUX UNCLAIMED) (GPIO UNCLAIMED)
pin 47 (lsio_i2c4_scl): 340f0000.i2c (GPIO UNCLAIMED) function lsio_iomuxc group i2c4grp
pin 48 (lsio_i2c4_sda): 340f0000.i2c (GPIO UNCLAIMED) function lsio_iomuxc group i2c4grp
pin 0 (lsio_uart7_rx): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_0
pin 1 (lsio_uart7_tx): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_1
pin 3 (lsio_uart7_rts): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_3
pin 2 (lsio_uart7_cts): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_2
pin 4 (lsio_uart1_rx): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_4
pin 5 (lsio_uart1_tx): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_5
pin 7 (lsio_uart1_rts): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_7
pin 6 (lsio_uart1_cts): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_6
pin 8 (lsio_uart2_rx): 34080000.serial (GPIO UNCLAIMED) function lsio_iomuxc group uart2grp
pin 9 (lsio_uart2_tx): 34080000.serial (GPIO UNCLAIMED) function lsio_iomuxc group uart2grp
pin 10 (lsio_uart3_rx): 341c0000.i2c (GPIO UNCLAIMED) function lsio_iomuxc group i2c5grp
pin 11 (lsio_uart3_tx): 341c0000.i2c (GPIO UNCLAIMED) function lsio_iomuxc group i2c5grp
pin 12 (lsio_uart4_rx): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_12
pin 13 (lsio_uart4_tx): gpiochip8 (GPIO UNCLAIMED) function lsio_iomuxc group lsio_gpio0_13

打印详细信息举例如下:

  • pin 36:表示引脚编号是 36。

  • lsio_spi0_miso:表示引脚名称。

  • vdd08_gpu_reg@3:这部分说明引脚设备树被 vdd08_gpu_reg 节点使用。

  • function lsio_iomuxc:表示引脚功能通过 lsio_iomuxc(IOMUX 控制器)来管理。

  • group lsio_gpio1_5:该引脚属于 lsio_gpio1_5 组,说明引脚复用为 GPIO 功能。