<think>我们正在讨论Linux内核中的platform总线。platform总线是一种虚拟总线,用于连接不依赖于物理总线(如PCI, USB等)的设备,例如SoC内部集成的各种外设控制器。以下是根据用户要求的详细回答:
### 1. Platform总线概述
Platform总线(`platform_bus_type`)是Linux内核中一种虚拟总线,用于管理嵌入式系统中不依附于标准物理总线的设备[^3]。其主要作用是将设备(`platform_device`)和驱动(`platform_driver`)进行匹配,使驱动程序能够顺利加载并初始化这些设备。这种机制特别适用于SoC(System on Chip)系统中的外设控制器,如GPIO控制器、I2C控制器、SPI控制器以及UART控制器等[^3]。
### 2. Platform总线的关键组成部分
#### 2.1 两个核心结构体
- **`platform_device`**:表示挂接在platform总线上的设备。它包含设备名称、设备ID、资源(如内存资源、中断资源等)以及设备私有数据等[^1][^4]。
- **`platform_driver`**:表示管理platform设备的驱动程序。它包含驱动名称、驱动匹配表(`of_match_table`)、驱动探测函数(`probe`)、移除函数(`remove`)等[^1][^4]。
#### 2.2 匹配机制
Platform总线使用`match`函数(通常是`platform_match`)来匹配设备和驱动。匹配依据包括:
- 设备树(Device Tree)兼容性字符串(`of_match_table`)。
- 设备名称与驱动名称。
- 设备ID与驱动ID[^4]。
### 3. Platform总线的初始化
Platform总线的注册通过`platform_bus_init`函数完成,该函数在内核启动时被调用。其主要步骤包括:
1. 注册总线类型:`bus_register(&platform_bus_type)`,将平台总线添加到内核的总线系统中[^1]。
2. 总线对象被添加到全局总线列表(`bus_kset->list`)中,在内核中以`bus_type`结构体的形式存在[^1]。
### 4. 设备树(Device Tree)的影响
设备树的引入极大提高了驱动开发的效率和可移植性[^2]:
- **无设备树**:开发者需要在内核代码中静态定义`platform_device`结构,导致内核代码臃肿且不易跨平台移植。
- **有设备树**:将设备的硬件描述(如寄存器地址、中断号等)从内核代码中分离出来,使用设备树文件(.dts)描述硬件。驱动只需通过设备树接口获取设备资源,大幅提升了代码的可维护性和可重用性[^2]。
例如,设备树中一个UART设备的描述片段:
```dts
uart0: serial@10000000 {
compatible = "vendor,my-uart";
reg = <0x10000000 0x1000>;
interrupts = <10>;
};
```
驱动程序中通过`of_match_table`匹配:
```c
static const struct of_device_id my_uart_ids[] = {
{ .compatible = "vendor,my-uart" },
{}
};
MODULE_DEVICE_TABLE(of, my_uart_ids);
static struct platform_driver my_uart_driver = {
.driver = {
.name = "my_uart",
.of_match_table = my_uart_ids,
},
.probe = my_uart_probe,
.remove = my_uart_remove,
};
```
### 5. Platform总线的接口函数
平台总线提供了两组核心接口:
- **设备接口**:用于注册/注销平台设备:
```c
int platform_device_register(struct platform_device *pdev);
void platform_device_unregister(struct platform_device *pdev);
```
- **驱动接口**:用于注册/注销平台驱动:
```c
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *drv);
```
这些函数定义在`drivers/base/platform.c`中[^1][^4]。
### 6. 实际工作流程
1. 内核启动时,扫描设备树并创建所有匹配的平台设备(或通过传统方式静态定义)。
2. 驱动注册时,其`of_match_table`与设备树节点的`compatible`属性匹配。
3. 匹配成功后,驱动`probe`函数被调用,完成设备初始化和驱动加载[^2][^4]。
### 总结
Platform总线作为Linux内核中管理非物理总线设备的枢纽,通过标准化的设备和驱动模型,使得嵌入式系统开发更加灵活。设备树的引入进一步提升了开发效率和可移植性,是现代Linux驱动的核心机制之一。
§§相关问题§§
1. Platform总线和传统总线(如PCI)之间的主要区别是什么?
2. 如何为自定义外设编写并使用Platform设备驱动?
3. 设备树(Device Tree)如何具体实现硬件和软件的解耦?
4. 在无设备树的内核版本中,如何手动注册一个Platform设备?
5. Platform总线的电源管理(`platform_dev_pm_ops`)具体是如何工作的?
---
[^1]: Linux platform子系统【1】-PLATFORM(平台)总线详解。
[^2]: Linux系统中,Platform平台总线----有无设备树会导致开发效率。
[^3]: Linux驱动程序中的platform总线详解。
[^4]: linux子系统-Platform总线。
</think>
### Linux内核中Platform总线的详细解析
#### 一、Platform总线的本质
Platform总线(`platform_bus_type`)是Linux内核中的**虚拟总线**,用于管理不依附于物理总线(如PCI、USB)的嵌入式设备。这类设备通常是SoC内部集成外设(如GPIO控制器、UART、时钟控制器等)或内存映射的片上外设[^3][^4]。其核心定义位于内核源码的`drivers/base/platform.c`中:
```c
struct bus_type platform_bus_type = {
.name = "platform",
.match = platform_match, // 设备-驱动匹配函数
.uevent = platform_uevent, // 热插拔事件处理
.dma_configure = platform_dma_configure,
.pm = &platform_dev_pm_ops, // 电源管理
};
```[^4]
#### 二、核心组件:设备和驱动
1. **设备(`platform_device`)**
描述硬件资源(IO地址空间、中断号等):
```c
struct platform_device {
const char *name; // 设备标识符
int id; // 实例ID
struct resource *resource; // 硬件资源(内存/Irq)
// ...其他字段
};
```
2. **驱动(`platform_driver`)**
实现设备控制逻辑:
```c
struct platform_driver {
int (*probe)(struct platform_device *); // 设备初始化
int (*remove)(struct platform_device *); // 资源释放
struct device_driver driver; // 内含匹配表
};
```
#### 三、工作流程
1. **总线初始化**
系统启动时调用`platform_bus_init()`,注册总线到内核的`bus_kset`链表[^1]。
2. **设备注册**
静态定义或通过设备树(Device Tree)动态添加`platform_device`。例如传统方式:
```c
struct resource res[] = {
[0] = DEFINE_RES_MEM(0x48000000, 0x100), // 寄存器地址
[1] = DEFINE_RES_IRQ(42), // 中断号
};
platform_device_register(&(struct platform_device){
.name = "my_device",
.resource = res,
.num_resources = ARRAY_SIZE(res)
});
```
3. **驱动注册**
开发者实现驱动并注册:
```c
static struct platform_driver my_driver = {
.driver = { .name = "my_device" },
.probe = my_probe, // 设备探测回调
.remove = my_remove,
};
module_platform_driver(my_driver);
```
4. **匹配机制**
通过`platform_match()`函数匹配设备和驱动,优先级:
- 设备树兼容性(`of_match_table`)
- ACPI ID
- 名称直接匹配(`platform_device.name == platform_driver.driver.name`)
```c
// 典型驱动匹配表(设备树兼容性)
static const struct of_device_id my_ids[] = {
{ .compatible = "vendor,device-123" },
{}
};
```
#### 四、设备树(Device Tree)的影响
设备树彻底改变了Platform总线的开发方式[^2]:
| **模式** | **无设备树** | **有设备树** |
|----------|--------------|--------------|
| **设备定义** | 需在内核源码硬编码`platform_device` | 设备树文本描述(`.dts`文件) |
| **硬件修改** | 需重新编译内核 | 只需修改设备树,独立编译 |
| **代码复用** | 驱动与硬件绑定,移植困难 | 驱动通过兼容性字符串匹配,可复用 |
```dts
// 设备树示例:UART设备
uart0: serial@10000000 {
compatible = "vendor,my-uart";
reg = <0x10000000 0x100>; // 寄存器地址空间
interrupts = <10>; // 中断号
};
```
#### 五、关键API接口
| **功能** | **接口函数** | **说明** |
|----------|--------------|----------|
| **设备操作** | `platform_device_register()` | 注册设备 |
| | `platform_device_unregister()` | 注销设备 |
| **驱动操作** | `platform_driver_register()` | 注册驱动 |
| | `platform_driver_unregister()` | 注销驱动 |
| **资源获取** | `platform_get_resource()` | 获取设备资源(内存/Irq) |
| **匹配检查** | `platform_match_device()` | 验证设备-驱动兼容性 |
#### 六、实际应用场景
1. **SoC内部外设**:如中断控制器(GIC)、内存控制器
2. **非标准外设**:自定义FPGA逻辑、传感器接口
3. **历史遗留设备**:无总线标准的旧式硬件
> **设计意义**:Platform总线通过抽象层实现**[设备和驱动解耦](https://www.kernel.org/doc/html/latest/driver-api/driver-model/overview.html)**,驱动程序只需关注`probe()`和`remove()`,无需处理硬件拓扑细节[^1][^3]。