找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3988

积分

0

好友

548

主题
发表于 2025-12-11 07:17:44 | 查看: 213| 回复: 0

在嵌入式Linux开发中,你是否曾为配置一个简单的GPIO引脚而翻阅数百页的数据手册,手动计算寄存器地址和位掩码?你是否遇到过因为引脚功能冲突导致的外设无法正常工作?这正是Linux Pinctrl子系统要解决的核心问题。

传统硬件引脚配置方式就像一座没有交通信号灯的城市——每个司机(驱动开发者)按照自己的方式使用道路(硬件引脚),极易引发冲突和混乱。Pinctrl子系统则像一位经验丰富的交通指挥官,统一管理所有道路的使用规则和调度安排。

一、Pinctrl子系统:芯片引脚的交通枢纽

1.1 核心价值与设计哲学

Pinctrl子系统的设计哲学可以概括为“抽象与统一”。在Linux 2.6时代,各芯片厂商对GPIO、时钟、引脚复用等基础设施的实现五花八门,几乎每家都有自己的API和实现方式。这种混乱局面促使内核社区在2011-2012年间进行了大规模的统一工作。

pinctrl子系统的三个核心作用:

  1. 引脚枚举与命名:像给城市每条街道命名一样,给每个物理引脚分配可识别的名称。
  2. 引脚复用控制:决定每条“街道”是用于“公交专用”(SPI/I2C)还是“普通车辆”(GPIO)。
  3. 引脚电气配置:设置街道的“交通规则”,如上拉/下拉、驱动强度、开漏模式等。

1.2 从交通枢纽看核心概念

为了更好地理解pinctrl,我们可以将其比作一个现代化的综合交通枢纽

图片

  • 引脚(Pins):就像交通枢纽的各个进出站口,是物理上的连接点。每个引脚有唯一的编号和名称。
  • 引脚组(Groups):将服务于同一外设的多个引脚组织在一起,就像将多个相邻的地铁出入口编组为一个地铁站。例如,一个UART外设需要TX、RX两个引脚,它们就形成一个引脚组。
  • 功能(Functions):定义引脚可以承担的角色。就像同一个站口既可以作为地铁入口,也可以作为公交枢纽入口,但不能同时承担两种角色。
  • 状态(States):设备在不同工作模式下需要的引脚配置组合。就像交通枢纽在早高峰、晚高峰和夜间的不同运营方案。

二、Pinctrl的架构:四层模型剖析

2.1 整体架构视图

pinctrl子系统采用经典的四层架构,从上到下分别是消费者驱动层、核心抽象层、厂商驱动层和硬件寄存器层。

图片

2.2 各层职责详解

  • 消费者驱动层:各种外设驱动程序,如UART、SPI、I2C等。它们只关心“我需要哪些引脚,配置成什么功能”,不关心具体如何实现。
  • 核心抽象层:pinctrl子系统的大脑,提供统一的API接口,管理所有注册的pin controller,处理引脚分配冲突等。
  • 厂商驱动层:芯片厂商提供的具体实现,将核心层的抽象操作映射到自家芯片的特定寄存器操作。
  • 硬件寄存器层:实际的物理硬件,包括各种配置寄存器和复用寄存器。

三、核心数据模型:角色与关系的交响曲

3.1 关键数据结构全景

Pinctrl子系统的数据模型可以用一个“剧团”来比喻:pinctrl_desc剧本,定义了整个剧的框架;pinctrl_dev舞台,提供表演场所;各种ops是演员的动作指导,规定每个角色如何表演。

/* 核心数据结构定义 */
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;                      // 模块所有者
};

struct pinctrl_dev {
    struct list_head node;                     // 链表节点
    struct pinctrl_desc *desc;                 // 控制器描述符
    void *driver_data;                         // 驱动私有数据
    struct device *dev;                        // 关联的设备
    unsigned int num_groups;                   // 引脚组数量
    const char **grp_names;                    // 引脚组名称数组
    struct pinctrl_gpio_range *gpio_ranges;    // GPIO范围数组
    unsigned int num_gpio_ranges;              // GPIO范围数量
};

