pinctrl 驱动

在 Linux 内核中,pinctrl 驱动是硬件平台相关的底层实现,负责将抽象的 pinctrl 配置转化为具体的硬件寄存器操作。以下是其核心实现细节与设计逻辑的分步解析:


1. 驱动核心数据结构

1.1 pinctrl_desc:驱动描述符

定义驱动支持的功能和操作接口:

static struct pinctrl_desc example_pinctrl_desc = {
    .name           = "example-pinctrl",
    .pins           = example_pins,        // 引脚列表(数组)
    .npins          = ARRAY_SIZE(example_pins), // 引脚总数
    .pctlops        = &example_pctl_ops,   // pinctrl 操作函数集
    .pmxops         = &example_pmx_ops,    // pinmux 操作函数集
    .confops        = &example_conf_ops,   // pinconf 操作函数集
    .owner          = THIS_MODULE,
};
1.2 操作函数集
  • pinctrl_ops:控制引脚状态切换(如 default/sleep 状态)。

    static const struct pinctrl_ops example_pctl_ops = {
        .get_groups_count = example_get_groups_count,
        .get_group_name   = example_get_group_name,
        .get_group_pins   = example_get_group_pins,
        .pin_dbg_show     = example_pin_dbg_show,
        // ...
    };
  • pinmux_ops:实现引脚复用配置(如将引脚设为 SPI 功能)。

    static const struct pinmux_ops example_pmx_ops = {
        .get_functions_count = example_get_functions_count,
        .get_function_name   = example_get_function_name,
        .set_mux             = example_set_mux,
        // ...
    };
  • pinconf_ops:配置电气参数(如驱动强度、上拉)。

    static const struct pinconf_ops example_conf_ops = {
        .is_generic        = true,
        .pin_config_get    = example_pin_config_get,
        .pin_config_set    = example_pin_config_set,
        // ...
    };

2. 驱动注册与初始化

2.1 注册流程

在驱动的 probe 函数中完成注册:

static int example_pinctrl_probe(struct platform_device *pdev) {
    struct pinctrl_desc *desc = &example_pinctrl_desc;
    struct pinctrl_dev *pctldev;

    // 1. 解析设备树,获取引脚配置
    parse_pinctrl_dt(pdev, desc);

    // 2. 注册 pinctrl 设备
    pctldev = pinctrl_register(desc, &pdev->dev, pdev);
    if (!pctldev) {
        dev_err(&pdev->dev, "Failed to register pinctrl driver\n");
        return -ENOMEM;
    }

    platform_set_drvdata(pdev, pctldev);
    return 0;
}
2.2 设备树解析

从设备树中提取引脚组、状态和配置:

static void parse_pinctrl_dt(struct platform_device *pdev, struct pinctrl_desc *desc) {
    struct device_node *np = pdev->dev.of_node;
    of_property_read_u32_array(np, "pinctrl-pins", desc->pins, desc->npins);
    // 解析引脚组、状态、电气参数等...
}

3. 硬件操作实现

3.1 引脚复用(Pinmux)

通过寄存器配置引脚功能:

static int example_set_mux(struct pinctrl_dev *pctldev, unsigned int selector,
                           unsigned int group) {
    struct example_pinctrl *ep = pinctrl_dev_get_drvdata(pctldev);
    u32 reg_offset = ep->mux_reg_base + (selector * 4);

    // 1. 计算寄存器偏移和掩码
    u32 mask = GENMASK(ep->mux_bit_width - 1, 0);
    u32 val = (group << ep->mux_shift) & mask;

    // 2. 写入寄存器
    writel_relaxed(val, ep->base + reg_offset);
    return 0;
}
3.2 电气参数配置

设置上拉/下拉、驱动强度等:

static int example_pin_config_set(struct pinctrl_dev *pctldev, unsigned int pin,
                                 unsigned long *configs, unsigned num_configs) {
    struct example_pinctrl *ep = pinctrl_dev_get_drvdata(pctldev);
    u32 reg_offset = ep->conf_reg_base + (pin * 4);
    u32 val = 0;

    // 解析配置参数(如 PIN_CONFIG_BIAS_PULL_UP)
    for (int i = 0; i < num_configs; i++) {
        unsigned long config = configs[i];
        if (pinconf_to_config_param(config) == PIN_CONFIG_BIAS_PULL_UP) {
            val |= BIT(ep->pull_bit);
        }
    }

    // 写入配置寄存器
    writel_relaxed(val, ep->base + reg_offset);
    return 0;
}

4. 状态管理与动态切换

4.1 状态配置

在设备树中定义多个状态(如 defaultsleep),驱动需支持运行时切换:

static int example_request_gpio_state(struct pinctrl_dev *pctldev,
                                     struct pinctrl_state *state) {
    const char *state_name = state->name;

    if (!strcmp(state_name, "default")) {
        // 配置默认状态(GPIO 输出低电平)
        example_set_mux(pctldev, 0, PINMUX_GPIO);
        example_set_gpio_output(pctldev, 0, 0);
    } else if (!strcmp(state_name, "sleep")) {
        // 休眠状态(关闭上拉)
        example_set_mux(pctldev, 0, PINMUX_GPIO);
        example_set_gpio_pull(pctldev, 0, PIN_CONFIG_BIAS_DISABLE);
    }
    return 0;
}
4.2 状态切换接口

通过 pinctrl_select_state 触发状态变更:

int pinctrl_select_state(struct pinctrl_dev *pctldev, struct pinctrl_state *state) {
    // 调用驱动的请求状态函数
    return pctldev->desc->pctlops->set_state(pctldev, state);
}

5. 调试与验证

5.1 内核日志

通过 dmesg 查看 pinctrl 配置过程:

[    1.234567] example-pinctrl example-pinctrl: pin 0 already requested by example-pinctrl
[    1.234578] example-pinctrl example-pinctrl: default state applied
5.2 工具支持

使用 pinctrl-utils 工具动态检查引脚状态:

# 列出所有引脚组和状态
cat /sys/kernel/debug/pinctrl/example-pinctrl/pinmux-functions

# 查看当前配置
cat /sys/kernel/debug/pinctrl/example-pinctrl/pins

6. 驱动与 GPIO 子系统的协作

  • pinctrl 职责:配置引脚为 GPIO 功能。
  • GPIO 子系统职责:管理 GPIO 方向(输入/输出)和电平读写。
  • 协作流程
    1. pinctrl 驱动配置引脚为 GPIO。
    2. GPIO 控制器驱动注册 GPIO 芯片(gpiochip)。
    3. 用户空间通过 /sys/class/gpio 或驱动通过 gpio_request 使用 GPIO。

总结

pinctrl 驱动是 Linux 内核中硬件引脚管理的核心实现,通过:

  1. 抽象硬件细节:将寄存器操作封装为统一接口。
  2. 设备树驱动配置:实现硬件无关的配置方式。
  3. 动态状态管理:支持运行时调整引脚行为。
  4. 与 GPIO 子系统协作:完成从功能配置到实际使用的完整链路。

开发者需根据具体 SoC 的寄存器布局实现 pinctrl_ops/pinmux_ops/pinconf_ops,并通过设备树描述硬件拓扑,最终实现跨平台的引脚管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值