Linux驱动开发:Platform总线全解析

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内核》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值