struct pinctrl_pin_desc {
    unsigned number;                           // 引脚编号
    const char *name;                          // 引脚名称
    void *drv_data;                            // 驱动私有数据
};

struct group_desc {
    const char *name;                          // 组名称
    int *pins;                                 // 引脚数组
    int num_pins;                              // 引脚数量
    const char * const *data;                  // 组数据
};

3.2 操作集:抽象的艺术

操作集是pinctrl子系统抽象能力的核心体现,它将硬件相关的具体操作抽象为统一的接口。

/* 引脚控制操作集 - 管理引脚和引脚组 */
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);
    /* 可选: 引脚组与设备树解析 */
    int (*dt_node_to_map) (struct pinctrl_dev *pctldev,
                           struct device_node *np_config,
                           struct pinctrl_map **map,
                           unsigned *num_maps);
};

/* 引脚复用操作集 - 管理功能复用 */
struct pinmux_ops {
    int (*get_functions_count) (struct pinctrl_dev *pctldev);
    const char *(*get_function_name) (struct pinctrl_dev *pctldev,
                                      unsigned selector);
    int (*get_function_groups) (struct pinctrl_dev *pctldev,
                                unsigned selector,
                                const char * const **groups,
                                unsigned * const num_groups);
    int (*set_mux) (struct pinctrl_dev *pctldev,
                    unsigned func_selector,
                    unsigned group_selector);
    int (*gpio_request_enable) (struct pinctrl_dev *pctldev,
                                struct pinctrl_gpio_range *range,
                                unsigned offset);
    void (*gpio_disable_free) (struct pinctrl_dev *pctldev,
                               struct pinctrl_gpio_range *range,
                               unsigned offset);
};

/* 引脚配置操作集 - 管理电气特性 */
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);
    int (*pin_config_group_get) (struct pinctrl_dev *pctldev,
                                 unsigned selector,
                                 unsigned long *config);
    int (*pin_config_group_set) (struct pinctrl_dev *pctldev,
                                 unsigned selector,
                                 unsigned long *configs,
                                 unsigned num_configs);
};

图片

四、设备树:声明式的硬件描述

4.1 生产者与消费者模型

设备树中的pinctrl配置采用生产者-消费者模型

  • 生产者:pinctrl控制器节点,定义引脚配置的能力(提供哪些引脚、支持哪些功能)。
  • 消费者:具体的外设节点,引用生产者的配置(我需要哪些引脚、配置成什么功能)。

4.2 RK3568实例解析

以Rockchip RK3568为例,生产者在rk3568-pinctrl.dtsi中定义:

/* 生产者: pinctrl控制器定义 */
pinctrl: pinctrl {
    compatible = "rockchip,rk3568-pinctrl";
    rockchip,grf = <&grf>;
    rockchip,pmu = <&pmugrf>;

    gpio0: gpio@fdd60000 {
        compatible = "rockchip,gpio-bank";
        reg = <0x0 0xfdd60000 0x0 0x100>;
        gpio-controller;
        #gpio-cells = <2>;
        gpio-ranges = <&pinctrl 0 0 32>;
        interrupt-controller;
        #interrupt-cells = <2>;
    };

    /* SPI功能定义 */
    spi3_cs0: spi3-cs0 {
        rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up_drv_level_1>;
    };

    /* I2C功能定义 */
    i2c3_xfer: i2c3-xfer {
        rockchip,pins = <1 RK_PB2 1 &pcfg_pull_none>,
                       <1 RK_PB3 1 &pcfg_pull_none>;
    };
};

消费者在具体的设备节点中引用:

