设备树之GPIO

📅 2026/8/26 17:45:10
设备树之GPIO
1、示例全景图┌──────────────────────────────────────────────│ 物理芯片上有 16 个引脚PA0 ~ PA15 ││ Pin Controller 决定PA0 今天是 GPIO还是 UART还是 SPI ││ GPIO Controller 决定PA0 既然是 GPIO那现在是高电平还是低电平 │└───────────────────────────────────────────────2、Pin Controller 节点pinctrl: pinctrl50000000 { compatible vendor,stm32-pinctrl; reg 0x50000000 0x4000; /* 定义一组引脚配置PA0~PA3 配成 GPIO 模式 */ gpioa_pins: gpioa-pins { pins PA0, PA1, PA2, PA3; function gpio; /* ← 身份GPIO */ drive-strength 8; /* 驱动能力 */ }; /* 另一组PA4~PA5 配成 UART 模式 */ uart1_pins: uart1-pins { pins PA4, PA5; function uart1_tx_rx; /* ← 身份UART */ bias-pull-up; /* 内部上拉 */ }; };通俗说这是引脚户口本管理处。gpioa_pins给 PA0~PA3 发了GPIO 身份证uart1_pins给 PA4~PA5 发了UART 身份证。同一个引脚不能同时领两个身份。3、GPIO Controller 节点gpioa: gpio50010000 { compatible vendor,stm32-gpio; reg 0x50010000 0x400; gpio-controller; /* ← 我是 GPIO 控制器 */ #gpio-cells 2; /* 引用我需要 2 个 cell */ /* 关键告诉内核我的 0~15 号线 pinctrl 的 PA0~PA15 */ gpio-ranges pinctrl 0 0 16; ngpios 16; };通俗说这是灯的开关总控台。它有 16 个开关0~15 号线。但它不知道这些开关对应哪个物理引脚所以通过gpio-ranges去问 Pin Controller我的 0 号线对应你的 PA01 号线对应 PA1……| 数字 | 值 | 含义 | | ---------- | ------- | ------------------------------- | | pinctrl | 引用 | 找这个 Pin Controller 要引脚 | | 0 | GPIO 偏移 | 从 GPIO 控制器的第 0 号线开始 | | 0 | Pin 偏移 | 对应 Pin Controller 的第 0 号引脚PA0 | | 16 | 数量 | 连续映射 16 条线0~15 → PA0~PA15 |Pin 偏移是芯片封装上物理引脚的全局编号PA00, PA11, PB016...‌Pin 偏移Index‌在pinctrl中使用直接对应硬件寄存器的位位置0-15。‌GPIO 偏移Base/Number‌在gpio子系统使用可能涉及全局编号计算例如 GPIOA 的基号是 0GPIOB 的基号是 16那么 PB5 的全局 GPIO 号是 16521。/dts-v1/; #include stm32mp157.dtsi #include stm32mp15xxac-pinctrl.dtsi / { model STM32MP157 Demo Board; compatible st,stm32mp157-demo, st,stm32mp157; /* * 【GPIO 控制器提供者 - Provider】 * 驱动代码中通过 devm_gpiochip_add_data() 向内核注册 * 消费者通过 my_gpio_expander N flags 引用其中的 GPIO * */ my_gpio_expander: gpio-expander20 { compatible vendor,my-gpio-exp; reg 0x20; /* I2C 地址 0x20 */ status okay; /* 声明本节点是一个 GPIO 控制器 */ gpio-controller; #gpio-cells 2; /* 消费者引用格式: ctrl pin flags */ ngpios 8; /* 该芯片提供 8 个 GPIO */ }; /* * 【消费者设备 - Consumer】 * pinctrl: 驱动中用 devm_pinctrl_get_select_default() 获取并应用 * GPIO: 驱动中用 devm_gpiod_get(dev, reset, ...) 获取 * */ my_device { compatible vendor,mydev; status okay; /* pinctrl 状态定义 */ pinctrl-names default, sleep; pinctrl-0 mydev_pins_default; /* 默认状态 */ pinctrl-1 mydev_pins_sleep; /* 低功耗状态 */ /* GPIO 消费者属性 */ reset-gpios gpioc 6 GPIO_ACTIVE_LOW; /* devm_gpiod_get(dev, reset, ...) */ done-gpios gpioa 13 GPIO_ACTIVE_HIGH; /* devm_gpiod_get(dev, done, ...) */ }; /* * 【另一个消费者LED】 * 引用外部 GPIO 控制器my_gpio_expander的第 2 号引脚 * 驱动中通过 devm_gpiod_get() 或 gpio-leds 子系统获取 * */ leds { compatible gpio-leds; status_led { /*gpios没有前缀用 devm_gpiod_get(dev, NULL, flags) 获取。*/ /*内核已经有 drivers/leds/leds-gpio.c开机自动会*/ /*echo 1 /sys/class/leds/exp-led2/brightness*/ /*echo 0 /sys/class/leds/exp-led2/brightness*/ gpios my_gpio_expander 2 GPIO_ACTIVE_HIGH; label exp-led2; default-state on; }; }; }; /* * 【pinctrl 引脚配置】 * 驱动中通过 devm_pinctrl_get_select_default() 自动应用 default 状态 * 通过 pinctrl_lookup_state() pinctrl_select_state() 手动切换 sleep 状态 * */ pinctrl { mydev_pins_default: mydev-pins-default { pins1 { pinmux STM32_PINMUX(C, 6, GPIO); /* PC6 reset */ drive-push-pull; bias-disable; slew-rate 0; }; pins2 { pinmux STM32_PINMUX(A, 13, GPIO); /* PA13 done */ drive-push-pull; bias-disable; }; }; mydev_pins_sleep: mydev-pins-sleep { pins { pinmux STM32_PINMUX(C, 6, ANALOG), STM32_PINMUX(A, 13, ANALOG); }; }; };#include linux/module.h #include linux/platform_device.h #include linux/pinctrl/consumer.h #include linux/gpio/consumer.h struct mydev_priv { struct pinctrl *pinctrl; struct pinctrl_state *sleep_state; struct gpio_desc *reset; struct gpio_desc *done; }; static int mydev_probe(struct platform_device *pdev) { struct device *dev pdev-dev; struct mydev_priv *priv; priv devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* 核心 API 1获取并应用 pinctrl default 状态 */ priv-pinctrl devm_pinctrl_get_select_default(dev); if (IS_ERR(priv-pinctrl)) return PTR_ERR(priv-pinctrl); /* 可选查找 sleep 状态供后续电源管理切换 */ priv-sleep_state pinctrl_lookup_state(priv-pinctrl, sleep); /* 核心 API 2获取 GPIO 消费者 */ priv-reset devm_gpiod_get(dev, reset, GPIOD_OUT_HIGH); if (IS_ERR(priv-reset)) return PTR_ERR(priv-reset); priv-done devm_gpiod_get(dev, done, GPIOD_IN); if (IS_ERR(priv-done)) return PTR_ERR(priv-done); /* 使用 GPIO */ gpiod_set_value(priv-reset, 0); /* 拉低复位 */ msleep(10); gpiod_set_value(priv-reset, 1); /* 释放复位 */ platform_set_drvdata(pdev, priv); dev_info(dev, mydev probed ok\n); return 0; } /* 手动切换 pinctrl 状态进入/退出睡眠 */ static int mydev_suspend(struct device *dev) { struct mydev_priv *priv dev_get_drvdata(dev); if (!IS_ERR_OR_NULL(priv-sleep_state)) pinctrl_select_state(priv-pinctrl, priv-sleep_state); return 0; } static int mydev_resume(struct device *dev) { struct mydev_priv *priv dev_get_drvdata(dev); struct pinctrl_state *def; def pinctrl_lookup_state(priv-pinctrl, default); if (!IS_ERR_OR_NULL(def)) pinctrl_select_state(priv-pinctrl, def); return 0; } static const struct dev_pm_ops mydev_pm_ops { .suspend mydev_suspend, .resume mydev_resume, }; static const struct of_device_id mydev_of_match[] { { .compatible vendor,mydev }, {} }; MODULE_DEVICE_TABLE(of, mydev_of_match); static struct platform_driver mydev_driver { .probe mydev_probe, .driver { .name mydev, .of_match_table mydev_of_match, .pm mydev_pm_ops, }, }; module_platform_driver(mydev_driver); MODULE_LICENSE(GPL); MODULE_DESCRIPTION(Demo Consumer Device);my_device { /* 区域 1pinctrl 状态一级节点 */ pinctrl-names default; pinctrl-0 mydev_pins_default; /* 区域 2GPIO 消费者属性和 gpiod API 对应 */ reset-gpios gpioc 6 GPIO_ACTIVE_LOW; // ← devm_gpiod_get(dev, reset, ...) led-gpios gpioa 13 GPIO_ACTIVE_HIGH; // ← devm_gpiod_get(dev, led, ...) }; pinctrl { /* 区域 3pinctrl 配置二级节点 */ mydev_pins_default: mydev-pins-default { // ← 一级状态节点 pins { // ← 二级配置节点 pinmux STM32_PINMUX(C, 6, GPIO); bias-disable; }; }; };总结你在设备树看到属于哪个世界驱动里怎么操作pinctrl-0 xxx世界 Adevm_pinctrl_get_select_default(dev)pinctrl-names sleep世界 Apinctrl_lookup_state()pinctrl_select_state()pins { pinmux ... }世界 C无 API内核自动解析reset-gpios ...世界 Bdevm_gpiod_get(dev, reset, ...)led-gpios ...世界 Bgpiod_set_value(led, 1/0)类型设备树写法内核消费者你需要写驱动吗LEDcompatible gpio-ledsdrivers/leds/leds-gpio.c❌ 不用写按键compatible gpio-keysdrivers/input/keyboard/gpio_keys.c❌ 不用写固定电压compatible regulator-fixeddrivers/regulator/fixed.c❌ 不用写简单 PWMcompatible pwm-ledsdrivers/leds/leds-pwm.c❌ 不用写看门狗 GPIOcompatible gpio-wdtdrivers/watchdog/gpio_wdt.c❌ 不用写厂商自定义compatible vendor,mydev你自己写的 mydev.c✅ 必须写