linux内核虚拟pinctrl驱动

❓ 问题描述:

编写一个虚拟的pin controller驱动
在现代的片上系统(SoC)中,物理引脚(Pin)是一种超级宝贵的稀缺资源。为了在有限的引脚上实现尽可能多的功能,芯片设计者会将一个物理引脚设计成**“多功能复用”**的。

例如,SoC 上的第 25 号引脚,它可能同时具备以下几种身份:

通用输入/输出口 (GPIO)
UART(串口)的发送端 (TX)
I2C(总线)的时钟线 (SCL)
SPI(总线)的片选信号 (CS)
这就带来了一个核心问题:在任意时刻,这个引脚到底应该扮演哪种角色?并且,它的电气特性(列如是上拉、下拉还是浮空?驱动能力是强还是弱?)应该如何配置?

在 pinctrl 子系统出现之前,这个配置工作是由各个外设驱动自己完成的。列如,UART 驱动会直接去写硬件寄存器,把引脚配置成 UART_TX;而 GPIO 驱动可能也想去写寄存器,把它配置成 GPIO。这会导致:
冲突与混乱:两个驱动可能会争抢同一个引脚的控制权,导致系统行为不可预测。
代码冗余与耦合:每个外设驱动都需要包含大量与具体 SoC 相关的、复杂的寄存器操作代码,可移植性极差。
状态管理困难:无法聚焦管理和查看所有引脚的当前状态,调试超级困难。
pinctrl 子系统的诞生就是为了解决以上所有问题。它的核心作用可以概括为:

pinctrlLinux 内核中用于统一管理和配置 SoC 引脚的中央仲裁系统。

pinctrl作用:

linux内核虚拟pinctrl驱动

日志

添加打印日志信息

分析步骤

第1步:
第2步:
...

代码片段

下面会列举3种常用的方式: 第一种简单方式方式:

设备树

/*
     * Part 1: The Pinctrl Provider Node
     * This node represents our virtual pinctrl hardware.
     */
    virtual_pinctrl_node: pinctrl@0 {
        compatible = "acme,virtual-pinctrl";
        #pinctrl-cells = <0>; /* States are referenced by phandle directly */

        /*
         * Define the basic building blocks for our states. Each node here
         * represents one valid "function-to-group" combination that the
         * hardware supports.
         */

        /* Combination 1: group_a can be used as uart */
        cfg_group_a_uart: grp-a-uart-cfg {
            function = "uart";
            groups = "group_a";
        };

        /* Combination 2: group_a can be used as gpio */
        cfg_group_a_gpio: grp-a-gpio-cfg {
            function = "gpio";
            groups = "group_a";
        };

        /* Combination 3: group_b can be used as spi */
        cfg_group_b_spi: grp-b-spi-cfg {
            function = "spi";
            groups = "group_b";
        };

        /* Combination 4: group_b can be used as gpio */
        cfg_group_b_gpio: grp-b-gpio-cfg {
            function = "gpio";
            groups = "group_b";
        };
    };

    /*
     * Part 2: The Pinctrl Consumer Node
     * This node represents the device that needs to use the pinctrl service.
     */
    test_client_node: test-client@0 {
        compatible = "acme,pinctrl-test-client";

//示例1: 单独的默认的功能
        // pinctrl-names = "default";
        // pinctrl-0 = <&cfg_group_a_gpio>;

//示例2:只使用一组引脚,并且一组引脚具有2个功能
        // pinctrl-names = "default", "peripheral_mode";
        // pinctrl-0 = <&cfg_group_a_gpio>;
        // pinctrl-1 = <&cfg_group_a_uart>;

//示例3:使用2组引脚,并且每一组引脚具有2个功能		
        pinctrl-names = "default", "peripheral_mode";
        pinctrl-0 = <&cfg_group_a_gpio &cfg_group_b_gpio>;
        pinctrl-1 = <&cfg_group_a_uart &cfg_group_b_spi>;
    };

pinctrl驱动代码

/*
 * FILENAME: virtual-pinctrl.c
 * VERSION:  Final - Complete Code
 *
 * DESCRIPTION:
 * A complete, data-driven virtual pinctrl controller driver for Linux 4.4.x.
 * This version clearly distinguishes between the "unique function list" required
 * by the pinctrl core API and the internal logic that maps functions to groups.
 * It is designed for maximum clarity, maintainability, and correctness.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>


/*=====================================================================
 * 1. Data Definitions: The "Capability Database"
 *=====================================================================*/

/* --- A. Pin and Group definitions (The physical layer) --- */

static const struct pinctrl_pin_desc virtual_pins[] = {
    PINCTRL_PIN(0, "VPIN0"),
    PINCTRL_PIN(1, "VPIN1"),
    PINCTRL_PIN(2, "VPIN2"),
    PINCTRL_PIN(3, "VPIN3"),
    PINCTRL_PIN(4, "VPIN4"),
    PINCTRL_PIN(5, "VPIN5"),
    PINCTRL_PIN(6, "VPIN6"),
    PINCTRL_PIN(7, "VPIN7"),
};
static const unsigned int group_a_pins[] = { 0, 1, 2, 3 };
static const unsigned int group_b_pins[] = { 4, 5, 6, 7 };

struct virtual_pingroup {
    const char *name;
    const unsigned int *pins;
    unsigned int num_pins;
};

static const struct virtual_pingroup virtual_groups[] = {
    { .name = "group_a", .pins = group_a_pins, .num_pins = ARRAY_SIZE(group_a_pins), },
    { .name = "group_b", .pins = group_b_pins, .num_pins = ARRAY_SIZE(group_b_pins), },
};

static const char * const virtual_unique_functions[] = {
    "uart",
    "spi",
    "gpio",
};

/* --- pinctrl_ops callbacks --- */
static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
    printk("virtual_get_groups_count
");
    return ARRAY_SIZE(virtual_groups);
}

