linux内核虚拟pinctrl驱动
❓ 问题描述:
编写一个虚拟的pin controller驱动
在现代的片上系统(SoC)中,物理引脚(Pin)是一种超级宝贵的稀缺资源。为了在有限的引脚上实现尽可能多的功能,芯片设计者会将一个物理引脚设计成**“多功能复用”**的。
例如,SoC 上的第 25 号引脚,它可能同时具备以下几种身份:
通用输入/输出口 (GPIO)
UART(串口)的发送端 (TX)
I2C(总线)的时钟线 (SCL)
SPI(总线)的片选信号 (CS)
这就带来了一个核心问题:在任意时刻,这个引脚到底应该扮演哪种角色?并且,它的电气特性(列如是上拉、下拉还是浮空?驱动能力是强还是弱?)应该如何配置?
在 pinctrl 子系统出现之前,这个配置工作是由各个外设驱动自己完成的。列如,UART 驱动会直接去写硬件寄存器,把引脚配置成 UART_TX;而 GPIO 驱动可能也想去写寄存器,把它配置成 GPIO。这会导致:
冲突与混乱:两个驱动可能会争抢同一个引脚的控制权,导致系统行为不可预测。
代码冗余与耦合:每个外设驱动都需要包含大量与具体 SoC 相关的、复杂的寄存器操作代码,可移植性极差。
状态管理困难:无法聚焦管理和查看所有引脚的当前状态,调试超级困难。
pinctrl 子系统的诞生就是为了解决以上所有问题。它的核心作用可以概括为:
pinctrl 是 Linux 内核中用于统一管理和配置 SoC 引脚的中央仲裁系统。
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:?
参考链接
- 官方文档
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...


