Linux平台总线模型

1. 平台总线模型概述

平台总线模型是Linux内核中用于描述片上系统(SoC)设备的一种抽象机制,用于管理那些没有物理总线的"伪"设备。

1.1 基本概念

  • 平台设备(Platform Device):集成在SoC中的设备,如GPIO控制器、中断控制器等
  • 平台驱动(Platform Driver):与平台设备匹配的驱动程序
  • 平台总线(Platform Bus):虚拟总线,负责设备和驱动的匹配

1.2 平台总线模型优势

  • 统一设备管理接口
  • 支持设备树(Device Tree)描述
  • 简化片上系统设备驱动开发

2. 平台设备注册

2.1 静态注册平台设备

#include <linux/platform_device.h>

// 平台设备资源定义
static struct resource my_device_resources[] = {
    [0] = {
        .start = 0x10000000,    // 设备寄存器基地址
        .end   = 0x100000FF,    // 设备寄存器结束地址
        .flags = IORESOURCE_MEM, // 内存资源
    },
    [1] = {
        .start = 42,            // 中断号
        .end   = 42,
        .flags = IORESOURCE_IRQ, // 中断资源
    },
};

// 平台设备定义
static struct platform_device my_platform_device = {
    .name = "my_device",        // 设备名称,用于匹配驱动
    .id = -1,                   // 设备实例ID
    .num_resources = ARRAY_SIZE(my_device_resources),
    .resource = my_device_resources,
    .dev = {
        .platform_data = NULL,  // 设备私有数据
    },
};

// 模块初始化时注册设备
static int __init my_device_init(void)
{
    int ret;
    
    ret = platform_device_register(&my_platform_device);
    if (ret) {
        pr_err("Failed to register platform device\n");
        return ret;
    }
    
    pr_info("Platform device registered successfully\n");
    return 0;
}

// 模块退出时注销设备
static void __exit my_device_exit(void)
{
    platform_device_unregister(&my_platform_device);
    pr_info("Platform device unregistered\n");
}

2.2 动态注册平台设备

#include <linux/platform_device.h>

static struct platform_device *my_device;

static int __init dynamic_device_init(void)
{
    int ret;
    
    // 动态分配平台设备
    my_device = platform_device_alloc("my_dynamic_device", -1);
    if (!my_device) {
        pr_err("Failed to allocate platform device\n");
        return -ENOMEM;
    }
    
    // 设置设备资源(可选)
    struct resource res[] = {
        {
            .start = 0x20000000,
            .end = 0x200000FF,
            .flags = IORESOURCE_MEM,
        },
    };
    
    ret = platform_device_add_resources(my_device, res, ARRAY_SIZE(res));
    if (ret) {
        pr_err("Failed to add resources\n");
        goto err_put_device;
    }
    
    // 添加设备到系统
    ret = platform_device_add(my_device);
    if (ret) {
        pr_err("Failed to add platform device\n");
        goto err_put_device;
    }
    
    pr_info("Dynamic platform device registered\n");
    return 0;

err_put_device:
    platform_device_put(my_device);
    return ret;
}

static void __exit dynamic_device_exit(void)
{
    platform_device_unregister(my_device);
    pr_info("Dynamic platform device unregistered\n");
}

3. 平台驱动注册

3.1 基本平台驱动

#include <linux/platform_device.h>
#include <linux/io.h>

struct my_driver_data {
    void __iomem *reg_base;
    int irq_number;
    struct device *dev;
};

