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

670

积分

0

好友

94

主题
发表于 昨天 07:17 | 查看: 2| 回复: 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. 设备树配置错误<br>2. 复用寄存器配置错误 1. 检查设备树pinctrl引用<br>2. 验证寄存器实际值
引脚冲突 1. 多个驱动使用同一引脚<br>2. 状态切换未正确释放 1. 检查引脚分配<br>2. 确保proper状态管理
电气特性异常 1. 上下拉配置错误<br>2. 驱动能力不匹配 1. 验证pinconf配置<br>2. 测量实际电气特性
GPIO无法操作 1. 未配置为GPIO功能<br>2. GPIO范围映射错误 1. 检查pinctrl配置<br>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, 2025-12-12 08:34 , Processed in 0.090368 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

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