跳到主要内容

PinCtrl

概述

PinCtrl(Pin Controller)子系统用于管理和配置芯片引脚的功能复用和电气特性。该子系统涉及两个主要对象:

  • Pin controller devices:提供引脚控制服务,负责引脚复用和配置
  • PinCtrl client devices:使用引脚控制服务的设备,通过设备树声明所需的引脚功能和配置
注意

Pin controller 与 GPIO controller 是不同的概念。Pin controller 负责将引脚配置为特定功能(如 GPIO、I2C 等),而 GPIO controller 则负责将已配置为 GPIO 功能的引脚设置为输入或输出模式。


功能描述

现代芯片包含大量引脚,每个引脚可能支持多种功能。手动配置每个引脚的寄存器既耗时又容易出错。Linux 内核引入 PinCtrl 子系统来抽象引脚的复用和配置,使开发者能够方便地管理引脚功能。

PinCtrl 子系统提供以下核心功能:

  • 引脚枚举与命名(Enumerating and naming)
  • 引脚复用(Multiplexing):配置引脚用于 GPIO、I2C 或其他外设功能
  • 引脚配置(Configuration):设置上拉、下拉、开漏、驱动强度等电气特性

典型的芯片引脚架构如下图所示:

E300 041 图示

如图所示,pinApinB 可以通过 IOMUX(引脚复用控制器)连接到不同的功能模块:

  • 配置为 GPIO 功能时,连接到 GPIO 模块
  • 配置为 I2C 功能时,连接到 I2C 模块

此外,还可以同时配置引脚的电气特性,如驱动能力、上拉/下拉电阻等。


设备树配置

在设备树中,Pin controller 和 client device 分别定义为两个节点:

pincontroller {
/* Pin controller 节点 */
state_0_node_a {
/* 引脚配置状态 */
};

state_1_node_a {
/* 引脚配置状态 */
};

state_1_node_b {
/* 引脚配置状态 */
};
};

/* Client device 节点 */
device {
pinctrl-names = "idle", "default", "idle", "sleep";
pinctrl-0 = <&state_0_node_a>;
pinctrl-1 = <&state_1_node_a &state_1_node_b>;
pinctrl-2 = <&state_1_node_a &state_1_node_b>;
pinctrl-3 = <&state_1_node_a &state_1_node_b>;
};

Client 端一般情况下无需额外编写软件,按照相应的规范编写 DTS 即可。


核心 API

int pinconf_set_config(struct pinctrl_dev *pctldev,
unsigned int pin,
unsigned long *configs,
unsigned int nconfigs);

参数说明

  • pctldev:PinCtrl 设备指针
  • pin:要配置的引脚编号
  • configs:配置参数数组
  • nconfigs:配置参数数量(数组元素个数)

配置参数格式

  • 低 8 位:参数类型(pin_config_param 定义)
  • 高 8 位:参数值

约束说明

DTS Pin Node 编写约束

在设计 pinctrl 时,把每一个 pin 脚当做一个 group 来对待,即有多少个 pin 脚就有多少个 group,因此用户配置时,pin 脚需要根据需要单独配置,一般来说针对一个具体功能时,比如 I2C、I2S、PWM 等,可能需要同时使用多个管脚,因此就需要定义多个管脚的 group 功能,如下所示:

// 定义 pin 236
pin236:pin236 {
groups = "pin236"; // 236 group 名是 pin236
function = "customized"; // 这里只能填写"customized"
function-value = <xxx>; // 对应 PINMUX 的功能值,请参考《xxx_pin_mux_peri.xlsx》
};
// 定义 pin 237,如下字段的解释同 pin 236
pin237:pin237 {
groups = "pin237";
function = "customized";
function-value = <xxx>;
};

若 A 驱动模块需要使用 pin236、pin237,可按下面在设备树中填写:

&xxx(即 A 驱动模块在设备树中的引用名) {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pin236 &pin237>; // 使用两个组,pin236 和 pin237
};

DTS Pin 功能配置约束

每个具体特定的功能只能到对应一组 pin,两组 pin 切换到同一功能,会导致硬件上功能异常。例如:pin100 的 func1 是 i2c5 scl、pin101 的 func1 是 i2c5 sda,而 pin200 的 func2 也是 i2c5 scl、pin201 的 func2 也是 i2c5 sda。则设备树中,仅可配置 pin100/pin101、或 pin200/pin201 这两组 pin 的其中之一为 i2c5。不可同时都配置为 i2c5。例如配置 pin100/pin101 为 i2c5 的 scl/sda,而 pin200/pin201 配置为非 i2c5 的其他功能,可正常工作。


Pin 配置规范

pin 脚的定义必须遵循一定的规范:pin 的节点名跟随管脚号定义,只能定义为 pinxxx,xxx 的范围为 0~237。如管脚号为 25 定义为 pin25,管脚号 130 定义为 pin130

groups 为组名跟随管脚号定义,组名只能定义为 pinxxx,xxx 的范围为 0~237。管脚号为 25,定义为 groups = "pin25",管脚号 130 定义为 groups = "pin130"

function 为复用的功能,此字段值固定,为 function = "customized";

function-value 值,范围为 0 ~ 3,请参考《MTT E300 MXM 模组规格书.pdf》中的 "6 低速接口与 GPIO 复用关系" 章节。

普通的 Pin 配置按照如下格式编写,num 取值范围为 0 ~ 237:

  1. groups 取值同节点名
  2. function = "customized"; 固定字段
  3. function-value 按照复用功能配置,范围为 0 ~ 3
  4. 如下为某个 pin 的配置
pinnum:pinnum {
groups = "pinnum";
function = "customized";
function-value = <xxx>;
};

更多的 pin 配置参考: pin 脚支持以下的配置,具体见如下的设备树注释:

pinnum:pinnum {
groups = "pinnum";
function = "customized";
function-value = <xxx>;
bias-pull-up = <0>; // supported; limited: bias-pull-up and bias-pull-down can not config together
bias-pull-down = <0>; // supported; limited: bias-pull-up and bias-pull-down can not config together
drive-strength = <2>; // supported: 2, 4, 8, 16; unit: mA
input-debounce = <0>; // supported; limited: acpi
input-schmitt-disable = <0>; // supported
input-schmitt-enable = <0>; // supported
output-disable; // supported
output-enable; // supported
output-high; // supported
output-low; // supported
power-source = <1800>; // supported: 3300, 2500, 1800; unit: mV; limited: RGMII_VDDIO0, RGMII_VDDIO1, SDIO1_VDDIO
slew-rate = <0>; // supported
};


配置流程

  1. 确定使用的 PIN 管脚号
  2. 查询 PIN 管脚号《xxx_pin_mux_peri.xlsx》以及《xxx_pin_mux_sm.xlsx》,确定功能值,范围为 0 ~ 3
  3. 编写 PIN DTS
  4. 若需要编写对应的驱动代码,参考后续章节

参考实例 I2S

以下以 I2S 为例,说明如何配置 pin 为 I2S 功能。

  1. 以 I2S 配置为例作为参考:

  2. I2S5 使用 pin195 ~ pin198,function-value 填写 1 为复用为 I2S 功能

  3. 编写对应的 PIN

    pin195:pin195 {
    groups = "pin195";
    function = "customized";
    function-value = <1>;
    drive-strength = <4>;
    slew-rate = <1>;
    };
    pin196:pin196 {
    groups = "pin196";
    function = "customized";
    function-value = <1>;
    drive-strength = <4>;
    slew-rate = <1>;
    };
    pin197:pin197 {
    groups = "pin197";
    function = "customized";
    function-value = <1>;
    drive-strength = <4>;
    slew-rate = <1>;
    };
    pin198:pin198 {
    groups = "pin198";
    function = "customized";
    function-value = <1>;
    drive-strength = <4>;
    slew-rate = <1>;
    };

    i2s5_2ch {
    // 其他与 pin 无关的配置省略
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pin195 &pin196 &pin197 &pin198>;
    };
  4. 编写 I2S 的驱动

开发参考

如下为编写一个 pin demo 的参考示例,说明如何配置 pin。大部分情况下,pin 的配置都是配合其他驱动如 I2S、I2C、PWM,不作为单独的驱动出现,若有需要也可参考如下配置,单独实现驱动。

  1. 编写 DTS,参考 m1000-t021.dts

    pin234:pin234 {
    groups = "pin234";
    function = "customized";
    function-value = <1>;
    };

    pin235:pin235 {
    groups = "pin235";
    function = "customized";
    function-value = <1>;
    };

    pin236:pin236 {
    groups = "pin236";
    function = "customized";
    function-value = <1>;
    };


    pin237:pin237 {
    groups = "pin237";
    function = "customized";
    function-value = <1>;
    };

    pin_customized: pin_customized {
    compatible = "mthreads,pin-customized";
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&pin234 &pin235 &pin236 &pin237>;
    };
  2. 源码编写,参考 pinctrl-mt-customized.c

    int pinmux_customized_probe(struct platform_device *pdev)
    {
    pingpio_inf(&pdev->dev, "pin customized! \n");
    // add more
    return 0;
    }

    int pinmux_customized_remove(struct platform_device *pdev)
    {
    // add more


    return 0;
    }

    static const struct of_device_id pinmux_customized_of_match[] = {
    { .compatible = "mthreads,pin-customized", .data = NULL },
    {}
    };

    static struct platform_driver pinmux_customized_driver = {
    .probe = pinmux_customized_probe,
    .remove = pinmux_customized_remove,
    .driver = {
    .name = "pin-customized",
    .of_match_table = pinmux_customized_of_match,
    },
    };

    module_platform_driver(pinmux_customized_driver);

    MODULE_AUTHOR("mthreads");
    MODULE_DESCRIPTION("pinmux customized demo");
    MODULE_LICENSE("Dual BSD/GPL");

调试方法说明

pin 与 gpio 共用系统调试接口,都可通过查看 gpio-ctxtrl 获取相应的当前配置信息,其中的 ctrl 以及 fs 字段主要用于 PinCtrl 在 系统 /sys 搜索: find /sys/ -name "gpio-ctrl",有类似如下结果

E300 042 图示


调试具体引脚

以 gpio_peri 为例,查看第 21,22 号引脚:

  1. 进入 peri_padc 目录

    E300 043 图示

  2. 将需要 debug 的引脚写入 index

    E300 044 图示

  3. 查看引脚的状态

    E300 045 图示

  4. 通过 echo -1 > clear 清除所有 debug 引脚。再次执行 cat stat 时不再显示引脚信息。

    E300 046 图示

stat 的字段说明

pin 对应具体的管脚

ctrl 用于配置上拉下拉等,各 bit 定义如下:

E300 047 图示

fs 复用功能,pinctrl 配置的值会在这里显示


常见问题

Q: 如何查看 Linux 系统的 PinCtrl 子系统?

A: 使用以下命令挂载 debugfs:

sudo mount -t debugfs none /sys/kernel/debug

Q: 如何查看系统中有多少个 pin?

A: 进入相应目录并查看 pin 文件:

cd /sys/kernel/debug/pinctrl/41000.pinctrl_p
cat pin

Q: 如何查看与 GPIO 的对应关系?

A: 进入相应目录并查看 gpio-ranges 文件:

cd /sys/kernel/debug/pinctrl/41000.pinctrl_p
cat gpio-ranges