PINCTRL子系统

Table of Contents

1. pinctrl子系统简介

PIN Control子系统驱动的硬件叫做Pin Controller,在有些芯片中,有专门的 管理模块,也有些是集成在GPIO模块中。主要功能包括:

  • pin multiplexing:pin引脚复用,将pin复用为GPIO、SPI、IIC等功能;
  • pin configuration: 这些配置参数包括如下,也可以根据芯片情况增加;
    • pull-up/down:电阻上下拉设定;
    • tri-state:三态设定;
    • drive-strength:驱动能力设定。

1.1. Pin Controller

Pin Controller这个硬件模块是dts中的一个节点node(也叫server node)。 device(如UART/IIC)也需要在它自己的dts node中描述相关内容。也就是说引 脚配置在Pin Controller的设备树节点中(server node),device的节点中 (client device)通过其phandle去引用这些节点去使用,U-BOOT中使用 pinctrl-name和pinctrl-n来引用server node中的配置。

1.1.1. Pin Configuration

每个pin configuration都是pin controller的child node,描述了client device要使用到的一组pin的配置信息。具体如何定义pin configuration是和具 体的pin controller相关的。在pin controller node中定义pin configuration 其目的是为了让client device引用。

Pin configuration的对象是pin或者pin group,SoC中的管脚有些属性可以配置, 例如上拉、下拉、高阻、驱动能力等,pinctrl subsystem使用pin configuration来封装这些功能,具体体现在struct pinconf_ops数据结构中, 如下,U-BOOT对这些API做了整合放在“struct pinctrl_ops”变量中:

struct pinconf_ops {
    #ifdef CONFIG_GENERIC_PINCONF
    bool is_generic;
    #endif
    /** 获取指定pin(管脚的编号,由pin的注册信息获得)当前配置,保存在
     * config指针中
     * */
    int (*pin_config_get) (...);

    /** 设置指定pin的配置(可以同时配置多个config,具体意义要由相应
     * pinctrl driver实现
     * */
    int (*pin_config_set) (...);

    /** 获取指定pin group的配置项 */
    int (*pin_config_group_get) (...);

    /** 设置指定pin group的配置项 */
    int (*pin_config_group_set) (...);
    int (*pin_config_dbg_parse_modify) (...);
    void (*pin_config_dbg_show) (...);
    void (*pin_config_group_dbg_show)(...);
    void (*pin_config_config_dbg_show)(...);
};

1.1.2. Pinctrl Driver

Pinctrl Driver需要根据实际情况,将系统中所有的pin组织成一个pin 描述结 构数组(struct pinctrl_pin_desc类型),并使用Pinctrl Driver来处理Pin Configuration的配置。

1.1.3. Pin Groups

有时需要将很多pin组合在一起,以实现特定的功能,例如SPI、I2C等。因此pin controller需要以group为单位,访问、控制多个pin。相应地,pin controller 子需要提供一些机制,来获取系统中到底有多少groups、每个groups包含哪些 pins等。因此,pinctrl的核心驱动在struct pinctrl_ops中抽象出三个回调函 数,用来获取groups相关信息,U-BOOT对这些API做了整合放在“struct pinctrl_ops”变量中。在dts中用属性groups来给出(如groups="uart0_tx_rx")。

struct pinctrl_ops {
    /** 获取系统中pin groups的个数,后续的操作,将以相应的索引为单位
     * (类似数组的下标selector)操作这个group里面的pin
     * */
    int (*get_groups_count) (struct pinctrl_dev *pctldev);
    /** 获取指定group(由索引selector指定)的名称,在dts中用属性groups
     * 来给出(如groups="uart0_tx_rx")
     * */
    const char *(*get_group_name) (...);
    /** 获取指定group的所有pins(由索引selector指定),结果保存在pins
     * 指针数组中
     * */
    int (*get_group_pins) (...);

    void (*pin_dbg_show) (...);

    /** 将device tree中的pin state信息转换为pin map */
    int (*dt_node_to_map) (...);
    void (*dt_free_map) (...);
};

配置node中groups属性和pins属性是平级,且U-BOOT中如果检测到pins属性, groups属性,就会被当成普通属性,并对pins的值一个一个的进行配置。如下, 如有groups,驱动中就会整合配置"uart0_0_tx_rx"所代表的这些引脚。如果有 pins,驱动就会一个一个的将这些属性配置给这些引脚。

