Linux Pinctrl子系统

前言

Pinctrl介绍

Pinctrl 子系统是什么?为什么需要它?

在 Linux 驱动开发中,Pinctrl(Pin Control)子系统是内核中专门用于管理和配置芯片引脚的一个模块 。它的核心作用可以概括为两点:引脚复用(Multiplexing) 和 引脚配置(Configuration)。
在没有 Pinctrl 的年代,驱动开发者需要在板级文件或驱动代码中直接操作寄存器来设置引脚功能。这种方式既繁琐又极易出错,比如很容易发生两个驱动无意中操作同一个引脚的功能冲突 。Pinctrl 子系统的出现就是为了解决这些问题,它将引脚的管理统一起来,实现了硬件描述与驱动代码的解耦 。现在,我们只需要在设备树中声明引脚该如何使用,Pinctrl 子系统就会在恰当的时机自动完成底层的寄存器配置

Pinctrl 的核心概念

为了理解 Pinctrl 是如何工作的,我们需要先掌握几个核心的抽象概念 :

  1. Pin (引脚) :这是最基础的单位,代表芯片的一个物理引脚。在 Pinctrl 子系统中,每个引脚都有一个本地的、从 0 开始的编号和名称(例如 PA0、PF6)。
  2. Pin Group (引脚组) :许多外设功能(如 I2C、SPI、UART)需要多个引脚协同工作,这些引脚会被组合在一起,形成一个"引脚组",例如一个 i2c1_pins 组可能包含了 SCL 和 SDA 两个引脚 。
  3. Function (功能) :"功能"定义了某个引脚或引脚组可以切换到的特定工作模式,例如 i2c1 功能、spi2 功能或最基础的 gpio_out 功能 。
  4. Pin Configuration (引脚配置) :除了设置功能,我们经常还需要配置引脚的电气特性,比如 bias-pull-up (上拉)bias-pull-down (下拉)drive-strength (驱动能力)bias-disable (高阻) 等 。
  5. Pin State (引脚状态) :一个设备在不同的工作场景下(例如正常工作、低功耗休眠),其引脚的功能和配置可能不同。Pinctrl 通过"状态"来管理这些场景,最常见的两个状态是 default 和 sleep

Pinctrl 子系统框架

从软件架构上看,Pinctrl 子系统主要分为三个层次 :

  1. Pinctrl Core (核心层):这是内核提供的核心框架,负责管理所有注册进来的 Pin Controller 驱动,并提供统一的接口给上层的 Client Device。
  2. Pin Controller Driver (控制器驱动层):这是由芯片厂商(如全志、NXP、瑞芯微)的工程师实现的底层驱动。它向下直接操作硬件寄存器,并向上层核心注册三个关键的操作函数集 :
  • struct pinctrl_ops:负责枚举芯片支持的所有引脚和引脚组,以及解析设备树中的引脚配置信息。
  • struct pinmux_ops :负责实现引脚复用功能,即将指定的引脚组切换到指定的功能模式。其中还有一个关键的标志 strict ,当它被设置为 true 时,表示该控制器不允许一个引脚同时被 GPIO 和其他功能复用,他保证了GPIO的功能唯一性,但是他也会带来一些兼容性的问题
  • struct pinconf_ops:负责实现引脚电气特性的配置,如设置上下拉、驱动强度等。
  1. Client Device (使用者):指任何需要使用引脚的设备驱动,比如 I2C、UART、SPI 或你自己编写的 LED 驱动。它们只需要在设备树中声明要使用哪个引脚组以及目标状态,Pinctrl 子系统会自动完成配置

Pinctrl子系统的使用方法

对于Pinctrl的子系统,我们日常生活里主要是通过DTS完成.而对于这块的一个使用,他又分为生产和消费两个块

Pinctrl生产端

Pinctrl的生成端,其实我个人理解他其实就是一些类似于.h文件一样的东西,在这里面,我们预先包装好一些可能用到的Pin的配置,然后来供人使用,如我们这个全志的dtsi