// 探测函数 - 当设备与驱动匹配时调用
static int my_driver_probe(struct platform_device *pdev)
{
    struct my_driver_data *data;
    struct resource *res;
    int ret;
    
    pr_info("Platform driver probe called for device: %s\n", pdev->name);
    
    // 分配驱动私有数据
    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->reg_base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(data->reg_base)) {
        dev_err(&pdev->dev, "Failed to map registers\n");
        return PTR_ERR(data->reg_base);
    }
    
    // 获取中断资源
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (res) {
        data->irq_number = res->start;
        dev_info(&pdev->dev, "IRQ number: %d\n", data->irq_number);
    }
    
    // 获取平台数据
    if (pdev->dev.platform_data) {
        dev_info(&pdev->dev, "Got platform data\n");
        // 处理平台数据...
    }
    
    data->dev = &pdev->dev;
    
    // 保存私有数据
    platform_set_drvdata(pdev, data);
    
    // 设备初始化...
    dev_info(&pdev->dev, "Device probed successfully\n");
    
    return 0;
}

// 移除函数 - 当设备移除或驱动卸载时调用
static int my_driver_remove(struct platform_device *pdev)
{
    struct my_driver_data *data = platform_get_drvdata(pdev);
    
    dev_info(&pdev->dev, "Platform driver remove called\n");
    
    // 清理资源
    // 设备寄存器会自动取消映射(因为使用了devm_ioremap_resource)
    
    return 0;
}

// 平台驱动定义
static struct platform_driver my_platform_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my_device",           // 与平台设备名称匹配
        .owner = THIS_MODULE,
        .of_match_table = NULL,        // 设备树匹配表(后面介绍)
    },
};

// 模块初始化
static int __init my_driver_init(void)
{
    int ret;
    
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        pr_err("Failed to register platform driver\n");
        return ret;
    }
    
    pr_info("Platform driver registered successfully\n");
    return 0;
}

// 模块退出
static void __exit my_driver_exit(void)
{
    platform_driver_unregister(&my_platform_driver);
    pr_info("Platform driver unregistered\n");
}

module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");

4. 设备树支持

4.1 设备树绑定

// 在驱动中添加设备树支持
#include <linux/of.h>
#include <linux/of_device.h>

// 设备树兼容性匹配表
static const struct of_device_id my_driver_of_match[] = {
    { .compatible = "vendor,my-device", .data = NULL },
    { .compatible = "vendor,my-device-v2", .data = (void *)1 },
    {}, // 空条目,表示结束
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);

// 从设备树获取资源
static int my_driver_probe_dt(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;
    const struct of_device_id *match;
    int ret;
    
    if (!np) {
        dev_err(&pdev->dev, "No device tree node\n");
        return -ENODEV;
    }
    
    // 检查设备兼容性
    match = of_match_device(my_driver_of_match, &pdev->dev);
    if (!match) {
        dev_err(&pdev->dev, "No compatible device found\n");
        return -ENODEV;
    }
    
    dev_info(&pdev->dev, "Compatible with: %s\n", match->compatible);
    
    // 从设备树获取寄存器地址
    ret = of_address_to_resource(np, 0, &pdev->resource[0]);
    if (ret) {
        dev_err(&pdev->dev, "Failed to get memory resource from DT\n");
        return ret;
    }
    
    // 从设备树获取中断
    pdev->num_resources = 1; // 内存资源
    ret = of_irq_to_resource_table(np, &pdev->resource[1], 1);
    if (ret > 0) {
        pdev->num_resources += ret;
    }
    
    return 0;
}

// 更新平台驱动定义
static struct platform_driver my_platform_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my_device",
        .owner = THIS_MODULE,
        .of_match_table = my_driver_of_match,  // 添加设备树匹配表
    },
};

4.2 设备树节点示例

// 设备树源文件 (.dts)
/ {
    compatible = "vendor,board";
    
    my_device: my_device@10000000 {
        compatible = "vendor,my-device", "vendor,my-device-v2";
        reg = <0x10000000 0x100>;      // 寄存器地址和大小
        interrupts = <0 42 4>;         // 中断号
        clock-frequency = <50000000>;  // 自定义属性
        vendor,custom-property = "hello"; // 厂商特定属性
        
        // 子节点
        child-device {
            compatible = "vendor,child-device";
            reg = <0x10000100 0x20>;
        };
    };
};

5. 完整的平台设备驱动示例

5.1 简单的GPIO控制器驱动

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

