linux gpio errno EBUSY问题举例分析
1,异常场景
在适配点灯功能的时候,测试发现🈶下面的报错dmesg 查看
# 加载第一个驱动
[ 12.345678] gpio-occupier gpio-occupier: Successfully requested and occupied GPIO 503# 加载第二个驱动,失败
[ 12.567890] gpio-requester gpio-requester: CRITICAL: Failed to request GPIO 503, err -17 (EBUSY)
[ 12.567895] platform gpio-requester: Driver gpio-requester probe failed with error -17
2,代码分析
驱动 A: gpio_occupier.c (先占用 GPIO)
这个驱动在加载时会申请并占用 GPIO 503。
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>#define OCCUPIER_GPIO 503static int gpio_occupier_probe(struct platform_device *pdev)
{int ret;// 申请 GPIO 503,并设置为输出高电平,标签为 "gpio-occupier"ret = devm_gpio_request_one(&pdev->dev, OCCUPIER_GPIO, GPIOF_OUT_INIT_HIGH, "gpio-occupier");if (ret < 0) {dev_err(&pdev->dev, "Failed to request GPIO %d\n", OCCUPIER_GPIO);return ret;}dev_info(&pdev->dev, "Successfully requested and occupied GPIO %d\n", OCCUPIER_GPIO);return 0;
}static const struct of_device_id occupier_of_match[] = {{ .compatible = "my-vendor,gpio-occupier", {} },{}
};
MODULE_DEVICE_TABLE(of, occupier_of_match);static struct platform_driver gpio_occupier_driver = {.probe = gpio_occupier_probe,.driver = {.name = "gpio-occupier",.of_match_table = occupier_of_match,},
};
module_platform_driver(gpio_occupier_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("AI Assistant");
MODULE_DESCRIPTION("A driver to occupy a GPIO for demonstration");
驱动 B: gpio_requester.c (后申请,会失败)
这个驱动尝试申请同一个 GPIO 503,将会失败。
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>#define REQUESTER_GPIO 503 // 与驱动 A 的 GPIO 相同static int gpio_requester_probe(struct platform_device *pdev)
{int ret;// 尝试申请已经被占用的 GPIO 503,标签为 "gpio-requester"ret = devm_gpio_request_one(&pdev->dev, REQUESTER_GPIO, GPIOF_OUT_INIT_LOW, "gpio-requester");if (ret < 0) {// 这里会打印错误信息,ret 的值将是 -17dev_err(&pdev->dev, "CRITICAL: Failed to request GPIO %d, err %d (EBUSY)\n", REQUESTER_GPIO, ret);return ret;}dev_info(&pdev->dev, "Successfully requested GPIO %d\n", REQUESTER_GPIO);return 0;
}static const struct of_device_id requester_of_match[] = {{ .compatible = "my-vendor,gpio-requester", {} },{}
};
MODULE_DEVICE_TABLE(of, requester_of_match);static struct platform_driver gpio_requester_driver = {.probe = gpio_requester_probe,.driver = {.name = "gpio-requester",.of_match_table = requester_of_match,},
};
module_platform_driver(gpio_requester_driver);MODULE_LICENSE("GPL");
MODULE_AUTHOR("AI Assistant");
MODULE_DESCRIPTION("A driver to demonstrate a failed GPIO request");
3. 异常日志
日志分析:
err -17就是EBUSY的错误码。- 日志明确指出了是哪个驱动 (
gpio-requester) 在申请哪个 GPIO (503) 时失败了。 Driver ... probe failed with error -17表明因为 probe 函数返回了错误,整个驱动的加载都失败了。
查看 sys/kernel/debug/gpio 的分析过程
这是定位 EBUSY 问题的最关键、最直接的步骤。
第 1 步:确保 debugfs 已挂载
# 检查是否已挂载
mount | grep debugfs# 如果没有输出,则手动挂载
sudo mount -t debugfs none /sys/kernel/debug
第 2 步:查看 GPIO 全局状态
cat /sys/kernel/debug/gpio
第 3 步:分析输出
gpiochip0: GPIOs 0-511, parent: platform/10000000.pinctrl, 10000000.pinctrl:gpio-493 ( |cd ) in hi IRQ...gpio-503 ( |gpio-occupier ) out hi <-- 关键信息在这里!...gpiochip1: GPIOs 512-1023, parent: platform/20000000.pinctrl, 20000000.pinctrl:...
分析过程:这个标签 gpio-occupier 和我们第一个驱动的标签完全一致!这直接证明了 GPIO 503 确实被 gpio-occupier 驱动占用了。
4,如何解决gpio复用的问题
1.1 常见原因
- 引脚未配置为GPIO功能:pinctrl子系统未将引脚复用功能设为GPIO,仍为I2C/SPI/UART等。
- 引脚被其他外设占用:设备树中多个节点引用同一引脚,或驱动加载顺序导致冲突。
- 驱动未正确引用pinctrl状态:设备节点未正确关联pinctrl配置。
1.2 调试命令
cat /sys/kernel/debug/gpio
如果目标GPIO未显示或标签异常,说明未被正确配置为GPIO。
1.3 设备树配置(关键步骤)
1.3.1 定义pinctrl节点
在设备树中,为GPIO功能创建专用pinctrl配置节点,确保 function 属性设为 gpio(或平台等效宏):
&pio {gpio_func: gpio_func {pins_cmd_dat {pins = <PINMUX_GPIO84__FUNC_GPIO84>; // 示例:GPIO84bias-pull-up; // 上拉(可选)output-high; // 默认输出高(可选)};};
};
说明:
PINMUX_GPIO84__FUNC_GPIO84为平台提供的宏,表示将引脚配置为GPIO功能89。- 不同SoC的宏名不同,需查阅芯片手册或内核头文件(如
imx6ul-pinfunc.h)3。
1.3.2 关联设备节点
在目标设备节点中引用pinctrl配置:
my_device {compatible = "my-vendor,my-device";pinctrl-names = "default";pinctrl-0 = <&gpio_func>; // 引用GPIO功能配置gpios = <&gpio1 84 GPIO_ACTIVE_HIGH>; // GPIO属性status = "okay";
};
注意:
pinctrl-0必须指向GPIO功能的pinctrl节点312。- 确保无其他节点(如I2C/SPI节点)复用同一引脚。
1.4. 驱动代码调整
1.4.1 自动处理(推荐)
如果设备树正确配置,pinctrl子系统会在驱动probe前自动应用GPIO配置,无需额外代码69。
1.4.2 手动切换(必要时)
若需动态切换,可在驱动中显式获取并设置pinctrl状态:
#include <linux/pinctrl/consumer.h>struct pinctrl *pinctrl;
struct pinctrl_state *gpio_state;// 获取pinctrl句柄
pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pinctrl)) {dev_err(&pdev->dev, "Failed to get pinctrl\n");return PTR_ERR(pinctrl);
}// 查找GPIO状态
gpio_state = pinctrl_lookup_state(pinctrl, "default");
if (IS_ERR(gpio_state)) {dev_err(&pdev->dev, "Failed to lookup GPIO state\n");return PTR_ERR(gpio_state);
}// 切换到GPIO状态
ret = pinctrl_select_state(pinctrl, gpio_state);
if (ret < 0) {dev_err(&pdev->dev, "Failed to select GPIO state\n");return ret;
}// 现在可以安全申请GPIO
ret = devm_gpio_request_one(&pdev->dev, gpio_num, GPIOF_OUT_INIT_LOW, "my-gpio");
说明:
- 适用于需在运行时切换引脚功能的场景(如休眠/唤醒)914。
- 多数情况下,设备树配置已足够,无需手动干预。
1.5. 验证与调试
1.5.1 检查pinctrl状态
# 查看当前pinctrl配置(需内核支持)
cat /sys/kernel/debug/pinctrl/*/pins
确认目标引脚的 function 为 gpio。
1.5.2 检查GPIO占用
cat /sys/kernel/debug/gpio | grep "gpio-84"
应显示类似:
gpio-84 ( |my-gpio ) out lo
1.5.3 动态调试
启用gpiolib调试日志:
echo 'file gpiolib.c +p' > /sys/kernel/debug/dynamic_debug/control
dmesg | grep -i "gpio_request"
可查看详细的申请失败原因。
1.6. 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
EBUSY 且debugfs无显示 | 引脚未配置为GPIO | 检查pinctrl节点的 function 属性是否为 gpio89。 |
EBUSY 且显示其他标签 | 引脚被其他外设占用 | 修改设备树,移除冲突节点的引脚引用。 |
| 驱动加载顺序问题 | GPIO驱动先于pinctrl加载 | 调整设备树节点顺序或确保pinctrl驱动优先加载。 |
1.7. 完整示例
1.7.1 设备树配置
&pio {my_gpio: my_gpio {pins = <PINMUX_GPIO84__FUNC_GPIO84>;bias-pull-up;output-low;};
};my_device {compatible = "my-vendor,my-device";pinctrl-names = "default";pinctrl-0 = <&my_gpio>;gpios = <&gpio1 84 GPIO_ACTIVE_HIGH>;status = "okay";
};
1.7.2 驱动代码
ret = devm_gpio_request_one(&pdev->dev, 84, GPIOF_OUT_INIT_LOW, "my-gpio");
if (ret < 0) {dev_err(&pdev->dev, "GPIO request failed: %d\n", ret);return ret;
}
