一、顶级架构一句话总结

platform_device(设备描述) ← Platform总线 → platform_driver(驱动实现)

Platform框架实现了设备与驱动的解耦,让同一份驱动代码可以适配不同的硬件平台。


二、为什么需要Platform框架?

传统驱动的问题

在Linux 2.6之前,设备信息硬编码在驱动中:

// 传统方式:硬件信息硬编码
static struct my_device {
    void __iomem *base = ioremap(0x10000000, 0x1000);  // 硬编码地址
    int irq = 32;                                        // 硬编码中断号
};

问题:

  • 更换硬件需要修改驱动代码
  • 无法支持设备热插拔
  • 代码可移植性差

Platform框架的解决方案

┌─────────────────────────────────────────────────────────┐
│                 Platform框架架构                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   platform_device          platform_driver              │
│   (描述硬件资源)    ←──匹配──→    (实现设备操作)          │
│         ↓                        ↓                      │
│   设备树/代码定义            probe/remove回调            │
│                                                         │
│              ↘            ↙                            │
│               Platform总线                              │
│            (负责匹配与绑定)                              │
└─────────────────────────────────────────────────────────┘

三、核心数据结构

1. platform_device

struct platform_device {
    const char *name;           // 设备名称(用于匹配)
    int id;                     // 设备ID
    struct device dev;          // 内嵌device结构
    u32 num_resources;          // 资源数量
    struct resource *resource;  // 资源数组
};

struct resource {
    resource_size_t start;      // 资源起始
    resource_size_t end;        // 资源结束
    const char *name;           // 资源名称
    unsigned long flags;        // 资源类型
};

2. platform_driver

struct platform_driver {
    int (*probe)(struct platform_device *);      // 匹配成功时调用
    int (*remove)(struct platform_device *);     // 设备移除时调用
    void (*shutdown)(struct platform_device *);  // 系统关机时调用
    int (*suspend)(struct platform_device *, pm_message_t);   // 休眠
    int (*resume)(struct platform_device *);     // 唤醒
    struct device_driver driver;                 // 内嵌driver结构
    const struct platform_device_id *id_table;   // 匹配表
};

四、匹配机制详解

匹配优先级

匹配顺序:
1. of_match_table(设备树compatible)  ← 推荐
2. id_table(平台设备ID表)
3. name(设备名称直接匹配)

匹配流程图

platform_device注册
        ↓
platform_driver注册
        ↓
遍历所有已注册的device
        ↓
检查匹配规则(按优先级)
        ↓
匹配成功 → 调用driver的probe函数
        ↓
绑定device和driver

五、设备定义方式

方式1:设备树定义(推荐)

/ {
    mydevice: mydevice@10000000 {
        compatible = "mycompany,mydevice";
        reg = <0x10000000 0x1000>;
        interrupts = <0 32 4>;
        clocks = <&clk_periph>;
        status = "okay";
    };
};

方式2:代码静态定义

static struct resource my_resources[] = {
    [0] = {
        .start = 0x10000000,
        .end = 0x10000000 + 0x1000 - 1,
        .flags = IORESOURCE_MEM,
        .name = "regs",
    },
    [1] = {
        .start = 32,
        .end = 32,
        .flags = IORESOURCE_IRQ,
        .name = "irq",
    },
};

static struct platform_device my_device = {
    .name = "mydevice",
    .id = 0,
    .num_resources = ARRAY_SIZE(my_resources),
    .resource = my_resources,
    .dev = {
        .platform_data = &my_pdata,
    },
};

// 注册设备
platform_device_register(&my_device);

六、驱动实现完整示例

1. 头文件和结构定义

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_device.h>

struct my_device_data {
    void __iomem *base;
    int irq;
    struct clk *clk;
    struct mutex lock;
};

2. probe函数实现

static int my_probe(struct platform_device *pdev)
{
    struct my_device_data *data;
    struct resource *res;
    int ret;
    
    pr_info("my_driver: probe called\n");
    
    // 分配设备数据
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;
    
    // 获取内存资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res) {
        dev_err(&pdev->dev, "No memory resource\n");
        return -ENODEV;
    }
    
    // 映射寄存器
    data->base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(data->base))
        return PTR_ERR(data->base);
    
    // 获取中断号
    data->irq = platform_get_irq(pdev, 0);
    if (data->irq < 0)
        return data->irq;
    
    // 获取时钟
    data->clk = devm_clk_get(&pdev->dev, NULL);
    if (IS_ERR(data->clk))
        return PTR_ERR(data->clk);
    
    // 使能时钟
    ret = clk_prepare_enable(data->clk);
    if (ret)
        return ret;
    
    // 初始化互斥锁
    mutex_init(&data->lock);
    
    // 保存设备数据
    platform_set_drvdata(pdev, data);
    
    dev_info(&pdev->dev, "Device initialized successfully\n");
    return 0;
}

3. remove函数实现

static int my_remove(struct platform_device *pdev)
{
    struct my_device_data *data = platform_get_drvdata(pdev);
    
    clk_disable_unprepare(data->clk);
    
    dev_info(&pdev->dev, "Device removed\n");
    return 0;
}

4. 设备树匹配表

static const struct of_device_id my_of_match[] = {
    { .compatible = "mycompany,mydevice", },
    { .compatible = "mycompany,mydevice-v2", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);

5. 平台驱动注册

static struct platform_driver my_driver = {
    .probe = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my_driver",
        .of_match_table = my_of_match,
        .pm = &my_pm_ops,  // 电源管理操作
    },
};

module_platform_driver(my_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Driver Developer");
MODULE_DESCRIPTION("Platform Driver Example");

七、资源获取API

常用资源获取函数

// 获取内存资源
struct resource *platform_get_resource(struct platform_device *pdev,
                                       unsigned int type, unsigned int num);

// 获取中断号
int platform_get_irq(struct platform_device *pdev, unsigned int num);

// 获取资源并映射(推荐)
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);

// 获取设备树节点
struct device_node *pdev->dev.of_node;

// 获取时钟
struct clk *devm_clk_get(struct device *dev, const char *id);

// 获取调节器
struct regulator *devm_regulator_get(struct device *dev, const char *id);

// 获取GPIO
int of_get_named_gpio(struct device_node *np, const char *propname, int index);

八、Platform vs 传统字符设备

特性 传统字符设备 Platform设备
设备信息 硬编码在驱动中 设备树/代码分离
热插拔 不支持 支持
电源管理 需手动实现 框架支持
设备匹配 手动 自动匹配
代码复用

九、调试技巧

查看已注册的Platform设备

# 查看所有platform设备
ls /sys/bus/platform/devices/

# 查看设备详细信息
cat /sys/bus/platform/devices/*.name/uevent

# 查看驱动绑定状态
ls -la /sys/bus/platform/drivers/

查看匹配过程

# 查看内核日志
dmesg | grep -i "platform"
dmesg | grep -i "probe"

十、终极总结

Platform框架 = 设备与驱动分离的优雅设计

  • 核心价值:设备信息与驱动代码解耦,提高可移植性
  • 关键机制:通过compatible/name匹配,自动调用probe
  • 资源管理:统一API获取内存、中断、时钟等资源
  • 最佳实践:优先使用设备树定义设备信息

Platform框架是Linux驱动开发的基石!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