#define DRIVER_NAME "my_gpio_controller"

struct my_gpio_data {
    void __iomem *reg_base;
    struct gpio_chip gpio_chip;
    int ngpios;
    spinlock_t lock;
};

static int my_gpio_get(struct gpio_chip *chip, unsigned offset)
{
    struct my_gpio_data *data = gpiochip_get_data(chip);
    u32 value;
    
    value = readl(data->reg_base);
    return !!(value & (1 << offset));
}

static void my_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
    struct my_gpio_data *data = gpiochip_get_data(chip);
    unsigned long flags;
    u32 reg;
    
    spin_lock_irqsave(&data->lock, flags);
    
    reg = readl(data->reg_base);
    if (value)
        reg |= (1 << offset);
    else
        reg &= ~(1 << offset);
    writel(reg, data->reg_base);
    
    spin_unlock_irqrestore(&data->lock, flags);
}

static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
    // 设置GPIO为输入方向
    return 0;
}

static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
{
    // 设置GPIO为输出方向并设置值
    my_gpio_set(chip, offset, value);
    return 0;
}

static int my_gpio_probe(struct platform_device *pdev)
{
    struct my_gpio_data *data;
    struct device_node *np = pdev->dev.of_node;
    struct resource *res;
    int ret;
    
    dev_info(&pdev->dev, "Probing GPIO controller\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->reg_base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(data->reg_base)) {
        dev_err(&pdev->dev, "Failed to map registers\n");
        return PTR_ERR(data->reg_base);
    }
    
    // 从设备树获取GPIO数量
    if (of_property_read_u32(np, "ngpios", &data->ngpios)) {
        data->ngpios = 32; // 默认值
    }
    
    // 初始化自旋锁
    spin_lock_init(&data->lock);
    
    // 设置GPIO芯片
    data->gpio_chip.label = DRIVER_NAME;
    data->gpio_chip.parent = &pdev->dev;
    data->gpio_chip.owner = THIS_MODULE;
    data->gpio_chip.get = my_gpio_get;
    data->gpio_chip.set = my_gpio_set;
    data->gpio_chip.direction_input = my_gpio_direction_input;
    data->gpio_chip.direction_output = my_gpio_direction_output;
    data->gpio_chip.base = -1; // 动态分配基号
    data->gpio_chip.ngpio = data->ngpios;
    data->gpio_chip.of_node = np;
    
    // 注册GPIO控制器
    ret = devm_gpiochip_add_data(&pdev->dev, &data->gpio_chip, data);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register GPIO chip\n");
        return ret;
    }
    
    platform_set_drvdata(pdev, data);
    
    dev_info(&pdev->dev, "GPIO controller registered with %d GPIOs\n", data->ngpios);
    return 0;
}

static int my_gpio_remove(struct platform_device *pdev)
{
    dev_info(&pdev->dev, "GPIO controller removed\n");
    return 0;
}

static const struct of_device_id my_gpio_of_match[] = {
    { .compatible = "vendor,my-gpio", },
    {},
};
MODULE_DEVICE_TABLE(of, my_gpio_of_match);

static struct platform_driver my_gpio_driver = {
    .probe = my_gpio_probe,
    .remove = my_gpio_remove,
    .driver = {
        .name = DRIVER_NAME,
        .of_match_table = my_gpio_of_match,
        .owner = THIS_MODULE,
    },
};