static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, unsigned selector)
{
    if (selector >= ARRAY_SIZE(virtual_groups))
        return NULL;
    printk("virtual_get_group_name selector = %u,group_name = %s
", selector, virtual_groups[selector].name);
    return virtual_groups[selector].name;
}

static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
                  const unsigned **pins, unsigned *num_pins)
{
    printk("virtual_get_group_pins
");
    if (selector >= ARRAY_SIZE(virtual_groups))
        return -EINVAL;
    *pins = virtual_groups[selector].pins;
    *num_pins = virtual_groups[selector].num_pins;
    printk("virtual_get_group_pins selector = %u,num_pins = %u
", selector, *num_pins);
    return 0;
}

/*=====================================================================
 * 3. Generic Callbacks, Descriptor, and Platform Driver
 *=====================================================================*/

static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np,
                  struct pinctrl_map **map, unsigned *num_maps)
{
    struct platform_device *pdev = pinctrl_dev_get_drvdata(pctldev);
    const char *function, *group;
    int ret;

    printk("virtual_dt_node_to_map
");
    ret = of_property_read_string(np, "function", &function);
    if (ret < 0)
        return 0; /* Not a pinmux node, just ignore it */

    ret = of_property_read_string(np, "groups", &group);
    if (ret < 0) {
        dev_err(&pdev->dev, "missing 'groups' property in node %pOFn
", np);
        return -EINVAL;
    }
    *num_maps = 1;
    *map = kzalloc(sizeof(**map), GFP_KERNEL);
    if (!*map)
        return -ENOMEM;

    (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
    (*map)->data.mux.group = group;
    (*map)->data.mux.function = function;
    return 0;
}

static void virtual_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps)
{
    printk("virtual_dt_free_map
");
    kfree(map);
}

static const struct pinctrl_ops virtual_pctrl_ops = {
    .get_groups_count = virtual_get_groups_count,
    .get_group_name = virtual_get_group_name,
    .get_group_pins = virtual_get_group_pins,
    .dt_node_to_map = virtual_dt_node_to_map,
    .dt_free_map = virtual_dt_free_map,
};


/* --- pinmux_ops callbacks --- */

/* Answers the question: "How many *different* function types do you have?" */
static int virtual_get_functions_count(struct pinctrl_dev *pctldev)
{
    printk("virtual_get_functions_count
");
    return ARRAY_SIZE(virtual_unique_functions);
}

/* Answers: "What is the name of your function with index 'selector'?" */
static const char *virtual_get_function_name(struct pinctrl_dev *pctldev, unsigned selector)
{
    if (selector >= ARRAY_SIZE(virtual_unique_functions))
        return NULL;
     printk("virtual_get_function_name selector = %u,func_name = %s
", selector, virtual_unique_functions[selector]);
    return virtual_unique_functions[selector];
}

/* Answers: "Which groups can be used for the function with index 'selector'?" */
static int virtual_get_function_groups(struct pinctrl_dev *pctldev, unsigned selector,
                       const char * const **groups,
                       unsigned * const num_groups)
{
    const char *func_name = virtual_get_function_name(pctldev, selector);
    printk("virtual_get_function_groups selector = %u,func_name = %s
", selector,func_name);
    if (!func_name)
        return -EINVAL;

    /*
     * Based on the function name, we provide a static list of compatible groups.
     * This approach is memory-safe (no leaks), simple, and efficient.
     */
    if (strcmp(func_name, "uart") == 0) {
        static const char * const uart_groups[] = { "group_a" };
        *groups = uart_groups;
        *num_groups = ARRAY_SIZE(uart_groups);
    } else if (strcmp(func_name, "spi") == 0) {
        static const char * const spi_groups[] = { "group_b" };
        *groups = spi_groups;
        *num_groups = ARRAY_SIZE(spi_groups);
    } else if (strcmp(func_name, "gpio") == 0) {
        /* For 'gpio', we return both groups that support it. */
        static const char * const gpio_groups[] = { "group_a", "group_b" };
        *groups = gpio_groups;
        *num_groups = ARRAY_SIZE(gpio_groups);
    } else {
        *groups = NULL;
        *num_groups = 0;
    }
    return 0;
}

static int virtual_set_mux(struct pinctrl_dev *pctldev, unsigned func_sel, unsigned grp_sel)
{
    struct platform_device *pdev = pinctrl_dev_get_drvdata(pctldev);
    const char *func = virtual_get_function_name(pctldev, func_sel);
    const char *group = virtual_get_group_name(pctldev, grp_sel);

    if (!func || !group)
        return -EINVAL;

    dev_info(&pdev->dev, "Muxing group '%s' to function '%s'
", group, func);
    return 0;
}

static const struct pinmux_ops virtual_pmx_ops = {
    .get_functions_count = virtual_get_functions_count,
    .get_function_name = virtual_get_function_name,
    .get_function_groups = virtual_get_function_groups,
    .set_mux = virtual_set_mux,
};

static struct pinctrl_desc virtual_pinctrl_desc = {
    .name = "virtual-pinctrl",
    .pins = virtual_pins,
    .npins = ARRAY_SIZE(virtual_pins),
    .pctlops = &virtual_pctrl_ops,
    .pmxops = &virtual_pmx_ops,
    .owner = THIS_MODULE,
};

static int virtual_pinctrl_probe(struct platform_device *pdev)
{
    struct pinctrl_dev *pctldev;

    dev_info(&pdev->dev, "Probing virtual pinctrl driver
");
    pctldev = pinctrl_register(&virtual_pinctrl_desc, &pdev->dev, pdev);
    if (IS_ERR(pctldev)) {
        dev_err(&pdev->dev, "Failed to register pinctrl device
");
        return PTR_ERR(pctldev);
    }
    platform_set_drvdata(pdev, pctldev);
    dev_info(&pdev->dev, "Registered successfully
");
    return 0;
}

static int virtual_pinctrl_remove(struct platform_device *pdev)
{
    struct pinctrl_dev *pctldev = platform_get_drvdata(pdev);
    dev_info(&pdev->dev, "Removing virtual pinctrl driver
");
    pinctrl_unregister(pctldev);
    return 0;
}

static const struct of_device_id virtual_pinctrl_of_match[] = {
    { .compatible = "acme,virtual-pinctrl" },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, virtual_pinctrl_of_match);

static struct platform_driver virtual_pinctrl_driver = {
    .driver = {
        .name = "virtual-pinctrl",
        .of_match_table = of_match_ptr(virtual_pinctrl_of_match),
    },
    .probe = virtual_pinctrl_probe,
    .remove = virtual_pinctrl_remove,
};

module_platform_driver(virtual_pinctrl_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Linuxtoyou");
MODULE_DESCRIPTION("Final virtual pinctrl driver for Linux 4.4.x");

第2种方式: 厂家自定格式 //组 引脚 功能 电气属性 设备树

#define GPIO_BANK0   0
#define GPIO_BANK1   1

#define VPIN_FUNC_GPIO  0
#define VPIN_FUNC_UART  1
#define VPIN_FUNC_SPI   2

#define PIN_CONFIG_ACME_PULL_UP      1
#define PIN_CONFIG_ACME_PULL_DOWN    2
#define PIN_CONFIG_ACME_PULL_NONE    3

/*
     * Part 1: The Pinctrl Provider Node
     */
    virtual_pinctrl_node: pinctrl@0 {
        compatible = "acme,virtual-pinctrl-vendor";

        pcfg_pull_up: pcfg-pull-up {
            acme,pull-up; /* A boolean property */
        };
        pcfg_pull_down: pcfg-pull-down {
            acme,pull-down;
        };
        pcfg_pull_none: pcfg-pull-none {
            /* No property means no pull */
        };

        state_default_gpio: state-default-gpio {
            acme,pins = <GPIO_BANK0 0 VPIN_FUNC_GPIO &pcfg_pull_up>,
                        <GPIO_BANK0 1 VPIN_FUNC_GPIO &pcfg_pull_up>, /* VPIN1 = Bank 0, Pin 1 */
                        <GPIO_BANK1 0 VPIN_FUNC_GPIO &pcfg_pull_up>; /* VPIN4 = Bank 1, Pin 0 */
        };

        state_peripheral_mode: state-peripheral-mode {
            acme,pins = <GPIO_BANK0 0 VPIN_FUNC_UART &pcfg_pull_down>,   /* VPIN0 = Bank 0, Pin 0 */
                    <GPIO_BANK0 1 VPIN_FUNC_UART &pcfg_pull_down>, /* VPIN1 = Bank 0, Pin 1 */
                    <GPIO_BANK1 1 VPIN_FUNC_SPI  &pcfg_pull_down>; /* VPIN4 = Bank 1, Pin 0 */
        };

    };

    test_client_node: test-client@0 {
        compatible = "acme,pinctrl-test-client";
        pinctrl-names = "default", "peripheral_mode";
        pinctrl-0 = <&state_default_gpio>;
        pinctrl-1 = <&state_peripheral_mode>;
    };

pinctrl驱动代码

/*
 * FILENAME: virtual-pinctrl.c
 * VERSION:  Absolute Final - All Callbacks Implemented
 *
 * DESCRIPTION:
 * A complete, self-contained virtual pinctrl driver. It correctly parses
 * the industry-standard <group> <pin> <func> <cfg> DTS format and implements
 * all necessary pinctrl_ops and pinmux_ops callbacks to register
 * successfully with the Linux pinctrl subsystem. This version is fully
 * expanded and formatted according to kernel coding style.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>

/*
 * Hardware definitions, kept in this C file for a self-contained example.
 * In a real project, these would live in a shared header file.
 */
#define GPIO_BANK0   0
#define GPIO_BANK1   1

#define VPIN_FUNC_GPIO  0
#define VPIN_FUNC_UART  1
#define VPIN_FUNC_SPI   2

/*
 * Define custom pin config parameters starting from PIN_CONFIG_END.
 * This avoids collisions with standard kernel pin configuration parameters.
 */
#define PIN_CONFIG_ACME_PULL_UP      1
#define PIN_CONFIG_ACME_PULL_DOWN    2
#define PIN_CONFIG_ACME_PULL_NONE    3

struct virtual_pinctrl_soc_data {
    struct device *dev;
    struct pinctrl_dev *pctldev;
    const struct pinctrl_pin_desc *pins;
    unsigned int num_pins;

    /* Array to hold all group names for get_function_groups */
    const char **group_names;
};

/* --- Forward Declarations --- */
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,
                  struct device_node *np_config,
                  struct pinctrl_map **map,
                  unsigned int *num_maps);
static void virtual_dt_free_map(struct pinctrl_dev *pctldev,
                struct pinctrl_map *map,
                unsigned int num_maps);

/*=====================================================================
 * 1. Pinctrl Subsystem Operations
 *=====================================================================*/

/* --- Pinmux Operations --- */

static const char * const virtual_unique_functions[] = {
    [VPIN_FUNC_GPIO] = "gpio",
    [VPIN_FUNC_UART] = "uart",
    [VPIN_FUNC_SPI]  = "spi",
};

static int virtual_get_functions_count(struct pinctrl_dev *pctldev)
{
    return ARRAY_SIZE(virtual_unique_functions);
}

static const char *virtual_get_function_name(struct pinctrl_dev *pctldev,
                         unsigned int selector)
{
    if (selector >= ARRAY_SIZE(virtual_unique_functions))
        return NULL;

    return virtual_unique_functions[selector];
}

static int virtual_get_function_groups(struct pinctrl_dev *pctldev,
                       unsigned int selector,
                       const char * const **groups,
                       unsigned int * const num_groups)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);

    /* In our model, we claim all groups are available for every function. */
    *groups = soc->group_names;
    *num_groups = soc->num_pins;

    return 0;
}

static int virtual_set_mux(struct pinctrl_dev *pctldev,
               unsigned int func_selector,
               unsigned int group_selector)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    const char *func_name = virtual_get_function_name(pctldev, func_selector);
    unsigned int pin_selector = group_selector;

    if (!func_name)
        return -EINVAL;

    dev_info(soc->dev, "[MUX] Setting pin VPIN%u to function '%s'
",
         pin_selector, func_name);

    return 0;
}

static const struct pinmux_ops virtual_pmx_ops = {
    .get_functions_count = virtual_get_functions_count,
    .get_function_name   = virtual_get_function_name,
    .get_function_groups = virtual_get_function_groups,
    .set_mux             = virtual_set_mux,
};


/* --- Pinctrl Operations --- */

static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    printk("virtual_get_groups_count num_pins = %u
", soc->num_pins);
    return soc->num_pins;
}

static const char *virtual_get_group_name(struct pinctrl_dev *pctldev,
                      unsigned int selector)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);

    printk("virtual_get_group_name selector = %u
", selector);
    return soc->pins[selector].name;
}

static int virtual_get_group_pins(struct pinctrl_dev *pctldev,
                  unsigned int selector,
                  const unsigned int **pins,
                  unsigned int *num_pins)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);

    printk("virtual_get_group_pins selector = %u
", selector);
    *pins = &soc->pins[selector].number;
    *num_pins = 1;

    return 0;
}

static const struct pinctrl_ops virtual_pctrl_ops = {
    .get_groups_count = virtual_get_groups_count,
    .get_group_name   = virtual_get_group_name,
    .get_group_pins   = virtual_get_group_pins,
    .dt_node_to_map   = virtual_dt_node_to_map,
    .dt_free_map      = virtual_dt_free_map,
};


/* --- Pinconf Operations --- */

static int virtual_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
                  unsigned long *configs,
                  unsigned int num_configs)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    enum pin_config_param param;
    int i;

    for (i = 0; i < num_configs; i++) {
        param = pinconf_to_config_param(configs[i]);

        printk("configs[%d] = %lu, param = %u
", i, configs[i], param);
        /* 跳过值为0的配置 */
        if (param == 0)
            continue;

        switch (param) {
        case PIN_CONFIG_ACME_PULL_UP:
            dev_info(soc->dev, "[CONF] Setting pin VPIN%u: PULL-UP
",
                 pin);
            break;
        case PIN_CONFIG_ACME_PULL_DOWN:
            dev_info(soc->dev, "[CONF] Setting pin VPIN%u: PULL-DOWN
",
                 pin);
            break;
        case PIN_CONFIG_ACME_PULL_NONE:
            dev_info(soc->dev, "[CONF] Setting pin VPIN%u: PULL-NONE
",
                 pin);
            break;
        default:
            dev_warn(soc->dev, "Unsupported config param %d for pin %u
",
                 param, pin);
            /* 可以选择忽略未知参数而不是返回错误 */
            break;
        }
    }
    return 0;
}

static const struct pinconf_ops virtual_pinconf_ops = {
    .is_generic     = true,
    .pin_config_set = virtual_pin_config_set,
};


/*=====================================================================
 * 2. Device Tree Parsing Logic
 *=====================================================================*/

static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,
                  struct device_node *np_config,
                  struct pinctrl_map **map,
                  unsigned int *num_maps)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    const __be32 *list;
    int size, num_pins, maps_per_pin, i = 0;
    struct pinctrl_map *new_map;

    list = of_get_property(np_config, "acme,pins", &size);
    printk("acme,pins size = %d
", size);
    if (!list)
        return -EINVAL;

    if (size % (4 * sizeof(u32)) != 0) {
        dev_err(soc->dev, "Malformed 'acme,pins' property in %pOFn
",
            np_config);
        return -EINVAL;
    }

    num_pins = size / (4 * sizeof(u32));
    maps_per_pin = 2;

    printk("num_pins = %d
", num_pins);
    *num_maps = num_pins * maps_per_pin;
    new_map = kcalloc(*num_maps, sizeof(*new_map), GFP_KERNEL);
    if (!new_map)
        return -ENOMEM;
    *map = new_map;

    for (i = 0; i < num_pins; i++) {
        unsigned int group_id, pin_offset, func_id, pin_id;
        phandle config_phandle;
        struct device_node *np_conf;
        unsigned int param;
        struct pinctrl_map *m_mux = &new_map[i * maps_per_pin];
        struct pinctrl_map *m_conf = &new_map[i * maps_per_pin + 1];
        unsigned long *configs;

        group_id     = be32_to_cpu(*list++);
        pin_offset   = be32_to_cpu(*list++);
        func_id      = be32_to_cpu(*list++);
        config_phandle = be32_to_cpu(*list++);

        switch (group_id) {
        case GPIO_BANK0:
            if (pin_offset > 3)
                goto err;
            pin_id = 0 + pin_offset;
            break;
        case GPIO_BANK1:
            if (pin_offset > 3)
                goto err;
            pin_id = 4 + pin_offset;
            break;
        default:
            goto err;
        }

        if (pin_id >= soc->num_pins ||
            func_id >= ARRAY_SIZE(virtual_unique_functions))
            goto err;

        m_mux->type = PIN_MAP_TYPE_MUX_GROUP;
        m_mux->data.mux.function = virtual_unique_functions[func_id];
        m_mux->data.mux.group = soc->pins[pin_id].name;

        m_conf->type = PIN_MAP_TYPE_CONFIGS_PIN;
        m_conf->data.configs.group_or_pin = soc->pins[pin_id].name;

        configs = kzalloc(sizeof(*configs), GFP_KERNEL);
        if (!configs)
            goto err_nomem;
        m_conf->data.configs.configs = configs;
        m_conf->data.configs.num_configs = 1;

        np_conf = of_find_node_by_phandle(config_phandle);
        if (!np_conf)
            goto err_free_configs;

        if (of_property_read_bool(np_conf, "acme,pull-up"))
            param = PIN_CONFIG_ACME_PULL_UP;
        else if (of_property_read_bool(np_conf, "acme,pull-down"))
            param = PIN_CONFIG_ACME_PULL_DOWN;
        else
            param = PIN_CONFIG_ACME_PULL_NONE;

        of_node_put(np_conf);
        printk("param = %d
", param);
        configs[0] = pinconf_to_config_packed(param, 1);
    }
    return 0;

err_free_configs:
    kfree(new_map[i * maps_per_pin + 1].data.configs.configs);
err_nomem:
    i = (i > 0) ? i : num_pins;
err:
    dev_err(soc->dev, "Invalid pin data in %pOFn
", np_config);
    for (i = i - 1; i >= 0; i--) {
        if (new_map[i * maps_per_pin + 1].data.configs.configs)
            kfree(new_map[i * maps_per_pin + 1].data.configs.configs);
    }
    kfree(new_map);
    *map = NULL;
    *num_maps = 0;
    return -EINVAL;
}

static void virtual_dt_free_map(struct pinctrl_dev *pctldev,
                struct pinctrl_map *map,
                unsigned int num_maps)
{
    int i;

    for (i = 0; i < num_maps; i++) {
        if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
            kfree(map[i].data.configs.configs);
    }
    kfree(map);
}


/*=====================================================================
 * 3. Pinctrl Descriptor and Platform Driver
 *=====================================================================*/
static const struct pinctrl_pin_desc virtual_pins[] = {
    PINCTRL_PIN(0, "VPIN0"), PINCTRL_PIN(1, "VPIN1"),
    PINCTRL_PIN(2, "VPIN2"), PINCTRL_PIN(3, "VPIN3"),
    PINCTRL_PIN(4, "VPIN4"), PINCTRL_PIN(5, "VPIN5"),
    PINCTRL_PIN(6, "VPIN6"), PINCTRL_PIN(7, "VPIN7"),
};

static struct pinctrl_desc virtual_pinctrl_desc = {
    .name    = "virtual-pinctrl-vendor",
    .pins    = virtual_pins,
    .npins   = ARRAY_SIZE(virtual_pins),
    .pctlops = &virtual_pctrl_ops,
    .pmxops  = &virtual_pmx_ops,
    .confops = &virtual_pinconf_ops,
    .owner   = THIS_MODULE,
};

static int virtual_pinctrl_probe(struct platform_device *pdev)
{
    struct virtual_pinctrl_soc_data *soc;
    int i;

    dev_info(&pdev->dev,
         "Probing final pinctrl driver (custom param fix)
");

    soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
    if (!soc)
        return -ENOMEM;

    soc->dev = &pdev->dev;
    soc->pins = virtual_pins;
    soc->num_pins = ARRAY_SIZE(virtual_pins);

    /*
     * Allocate and populate the group names array needed by
     * get_function_groups. devm_kcalloc handles cleanup automatically.
     */
    soc->group_names = devm_kcalloc(&pdev->dev, soc->num_pins,
                    sizeof(*soc->group_names), GFP_KERNEL);
    if (!soc->group_names)
        return -ENOMEM;

    for (i = 0; i < soc->num_pins; i++)
        soc->group_names[i] = soc->pins[i].name;

    platform_set_drvdata(pdev, soc);

    soc->pctldev = pinctrl_register(&virtual_pinctrl_desc, &pdev->dev, soc);
    if (IS_ERR(soc->pctldev)) {
        dev_err(&pdev->dev, "Failed to register pinctrl device
");
        return PTR_ERR(soc->pctldev);
    }

    dev_info(&pdev->dev, "Registered successfully
");
    return 0;
}

static int virtual_pinctrl_remove(struct platform_device *pdev)
{
    struct virtual_pinctrl_soc_data *soc = platform_get_drvdata(pdev);

    dev_info(&pdev->dev, "Removing virtual pinctrl driver
");
    pinctrl_unregister(soc->pctldev);
    return 0;
}

static const struct of_device_id virtual_pinctrl_of_match[] = {
    { .compatible = "acme,virtual-pinctrl-vendor" },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, virtual_pinctrl_of_match);

static struct platform_driver virtual_pinctrl_driver = {
    .driver = {
        .name           = "virtual-pinctrl-vendor",
        .of_match_table = of_match_ptr(virtual_pinctrl_of_match),
    },
    .probe  = virtual_pinctrl_probe,
    .remove = virtual_pinctrl_remove,
};
module_platform_driver(virtual_pinctrl_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Linuxtoyou");
MODULE_DESCRIPTION("Absolute Final, correct virtual pinctrl driver");

第3种方式: 在第2种方式的基础上,增加了驱动能力 设备树

/* 定义常量 */
    #define GPIO_BANK0 0
    #define GPIO_BANK1 1
    #define VPIN_FUNC_GPIO 0
    #define VPIN_FUNC_UART 1
    #define VPIN_FUNC_SPI  2

    /* Pinctrl controller node */
    virtual_pinctrl_node: pinctrl@0 {
        compatible = "acme,virtual-pinctrl-vendor";

        /* Combined configurations with pull and drive strength */
        pcfg_pull_up_drv_0: pcfg-pull-up-drv-0 {
            acme,pull-up;
            acme,drive-strength = <0>;
        };

        pcfg_pull_up_drv_1: pcfg-pull-up-drv-1 {
            acme,pull-up;
            acme,drive-strength = <1>;
        };

        pcfg_pull_up_drv_2: pcfg-pull-up-drv-2 {
            acme,pull-up;
            acme,drive-strength = <2>;
        };

        pcfg_pull_up_drv_3: pcfg-pull-up-drv-3 {
            acme,pull-up;
            acme,drive-strength = <3>;
        };

        pcfg_pull_down_drv_0: pcfg-pull-down-drv-0 {
            acme,pull-down;
            acme,drive-strength = <0>;
        };

        pcfg_pull_down_drv_1: pcfg-pull-down-drv-1 {
            acme,pull-down;
            acme,drive-strength = <1>;
        };

        pcfg_pull_down_drv_2: pcfg-pull-down-drv-2 {
            acme,pull-down;
            acme,drive-strength = <2>;
        };

        pcfg_pull_down_drv_3: pcfg-pull-down-drv-3 {
            acme,pull-down;
            acme,drive-strength = <3>;
        };

        pcfg_pull_none_drv_1: pcfg-pull-none-drv-1 {
            acme,pull-none;
            acme,drive-strength = <1>;
        };

        /* Pin state definitions */
        state_default_gpio: state-default-gpio {
            acme,pins = 
                /* Simple pull-up only */
                <GPIO_BANK0 0 VPIN_FUNC_GPIO &pcfg_pull_up_drv_0>,
                /* Pull-up with drive strength 2 */
                <GPIO_BANK0 1 VPIN_FUNC_GPIO &pcfg_pull_up_drv_1>,
                /* Pull-down with drive strength 3 */
                <GPIO_BANK1 0 VPIN_FUNC_GPIO &pcfg_pull_up_drv_2>,
                /* Pull-none with drive strength 1 */
                <GPIO_BANK1 1 VPIN_FUNC_GPIO &pcfg_pull_up_drv_3>;
        };

        state_peripheral_mode: state-peripheral-mode {
            acme,pins = 
                /* UART pins with different drive strengths */
                <GPIO_BANK0 0 VPIN_FUNC_UART &pcfg_pull_down_drv_0>,
                <GPIO_BANK0 1 VPIN_FUNC_UART &pcfg_pull_down_drv_1>,
                /* SPI pins with pull-down and various drive strengths */
                <GPIO_BANK0 0 VPIN_FUNC_SPI &pcfg_pull_down_drv_2>,
                <GPIO_BANK0 1 VPIN_FUNC_SPI &pcfg_pull_down_drv_3>;
        };

    };

    /* Test client node */
    test_client_node: test-client@0 {
        compatible = "acme,pinctrl-test-client";
        pinctrl-names = "default", "peripheral_mode";
        pinctrl-0 = <&state_default_gpio>;
        pinctrl-1 = <&state_peripheral_mode>;
    };

pinctrl驱动代码

/*
 * FILENAME: virtual-pinctrl.c
 * VERSION:  Multi-config support with drive-strength
 *
 * DESCRIPTION:
 * A complete virtual pinctrl driver that supports multiple configurations
 * per pin, including pull settings and drive strength.
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>

/*
 * Hardware definitions
 */
#define GPIO_BANK0   0
#define GPIO_BANK1   1

#define VPIN_FUNC_GPIO  0
#define VPIN_FUNC_UART  1
#define VPIN_FUNC_SPI   2

/*
 * Define custom pin config parameters starting from PIN_CONFIG_END.
 * This avoids collisions with standard kernel pin configuration parameters.
 */
#define PIN_CONFIG_ACME_PULL_UP        1
#define PIN_CONFIG_ACME_PULL_DOWN      2
#define PIN_CONFIG_ACME_PULL_NONE      3
#define PIN_CONFIG_ACME_DRIVE_STRENGTH 4

struct virtual_pinctrl_soc_data {
    struct device *dev;
    struct pinctrl_dev *pctldev;
    const struct pinctrl_pin_desc *pins;
    unsigned int num_pins;

    /* Array to hold all group names for get_function_groups */
    const char **group_names;
};

/* --- Forward Declarations --- */
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,
                  struct device_node *np_config,
                  struct pinctrl_map **map,
                  unsigned int *num_maps);
static void virtual_dt_free_map(struct pinctrl_dev *pctldev,
                struct pinctrl_map *map,
                unsigned int num_maps);

/*=====================================================================
 * 1. Pinctrl Subsystem Operations
 *=====================================================================*/

/* --- Pinmux Operations --- */

static const char * const virtual_unique_functions[] = {
    [VPIN_FUNC_GPIO] = "gpio",
    [VPIN_FUNC_UART] = "uart",
    [VPIN_FUNC_SPI]  = "spi",
};

static int virtual_get_functions_count(struct pinctrl_dev *pctldev)
{
    return ARRAY_SIZE(virtual_unique_functions);
}

static const char *virtual_get_function_name(struct pinctrl_dev *pctldev,
                         unsigned int selector)
{
    if (selector >= ARRAY_SIZE(virtual_unique_functions))
        return NULL;

    return virtual_unique_functions[selector];
}

static int virtual_get_function_groups(struct pinctrl_dev *pctldev,
                       unsigned int selector,
                       const char * const **groups,
                       unsigned int * const num_groups)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);

    /* In our model, we claim all groups are available for every function. */
    *groups = soc->group_names;
    *num_groups = soc->num_pins;

    return 0;
}

static int virtual_set_mux(struct pinctrl_dev *pctldev,
               unsigned int func_selector,
               unsigned int group_selector)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    const char *func_name = virtual_get_function_name(pctldev, func_selector);
    unsigned int pin_selector = group_selector;

    if (!func_name)
        return -EINVAL;

    dev_info(soc->dev, "[MUX] Setting pin VPIN%u to function '%s'
",
         pin_selector, func_name);

    return 0;
}

static const struct pinmux_ops virtual_pmx_ops = {
    .get_functions_count = virtual_get_functions_count,
    .get_function_name   = virtual_get_function_name,
    .get_function_groups = virtual_get_function_groups,
    .set_mux             = virtual_set_mux,
};

/* --- Pinctrl Operations --- */

static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    return soc->num_pins;
}

static const char *virtual_get_group_name(struct pinctrl_dev *pctldev,
                      unsigned int selector)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    return soc->pins[selector].name;
}

static int virtual_get_group_pins(struct pinctrl_dev *pctldev,
                  unsigned int selector,
                  const unsigned int **pins,
                  unsigned int *num_pins)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);

    *pins = &soc->pins[selector].number;
    *num_pins = 1;

    return 0;
}

static const struct pinctrl_ops virtual_pctrl_ops = {
    .get_groups_count = virtual_get_groups_count,
    .get_group_name   = virtual_get_group_name,
    .get_group_pins   = virtual_get_group_pins,
    .dt_node_to_map   = virtual_dt_node_to_map,
    .dt_free_map      = virtual_dt_free_map,
};

/* --- Pinconf Operations --- */

static int virtual_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
                  unsigned long *configs,
                  unsigned int num_configs)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    enum pin_config_param param;
    u16 arg;
    int i;

    dev_info(soc->dev, "[CONF] Setting %u configs for pin VPIN%u
",
         num_configs, pin);

    for (i = 0; i < num_configs; i++) {
        param = pinconf_to_config_param(configs[i]);
        arg = pinconf_to_config_argument(configs[i]);

        switch (param) {
        case PIN_CONFIG_ACME_PULL_UP:
            dev_info(soc->dev, "  [%d] PULL-UP (enabled=%u)
", i, arg);
            break;
        case PIN_CONFIG_ACME_PULL_DOWN:
            dev_info(soc->dev, "DOWN (enabled=%u)
", i, arg);
            break;
        case PIN_CONFIG_ACME_PULL_NONE:
            dev_info(soc->dev, "  [%d] PULL-NONE
", i);
            break;
        case PIN_CONFIG_ACME_DRIVE_STRENGTH:
            dev_info(soc->dev, "  [%d] DRIVE-STRENGTH = %u
", i, arg);
            if (arg > 3) {
                dev_err(soc->dev, "Invalid drive strength %u (max 3)
", arg);
                return -EINVAL;
            }
            break;
        default:
            /* Silently ignore standard configs we don't support */
            if (param < PIN_CONFIG_END) {
                dev_dbg(soc->dev, "  [%d] Ignoring standard param %d
", i, param);
            } else {
                dev_warn(soc->dev, "  [%d] Unknown param %d
", i, param);
            }
            break;
        }
    }
    return 0;
}

static const struct pinconf_ops virtual_pinconf_ops = {
    .is_generic     = true,
    .pin_config_set = virtual_pin_config_set,
};

/*=====================================================================
 * 2. Device Tree Parsing Logic
 *=====================================================================*/

static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,
                  struct device_node *np_config,
                  struct pinctrl_map **map,
                  unsigned int *num_maps)
{
    struct virtual_pinctrl_soc_data *soc = pinctrl_dev_get_drvdata(pctldev);
    const __be32 *list;
    int size, num_pins, maps_per_pin, i = 0;
    struct pinctrl_map *new_map;

    list = of_get_property(np_config, "acme,pins", &size);
    if (!list)
        return -EINVAL;

    if (size % (4 * sizeof(u32)) != 0) {
        dev_err(soc->dev, "Malformed 'acme,pins' property in %pOFn
",
            np_config);
        return -EINVAL;
    }

    num_pins = size / (4 * sizeof(u32));
    maps_per_pin = 2;  /* One mux map and one config map per pin */

    *num_maps = num_pins * maps_per_pin;
    new_map = kcalloc(*num_maps, sizeof(*new_map), GFP_KERNEL);
    if (!new_map)
        return -ENOMEM;
    *map = new_map;

    for (i = 0; i < num_pins; i++) {
        unsigned int group_id, pin_offset, func_id, pin_id;
        phandle config_phandle;
        struct device_node *np_conf;
        struct pinctrl_map *m_mux = &new_map[i * maps_per_pin];
        struct pinctrl_map *m_conf = &new_map[i * maps_per_pin + 1];
        unsigned long *configs;
        int num_configs = 0;
        int cfg_idx = 0;
        u32 drive_strength;

        group_id     = be32_to_cpu(*list++);
        pin_offset   = be32_to_cpu(*list++);
        func_id      = be32_to_cpu(*list++);
        config_phandle = be32_to_cpu(*list++);

        switch (group_id) {
        case GPIO_BANK0:
            if (pin_offset > 3)
                goto err;
            pin_id = 0 + pin_offset;
            break;
        case GPIO_BANK1:
            if (pin_offset > 3)
                goto err;
            pin_id = 4 + pin_offset;
            break;
        default:
            goto err;
        }

        if (pin_id >= soc->num_pins ||
            func_id >= ARRAY_SIZE(virtual_unique_functions))
            goto err;

        /* Setup mux map */
        m_mux->type = PIN_MAP_TYPE_MUX_GROUP;
        m_mux->data.mux.function = virtual_unique_functions[func_id];
        m_mux->data.mux.group = soc->pins[pin_id].name;

        /* Setup config map */
        m_conf->type = PIN_MAP_TYPE_CONFIGS_PIN;
        m_conf->data.configs.group_or_pin = soc->pins[pin_id].name;

        /* Parse config node and count how many configs we need */
        np_conf = of_find_node_by_phandle(config_phandle);
        if (!np_conf)
            goto err;

        /* Count configs first */
        if (of_property_read_bool(np_conf, "acme,pull-up") ||
            of_property_read_bool(np_conf, "acme,pull-down") ||
            of_property_read_bool(np_conf, "acme,pull-none"))
            num_configs++;
        
        if (of_property_read_u32(np_conf, "acme,drive-strength", &drive_strength) == 0)
            num_configs++;

        /* Allocate config array */
        configs = kcalloc(num_configs, sizeof(*configs), GFP_KERNEL);
        if (!configs) {
            of_node_put(np_conf);
            goto err_nomem;
        }

        /* Fill in configs */
        cfg_idx = 0;
        
        /* Pull configuration */
        if (of_property_read_bool(np_conf, "acme,pull-up")) {
            configs[cfg_idx++] = pinconf_to_config_packed(PIN_CONFIG_ACME_PULL_UP, 1);
        } else if (of_property_read_bool(np_conf, "acme,pull-down")) {
            configs[cfg_idx++] = pinconf_to_config_packed(PIN_CONFIG_ACME_PULL_DOWN, 1);
        } else if (of_property_read_bool(np_conf, "acme,pull-none")) {
            configs[cfg_idx++] = pinconf_to_config_packed(PIN_CONFIG_ACME_PULL_NONE, 1);
        }

        /* Drive strength configuration */
        if (of_property_read_u32(np_conf, "acme,drive-strength", &drive_strength) == 0) {
            if (drive_strength > 3) {
                dev_err(soc->dev, "Invalid drive-strength %u in %pOFn
",
                    drive_strength, np_conf);
                kfree(configs);
                of_node_put(np_conf);
                goto err;
            }
            configs[cfg_idx++] = pinconf_to_config_packed(
                PIN_CONFIG_ACME_DRIVE_STRENGTH, drive_strength);
        }

        of_node_put(np_conf);

        m_conf->data.configs.configs = configs;
        m_conf->data.configs.num_configs = num_configs;

        dev_dbg(soc->dev, "Pin %u: %u configs allocated
", pin_id, num_configs);
    }
    return 0;

err_nomem:
    i = (i > 0) ? i : num_pins;
err:
    dev_err(soc->dev, "Invalid pin data in %pOFn
", np_config);
    for (i = i - 1; i >= 0; i--) {
        if (new_map[i * maps_per_pin + 1].data.configs.configs)
            kfree(new_map[i * maps_per_pin + 1].data.configs.configs);
    }
    kfree(new_map);
    *map = NULL;
    *num_maps = 0;
    return -EINVAL;
}

static void virtual_dt_free_map(struct pinctrl_dev *pctldev,
                struct pinctrl_map *map,
                unsigned int num_maps)
{
    int i;

    for (i = 0; i < num_maps; i++) {
        if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
            kfree(map[i].data.configs.configs);
    }
    kfree(map);
}

/*=====================================================================
 * 3. Pinctrl Descriptor and Platform Driver
 *=====================================================================*/
static const struct pinctrl_pin_desc virtual_pins[] = {
    PINCTRL_PIN(0, "VPIN0"), PINCTRL_PIN(1, "VPIN1"),
    PINCTRL_PIN(2, "VPIN2"), PINCTRL_PIN(3, "VPIN3"),
    PINCTRL_PIN(4, "VPIN4"), PINCTRL_PIN(5, "VPIN5"),
    PINCTRL_PIN(6, "VPIN6"), PINCTRL_PIN(7, "VPIN7"),
};

static struct pinctrl_desc virtual_pinctrl_desc = {
    .name    = "virtual-pinctrl-vendor",
    .pins    = virtual_pins,
    .npins   = ARRAY_SIZE(virtual_pins),
    .pctlops = &virtual_pctrl_ops,
    .pmxops  = &virtual_pmx_ops,
    .confops = &virtual_pinconf_ops,
    .owner   = THIS_MODULE,
};

static int virtual_pinctrl_probe(struct platform_device *pdev)
{
    struct virtual_pinctrl_soc_data *soc;
    int i;

    dev_info(&pdev->dev, "Probing virtual pinctrl driver with multi-config support
");

    soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
    if (!soc)
        return -ENOMEM;

    soc->dev = &pdev->dev;
    soc->pins = virtual_pins;
    soc->num_pins = ARRAY_SIZE(virtual_pins);

    soc->group_names = devm_kcalloc(&pdev->dev, soc->num_pins,
                    sizeof(*soc->group_names), GFP_KERNEL);
    if (!soc->group_names)
        return -ENOMEM;

    for (i = 0; i < soc->num_pins; i++)
        soc->group_names[i] = soc->pins[i].name;

    platform_set_drvdata(pdev, soc);

    soc->pctldev = pinctrl_register(&virtual_pinctrl_desc, &pdev->dev, soc);
    if (IS_ERR(soc->pctldev)) {
        dev_err(&pdev->dev, "Failed to register pinctrl device
");
        return PTR_ERR(soc->pctldev);
    }

    dev_info(&pdev->dev, "Registered successfully with multi-config support
");
    return 0;
}

static int virtual_pinctrl_remove(struct platform_device *pdev)
{
    struct virtual_pinctrl_soc_data *soc = platform_get_drvdata(pdev);

    dev_info(&pdev->dev, "Removing virtual pinctrl driver
");
    pinctrl_unregister(soc->pctldev);
    return 0;
}

static const struct of_device_id virtual_pinctrl_of_match[] = {
    { .compatible = "acme,virtual-pinctrl-vendor" },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, virtual_pinctrl_of_match);

static struct platform_driver virtual_pinctrl_driver = {
    .driver = {
        .name           = "virtual-pinctrl-vendor",
        .of_match_table = of_match_ptr(virtual_pinctrl_of_match),
    },
    .probe  = virtual_pinctrl_probe,
    .remove = virtual_pinctrl_remove,
};
module_platform_driver(virtual_pinctrl_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Linuxtoyou");
MODULE_DESCRIPTION("Virtual pinctrl driver with multi-config support");

测试代码

/*
 * @Author: your name
 * @Date: 2025-10-14 12:03:03
 * @LastEditTime: 2025-10-14 18:22:42
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: linux-4.4.159driversgpudrm	estpinctrl-test-client.c
 */


#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/delay.h> /* For msleep */

static int test_client_probe(struct platform_device *pdev)
{
    struct pinctrl *pinctrl;
    struct pinctrl_state *gpio_state, *peripheral_state;
    int ret;

    dev_info(&pdev->dev, "Probing complex pinctrl test client
");

    pinctrl = devm_pinctrl_get(&pdev->dev);
    if (IS_ERR(pinctrl)) {
        if (PTR_ERR(pinctrl) == -EPROBE_DEFER) {
            dev_info(&pdev->dev, "Pinctrl provider not ready, deferring probe
");
            return -EPROBE_DEFER;
        }
        dev_err(&pdev->dev, "Failed to get pinctrl handle
");
        return PTR_ERR(pinctrl);
    }

    /* Lookup the two states defined in the device tree */
    gpio_state = pinctrl_lookup_state(pinctrl, "default");
    if (IS_ERR(gpio_state)) {
        dev_err(&pdev->dev, "Failed to lookup 'default' state
");
        return PTR_ERR(gpio_state);
    }

        /* === STATE 1: Set initial state to 'default_gpio' === */
    dev_info(&pdev->dev, "--- STEP 1: Setting initial state to 'default_gpio' ---
");
    ret = pinctrl_select_state(pinctrl, gpio_state);
    if (ret) {
        dev_err(&pdev->dev, "Failed to select 'default_gpio' state
");
        return ret;
    }
    dev_info(&pdev->dev, "All pin groups are now in GPIO mode.
");
    
    peripheral_state = pinctrl_lookup_state(pinctrl, "peripheral_mode");
    if (IS_ERR(peripheral_state)) {
        dev_err(&pdev->dev, "Failed to lookup 'peripheral_mode' state
");
        return PTR_ERR(peripheral_state);
    }
    dev_info(&pdev->dev, "Successfully found both 'default_gpio' and 'peripheral_mode' states
");

    /* === STATE 2: Dynamically switch to 'peripheral_mode' === */
    dev_info(&pdev->dev, "--- STEP 2: Dynamically switching to 'peripheral_mode' ---
");
    ret = pinctrl_select_state(pinctrl, peripheral_state);
    if (ret) {
        dev_err(&pdev->dev, "Failed to select 'peripheral_mode' state
");
        return ret;
    }

    return 0;
}

static int test_client_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "Removing pinctrl test client
");
    return 0;
}

static const struct of_device_id test_client_of_match[] = {
    { .compatible = "acme,pinctrl-test-client" },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, test_client_of_match);

static struct platform_driver test_client_driver = {
    .driver = {
        .name = "pinctrl-test-client",
        .of_match_table = of_match_ptr(test_client_of_match),
    },
    .probe = test_client_probe,
    .remove = test_client_remove,
};

module_platform_driver(test_client_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Linuxtoyou");
MODULE_DESCRIPTION("Final test client for the virtual pinctrl driver");

日志:

可以通过该节点查看当前引脚处于哪种配置中
/ # cat /sys/kernel/debug/pinctrl/pinctrl-handles

Requested pin control handlers their pinmux maps:
device: pinctrl@0 current state: none
device: test-client@0 current state: peripheral_mode
  state: default
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN0 (0) function: gpio (0)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN0 (0)config 00010001
config 00000004
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN1 (1) function: gpio (0)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN1 (1)config 00010001
config 00010004
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN4 (4) function: gpio (0)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN4 (4)config 00010001
config 00020004
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN5 (5) function: gpio (0)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN5 (5)config 00010001
config 00030004
  state: peripheral_mode
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN0 (0) function: uart (1)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN0 (0)config 00010002
config 00000004
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN1 (1) function: uart (1)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN1 (1)config 00010002
config 00010004
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN0 (0) function: spi (2)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN0 (0)config 00010002
config 00020004
    type: MUX_GROUP controller virtual-pinctrl-vendor group: VPIN1 (1) function: spi (2)
    type: CONFIGS_PIN controller virtual-pinctrl-vendor pin VPIN1 (1)config 00010002
config 00030004

图片

✅ 结论

输出结论

待查资料问题

  • ❓ 问题 1:?
  • ❓ 问题 2:?

参考链接

  • 官方文档
© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...