/* 消费者: 具体设备引用pinctrl配置 */
&i2c3 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c3_xfer>;

    /* 连接的设备 */
    touchscreen@38 {
        compatible = "edt,edt-ft5x06";
        reg = <0x38>;
        interrupt-parent = <&gpio0>;
        interrupts = <RK_PC5 IRQ_TYPE_EDGE_FALLING>;
        reset-gpios = <&gpio0 RK_PC6 GPIO_ACTIVE_LOW>;
        pinctrl-names = "default";
        pinctrl-0 = <&touchscreen_pins>;
    };
};

4.3 引脚配置格式详解

Rockchip的引脚配置采用四元组格式:<PIN_BANK PIN_BANK_IDX MUX &phandle>

  • PIN_BANK:引脚所属的组,RK3568有GPIO0~GPIO4共5组,对应0~4。例如GPIO0_C0的PIN_BANK是0。
  • PIN_BANK_IDX:组内编号,如RK_PA0、RK_PA1等,GPIO0_C0对应RK_PC0。
  • MUX:复用功能选择,0~15对应16种功能,0通常是GPIO功能。例如将GPIO0_C0设置为PWM1_M0功能,MUX设为1。
  • phandle:引脚通用配置的引用,如&pcfg_pull_up表示上拉配置。

五、Pinctrl与GPIO子系统的协同

5.1 相辅相成的兄弟系统

Pinctrl和GPIO子系统就像城市规划局和交通管理局的关系:

  • Pinctrl子系统:决定一条路是作为高速公路(专用外设)还是城市道路(GPIO)。
  • GPIO子系统:管理作为城市道路(GPIO)时的具体交通规则。

这种抽象与统一的设计思想,是Linux内核在硬件管理层面的重要体现。

5.2 GPIO范围映射机制

当GPIO子系统需要使用某个引脚时,需要通过pinctrl子系统申请,这依赖于gpio_ranges映射机制。

/* GPIO范围定义 */
struct pinctrl_gpio_range {
    const char *name;
    unsigned int id;
    unsigned int base;          // GPIO编号起始
    unsigned int pin_base;      // 引脚编号起始
    unsigned int npins;         // 引脚数量
    struct gpio_chip *gc;       // 关联的GPIO控制器
};

/* 从pinctrl角度看GPIO请求 */
int pinctrl_request_gpio(unsigned gpio) {
    struct pinctrl_dev *pctldev;
    struct pinctrl_gpio_range *range;

    /* 1. 根据gpio编号查找对应的pinctrl控制器和范围 */
    range = pinctrl_find_gpio_range_from_gpio(pctldev, gpio);
    if (!range)
        return -EPROBE_DEFER;

    /* 2. 将gpio编号转换为pin编号 */
    pin = range->pin_base + (gpio - range->base);

    /* 3. 通过pinctrl配置该pin为GPIO功能 */
    ret = pinctrl_request_gpio(pctldev, pin, range->id);

    return ret;
}

图片

5.3 设备树中的协作

在设备树中,GPIO控制器通过gpio-ranges属性声明自己管理的引脚范围。

/* GPIO控制器声明引脚范围 */
gpio1: gpio@fe740000 {
    compatible = "rockchip,gpio-bank";
    reg = <0x0 0xfe740000 0x0 0x100>;
    gpio-controller;
    #gpio-cells = <2>;
    gpio-ranges = <&pinctrl 0 0 32>;  /* 管理pinctrl的引脚0-31 */
    interrupt-controller;
    #interrupt-cells = <2>;
};

六、状态管理:设备生命周期的引脚配置

6.1 状态概念与设计

设备在不同工作状态下需要不同的引脚配置,pinctrl的状态管理机制为此而生。

状态名 典型场景 引脚配置特点
default 设备正常工作时 功能引脚正确复用,电气特性优化
sleep 设备低功耗休眠时 引脚可能配置为高阻态,降低功耗
idle 设备空闲时 部分引脚可能释放,电气特性调整
active 设备高负载运行时 驱动能力增强,性能优先

