Pinctrl&gpio驱动子系统笔记

Pinctrl&gpio驱动子系统笔记
1 概述
pinctrl ==> pin controler的缩写
Pinctrl:Pin Controller,顾名思义,就是用来控制引脚的:
- 引脚枚举与命名(Enumerating and naming)
 - 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
 - 引脚配置(Configuration):比如上拉、下来、open drain、驱动强度等
 
Pinctrl驱动由芯片厂家的BSP工程师提供,一般的驱动工程师只需要在设备树里:
- 指明使用那些引脚
 - 复用为哪些功能
 - 配置为哪些状态
 

2 pintrl配置
2.1 在设备树中定义
client端定义

状态的对应:
"default" ==> pinctrl-0 = <&mmc1_pins_default>;
"sleep" ==> pinctrl-1 = <&mmc1_pins_sleep>
上面的状态是如何实现的?
在类似的文件中
mmc1_pins_default: pinmux_mmc1_pins_default {
    pinctrl-single,pins = <
        AM4372_IOPAD(0x960, PIN_INPUT | MUX_MODE7) /* spi0_cs1.gpio0_6 */
    >;
};
mmc1_pins_sleep: pinmux_mmc1_pins_sleep {
    pinctrl-single,pins = <
        AM4372_IOPAD(0x960, DS0_PIN_OUTPUT_PULLUP | PIN_INPUT | MUX_MODE7)
    >;
};可以得到定义实现
2.2 pinctrl数据结构
记住pinctrl的三大作用,有助于理解所涉及的数据结构:
- 引脚枚举与命名(Enumerating and naming)
 - 引脚复用(Multiplexing):比如用作GPIO、I2C或其他功能
 - 引脚配置(Configuration):比如上拉、下来、open drain、驱动强度等
 
2.2.1 pinctrl_desc和pinctrl_dev
pincontroller虽然是一个软件的概念,但是它背后是有硬件支持的,所以可以使用一个结构体来表示它:pinctrl_dev。
怎么构造出pinctrl_dev?我们只需要描述它:提供一个pinctrl_desc,然后调用pinctrl_register就可以:
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
                    struct device *dev, void *driver_data);pinctrl_dev
