1. 设备树节点
1.1 PinCtrl
作用:模拟硬件引脚控制器,管理引脚复用和配置
virtual_pincontroller {
compatible = "100ask,virtual_pinctrl"; // 匹配虚拟pinctrl驱动
myled_pin: myled_pin { // 引脚配置节点,标签为myled_pin
functions = "gpio"; // 引脚功能:GPIO模式
groups = "pin0"; // 引脚组:pin0
configs = <0x11223344>; // 电气特性配置值
};
};
对应驱动 :需要实现 100ask,virtual_pinctrl
兼容的pinctrl驱动
1.2 GPIO控制器
gpio_virt: virtual_gpiocontroller {
compatible = "100ask,virtual_gpio"; // 匹配虚拟GPIO驱动
gpio-controller; // 声明这是GPIO控制器
#gpio-cells = <2>; // GPIO说明符有2个cell
ngpios = <4>; // 提供4个GPIO
};
作用:提供GPIO操作接口
关键属性:
-
#gpio-cells = <2>
:引用此GPIO时需要2个参数-
参数1:GPIO编号(0-3)
-
参数2:GPIO标志(如GPIO_ACTIVE_LOW)
-
对应驱动 :需要实现 100ask,virtual_gpio
兼容的GPIO驱动
1.3 LED设备
myled {
compatible = "100ask,leddrv"; // 匹配LED驱动
led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>; // 引用GPIO控制器
pinctrl-names = "default"; // 引脚状态名称
pinctrl-0 = <&myled_pin>; // 引用引脚配置
};
作用:具体的LED设备,使用前面定义的资源
关键引用:
-
&gpio_virt
:引用GPIO控制器标签 -
&myled_pin
:引用引脚配置标签
2. 节点关系图
virtual_pincontroller gpio_virt
↓ ↓
myled_pin GPIO控制器
↓ ↓
pinctrl-0 ────────┐ ↓
↓ ↓
myled (LED设备) ──┘
↓
led-gpios引用
4. 交互流程
4.1 系统启动和驱动加载顺序
1. 虚拟pinctrl驱动加载 → 注册pinctrl设备
2. 虚拟GPIO驱动加载 → 注册GPIO控制器
3. LED驱动加载 → 设备匹配和初始化
4.2 LED驱动probe函数执行流程
static int led_probe(struct platform_device *pdev)
{
// 1. 内核自动应用pinctrl配置
// 在probe调用前,内核会自动调用pinctrl_select_state()
// 将myled_pin配置应用到硬件
// 2. 获取GPIO描述符
struct gpio_desc *led_gpio;
led_gpio = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
// 这会解析: led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>
// 3. 配置GPIO方向(如果需要)
gpiod_direction_output(led_gpio, 0);
// 4. 使用GPIO控制LED
gpiod_set_value(led_gpio, 1); // 根据GPIO_ACTIVE_LOW,实际输出低电平
}
4.3 具体的引用解析过程
设备树引用解析:
led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>;
解析为:
-
&gpio_virt
→ 找到gpio_virt: virtual_gpiocontroller
节点 -
0
→ GPIO编号0 -
GPIO_ACTIVE_LOW
→ 低电平有效标志
5. 对应的驱动实现框架
5.1 pinctrl驱动
static const struct of_device_id virtual_pinctrl_of_match[] = {
{ .compatible = "100ask,virtual_pinctrl" },
{}
};
static struct platform_driver virtual_pinctrl_driver = {
.driver = {
.name = "virtual_pinctrl",
.of_match_table = virtual_pinctrl_of_match,
},
.probe = virtual_pinctrl_probe,
};
5.2 虚拟GPIO驱动
static const struct of_device_id virtual_gpio_of_match[] = {
{ .compatible = "100ask,virtual_gpio" },
{}
};
static struct platform_driver virtual_gpio_driver = {
.driver = {
.name = "virtual_gpio",
.of_match_table = virtual_gpio_of_match,
},
.probe = virtual_gpio_probe,
};
5.3 LED驱动
static const struct of_device_id led_drv_of_match[] = {
{ .compatible = "100ask,leddrv" },
{}
};
static struct platform_driver led_driver = {
.driver = {
.name = "led_drv",
.of_match_table = led_drv_of_match,
},
.probe = led_probe,
};
6. 在系统中的体现
6.1 加载后的sysfs结构
/sys/class/gpio/
└── gpiochipXXX/ # 虚拟GPIO控制器
├── base → 480
├── label → "virtual_gpiocontroller"
└── ngpio → 4
/sys/class/leds/
└── myled/ # LED设备
/sys/kernel/debug/pinctrl/
└── virtual_pincontroller/ # 虚拟引脚控制器
6.2 用户空间使用
# 导出GPIO
echo 480 > /sys/class/gpio/export # GPIO 480 (虚拟GPIO0)
echo out > /sys/class/gpio/gpio480/direction
echo 1 > /sys/class/gpio/gpio480/value # 控制LED
7. 完整的工作流程
步骤1:设备树解析
-
内核解析设备树,建立设备节点关系
-
识别
virtual_pincontroller
、gpio_virt
、myled
节点
步骤2:驱动匹配和加载
// pinctrl驱动匹配virtual_pincontroller
// GPIO驱动匹配gpio_virt
// LED驱动匹配myled
步骤3:LED设备初始化
// 1. 内核自动调用pinctrl,应用myled_pin配置
// 2. LED驱动probe函数执行
// 3. 获取GPIO描述符,控制LED
步骤4:正常运行
-
用户空间或内核其他部分可以通过GPIO接口控制LED
-
所有硬件抽象层正常工作
gpio-ranges
属性
基本语法:
gpio-ranges = <&pinctrl_phandle gpio_base pin_base count>;
参数解释:
-
&pinctrl_phandle
:指向Pinctrl控制器的句柄 -
gpio_base
:GPIO控制器中的起始GPIO编号 -
pin_base
:Pinctrl控制器中的起始引脚编号 -
count
:映射的引脚数量
完整的设备树
传统方式(需要显式pinctrl配置)
// Pinctrl控制器
pinctrlA: pinctrl@40020000 {
compatible = "vendor,pinctrl";
reg = <0x40020000 0x1000>;
// 引脚配置
uart1_pins: uart1-pins {
pins = "PIOA0", "PIOA1";
function = "uart1";
};
gpio_pins: gpio-pins {
pins = "PIOA2", "PIOA3";
function = "gpio";
};
};
// GPIO控制器
gpioA: gpio@40021000 {
compatible = "vendor,gpio";
reg = <0x40021000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <16>;
// 没有gpio-ranges,需要显式配置pinctrl
};
// 使用GPIO的设备
led {
compatible = "vendor,led";
led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&gpio_pins>; // 必须显式配置
};
使用gpio-ranges的方式
// Pinctrl控制器
pinctrlA: pinctrl@40020000 {
compatible = "vendor,pinctrl";
reg = <0x40020000 0x1000>;
// 注意:这里不需要定义gpio-pins了
};
// GPIO控制器
gpioA: gpio@40021000 {
compatible = "vendor,gpio";
reg = <0x40021000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
ngpios = <16>;
// 建立GPIO和Pinctrl的映射关系
gpio-ranges = <&pinctrlA 0 128 12>; // GPIO0-11 → Pinctrl引脚128-139
};
// 使用GPIO的设备
led {
compatible = "vendor,led";
led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;
// 不需要pinctrl-0,GPIO子系统自动处理
};
映射关系的内部实现
内核中的映射处理
当GPIO控制器注册时:
int gpiochip_add(struct gpio_chip *chip)
{
// 解析gpio-ranges属性
ret = gpiochip_add_pin_range(chip, dev_name(&pdev->dev),
0, 0, chip->ngpio);
// 建立映射关系
pinctrl_add_gpio_range(chip->pinctrl, &range);
}
GPIO请求时的自动处理
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
{
// 1. 获取GPIO描述符
desc = of_get_named_gpiod_flags(dev->of_node, con_id, 0, &flags);
// 2. 自动通过pinctrl配置引脚为GPIO功能
ret = pinctrl_request_gpio(desc);
// 3. 配置GPIO方向
gpiod_direction_output(desc, flags & GPIOD_OUT_HIGH ? 1 : 0);
return desc;
}
复杂的映射场景
多个不连续的映射范围
gpioA: gpio@40021000 {
compatible = "vendor,gpio";
gpio-controller;
#gpio-cells = <2>;
ngpios = <32>;
// 多个映射范围
gpio-ranges =
<&pinctrlA 0 128 8>, // GPIO0-7 → 引脚128-135
<&pinctrlA 8 200 4>, // GPIO8-11 → 引脚200-203
<&pinctrlA 12 240 16>; // GPIO12-27 → 引脚240-255
};
多个Pinctrl控制器的映射
gpioA: gpio@40021000 {
compatible = "vendor,gpio";
gpio-controller;
#gpio-cells = <2>;
ngpios = <32>;
// 映射到不同的Pinctrl控制器
gpio-ranges =
<&pinctrlA 0 128 16>, // GPIO0-15 → PinctrlA的128-143
<&pinctrlB 16 64 16>; // GPIO16-31 → PinctrlB的64-79
};
驱动中的实际效果
使用gpio-ranges前的代码
static int led_probe(struct platform_device *pdev)
{
struct gpio_desc *led;
// 需要确保设备树中有pinctrl-0配置
led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
if (IS_ERR(led)) {
// 可能因为pinctrl配置失败
return PTR_ERR(led);
}
return 0;
}
使用gpio-ranges后的代码
static int led_probe(struct platform_device *pdev)
{
struct gpio_desc *led;
// 直接获取GPIO,不需要关心pinctrl配置
led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
// GPIO子系统自动处理引脚复用
return 0;
}
验证映射关系
在sysfs中查看映射
# 查看GPIO范围
cat /sys/kernel/debug/gpio
# 查看pinctrl映射
cat /sys/kernel/debug/pinctrl/pinctrl-handles
# 查看具体的引脚状态
cat /sys/kernel/debug/pinctrl/<pinctrl>/pinmux-pins
在驱动中调试
// 在GPIO驱动probe函数中添加调试信息
static int gpio_probe(struct platform_device *pdev)
{
struct gpio_chip *chip;
// 解析gpio-ranges
ret = gpiochip_add_pin_range(chip, dev_name(&pdev->dev), 0, 0, chip->ngpio);
if (ret) {
dev_info(&pdev->dev, "Added pin range: GPIO%d-%d -> Pin%d-%d\n",
range.base, range.base + range.npins - 1,
range.pin_base, range.pin_base + range.npins - 1);
}
}
1. 功能定位对比
第一段代码:显式引脚配置
uart1_pins: uart1-pins {
pins = "PIOA0", "PIOA1";
function = "uart1";
};
gpio_pins: gpio-pins {
pins = "PIOA2", "PIOA3";
function = "gpio";
};
作用 :为特定功能显式定义引脚配置
第二段代码:GPIO-Pinctrl映射
gpio-ranges = <&pinctrlA 0 128 12>;
2. 使用方式的根本区别
方式A:功能驱动配置(第一段代码)
// 设备树
my_uart_device {
compatible = "vendor,uart";
pinctrl-names = "default";
pinctrl-0 = <&uart1_pins>; // 显式选择UART功能
};
my_led_device {
compatible = "vendor,led";
pinctrl-names = "default";
pinctrl-0 = <&gpio_pins>; // 显式选择GPIO功能
led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>;
};
方式B:自动GPIO配置(第二段代码)
// 设备树
my_led_device {
compatible = "vendor,led";
// 不需要pinctrl-0配置!
led-gpios = <&gpioA 2 GPIO_ACTIVE_HIGH>; // 自动配置为GPIO
};
3. 底层机制对比
方式A的底层流程:
设备probe
↓
pinctrl-0应用 → 配置为指定功能
↓
GPIO操作(如果配置的是GPIO功能)
方式B的底层流程:
设备probe
↓
GPIO获取请求
↓
通过gpio-ranges自动映射 → 自动配置为GPIO功能
↓
GPIO操作
4. 具体关联分析
4.1 它们是互斥的设计选择
在实际项目中,你通常选择其中一种方式:
选择方式A(显式配置)时:
// 需要定义详细的功能配置
pinctrlA: pinctrl {
uart1_pins: uart1-pins { ... };
gpio_pins: gpio-pins { ... };
i2c1_pins: i2c1-pins { ... };
};
gpioA: gpio {
// 不需要gpio-ranges
// 或者有gpio-ranges但不依赖它
};
选择方式B(自动映射)时:
// Pinctrl中可能不定义GPIO功能配置
pinctrlA: pinctrl {
uart1_pins: uart1-pins { ... }; // 只有非GPIO功能
i2c1_pins: i2c1-pins { ... }; // 只有非GPIO功能
// 没有gpio-pins!
};
gpioA: gpio {
gpio-ranges = <&pinctrlA 0 128 12>; // 关键映射
};
4.2 混合使用的情况
有些系统可能同时使用两种方式:
pinctrlA: pinctrl {
// 为复杂功能定义显式配置
uart1_pins: uart1-pins {
pins = "PIOA0", "PIOA1";
function = "uart1";
bias-pull-up; // 需要特殊电气特性
};
// 为GPIO定义特殊配置
gpio_strong_pins: gpio-strong {
pins = "PIOA2", "PIOA3";
function = "gpio";
drive-strength = <20>; // 强驱动强度
};
};
gpioA: gpio {
gpio-ranges = <&pinctrlA 0 128 12>; // 通用映射
// 特殊GPIO可以引用显式配置
special-gpio {
gpio-ranges = <&pinctrlA 4 132 2>; // 特定范围的映射
};
};
5. 实际芯片中的差异
芯片类型1:需要显式配置(如某些老款芯片)
// 必须为每个GPIO使用显式pinctrl
pinctrl {
led1_pins: led1-pins {
pins = "GPIO0_5";
function = "gpio";
};
led2_pins: led2-pins {
pins = "GPIO0_6";
function = "gpio";
};
};
leds {
led1 {
pinctrl-0 = <&led1_pins>;
gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
};
led2 {
pinctrl-0 = <&led2_pins>;
gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
};
};
芯片类型2:支持自动映射(如STM32)
// 通过gpio-ranges自动处理
pinctrl {
// 可能只有非GPIO功能的配置
uart1_pins: uart1-pins {
pins = "PA9", "PA10";
function = "uart1";
};
};
gpioA: gpio@50000000 {
gpio-ranges = <&pinctrl 0 0 16>; // GPIOA0-15 -> 引脚0-15
};
leds {
led1 {
// 不需要pinctrl-0!
gpios = <&gpioA 5 GPIO_ACTIVE_HIGH>; // 自动配置为GPIO
};
};
7. 在驱动代码中的体现
使用显式配置的驱动:
static int device_probe(struct platform_device *pdev)
{
// 内核会在probe前自动应用pinctrl-0
// 驱动假设引脚已正确配置
struct gpio_desc *gpio;
gpio = gpiod_get(&pdev->dev, "signal", GPIOD_OUT_LOW);
// 这里只是使用已配置好的GPIO
}
使用自动映射的驱动:
static int device_probe(struct platform_device *pdev)
{
// 驱动不关心引脚如何配置
struct gpio_desc *gpio;
gpio = gpiod_get(&pdev->dev, "signal", GPIOD_OUT_LOW);
// GPIO获取过程中自动配置引脚功能
}