6.2 状态切换流程

状态切换的核心API是pinctrl_select_state(),其内部流程如下。

/* 状态选择核心逻辑 */
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) {
    struct pinctrl_setting *setting, *setting2;
    int ret;

    /* 遍历状态中的所有设置 */
    list_for_each_entry(setting, &state->settings, node) {
        switch (setting->type) {
        case PIN_MAP_TYPE_MUX_GROUP:
            /* 引脚复用配置 */
            ret = pinmux_enable_setting(setting);
            break;
        case PIN_MAP_TYPE_CONFIGS_PIN:
        case PIN_MAP_TYPE_CONFIGS_GROUP:
            /* 引脚电气配置 */
            ret = pinconf_apply_setting(setting);
            break;
        default:
            ret = -EINVAL;
            break;
        }

        if (ret < 0) {
            /* 出错回滚: 恢复之前的设置 */
            goto apply_failed;
        }
    }

    return 0;

apply_failed:
    /* 回滚已应用的设置 */
    list_for_each_entry(setting2, &state->settings, node) {
        if (setting2 == setting)
            break;
        /* 恢复之前的配置 */
    }
    return ret;
}

七、厂商驱动实现:以Rockchip为例

7.1 驱动初始化流程

厂商驱动的主要任务是将通用的pinctrl操作映射到具体芯片的寄存器操作。

/* Rockchip pinctrl驱动初始化 */
static int rockchip_pinctrl_probe(struct platform_device *pdev) {
    struct rockchip_pinctrl *info;
    struct pinctrl_desc *ctrldesc;
    struct pinctrl_pin_desc *pindesc;
    int ret, i;

    /* 1. 分配内存 */
    info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);

    /* 2. 从设备树获取硬件信息 */
    info->reg_base = of_iomap(np, 0);
    info->regmap_pmu = syscon_regmap_lookup_by_phandle(np, "rockchip,pmu");

    /* 3. 构建引脚描述数组 */
    pindesc = devm_kcalloc(&pdev->dev, info->ctrl->nr_pins,
                           sizeof(*pindesc), GFP_KERNEL);
    for (i = 0; i < info->ctrl->nr_pins; i++) {
        pindesc[i].number = i;
        pindesc[i].name = kasprintf(GFP_KERNEL, "GPIO%d-%d",
                                    i / 32, i % 32);
    }

    /* 4. 构建控制器描述符 */
    ctrldesc = &info->pctl;
    ctrldesc->name = "rockchip-pinctrl";
    ctrldesc->owner = THIS_MODULE;
    ctrldesc->pins = pindesc;
    ctrldesc->npins = info->ctrl->nr_pins;
    ctrldesc->pctlops = &rockchip_pctrl_ops;
    ctrldesc->pmxops = &rockchip_pinmux_ops;
    ctrldesc->confops = &rockchip_pinconf_ops;

    /* 5. 注册到pinctrl子系统 */
    info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info);

    /* 6. 注册GPIO控制器 */
    ret = rockchip_gpiolib_register(pdev, info);

    return 0;
}

/* 复用功能设置的具体实现 */
static int rockchip_pinmux_set_mux(struct pinctrl_dev *pctldev,
                                   unsigned selector,
                                   unsigned group) {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    const struct rockchip_pin_group *grp = &info->groups[group];
    int i, ret = 0;

    for (i = 0; i < grp->npins; i++) {
        int mux = grp->func;
        int pin = grp->pins[i];

        /* 计算寄存器地址和位偏移 */
        reg = info->reg_base + (pin / 16) * 4;
        bit = (pin % 16) * 2;

        /* 写入复用配置 */
        regmap_write_bits(info->regmap_grf, reg,
                          0x3 << bit, mux << bit);
    }

    return ret;
}

7.2 配置操作的实现