struct pinctrl_dev {
    struct list_head node;
    struct pinctrl_desc *desc;
    struct radix_tree_root pin_desc_tree;
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
    struct radix_tree_root pin_group_tree;
    unsigned int num_groups;
#endif
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
    struct radix_tree_root pin_function_tree;
    unsigned int num_functions;
#endif
    struct list_head gpio_ranges;
    struct device *dev;
    struct module *owner;
    void *driver_data;
    struct pinctrl *p;
    struct pinctrl_state *hog_default;
    struct pinctrl_state *hog_sleep;
    struct mutex mutex;
#ifdef CONFIG_DEBUG_FS
    struct dentry *device_root;
#endif
};pinctrl_desc
struct pinctrl_desc {
    const char *name;
    const struct pinctrl_pin_desc *pins;
    unsigned int npins;
    const struct pinctrl_ops *pctlops;
    const struct pinmux_ops *pmxops;
    const struct pinconf_ops *confops;
    struct module *owner;
#ifdef CONFIG_GENERIC_PINCONF
    unsigned int num_custom_params;
    const struct pinconf_generic_params *custom_params;
    const struct pin_config_item *custom_conf_items;
#endif
};imx6ull的pinctrl_desc,实现的函数名字
int imx_pinctrl_probe(struct platform_device *pdev, const struct imx_pinctrl_soc_info *info);
在imx6ull中,
static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{
    const struct imx_pinctrl_soc_info *pinctrl_info;
    const struct of_device_id *match;
    pinctrl_info = of_device_get_match_data(&pdev->dev);
    if (!pinctrl_info)
        return -ENODEV;
    match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);
    if (!match)
        return -ENODEV;
    pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;
    return imx_pinctrl_probe(pdev, pinctrl_info);
}
static struct platform_driver imx6ul_pinctrl_driver = {
    .driver = {
        .name = "imx6ul-pinctrl",
        .of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),
    },
    .probe = imx6ul_pinctrl_probe,
};此时我们就明白设备树到pinctrl的实现
2.3 设备树到pinctrl_map的
在函数imx_dt_node_to_map实现
struct pinctrl_maps {
    struct list_head node;
    const struct pinctrl_map *maps;
    unsigned num_maps;
};
struct pinctrl_map {
    const char *dev_name;
    const char *name;
    enum pinctrl_map_type type;
    const char *ctrl_dev_name;
    union {
        struct pinctrl_map_mux mux;
        struct pinctrl_map_configs configs;
    } data;
};
2.3.0.1 1. 描述、获得引脚
使用pinctrl描述引脚
static const struct pinctrl_pin_desc imx6ul_pinctrl_pads[] = {
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE0),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE1),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE2),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE3),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE4),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE5),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE6),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE7),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE8),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE9),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE10),
    IMX_PINCTRL_PIN(MX6UL_PAD_SNVS_TAMPER4),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE12),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE13),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE14),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE15),
    IMX_PINCTRL_PIN(MX6UL_PAD_RESERVE16),
    IMX_PINCTRL_PIN(MX6UL_PAD_JTAG_MOD),
    IMX_PINCTRL_PIN(MX6UL_PAD_JTAG_TMS),
    IMX_PINCTRL_PIN(MX6UL_PAD_JTAG_TDO),
    IMX_PINCTRL_PIN(MX6UL_PAD_JTAG_TDI),
    IMX_PINCTRL_PIN(MX6UL_PAD_JTAG_TCK),
    IMX_PINCTRL_PIN(MX6UL_PAD_JTAG_TRST_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO00),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO01),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO02),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO03),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO04),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO05),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO06),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO07),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO08),
    IMX_PINCTRL_PIN(MX6UL_PAD_GPIO1_IO09),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART1_TX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART1_RX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART1_CTS_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART1_RTS_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART2_TX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART2_RX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART2_CTS_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART2_RTS_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART3_TX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART3_RX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART3_CTS_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART3_RTS_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART4_TX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART4_RX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART5_TX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_UART5_RX_DATA),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_RX_DATA0),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_RX_DATA1),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_RX_EN),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_TX_DATA0),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_TX_DATA1),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_TX_EN),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_TX_CLK),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET1_RX_ER),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_RX_DATA0),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_RX_DATA1),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_RX_EN),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_TX_DATA0),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_TX_DATA1),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_TX_EN),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_TX_CLK),
    IMX_PINCTRL_PIN(MX6UL_PAD_ENET2_RX_ER),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_CLK),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_ENABLE),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_HSYNC),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_VSYNC),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_RESET),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA00),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA01),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA02),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA03),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA04),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA05),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA06),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA07),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA08),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA09),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA10),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA11),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA12),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA13),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA14),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA15),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA16),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA17),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA18),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA19),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA20),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA21),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA22),
    IMX_PINCTRL_PIN(MX6UL_PAD_LCD_DATA23),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_RE_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_WE_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA00),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA01),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA02),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA03),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA04),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA05),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA06),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DATA07),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_ALE),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_WP_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_READY_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_CE0_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_CE1_B),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_CLE),
    IMX_PINCTRL_PIN(MX6UL_PAD_NAND_DQS),
    IMX_PINCTRL_PIN(MX6UL_PAD_SD1_CMD),
    IMX_PINCTRL_PIN(MX6UL_PAD_SD1_CLK),
    IMX_PINCTRL_PIN(MX6UL_PAD_SD1_DATA0),
    IMX_PINCTRL_PIN(MX6UL_PAD_SD1_DATA1),
    IMX_PINCTRL_PIN(MX6UL_PAD_SD1_DATA2),
    IMX_PINCTRL_PIN(MX6UL_PAD_SD1_DATA3),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_MCLK),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_PIXCLK),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_VSYNC),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_HSYNC),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA00),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA01),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA02),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA03),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA04),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA05),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA06),
    IMX_PINCTRL_PIN(MX6UL_PAD_CSI_DATA07),
};使用pinctrl_ops来操作引脚,主要功能有二:
- 来取出某组的引脚:get_groups_count、get_group_pins
 - 处理设备树中pin controller中的某个节点:dt_node_to_map,把device_node转换为一系列的pinctrl_map
 