pinconfig_uart0_default: uart0-default-pin {
        bootph-all;
        # groups和function合作
        groups = "uart0_0_tx_rx";
        function = "uart"; 
        bias-disable;
        speed-freq-very-high;
        # pins,pinmux和groups冲突
        pins = "PS_MIO15_500", "PS_MIO14_500";
        pinmux = <6>;
};

dts 配置node中groups用于确定引脚,function用于确定引脚的功能,驱动中需 要做对应的定义。

static int hwd10909_emmc_pins[] = { 40, 41, 42, 43, 44, 45, 47, 48,49, 50, }; /* pin序号 */
static int hwd10909_emmc_funcs[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, };/* pin功能 */

static int hwd10909_emmc_rst_pins[] = { 37, };
static int hwd10909_emmc_rst_funcs[] = { 1, };

static int hwd10909_uart0_0_tx_rx_pins[] = { 15, 14, }; /* RX: MIO14 TX: MIO15 */
static int hwd10909_uart0_0_tx_rx_funcs[] = { 6, 6, }; /* AF6 AF6 */

static const struct hwd_group_desc hwd10909_groups[] = {
    { "emmc", hwd10909_emmc_pins, sizeof(hwd10909_emmc_pins), hwd10909_emmc_funcs},
    { "emmc_rst", hwd10909_emmc_rst_pins, sizeof(hwd10909_emmc_rst_pins), hwd10909_emmc_rst_funcs},
    { "uart0_0_tx_rx", hwd10909_uart0_0_tx_rx_pins, sizeof(hwd10909_uart0_0_tx_rx_pins), hwd10909_uart0_0_tx_rx_funcs},
};
static const char *const hwd10909_emmc_groups[] = { "emmc", "emmc_rst", };
static const char *const hwd10909_uart_groups[] = { "uart0_0_tx_rx"};

static const struct hwd_function_desc hwd10909_functions[] = {
    {"emmc",    hwd10909_emmc_groups, ARRAY_SIZE(hwd10909_emmc_groups)},
    {"uart",    hwd10909_uart_groups, ARRAY_SIZE(hwd10909_uart_groups)},
};

1.1.4. Pin function

为了管理SoC的管脚复用,pinctrl subsystem抽象出function的概念,用来表示 I2C0、UART5等功能。pin(或者pin group)所对应的function一经确定,它们 的管脚复用状态也就确定了。在dts中用属性function来给出(如 function="uart")。

和Pin groups类似,pinctrl核心驱动不关心function的具体形态,只要求 pinctrl驱动将SoC的所有可能的function枚举出来(格式自行定义,不过需要有 编号、名称等内容),并注册给pinctrl核心驱动。后续pinctrl核心驱动将会通 过function的索引,访问、控制相应的function。同一个function(如I2C0), 可能可以map到不同的pin(或者pin group)上。

1.1.5. Pin state

pin(pin group)以及相应的function和configuration的组合,可以确定一个设 备的一个“状态”。这个状态在pinctrl subsystem中就称作pin state。在dts的 client device中使用pinctrl-name属性来给出这个client device可能的状态 state,然后这些使用这些pinctrl-n句柄来引用server node中的每个 configuration,通常引用标签(如pinctrl-names = "default"; pinctrl-0 = <&pinconfig_uart0_default>;),然后在client device驱动中, 可使用API(pinctrl_select_state(dev, "default");)来切换这个设备的引脚 状态到default态。

1.1.6. Pin map

在Linux的pinctrl subsystem中,pin state有关的信息是通过pin map收集。在 旧时代,kernel的bsp工程师需要在machine有关的代码中,静态的定义pin map 数组,这一个非常繁琐且不易维护的过程。不过当kernel引入device tree之后, 事情就简单了很多:pinctrl driver确定了pin map各个字段的格式之后,就可 以在dts文件中维护pin state以及相应的mapping table。pinctrl core在初始 化的时候,会读取并解析dts,并生成pin map。而各个consumer,可以在自己的 dts node中,直接引用pinctrl driver定义的pin state,并在设备驱动的相应 的位置,调用pinctrl subsystem提供的API,active或者deactive这些state。 至于dts中pin map描述的格式是什么,则完全由pinctrl driver自己决定,因为, 最终的解析工作(dts to map)也是它自己做的。

Author: Joe

Email: bitman@163.com

Created: 2026-07-12 日 12:50

Validate