引脚电气特性的配置实现相对复杂,需要处理各种配置参数。

/* 引脚配置设置实现 */
static int rockchip_pinconf_set(struct pinctrl_dev *pctldev,
                                unsigned pin,
                                unsigned long *configs,
                                unsigned num_configs) {
    struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
    enum pin_config_param param;
    u16 arg;
    int i, ret = 0;

    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_BIAS_PULL_UP:
            /* 配置上拉电阻 */
            rockchip_set_pull(info, pin, RK_PULL_UP);
            break;
        case PIN_CONFIG_BIAS_PULL_DOWN:
            /* 配置下拉电阻 */
            rockchip_set_pull(info, pin, RK_PULL_DOWN);
            break;
        case PIN_CONFIG_BIAS_DISABLE:
            /* 禁用上下拉 */
            rockchip_set_pull(info, pin, RK_NO_PULL);
            break;
        case PIN_CONFIG_DRIVE_STRENGTH:
            /* 配置驱动强度 */
            rockchip_set_drive_strength(info, pin, arg);
            break;
        case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
            /* 配置施密特触发器 */
            rockchip_set_schmitt(info, pin, arg);
            break;
        default:
            ret = -ENOTSUPP;
            break;
        }
    }

    return ret;
}

八、调试与问题排查

8.1 常用调试工具和命令

掌握正确的调试方法可以极大提高pinctrl相关问题的排查效率。

1. Sysfs调试接口

# 查看系统中所有pinctrl控制器
ls /sys/class/pinctrl/

# 查看特定控制器的信息
cat /sys/class/pinctrl/pinctrl.0/pins        # 引脚列表
cat /sys/class/pinctrl/pinctrl.0/pinmux-pins # 复用状态
cat /sys/class/pinctrl/pinctrl.0/pinconf-pins # 配置状态

# 查看GPIO状态
cat /sys/kernel/debug/gpio

2. 设备树调试

# 查看设备树中的pinctrl配置
dtc -I dtb -O dts /sys/firmware/fdt | grep -A 10 -B 5 pinctrl

# 检查特定节点的pinctrl引用
cat /proc/device-tree/soc/i2c@ff160000/pinctrl-names

3. 内核日志分析

# 启用pinctrl调试日志
echo 8 > /proc/sys/kernel/printk
dmesg | grep pinctrl

# 常见错误信息分析
# "pin already requested" - 引脚冲突
# "could not request pin" - 引脚请求失败
# "failed to find state" - 状态配置不存在

8.2 常见问题与解决方案

问题现象 可能原因 解决方案
引脚功能不正确 1. 设备树配置错误
2. 复用寄存器配置错误
1. 检查设备树pinctrl引用
2. 验证寄存器实际值
引脚冲突 1. 多个驱动使用同一引脚
2. 状态切换未正确释放
1. 检查引脚分配
2. 确保proper状态管理
电气特性异常 1. 上下拉配置错误
2. 驱动能力不匹配
1. 验证pinconf配置
2. 测量实际电气特性
GPIO无法操作 1. 未配置为GPIO功能
2. GPIO范围映射错误
1. 检查pinctrl配置
2. 验证gpio-ranges

调试示例:分析引脚冲突问题

/* 典型的引脚冲突错误信息 */
[    1.234567] pinctrl-single 44e10800.pinmux: pin 44e10864.0 already requested by 48060000.serial; cannot claim for 4819c000.i2c
[    1.234568] pinctrl-single 44e10800.pinmux: pin-0 (4819c000.i2c) status -22
[    1.234569] pinctrl-single 44e10800.pinmux: could not request pin 0 (44e10864.0) from group pinmux_i2c1_pins  on device pinctrl-single

/* 解决方案步骤 */
1. 定位冲突引脚: pin 0 (44e10864.0)
2. 查找当前使用者: 48060000.serial (UART设备)
3. 检查设备树配置:
   - 确认UART和I2C是否配置了同一引脚
   - 检查引脚复用是否正确
