Platform 驱动模型

一、Platform 总线

Linux 把 SoC 内置外设抽象为"platform device":
- 在设备树中描述硬件
- 驱动通过 compatible 匹配
- 加载时调用 probe,卸载时调用 remove

arch/arm/boot/dts/xxx.dts
  ↓ 编译
.dtb
  ↓ 内核启动时解析
platform_device(每个节点)
  ↓ 匹配
platform_driver(probe)

二、关键结构

1. platform_device

struct platform_device {
    const char *name;
    int id;
    bool id_auto;
    struct device dev;
    u64 platform_dma_mask;
    struct resource resource[PLATFORM_MAX_RESOURCES];
    unsigned int num_resources;
    const struct platform_device_id *id_entry;
    struct device_node *of_node;
};

2. resource(资源)

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;     // IORESOURCE_MEM / IORESOURCE_IRQ / IORESOURCE_DMA ...
};

3. 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 state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};

三、完整示例

1. 设备树

myuart: serial@40010000 {
    compatible = "myvendor,myuart";
    reg = <0x40010000 0x1000>;
    interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_UART1_IPG>;
    pinctrl-0 = <&pinctrl_uart1>;
    pinctrl-names = "default";
    status = "okay";
};

2. 驱动代码

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

struct my_priv {
    void __iomem *base;
    int irq;
    spinlock_t lock;
};

static irqreturn_t my_irq(int irq, void *dev_id)
{
    struct my_priv *priv = dev_id;
    u32 status = readl(priv->base + 0x10);
    writel(status, priv->base + 0x10);     // 清中断
    return IRQ_HANDLED;
}

static int my_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct my_priv *priv;
    int ret;

    priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    if (!priv) return -ENOMEM;

    spin_lock_init(&priv->lock);

    // 1. 资源获取
    priv->base = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(priv->base)) return PTR_ERR(priv->base);

    priv->irq = platform_get_irq(pdev, 0);
    if (priv->irq < 0) return priv->irq;

    // 2. 时钟、复位
    // clk = devm_clk_get(dev, NULL);
    // clk_prepare_enable(clk);

    // 3. 中断
    ret = devm_request_irq(dev, priv->irq, my_irq, 0, "myuart", priv);
    if (ret) return ret;

    // 4. 设备初始化
    writel(0x01, priv->base + 0x00);   // 复位
    writel(0x08, priv->base + 0x04);   // 配置
    writel(0x01, priv->base + 0x08);   // 使能

    platform_set_drvdata(pdev, priv);
    dev_info(dev, "probed\n");
    return 0;
}

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

static const struct of_device_id my_of_match[] = {
    { .compatible = "myvendor,myuart" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);

static struct platform_driver my_driver = {
    .probe  = my_probe,
    .remove = my_remove,
    .driver = {
        .name           = "myuart",
        .of_match_table = my_of_match,
    },
};
module_platform_driver(my_driver);

MODULE_LICENSE("GPL");

3. Makefile

obj-m += myuart.o

四、常用 API

1. 获取资源

struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tx");

int irq = platform_get_irq(pdev, 0);
int irq = platform_get_irq_byname(pdev, "rx");

2. 设备属性

struct device_node *np = pdev->dev.of_node;
u32 val;
of_property_read_u32(np, "myprop", &val);
of_property_read_u32_array(np, "reg", arr, 2);

3. devm 自动管理资源

devm_kzalloc / devm_kmalloc
devm_request_irq
devm_ioremap / devm_platform_ioremap_resource
devm_clk_get / devm_reset_control_get
devm_regulator_get
devm_gpio_request
devm_input_allocate_device

离开时自动 release,无需在 remove 中手动清理。

五、id_table 与 ACPI

static const struct platform_device_id my_id_table[] = {
    { "myuart-v1",  (void *)0x100 },
    { "myuart-v2",  (void *)0x200 },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, my_id_table);

ACPI 设备(x86):

static const struct acpi_device_id my_acpi_match[] = {
    { "MYVND0001", 0 },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(acpi, my_acpi_match);

六、子设备 / MFD

七、调试

ls /sys/bus/platform/devices/
ls /sys/bus/platform/drivers/
cat /sys/bus/platform/devices/myuart/uevent
// 驱动打印
dev_info(&pdev->dev, "info");
dev_err(&pdev->dev, "error: %d\n", ret);