复制代码
/* For uboot */
/* for mmc */
&pio {

    vcc-pg-supply = <&reg_pio1_8>;

    uart0_ph_pins: uart0-ph-pins {
        pins = "PH0", "PH1";
        function = "uart0";
        bias-pull-up;
    };

    uart0_ph_sleep: uart0-ph-sleep {
        pins = "PH0", "PH1";
        function = "gpio_in";
    };

    uart1_ph_pins: uart1-ph-pins {
        pins = "PG6", "PG7", "PG8", "PG9";
        function = "uart1";
    };

    uart1_ph_sleep: uart1-ph-sleep {
        pins = "PG6", "PG7", "PG8", "PG9";
        function = "gpio_in";
    };

我节选了其中的一部分,这里可以看到,他将一些引脚如,PH0,PH1,进行了分组配置,将他们分配到了uart sleep状态时候的一个引脚的配置,提前将他们分配到了gpio_input模式,这个其实就是所谓的生产端

Pinctrl消费端

对于消费端,我个人理解为是一种使用,即在dts里面对他的使用,这个可以直接查看一个dts例子

复制代码
// 向pio添加
&pio {
    wzb_led_pin: wzb_led_pin {
            pins = "PF6";
            function = "gpio_out";
            /* 强制配置为GPIO输出模式 */
            bias-pull-up; /* 上拉 */
            drive-strength = <10>; /* 驱动能力 */
        };
};

// 向根节点添加
&{/} {
    wzb_led: wzb_led {
        compatible = "allwinner,wzb_led";
        status = "okay";
        /* LED灯低电平点亮 */
        led-gpios = <&pio PF 6 GPIO_ACTIVE_LOW>;
        /* 配置其正常工作时候的状态 */
        pinctrl-names = "default";
        pinctrl-0 = <&wzb_led_pin>;
    };
};

这个就是在wzb_led这个节点内直接引用了wzb_led_pin配置的电气特性,由此即是消费

Pinctrl的一些调试方法

在Pinctrl的使用里面,我们需要借助一些方法对其进行调试,查看当前的一些状态,这个在我们初步确定问题的时候非常重要

查看pin mux状态

当我们需要知道一个pin当前的复用状态的时候,我们可以通过下面命令来查看

cat /sys/kernel/debug/pinctrl/*/pinmux-pins

复制代码
Pinmux settings per pinel/debug/pinctrl# cat /sys/kernel/debug/pinctrl/*/pinmux-pins
Format: pin (name): mux_owner|gpio_owner (strict) hog?
pin 0 (PA0): UNCLAIMED
pin 1 (PA1): UNCLAIMED
pin 2 (PA2): UNCLAIMED
pin 3 (PA3): UNCLAIMED
pin 4 (PA4): UNCLAIMED
pin 5 (PA5): UNCLAIMED
pin 6 (PA6): UNCLAIMED
pin 7 (PA7): UNCLAIMED
pin 8 (PA8): UNCLAIMED
pin 9 (PA9): UNCLAIMED
pin 10 (PA10): UNCLAIMED
pin 11 (PA11): UNCLAIMED
pin 12 (PA12): UNCLAIMED
pin 64 (PC0): device 4022000.sdmmc function sdc2 group PC0
pin 65 (PC1): device 4022000.sdmmc function sdc2 group PC1
pin 66 (PC2): GPIO 300b000.pinctrl:66
pin 67 (PC3): UNCLAIMED
pin 68 (PC4): UNCLAIMED
pin 69 (PC5): device 4022000.sdmmc function sdc2 group PC5
pin 70 (PC6): device 4022000.sdmmc function sdc2 group PC6
pin 71 (PC7): UNCLAIMED
pin 72 (PC8): device 4022000.sdmmc function sdc2 group PC8
pin 73 (PC9): device 4022000.sdmmc function sdc2 group PC9
pin 74 (PC10): device 4022000.sdmmc function sdc2 group PC10
pin 75 (PC11): device 4022000.sdmmc function sdc2 group PC11
pin 76 (PC12): UNCLAIMED
pin 77 (PC13): device 4022000.sdmmc function sdc2 group PC13
pin 78 (PC14): device 4022000.sdmmc function sdc2 group PC14
pin 79 (PC15): device 4022000.sdmmc function sdc2 group PC15
pin 80 (PC16): device 4022000.sdmmc function sdc2 group PC16

这里从里面的function项可以看到这个引脚当前的配置模式,而括号内的引脚号则表示了这个引脚的名称

查看引脚电气特性配置

当我们需要知道一个引脚是否进行了上拉下拉或者配置了多少的驱动强度的时候,我们可以通过下面命令进行查看

cat /sys/kernel/debug/pinctrl/*/pinconf-pins

复制代码
root@lubapinconf-pinsrnel/debug/pinctrl/300b000.pinctrl# cat /sys/kernel/debug/pinctrl/*/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (PA0): input bias disabled, output drive strength (20 mA)
pin 1 (PA1): input bias disabled, output drive strength (20 mA)
pin 2 (PA2): input bias disabled, output drive strength (20 mA)
pin 3 (PA3): input bias disabled, output drive strength (20 mA)
pin 4 (PA4): input bias disabled, output drive strength (20 mA)
pin 5 (PA5): input bias disabled, output drive strength (20 mA)
pin 6 (PA6): input bias disabled, output drive strength (20 mA)
pin 7 (PA7): input bias disabled, output drive strength (20 mA)
pin 8 (PA8): input bias disabled, output drive strength (20 mA)
pin 9 (PA9): input bias disabled, output drive strength (20 mA)
pin 10 (PA10): input bias disabled, output drive strength (20 mA)
pin 11 (PA11): input bias disabled, output drive strength (20 mA)
pin 12 (PA12): input bias disabled, output drive strength (20 mA)
pin 64 (PC0): input bias pull down, output drive strength (30 mA)

这里的input bias项展示了其内部是否pull down或者pull up,后面的output drive strength则展示了其驱动强度(最大可支持的电流)

参考资料

Deepseek

相关推荐
AlfredZhao2 天前
生产环境里,为什么不建议把普通端口直接暴露到公网?
linux·https·443·80
戴为沐3 天前
Linux内存扩容指南
linux
zylyehuo3 天前
Linux 彻底且安全地删除文件
linux
用户805533698034 天前
主线 U-Boot 上 RK3506:和闭源 rkbin 拔河的三个隐性契约
linux·嵌入式
用户034095297914 天前
linux fcitx 5 雾凇拼音 设置在中文输入法下仍然输入英文标点
linux
乘云数字DATABUFF4 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
Web3探索者6 天前
可视化服务器管理和传统命令行区别是什么?新手教程:Linux 运维到底该用图形界面还是 SSH 命令行?
linux·ssh
zylyehuo6 天前
Linux系统中网线与USB网络共享冲突
linux
荣--6 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森6 天前
动手实战学 Docker — 从零到集群编排完全指南
运维