4. 修改设备树,分配不同的引脚

九、实战案例:实现简单的虚拟Pinctrl驱动

9.1 虚拟硬件描述

假设我们有一个虚拟的SoC,包含以下资源:

  • 32个可配置引脚,编号0-31
  • 支持3种功能:GPIO(0)、UART(1)、SPI(2)
  • 简单的配置寄存器:每个引脚2位控制功能选择

9.2 驱动实现核心代码

/* 虚拟pinctrl驱动实现 */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/pinconf.h>

/* 虚拟寄存器 */
struct virtual_pinctrl_regs {
    u32 mux_reg[8];    /* 32引脚 × 2位 = 64位 = 8×32位寄存器 */
    u32 conf_reg[32];   /* 配置寄存器,每引脚32位 */
};

/* 引脚描述 */
static const struct pinctrl_pin_desc virtual_pins[] = {
    PINCTRL_PIN(0, "GPIO0"),
    PINCTRL_PIN(1, "GPIO1"),
    /* ... 省略部分引脚 ... */
    PINCTRL_PIN(31, "GPIO31"),
};

/* 引脚组定义 */
static const unsigned int uart_pins[] = {0, 1};  /* UART使用引脚0(TX)、1(RX) */
static const unsigned int spi_pins[] = {2, 3, 4, 5}; /* SPI使用4个引脚 */
static const char * const uart_groups[] = {"uart0_grp"};
static const char * const spi_groups[] = {"spi0_grp"};

/* 引脚控制操作 */
static int virtual_get_groups_count(struct pinctrl_dev *pctldev) {
    return 2; /* uart0_grp 和 spi0_grp */
}

static const char *virtual_get_group_name(struct pinctrl_dev *pctldev,
                                          unsigned selector) {
    switch (selector) {
    case 0: return "uart0_grp";
    case 1: return "spi0_grp";
    default: return NULL;
    }
}

static int virtual_get_group_pins(struct pinctrl_dev *pctldev,
                                  unsigned selector,
                                  const unsigned **pins,
                                  unsigned *num_pins) {
    switch (selector) {
    case 0: /* UART组 */
        *pins = uart_pins;
        *num_pins = ARRAY_SIZE(uart_pins);
        break;
    case 1: /* SPI组 */
        *pins = spi_pins;
        *num_pins = ARRAY_SIZE(spi_pins);
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

static 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,
};

/* 引脚复用操作 */
static int virtual_set_mux(struct pinctrl_dev *pctldev,
                           unsigned func_selector,
                           unsigned group_selector) {
    struct virtual_pinctrl_regs *regs = pinctrl_dev_get_drvdata(pctldev);
    const unsigned *pins;
    unsigned num_pins;
    int i, ret;

    /* 获取组内引脚 */
    ret = virtual_get_group_pins(pctldev, group_selector, &pins, &num_pins);
    if (ret)
        return ret;

    /* 为每个引脚设置复用功能 */
    for (i = 0; i < num_pins; i++) {
        unsigned pin = pins[i];
        unsigned reg_idx = pin / 16;      /* 每个寄存器控制16个引脚 */
        unsigned bit_offset = (pin % 16) * 2; /* 每个引脚2位 */
        u32 mask = 0x3 << bit_offset;
        u32 value = func_selector << bit_offset;

        /* 更新寄存器 */
        regs->mux_reg[reg_idx] = (regs->mux_reg[reg_idx] & ~mask) | value;

        pr_info("virtual-pinctrl: pin %u set to function %u\n",
                pin, func_selector);
    }

    return 0;
}

static struct pinmux_ops virtual_pmx_ops = {
    .set_mux = virtual_set_mux,
};

/* 引脚配置操作 */
static int virtual_pin_config_set(struct pinctrl_dev *pctldev,
                                  unsigned pin,
                                  unsigned long *configs,
                                  unsigned num_configs) {
    struct virtual_pinctrl_regs *regs = pinctrl_dev_get_drvdata(pctldev);
    int i;

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

        switch (param) {
        case PIN_CONFIG_BIAS_PULL_UP:
            regs->conf_reg[pin] |= (1 << 0); /* 上拉使能位 */
            pr_info("virtual-pinctrl: pin %u pull-up enabled\n", pin);
            break;
        case PIN_CONFIG_BIAS_PULL_DOWN:
            regs->conf_reg[pin] |= (1 << 1); /* 下拉使能位 */
            pr_info("virtual-pinctrl: pin %u pull-down enabled\n", pin);
            break;
        case PIN_CONFIG_DRIVE_STRENGTH:
            regs->conf_reg[pin] = (regs->conf_reg[pin] & ~0xFF00) |
                                  ((arg & 0xFF) << 8);
            pr_info("virtual-pinctrl: pin %u drive strength set to %u\n",
                    pin, arg);
            break;
        default:
            pr_warn("virtual-pinctrl: unsupported config param %u\n", param);
            return -ENOTSUPP;
        }
    }

    return 0;
}