struct pinctrl_ops {
    int (*get_groups_count) (struct pinctrl_dev *pctldev);
    const char *(*get_group_name) (struct pinctrl_dev *pctldev,
                       unsigned selector);
    int (*get_group_pins) (struct pinctrl_dev *pctldev,
                   unsigned selector,
                   const unsigned **pins,
                   unsigned *num_pins);
    void (*pin_dbg_show) (struct pinctrl_dev *pctldev, struct seq_file *s,
              unsigned offset);
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
                   struct device_node *np_config,
                   struct pinctrl_map **map, unsigned *num_maps);
    void (*dt_free_map) (struct pinctrl_dev *pctldev,
                 struct pinctrl_map *map, unsigned num_maps);
};
static const struct pinctrl_ops imx_pctrl_ops = {
    .get_groups_count = pinctrl_generic_get_group_count,
    .get_group_name = pinctrl_generic_get_group_name,
    .get_group_pins = pinctrl_generic_get_group_pins,
    .pin_dbg_show = imx_pin_dbg_show,
    .dt_node_to_map = imx_dt_node_to_map,
    .dt_free_map = imx_dt_free_map,
};2.3.0.2 2. 引脚复用
使用pinmux描述
struct pinmux_ops imx_pmx_ops = {
    .get_functions_count = pinmux_generic_get_function_count,
    .get_function_name = pinmux_generic_get_function_name,
    .get_function_groups = pinmux_generic_get_function_groups,
    .set_mux = imx_pmx_set,
};2.3.0.3 3. 引脚配置
static const struct pinconf_ops imx_pinconf_ops = {
    .pin_config_get = imx_pinconf_get,
    .pin_config_set = imx_pinconf_set,
    .pin_config_dbg_show = imx_pinconf_dbg_show,
    .pin_config_group_dbg_show = imx_pinconf_group_dbg_show,
};2.3.0.4 pinctrl_desc注册得到pinctrl_dev
graph LR devm_pinctrl_register --> pinctrl_register

使能pinctrl配置
/* Global list of pin control devices (struct pinctrl_dev) */
static LIST_HEAD(pinctrldev_list);
int pinctrl_enable(struct pinctrl_dev *pctldev)
{
    int error;
    error = pinctrl_claim_hogs(pctldev);
    if (error) {
        dev_err(pctldev->dev, "could not claim hogs: %i\n",
            error);
        mutex_destroy(&pctldev->mutex);
        kfree(pctldev);
        return error;
    }
    mutex_lock(&pinctrldev_list_mutex);
    list_add_tail(&pctldev->node, &pinctrldev_list);
    mutex_unlock(&pinctrldev_list_mutex);
    pinctrl_init_device_debugfs(pctldev);
    return 0;
}3 client的数据结构

每一个device结构体都有一个dev_pin_info结构体,保存设备的pinctrl信息

假设芯片上有多个pin controller,那么这个设备使用哪个pin controller?
这需要通过设备树来确定:
- 分析设备树,找到pin controller
 - 对于每个状态,比如default、init,去分析pin controller中的设备树节点
- 使用pin controller的pinctrl_ops.dt_node_to_map来处理设备树的pinctrl节点信息,得到一系列的pinctrl_map
 - 这些pinctrl_map放在pinctrl.dt_maps链表中
 - 每个pinctrl_map都被转换为pinctrl_setting,放在对应的pinctrl_state.settings链表中
 
 

调用过程
graph LR really_probe --> pinctrl_bind_pins pinctrl_bind_pins --> pinctrl_select_state pinctrl_select_state --> pinctrl_commit_state
最终调用到

