Linux 驱动开发专题:Platform总线详解
目录
1. 前言
在Linux驱动开发中,设备总线(bus)是驱动框架的核心概念之一。总线的抽象帮助内核管理各种硬件设备的发现、注册与驱动匹配。Platform总线作为嵌入式Linux常用的总线类型,承担着管理SoC(系统芯片)上各种非即插即用设备的重要职责。
本文将从理论到实践,系统详解Linux Platform总线的设计理念、核心机制以及驱动开发流程。希望对嵌入式开发工程师和Linux驱动学习者有实质帮助。
2. Linux设备模型简介
Linux设备模型(device model)通过总线(bus)、设备(device)、驱动(driver)三大核心结构体,构建了统一的设备管理框架。
- bus:总线,代表设备的连接总线,如PCI、USB、Platform等。
- device:设备,是具体的硬件实例。
- driver:驱动,是设备的操作代码。
设备模型的核心职责包括:
- 设备与驱动的匹配。
- 设备树的组织管理。
- 设备生命周期管理。
这些模型的实现使内核能够对不同类型设备统一管理,方便驱动复用和系统扩展。
3. Platform总线简介
Platform总线是Linux内核中专门针对SoC平台上内嵌设备的总线类型,主要负责管理与CPU紧密耦合的设备,如片上外设、片上存储控制器、GPIO、UART等。
Platform总线特点:
- 非即插即用设备:不像PCI或USB设备有自动发现机制,platform设备通常由设备树(Device Tree)或board文件静态定义。
- 与设备树紧密结合:设备树描述硬件拓扑和资源,platform设备从设备树解析得到资源。
- 驱动绑定明确:platform驱动通过名字匹配绑定到platform设备。
- 资源管理:platform设备管理寄存器地址、IRQ中断号等资源。
总线类型在Linux内核中由 struct bus_type
表示,Platform总线对应的是 platform_bus_type
。
4. Platform设备和驱动
4.1 platform_device
struct platform_device
表示一个platform设备,主要包含:
- 设备名称(name)
- 设备ID(id)
- 资源(如寄存器基址、IRQ)
- 设备的私有数据指针
- 设备树节点(如果有)
4.2 platform_driver
struct platform_driver
表示一个platform设备驱动,包含:
- 驱动名称
- probe函数:设备匹配后调用,初始化设备
- remove函数:设备移除时调用,释放资源
- suspend/resume函数:电源管理
- driver结构体,继承
struct device_driver
5. Platform总线的设备注册与驱动绑定流程
5.1 设备注册
平台设备通常通过以下方式注册:
- 设备树解析后由内核自动注册(现代方案)
- board文件中静态声明
- 动态注册:调用
platform_device_register()
5.2 驱动注册
驱动通过调用 platform_driver_register()
注册到内核platform总线上。
5.3 设备驱动匹配
内核会遍历所有platform设备和platform驱动进行匹配,匹配规则为:
- 设备和驱动名称相同(
name
) - 设备树compatible属性与驱动of_match_table匹配
- ID匹配(可选)
6. 设备树与Platform总线的关系
设备树(Device Tree,简称DT)是描述硬件的设备信息数据结构。在嵌入式Linux中,平台设备几乎都是通过设备树描述硬件资源的。
6.1 设备树节点与platform_device
设备树中的每个设备节点会被转换成一个 platform_device
结构体。
6.2 驱动匹配设备树
platform驱动中常用 of_match_table
定义设备树匹配表:
static const struct of_device_id my_device_of_match[] = {
{ .compatible = "vendor,mydevice" },
{ }
};
MODULE_DEVICE_TABLE(of, my_device_of_match);
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "mydevice",
.of_match_table = my_device_of_match,
},
};
设备树compatible属性和驱动的of_match_table匹配成功即绑定驱动。
7. Platform总线相关核心源码分析
7.1 总线类型定义
Platform总线在内核中由 drivers/base/platform.c
中的 platform_bus_type
定义:
struct bus_type platform_bus_type = {
.name = "platform",
.match = platform_match,
.probe = platform_probe,
.remove = platform_remove,
};
7.2 设备注册流程
平台设备注册主要通过 platform_device_register() 实现:
int platform_device_register(struct platform_device *pdev)
{
return device_register(&pdev->dev);
}
该函数会将设备添加到设备模型中,进而触发驱动匹配流程。
7.3 驱动注册流程
平台驱动注册使用 platform_driver_register():
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
return driver_register(&drv->driver);
}
设置驱动所属总线为 platform,并注册到内核驱动框架。
7.4 匹配过程详解
匹配函数为 platform_match():
static int platform_match(struct device *dev, struct device_driver *drv)
{
// 1. 设备树 compatible 匹配
// 2. name 匹配
// 3. id 匹配(可选)
}
匹配优先顺序为:
-
1、设备树 of_match_table 匹配;
-
2、name 匹配(pdev->name 与 drv->driver.name);
-
3、如果设置了 id_table,也可按 ID 进一步精细匹配。
8. Platform驱动编写实战示例
以一个简单的 GPIO 灯设备为例,演示 platform 驱动编写。
8.1 设备树节点定义
led_gpio: led@0 {
compatible = "mycompany,my-led";
gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>;
status = "okay";
};
8.2 Platform驱动实现
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
static int my_led_probe(struct platform_device *pdev)
{
struct gpio_desc *led_gpio;
led_gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
if (IS_ERR(led_gpio)) {
dev_err(&pdev->dev, "Failed to get GPIO\n");
return PTR_ERR(led_gpio);
}
gpiod_set_value(led_gpio, 1); // 点亮 LED
dev_info(&pdev->dev, "LED turned on\n");
return 0;
}
static int my_led_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "LED driver removed\n");
return 0;
}
static const struct of_device_id my_led_of_match[] = {
{ .compatible = "mycompany,my-led" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_led_of_match);
static struct platform_driver my_led_driver = {
.driver = {
.name = "my_led",
.of_match_table = my_led_of_match,
},
.probe = my_led_probe,
.remove = my_led_remove,
};
module_platform_driver(my_led_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple Platform LED Driver");
9. 常见问题与调试技巧
9.1 probe函数未执行?
检查设备树中 compatible 与驱动匹配表是否一致。
-
查看驱动是否注册成功(dmesg)。
-
使用 ls /sys/bus/platform/devices/ 查看设备是否存在。
-
确认模块已成功加载。
9.2 资源申请失败
-
检查 devm_ 函数是否成功。
-
确保设备树中资源定义正确。
-
查看 dmesg 是否有错误输出。
9.3 建议使用 devm_ 系列函数
- devm_ 系列函数可以让内核在设备移除时自动释放资源,避免内存泄露与重复释放。
10. 总结与参考资料
Platform 总线作为 Linux 内核中 SoC 设备的重要总线机制,是驱动开发的基础设施之一。理解它的结构、匹配流程与设备树关系,对于嵌入式平台驱动编写至关重要。
10.1 核心知识回顾
-
platform_device 与 platform_driver 分别描述设备与驱动;
-
匹配依赖于 name 或设备树中的 compatible;
-
设备树是 platform 总线最常用的设备定义方式;
-
使用 devm_ 系列函数有助于提高资源管理效率。
10.2 推荐参考资料
-
Linux 内核源码 drivers/base/platform.c
-
《Linux设备驱动开发详解》
-
Linux 官方文档 Documentation/driver-api/platform
-
《深入理解Linux内核》