static struct pinconf_ops virtual_conf_ops = {
    .pin_config_set = virtual_pin_config_set,
};

/* 主描述符 */
static struct pinctrl_desc virtual_desc = {
    .name = "virtual-pinctrl",
    .pins = virtual_pins,
    .npins = ARRAY_SIZE(virtual_pins),
    .pctlops = &virtual_pctrl_ops,
    .pmxops = &virtual_pmx_ops,
    .confops = &virtual_conf_ops,
    .owner = THIS_MODULE,
};

/* 探测函数 */
static int virtual_pinctrl_probe(struct platform_device *pdev) {
    struct virtual_pinctrl_regs *regs;
    struct pinctrl_dev *pctl_dev;

    /* 分配模拟的寄存器内存 */
    regs = devm_kzalloc(&pdev->dev, sizeof(*regs), GFP_KERNEL);
    if (!regs)
        return -ENOMEM;

    /* 注册pinctrl控制器 */
    pctl_dev = pinctrl_register(&virtual_desc, &pdev->dev, regs);
    if (IS_ERR(pctl_dev)) {
        dev_err(&pdev->dev, "failed to register pinctrl\n");
        return PTR_ERR(pctl_dev);
    }

    platform_set_drvdata(pdev, pctl_dev);
    dev_info(&pdev->dev, "virtual pinctrl driver loaded\n");

    return 0;
}

/* 设备树匹配 */
static const struct of_device_id virtual_pinctrl_of_match[] = {
    { .compatible = "virtual,pinctrl" },
    { }
};
MODULE_DEVICE_TABLE(of, virtual_pinctrl_of_match);

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

module_platform_driver(virtual_pinctrl_driver);

十、总结

通过本文的深入分析,我们可以将Linux Pinctrl子系统的核心思想总结为以下几点:

  1. 抽象分层:通过pinctrl_desc、pinctrl_ops等数据结构,将硬件差异抽象化,提供统一接口。
  2. 声明式配置:利用设备树声明引脚配置,实现硬件描述与驱动代码的分离。
  3. 状态管理:支持设备不同工作状态下的引脚配置动态切换。
  4. 协同工作:与GPIO子系统紧密配合,共同管理芯片引脚资源。

Pinctrl子系统作为嵌入式开发中硬件抽象层的重要组成部分,体现了Linux内核设计的精髓:通过良好的抽象和分层,既隐藏了硬件复杂性,又提供了充分的灵活性和控制能力。




上一篇:树莓派CM0部署Home Assistant实战:在512MB内存上构建智能家居控制中心
下一篇:Linux电源管理内核机制详解:从ACPI、CPUFreq到实战调优
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-13 04:12 , Processed in 0.445766 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表