module_platform_driver(my_gpio_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple GPIO controller platform driver");

6. 电源管理支持

6.1 挂起和恢复

#include <linux/pm.h>

static int my_driver_suspend(struct device *dev)
{
    struct my_driver_data *data = dev_get_drvdata(dev);
    
    dev_info(dev, "Suspending device\n");
    
    // 保存设备状态
    // 禁用中断
    // 进入低功耗模式
    
    return 0;
}

static int my_driver_resume(struct device *dev)
{
    struct my_driver_data *data = dev_get_drvdata(dev);
    
    dev_info(dev, "Resuming device\n");
    
    // 恢复设备状态
    // 重新使能中断
    // 退出低功耗模式
    
    return 0;
}

static const struct dev_pm_ops my_driver_pm_ops = {
    .suspend = my_driver_suspend,
    .resume = my_driver_resume,
    .freeze = my_driver_suspend,
    .thaw = my_driver_resume,
    .poweroff = my_driver_suspend,
    .restore = my_driver_resume,
};

// 在平台驱动中包含电源管理操作
static struct platform_driver my_platform_driver = {
    .probe = my_driver_probe,
    .remove = my_driver_remove,
    .driver = {
        .name = "my_device",
        .owner = THIS_MODULE,
        .of_match_table = my_driver_of_match,
        .pm = &my_driver_pm_ops,  // 添加电源管理支持
    },
};

7. 多设备实例支持

7.1 支持多个设备实例

// 在驱动中支持多个设备实例
static int my_driver_probe(struct platform_device *pdev)
{
    struct my_driver_data *data;
    int instance_id;
    
    // 获取设备实例ID
    instance_id = pdev->id;
    if (instance_id < 0) {
        // 从设备树获取实例信息
        instance_id = of_alias_get_id(pdev->dev.of_node, "my_device");
        if (instance_id < 0)
            instance_id = 0; // 默认实例
    }
    
    dev_info(&pdev->dev, "Probing device instance %d\n", instance_id);
    
    // 为每个实例分配独立的数据结构
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;
    
    data->instance_id = instance_id;
    
    // 设备特定的初始化...
    
    platform_set_drvdata(pdev, data);
    return 0;
}

8. 调试和最佳实践

8.1 调试技巧

// 在驱动中添加调试支持
#define DEBUG

#ifdef DEBUG
#define DRV_DEBUG(fmt, args...) \
    dev_dbg(&pdev->dev, "DEBUG: " fmt, ##args)
#else
#define DRV_DEBUG(fmt, args...)
#endif

static int my_driver_probe(struct platform_device *pdev)
{
    DRV_DEBUG("Starting probe\n");
    
    // 打印设备资源信息
    struct resource *res;
    int i;
    
    for (i = 0; i < pdev->num_resources; i++) {
        res = &pdev->resource[i];
        if (resource_type(res) == IORESOURCE_MEM) {
            DRV_DEBUG("Memory resource: %pr\n", res);
        } else if (resource_type(res) == IORESOURCE_IRQ) {
            DRV_DEBUG("IRQ resource: %pr\n", res);
        }
    }
    
    return 0;
}

8.2 最佳实践

// 1. 使用设备资源管理API
static int good_probe(struct platform_device *pdev)
{
    // 使用devm_系列函数自动管理资源
    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
    data->reg_base = devm_ioremap_resource(&pdev->dev, res);
    data->irq = platform_get_irq(pdev, 0);
    
    // 错误时资源会自动释放
    return 0;
}

// 2. 正确的错误处理
static int robust_probe(struct platform_device *pdev)
{
    int ret;
    
    ret = some_initialization();
    if (ret) {
        dev_err(&pdev->dev, "Initialization failed: %d\n", ret);
        return ret;
    }
    
    // 使用goto进行清晰的错误处理
    ret = setup_resource_a();
    if (ret)
        goto err_cleanup_a;
        
    ret = setup_resource_b();
    if (ret)
        goto err_cleanup_b;
        
    return 0;
    
err_cleanup_b:
    cleanup_resource_b();
err_cleanup_a:
    cleanup_resource_a();
    return ret;
}

// 3. 支持模块参数
static unsigned int debug_level;
module_param(debug_level, uint, 0644);
MODULE_PARM_DESC(debug_level, "Debug level (0=off, 1=basic, 2=verbose)");

平台总线模型是现代Linux设备驱动开发的核心机制,特别是在嵌入式系统和SoC平台上。通过合理使用平台设备和驱动,结合设备树描述,可以创建可移植、易维护的设备驱动程序。