From c4e97d69a5f60bd28be923660480fa36190e92fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 22:36:45 +0000 Subject: [PATCH] [documentation][device_driver_model] Add pinctrl Chinese documentation - complete module 4/26 Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com> --- .../device_driver_model/pinctrl/README_zh.md | 627 ++++++++++++++++++ 1 file changed, 627 insertions(+) create mode 100644 documentation/6.components/device-driver/device_driver_model/pinctrl/README_zh.md diff --git a/documentation/6.components/device-driver/device_driver_model/pinctrl/README_zh.md b/documentation/6.components/device-driver/device_driver_model/pinctrl/README_zh.md new file mode 100644 index 0000000000..2857660bb1 --- /dev/null +++ b/documentation/6.components/device-driver/device_driver_model/pinctrl/README_zh.md @@ -0,0 +1,627 @@ +# Pin Control (Pinctrl) 框架 + +## 概述 + +Pin Control 框架提供了一个统一的接口来管理 SoC 的引脚功能复用和配置。现代 SoC 通常具有多功能引脚,可以配置为不同的功能(如 GPIO、UART、SPI 等)并具有各种电气属性(上拉、下拉、驱动强度等)。 + +### RT-Thread 实现 + +RT-Thread 的 Pinctrl 框架集成了设备驱动模型,允许: + +- 基于设备树的引脚配置 +- 运行时引脚功能切换 +- 引脚状态管理(用于电源管理) +- 与 GPIO 框架协同工作 +- 通过 SCMI 进行引脚控制 + +## Kconfig 配置 + +### 主要配置选项 + +#### RT_USING_PINCTRL + +启用 Pin Control 框架支持。 + +**路径**: `RT-Thread Components` → `Device Drivers` → `Using Pin Control (pinctrl)` + +**说明**: 启用后可以使用设备树配置引脚功能和参数。 + +#### RT_PINCTRL_SCMI + +通过 SCMI (System Control and Management Interface) 协议进行引脚控制。 + +**路径**: `RT-Thread Components` → `Device Drivers` → `Using Pin Control (pinctrl)` → `Enable SCMI pinctrl driver` + +**说明**: 用于带有专用系统控制处理器的系统。 + +#### RT_PINCTRL_SINGLE + +Simple pinctrl driver 支持单寄存器引脚配置。 + +**路径**: `RT-Thread Components` → `Device Drivers` → `Using Pin Control (pinctrl)` → `Enable simple pinctrl driver` + +**说明**: 适用于简单的 SoC,其中每个引脚用单个寄存器配置。 + +## 设备树绑定 + +### Pinctrl 控制器节点 + +```dts +pinctrl: pinctrl@11002000 { + compatible = "vendor,soc-pinctrl"; + reg = <0x11002000 0x1000>; + #pinctrl-cells = <2>; /* 功能, 配置 */ + + /* 引脚组定义 */ + uart0_pins: uart0 { + pins = <10 11>; /* TX, RX 引脚 */ + function = <1>; /* UART 功能 */ + drive-strength = <8>; /* mA */ + bias-pull-up; + }; + + spi0_default: spi0_default { + pins = <20 21 22 23>; /* CLK, MOSI, MISO, CS */ + function = <2>; /* SPI 功能 */ + drive-strength = <12>; + }; + + spi0_sleep: spi0_sleep { + pins = <20 21 22 23>; + function = <0>; /* GPIO 功能 */ + bias-pull-down; + }; +}; +``` + +### 消费者设备 + +```dts +uart0: serial@11003000 { + compatible = "vendor,uart"; + reg = <0x11003000 0x1000>; + + /* 引用引脚配置 */ + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; +}; + +spi0: spi@11004000 { + compatible = "vendor,spi"; + reg = <0x11004000 0x1000>; + + /* 多状态引脚配置 */ + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&spi0_default>; + pinctrl-1 = <&spi0_sleep>; +}; + +/* 具有 GPIO 引用的设备 */ +device@11005000 { + compatible = "vendor,device"; + reg = <0x11005000 0x1000>; + + reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>; + enable-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>; +}; +``` + +### 引脚配置属性 + +常用的引脚配置属性: + +- `bias-disable`: 禁用上拉/下拉 +- `bias-pull-up`: 启用上拉 +- `bias-pull-down`: 启用下拉 +- `drive-strength`: 驱动强度(mA) +- `input-enable`: 启用输入缓冲器 +- `output-high`: 设置输出为高电平 +- `output-low`: 设置输出为低电平 + +## 应用层 API + +### 引脚配置 API + +#### rt_pin_ctrl_confs_apply + +按索引应用引脚配置。 + +```c +rt_err_t rt_pin_ctrl_confs_apply(struct rt_device *dev, int index); +``` + +**参数**: +- `dev`: 设备指针 +- `index`: 设备树中的配置索引(0 表示 pinctrl-0) + +**返回值**: +- `RT_EOK`: 成功 +- `-RT_EINVAL`: 无效参数 +- `-RT_EIO`: I/O 错误 + +**示例**: +```c +/* 应用默认配置(pinctrl-0) */ +rt_pin_ctrl_confs_apply(spi_dev, 0); + +/* 应用休眠配置(pinctrl-1) */ +rt_pin_ctrl_confs_apply(spi_dev, 1); +``` + +#### rt_pin_ctrl_confs_apply_by_name + +按名称应用引脚配置。 + +```c +rt_err_t rt_pin_ctrl_confs_apply_by_name(struct rt_device *dev, const char *name); +``` + +**参数**: +- `dev`: 设备指针 +- `name`: pinctrl-names 中的配置名称 + +**返回值**: +- `RT_EOK`: 成功 +- `-RT_EINVAL`: 无效参数或未找到名称 +- `-RT_EIO`: I/O 错误 + +**示例**: +```c +/* 在设备初始化时应用默认引脚 */ +rt_pin_ctrl_confs_apply_by_name(dev, "default"); + +/* 在挂起前切换到低功耗引脚 */ +rt_pin_ctrl_confs_apply_by_name(dev, "sleep"); +``` + +#### rt_pin_ctrl_confs_lookup + +在设备树中查找引脚配置索引。 + +```c +int rt_pin_ctrl_confs_lookup(struct rt_device *dev, const char *name); +``` + +**参数**: +- `dev`: 设备指针 +- `name`: pinctrl-names 中的配置名称 + +**返回值**: +- `>= 0`: 配置索引 +- `< 0`: 未找到错误 + +**示例**: +```c +int idx = rt_pin_ctrl_confs_lookup(dev, "idle"); +if (idx >= 0) { + rt_pin_ctrl_confs_apply(dev, idx); +} +``` + +### GPIO 辅助 API + +#### rt_pin_get_named_pin + +从设备树获取 GPIO 引脚编号。 + +```c +rt_ssize_t rt_pin_get_named_pin(struct rt_device *dev, + const char *propname, + int index, + rt_uint8_t *out_mode, + rt_uint8_t *out_value); +``` + +**参数**: +- `dev`: 设备指针 +- `propname`: GPIO 属性名称(如 "reset-gpios") +- `index`: GPIO 数组中的索引(对于单个 GPIO 为 0) +- `out_mode`: 输出 GPIO 模式(可以为 NULL) +- `out_value`: 输出 GPIO 值(可以为 NULL) + +**返回值**: +- `>= 0`: GPIO 引脚编号 +- `< 0`: 错误 + +**示例**: +```c +rt_uint8_t mode, value; +rt_ssize_t pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, &mode, &value); +if (pin >= 0) { + /* 配置和使用 GPIO 引脚 */ + rt_pin_mode(pin, mode); + rt_pin_write(pin, value); +} +``` + +#### rt_pin_get_named_pin_count + +计算 GPIO 属性中的引脚数量。 + +```c +int rt_pin_get_named_pin_count(struct rt_device *dev, const char *propname); +``` + +**参数**: +- `dev`: 设备指针 +- `propname`: GPIO 属性名称 + +**返回值**: +- `>= 0`: GPIO 引脚数量 +- `< 0`: 错误 + +**示例**: +```c +int count = rt_pin_get_named_pin_count(dev, "gpios"); +for (int i = 0; i < count; i++) { + rt_ssize_t pin = rt_pin_get_named_pin(dev, "gpios", i, NULL, NULL); + /* 使用引脚 */ +} +``` + +## 使用示例 + +### SPI 驱动的引脚管理示例 + +```c +#include +#include +#include + +struct my_spi { + struct rt_spi_bus parent; + void *base; + struct rt_device *dev; + + /* GPIO 引脚 */ + rt_ssize_t cs_pin; + rt_ssize_t reset_pin; +}; + +static rt_err_t my_spi_probe(struct rt_platform_device *pdev) +{ + struct my_spi *spi; + struct rt_device *dev = &pdev->parent; + rt_err_t ret; + + spi = rt_calloc(1, sizeof(*spi)); + if (!spi) + return -RT_ENOMEM; + + spi->dev = dev; + + /* 应用默认引脚配置 */ + ret = rt_pin_ctrl_confs_apply_by_name(dev, "default"); + if (ret) { + rt_kprintf("Failed to apply default pinctrl: %d\n", ret); + goto err_free; + } + + /* 获取 GPIO 引脚 */ + spi->cs_pin = rt_pin_get_named_pin(dev, "cs-gpios", 0, NULL, NULL); + if (spi->cs_pin < 0) { + rt_kprintf("Failed to get CS GPIO\n"); + ret = spi->cs_pin; + goto err_free; + } + + spi->reset_pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, NULL, NULL); + if (spi->reset_pin >= 0) { + /* 配置复位引脚 */ + rt_pin_mode(spi->reset_pin, PIN_MODE_OUTPUT); + rt_pin_write(spi->reset_pin, PIN_HIGH); + } + + /* 配置 CS 引脚 */ + rt_pin_mode(spi->cs_pin, PIN_MODE_OUTPUT); + rt_pin_write(spi->cs_pin, PIN_HIGH); + + /* 注册 SPI 总线 */ + ret = rt_spi_bus_register(&spi->parent, "spi0", &spi_ops); + if (ret) + goto err_free; + + rt_platform_set_drvdata(pdev, spi); + + rt_kprintf("SPI driver probed successfully\n"); + return RT_EOK; + +err_free: + rt_free(spi); + return ret; +} + +static rt_err_t my_spi_remove(struct rt_platform_device *pdev) +{ + struct my_spi *spi = rt_platform_get_drvdata(pdev); + + /* 释放 GPIO 引脚 */ + if (spi->reset_pin >= 0) { + rt_pin_write(spi->reset_pin, PIN_LOW); + } + + rt_spi_bus_unregister(&spi->parent); + rt_free(spi); + + return RT_EOK; +} + +/* 电源管理钩子 */ +static int my_spi_suspend(const struct rt_device *dev, rt_uint8_t mode) +{ + /* 切换到休眠引脚配置 */ + return rt_pin_ctrl_confs_apply_by_name((struct rt_device *)dev, "sleep"); +} + +static void my_spi_resume(const struct rt_device *dev, rt_uint8_t mode) +{ + /* 恢复默认引脚配置 */ + rt_pin_ctrl_confs_apply_by_name((struct rt_device *)dev, "default"); +} + +static const struct rt_device_ops spi_ops = { + .suspend = my_spi_suspend, + .resume = my_spi_resume, +}; + +static const struct rt_ofw_node_id my_spi_ids[] = { + { .compatible = "vendor,my-spi" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver my_spi_driver = { + .name = "my-spi", + .ids = my_spi_ids, + .probe = my_spi_probe, + .remove = my_spi_remove, +}; + +static int my_spi_drv_register(void) +{ + rt_platform_driver_register(&my_spi_driver); + return 0; +} +INIT_SUBSYS_EXPORT(my_spi_drv_register); +``` + +## 驱动实现指南 + +### Simple Pinctrl 驱动示例 + +```c +#include +#include + +struct simple_pinctrl { + struct rt_device parent; + void *base; + rt_size_t ngroups; +}; + +static rt_err_t simple_pinctrl_set_mux(struct rt_pinctrl *ctrl, + rt_uint32_t group, + rt_uint32_t function) +{ + struct simple_pinctrl *pinctrl = rt_container_of(ctrl, struct simple_pinctrl, parent); + void *reg = pinctrl->base + (group * 4); + + /* 设置功能复用 */ + rt_uint32_t val = readl(reg); + val &= ~0xFF; + val |= function & 0xFF; + writel(val, reg); + + return RT_EOK; +} + +static rt_err_t simple_pinctrl_set_config(struct rt_pinctrl *ctrl, + rt_uint32_t group, + rt_ubase_t config) +{ + struct simple_pinctrl *pinctrl = rt_container_of(ctrl, struct simple_pinctrl, parent); + void *reg = pinctrl->base + (group * 4); + rt_uint32_t param = RT_PINCTRL_CONFIG_PARAM(config); + rt_uint32_t arg = RT_PINCTRL_CONFIG_ARG(config); + rt_uint32_t val; + + val = readl(reg); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + val &= ~(BIT(8) | BIT(9)); /* 清除上拉/下拉 */ + break; + + case PIN_CONFIG_BIAS_PULL_UP: + val |= BIT(8); + val &= ~BIT(9); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + val |= BIT(9); + val &= ~BIT(8); + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + val &= ~(0xF << 16); + val |= (arg & 0xF) << 16; + break; + + default: + return -RT_EINVAL; + } + + writel(val, reg); + return RT_EOK; +} + +static const struct rt_pinctrl_ops simple_pinctrl_ops = { + .set_mux = simple_pinctrl_set_mux, + .set_config = simple_pinctrl_set_config, +}; + +static rt_err_t simple_pinctrl_probe(struct rt_platform_device *pdev) +{ + struct simple_pinctrl *pinctrl; + rt_err_t ret; + + pinctrl = rt_calloc(1, sizeof(*pinctrl)); + if (!pinctrl) + return -RT_ENOMEM; + + /* 映射寄存器 */ + pinctrl->base = rt_dm_dev_iomap(&pdev->parent, 0); + if (!pinctrl->base) { + ret = -RT_EIO; + goto err_free; + } + + /* 获取引脚组数量 */ + rt_dm_dev_prop_read_u32(&pdev->parent, "pinctrl-groups", &pinctrl->ngroups); + + /* 注册 pinctrl 设备 */ + pinctrl->parent.ops = &simple_pinctrl_ops; + ret = rt_pinctrl_register(&pinctrl->parent, &pdev->parent); + if (ret) + goto err_unmap; + + rt_platform_set_drvdata(pdev, pinctrl); + + return RT_EOK; + +err_unmap: + rt_dm_dev_iounmap(&pdev->parent, pinctrl->base); +err_free: + rt_free(pinctrl); + return ret; +} + +static const struct rt_ofw_node_id simple_pinctrl_ids[] = { + { .compatible = "simple-pinctrl" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver simple_pinctrl_driver = { + .name = "simple-pinctrl", + .ids = simple_pinctrl_ids, + .probe = simple_pinctrl_probe, +}; +``` + +## 最佳实践 + +### 消费者最佳实践 + +1. **始终应用默认配置** + ```c + rt_pin_ctrl_confs_apply_by_name(dev, "default"); + ``` + +2. **使用命名状态进行电源管理** + ```c + /* 挂起 */ + rt_pin_ctrl_confs_apply_by_name(dev, "sleep"); + + /* 恢复 */ + rt_pin_ctrl_confs_apply_by_name(dev, "default"); + ``` + +3. **检查 GPIO 可用性** + ```c + rt_ssize_t pin = rt_pin_get_named_pin(dev, "reset-gpios", 0, NULL, NULL); + if (pin >= 0) { + /* GPIO 可用 */ + } + ``` + +4. **使用 pinctrl 进行专用功能,使用 GPIO 进行 GPIO** + - Pinctrl:功能复用、电气配置 + - GPIO:运行时 GPIO 操作 + +### 提供者最佳实践 + +1. **支持所有相关的配置参数** + - 上拉/下拉 + - 驱动强度 + - 输入使能 + - 其他特定于硬件的参数 + +2. **验证引脚组索引** + ```c + if (group >= pinctrl->ngroups) + return -RT_EINVAL; + ``` + +3. **实现原子配置更新** + - 读取-修改-写入序列 + - 考虑寄存器锁定 + +## 故障排除 + +### 引脚配置失败 + +**症状**: rt_pin_ctrl_confs_apply 返回错误 + +**可能原因**: +1. 设备树中的 pinctrl 引用不正确 +2. Pinctrl 驱动未加载 +3. 引脚组索引超出范围 + +**解决方案**: +```c +/* 检查 pinctrl 引用 */ +int idx = rt_pin_ctrl_confs_lookup(dev, "default"); +if (idx < 0) { + rt_kprintf("Pinctrl 'default' not found in DT\n"); +} + +/* 验证 pinctrl 驱动 */ +if (!dev->pinctrl) { + rt_kprintf("No pinctrl controller available\n"); +} +``` + +### GPIO 与 Pinctrl 冲突 + +**症状**: GPIO 操作不起作用或引脚不响应 + +**可能原因**: +- 引脚复用为外设功能而不是 GPIO +- 冲突的引脚配置 + +**解决方案**: +```c +/* 对于 GPIO 使用,确保引脚复用为 GPIO 功能 */ +/* 在设备树中或通过 pinctrl 驱动程序 */ +``` + +### 电源管理状态切换 + +**症状**: 从休眠恢复后外设不工作 + +**可能原因**: +- 忘记恢复默认引脚状态 +- 引脚状态切换顺序不正确 + +**解决方案**: +```c +/* 在恢复时始终恢复默认状态 */ +static void my_device_resume(const struct rt_device *dev, rt_uint8_t mode) +{ + rt_pin_ctrl_confs_apply_by_name((struct rt_device *)dev, "default"); + + /* 然后重新初始化硬件 */ +} +``` + +## 相关模块 + +- **GPIO 框架**: 运行时 GPIO 操作 +- **OFW (设备树)**: 设备树解析 +- **电源管理**: 引脚状态用于低功耗模式 +- **SCMI**: 固件引脚控制 + +## 参考 + +- `components/drivers/include/drivers/pin.h` +- `components/drivers/core/pinctrl.c` +- Linux Pinctrl 子系统文档