4 pinctrl子系统总结
Linux内核中的pinctrl(Pin Control)子系统用于管理SoC(System-on-Chip)的引脚复用(Pin Multiplexing)和引脚配置(Pin Configuration)。它提供了一种标准化的方式,允许设备驱动通过设备树(Device Tree)或ACPI动态配置硬件引脚的功能和电气属性(如上拉、下拉、驱动强度等)。以下是其核心实现机制的分析:
4.1 1. 核心概念
- 引脚(Pin):SoC的物理引脚。
 - 引脚组(Pin Group):功能相关的多个引脚集合(例如一组I2C引脚)。
 - 引脚功能(Function):引脚的可选功能(如GPIO、UART、SPI等)。
 - 引脚配置(Configuration):引脚的电气属性(如上拉电阻、驱动强度、斜率控制等)。
 
4.2 2. 子系统架构
4.2.1 2.1 核心数据结构
struct pinctrl_desc:描述一个引脚控制器的能力,包括支持的引脚、组、功能等。struct pinctrl_desc { const char *name; // 控制器名称 const struct pinctrl_pin_desc *pins; // 引脚描述数组 unsigned int npins; // 引脚数量 const struct pinctrl_ops *pctlops; // 控制器操作函数集 const struct pinmux_ops *pmxops; // 复用功能操作函数集 const struct pinconf_ops *confops; // 配置操作函数集 // ... };struct pinctrl_dev:表示一个已注册的引脚控制器实例。struct pinctrl_map:描述引脚配置到设备驱动的映射关系(通过设备树定义)。
4.2.2 2.2 操作函数集
struct pinctrl_ops:提供基础引脚管理操作:struct pinctrl_ops { int (*get_groups_count)(struct pinctrl_dev *pctldev); const char *(*get_group_name)(struct pinctrl_dev *pctldev, unsigned selector); // ... };struct pinmux_ops:处理引脚复用功能:struct pinmux_ops { int (*set_mux)(struct pinctrl_dev *pctldev, unsigned function, unsigned group); // ... };struct pinconf_ops:配置引脚的电气属性:struct pinconf_ops { int (*pin_config_get)(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); int (*pin_config_set)(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs); // ... };
4.3 3. 工作流程
4.3.1 3.1 初始化阶段
- 注册引脚控制器:
- 驱动调用
devm_pinctrl_register()注册pinctrl_desc,内核创建pinctrl_dev实例。 
 - 驱动调用
 - 解析设备树:
- 从设备树中解析
pinctrl-names和pinctrl-0/1/...属性,生成pinctrl_map映射表。 
 - 从设备树中解析
 
4.3.2 3.2 运行时阶段
设备驱动申请配置:
- 设备驱动在
probe()函数中调用devm_pinctrl_get()获取引脚控制句柄。 - 调用
pinctrl_lookup_state()查找预定义的配置状态(如default、sleep)。 - 调用
pinctrl_select_state()应用配置。 
struct pinctrl *pinctrl = devm_pinctrl_get(dev); struct pinctrl_state *state = pinctrl_lookup_state(pinctrl, "default"); pinctrl_select_state(pinctrl, state);- 设备驱动在
 配置生效:
- 内核通过回调
pinmux_ops->set_mux和pinconf_ops->pin_config_set,操作硬件寄存器完成引脚复用和配置。 
- 内核通过回调
 
4.4 4. 设备树配置
设备树中通过pinctrl节点描述引脚配置,例如:
// 定义引脚控制器节点
soc {
    pinctrl: pinctrl@1000000 {
        compatible = "vendor,some-pinctrl";
        reg = <0x1000000 0x1000>;
        // 定义UART0的引脚配置
        uart0_default: uart0_default {
            mux {
                groups = "uart0_tx", "uart0_rx";
                function = "uart0";
            };
            config {
                pins = "PIO_A0", "PIO_A1";
                bias-disable;       // 禁用上下拉
                drive-strength = <8>; // 驱动强度8mA
            };
        };
    };
};
// 设备节点引用pinctrl配置
uart0: serial@2000000 {
    compatible = "vendor,uart";
    reg = <0x2000000 0x1000>;
    pinctrl-names = "default";      // 配置状态名称
    pinctrl-0 = <&uart0_default>;  // 引用具体配置
};4.5 5. 与GPIO子系统的关系
- 职责划分:
pinctrl:负责引脚的复用和电气配置。gpio:负责将配置为GPIO的引脚抽象为通用的输入/输出接口。
 - 协作流程:
- 设备树中通过
pinctrl配置引脚为GPIO功能。 - GPIO子系统通过
gpio_request()申请引脚,此时会隐式调用pinctrl确保引脚配置正确。 - 用户可通过
gpiod_set_value()操作GPIO电平。 
 - 设备树中通过
 
4.6 6. 调试与问题排查
4.6.1 6.1 调试工具
- sysfs接口:
# 查看已注册的pinctrl设备 ls /sys/class/pinctrl/ # 查看某个pinctrl的引脚状态 cat /sys/class/pinctrl/pinctrl.0/pins - 内核日志:启用
CONFIG_DEBUG_PINCTRL,查看引脚配置的详细操作日志。 - 设备树检查:使用
dtc编译设备树时检查语法错误。 
4.6.2 6.2 常见问题
- 引脚冲突:多个设备尝试复用同一引脚,需检查设备树配置。
 - 电气配置错误:例如未正确设置上拉电阻导致信号不稳定。
 - 设备树绑定错误:未正确引用
pinctrl节点或配置名称拼写错误。 
4.7 7. 示例驱动实现
4.7.1 7.1 引脚控制器驱动
#include <linux/pinctrl/pinctrl.h>
static const struct pinctrl_pin_desc my_pins[] = {
    PINCTRL_PIN(0, "PIO_A0"),
    PINCTRL_PIN(1, "PIO_A1"),
};
static const char * const uart0_groups[] = { "uart0_tx", "uart0_rx" };
static struct pinctrl_ops my_pctl_ops = {
    .get_groups_count = my_get_groups_count,
    .get_group_name = my_get_group_name,
    .get_group_pins = my_get_group_pins,
};
static struct pinmux_ops my_pmx_ops = {
    .get_functions_count = my_get_functions_count,
    .get_function_name = my_get_function_name,
    .get_function_groups = my_get_function_groups,
    .set_mux = my_set_mux,
};
static struct pinctrl_desc my_desc = {
    .name = "my_pinctrl",
    .pins = my_pins,
    .npins = ARRAY_SIZE(my_pins),
    .pctlops = &my_pctl_ops,
    .pmxops = &my_pmx_ops,
};
static int my_probe(struct platform_device *pdev) {
    struct pinctrl_dev *pctldev;
    pctldev = devm_pinctrl_register(&pdev->dev, &my_desc, NULL);
    // ...
}4.7.2 7.2 设备驱动使用pinctrl
static int my_device_probe(struct platform_device *pdev) {
    struct pinctrl *pinctrl;
    struct pinctrl_state *state;
    pinctrl = devm_pinctrl_get(&pdev->dev);
    state = pinctrl_lookup_state(pinctrl, "active");
    pinctrl_select_state(pinctrl, state);
    // ...
}4.8 8. 高级主题
4.8.1 8.1 动态配置
- 运行时通过
pinctrl_select_state()切换引脚状态(如从default切换到sleep模式)。 
4.8.2 8.2 延迟配置
- 对于某些设备,引脚配置可能需要延迟到设备上电后(通过
DEFINE_PINCTRL_DEV()和pinctrl_register()的延迟机制)。 
4.8.3 8.3 复合引脚控制器
- 对于包含多个子控制器的复杂SoC,使用
pinctrl_register_child()注册子控制器。 
4.9 总结
pinctrl子系统的核心目标是通过统一接口管理SoC引脚的复用和配置,其实现依赖于:
- 设备树描述:定义引脚功能与设备之间的映射。
 - 操作函数集:通过
pinctrl_ops、pinmux_ops、pinconf_ops实现硬件操作。 - 运行时状态管理:设备驱动通过
pinctrl_select_state()动态切换配置。 
理解pinctrl子系统对开发嵌入式驱动和调试硬件配置问题至关重要,尤其是在多外设共享引脚的复杂系统中。

