当前位置:首页>Linux>Linux platform总线

Linux platform总线

  • 2026-08-26 19:37:48
Linux platform总线

1. 结构体定义

struct platform_device {    const char    *name; /* 设备名称,用于设备和驱动匹配 */    int        id; /* 设备实例编号,用于区分多个同名设备 */    bool        id_auto; /* 自动分配设备ID标志 */struct device    dev;    u32        num_resources; /* 资源数量 */struct resource    *resource; /* 资源数组 */    conststruct platform_device_id    *id_entry;    char *driver_override; /* 强制匹配的驱动名称 */    /* MFD cell pointer */struct mfd_cell *mfd_cell;    /* arch specific additions */struct pdev_archdata    archdata;};
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;    conststruct platform_device_id *id_table; /* 设备ID匹配表 */    bool prevent_deferred_probe; /* 阻止延迟探测标志 */};

2. 总线

platform_bus_init注册一个platform总线,可以看到在函数内部注册了一个名叫platform的device ,目的在于让总线本身也纳入统一的device模型体系,成为sysfs中一个有父节点、可挂属性、可绑定驱动的对象。总线bus并不是device,而很多机制(sysfs、driver 绑定、电源管理、属性文件)都要求必须挂在某个device下面,所以需要为platform总线专门创建一个承载它的 device,即platform_bus。

struct device platform_bus = {    .init_name    = "platform",};int __init platform_bus_init(void){  /* 清理由早期early平台机制创建的临时platformz资源 */    early_platform_cleanup();  /* 注册名为platform的device */    device_register(&platform_bus);  /* 注册总线 */    bus_register(&platform_bus_type);  /* 注册一个 notifier,用于在设备树发生运行期变”时,同步创建或销毁 platform 设备 */    of_platform_register_reconfig_notifier();}

注册总线时传入的参数为platform_bus_type,它定义的总线的行为

struct bus_type platform_bus_type = {    .name        = "platform",    .dev_groups    = platform_dev_groups, /*为每一个device自动创建一组sysfs属性文件 */    .match        = platform_match, /* dev和drv的匹配方式 */    .uevent        = platform_uevent, /* 生成uevent时,补充/定制该设备的环境变量内容 */    .pm        = &platform_dev_pm_ops, /* 电源管理 */};

关于platform总线的match函数实现如下,优先级最高的是强制匹配,举个例子echo "vfio-platform" > /sys/bus/platform/devices/xxx/driver_override 。

static int platform_match(struct device *dev, struct device_driver *drv){struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);    /* 强制匹配 */    if (pdev->driver_override)        return !strcmp(pdev->driver_override, drv->name);    /* 设备树匹配 */    if (of_driver_match_device(dev, drv))        return 1;    /* ACPI匹配 */    if (acpi_driver_match_device(dev, drv))        return 1;    /* ID表匹配 */    if (pdrv->id_table)        return platform_match_id(pdrv->id_table, pdev) != NULL;    /* 名称匹配 */    return (strcmp(pdev->name, drv->name) == 0);}

3. 设备

关于platform设备的定义有两种方式,设备树定义和内核代码定义。

3.1 设备树定义

uart0: serial@ff1a0000 {    compatible = "rockchip,rk3568-uart";    reg = <0x0 0xff1a0000 0x0 0x1000>;    interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;    clocks = <&cru SCLK_UART0>;    status = "okay";};
  • • compatible:用于和platform_driver匹配
  • • reg:资源(IO内存、寄存器)
  • • interrupts:中断资源
  • • status = "okay":否则不会创建device

系统启动阶段会调用of_platform_default_populate_init(),进而调用 of_platform_default_populate(NULL, ...),最终进入of_platform_populate() 对 "/" 的子节点做递归处理。

/** * of_platform_default_populate_init *   ├── of_platform_default_populate *   └── of_platform_populate*/int of_platform_populate(struct device_node *root,            const struct of_device_id *matches,            const struct of_dev_auxdata *lookup,            struct device *parent){struct device_node *child;    int rc = 0;  /* 获取遍历的起点节点 */    root = root ? of_node_get(root) : of_find_node_by_path("/");    if (!root)        return -EINVAL;  /* 遍历root的一级子节点 */    for_each_child_of_node(root, child) {      /* 从设备树节点创建platform设备,并递归处理子节点 */        rc = of_platform_bus_create(child, matches, lookup, parent, true);        if (rc) {            of_node_put(child);            break;        }    }    of_node_set_flag(root, OF_POPULATED_BUS);    of_node_put(root);    return rc;}static int of_platform_bus_create(struct device_node *bus,                  const struct of_device_id *matches,                  const struct of_dev_auxdata *lookup,                  struct device *parent, bool strict){    conststruct of_dev_auxdata *auxdata;struct device_node *child;struct platform_device *dev;    const char *bus_id = NULL;    void *platform_data = NULL;    int rc = 0;    /* 确保有compatible属性 */    if (strict && (!of_get_property(bus, "compatible", NULL))) {        return 0;    }  /* 避免重复创建 */    if (of_node_check_flag(bus, OF_POPULATED_BUS)) {        return 0;    }    auxdata = of_dev_lookup(lookup, bus);    if (auxdata) {        bus_id = auxdata->name;        platform_data = auxdata->platform_data;    }  /*  AMBA设备 */    if (of_device_is_compatible(bus, "arm,primecell")) {        of_amba_device_create(bus, bus_id, platform_data, parent);        return 0;    }  /* 创建platform设备 */    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);    if (!dev || !of_match_node(matches, bus))        return 0;  /* 对每个子节点递归调用of_platform_bus_create */    for_each_child_of_node(bus, child) {        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);        if (rc) {            of_node_put(child);            break;        }    }    /* 设置总线已填充 */    of_node_set_flag(bus, OF_POPULATED_BUS);    return rc;}

对每个要创建的节点of_platform_device_create_pdata()调用of_device_alloc() ,该函数从设备树节点分配并初始化一个platform_device,解析资源,并设置设备的基本属性。

/** * of_platform_device_create_pdata * ├── of_device_alloc:该函数仅分配和初始化,不注册设备 * └── of_device_add:注册为设备模型中的struct device*/struct platform_device *of_device_alloc(struct device_node *np,                  const char *bus_id,                  struct device *parent){struct platform_device *dev;    int rc, i, num_reg = 0, num_irq;struct resource *res, temp_res;    dev = platform_device_alloc("", PLATFORM_DEVID_NONE);    if (!dev)        return NULL;    /* 统计资源数量 */    while (of_address_to_resource(np, num_reg, &temp_res) == 0)        num_reg++;    num_irq = of_irq_count(np);    /* 分配并填充资源表 */    if (num_irq || num_reg) {        res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);        if (!res) {            platform_device_put(dev);            return NULL;        }        dev->num_resources = num_reg + num_irq;        dev->resource = res;        for (i = 0; i < num_reg; i++, res++) {            rc = of_address_to_resource(np, i, res);            WARN_ON(rc);        }        if (of_irq_to_resource_table(np, res, num_irq) != num_irq)            pr_debug("not all legacy IRQ resources mapped for %s\n",                 np->name);    }  /* 设置设备树节点和父设备 */    dev->dev.of_node = of_node_get(np);    dev->dev.fwnode = &np->fwnode;    dev->dev.parent = parent ? : &platform_bus;    if (bus_id)        dev_set_name(&dev->dev, "%s", bus_id);    else        of_device_make_bus_id(&dev->dev);    return dev;}

完成 device_add() 后,该 DT 节点对应的设备会出现在/sys/bus/platform/devices/<device-name>/

,其of_node指向原始DT节点。

3.2 内核代码定义

静态定义

staticstruct resource demo_resources[] = {    {        .start = 0xff1a0000,        .end   = 0xff1a0fff,        .flags = IORESOURCE_MEM,    },    {        .start = 32,        .end   = 32,        .flags = IORESOURCE_IRQ,    },};staticstruct platform_device demo_pdev = {    .name = "demo-uart",    .id   = -1,    .num_resources = ARRAY_SIZE(demo_resources),    .resource      = demo_resources,};platform_device_register(&demo_pdev);

静态注册platform device时会调用platform_device_register函数

/** * platform_device_register * ├── device_initialize: 完成struct device的基础设施初始化 * ├── arch_setup_pdev_archdata: 补齐与体系结构相关的pdev->archdata/DMA等信息 * └── platform_device_add: 注册到设备模型*/int platform_device_add(struct platform_device *pdev){    int i, ret;    if (!pdev)        return -EINVAL;  /* 若parent为空,则默认挂载到platform_bus */    if (!pdev->dev.parent)        pdev->dev.parent = &platform_bus;    pdev->dev.bus = &platform_bus_type;  /* 生成设备名 */    switch (pdev->id) {    default:        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);        break;    case PLATFORM_DEVID_NONE:        dev_set_name(&pdev->dev, "%s", pdev->name);        break;    case PLATFORM_DEVID_AUTO:        ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);        if (ret < 0)            goto err_out;        pdev->id = ret;        pdev->id_auto = true;        dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);        break;    }  /* 资源声明与占用 */    for (i = 0; i < pdev->num_resources; i++) {struct resource *p, *r = &pdev->resource[i];        if (r->name == NULL)            r->name = dev_name(&pdev->dev);        p = r->parent;        if (!p) {            if (resource_type(r) == IORESOURCE_MEM)                p = &iomem_resource;            else if (resource_type(r) == IORESOURCE_IO)                p = &ioport_resource;        }        if (p && insert_resource(p, r)) {            ret = -EBUSY;            goto failed;        }    }  /* 加入设备模型 */    ret = device_add(&pdev->dev);    if (ret == 0)        return ret; failed:    if (pdev->id_auto) {        ida_simple_remove(&platform_devid_ida, pdev->id);        pdev->id = PLATFORM_DEVID_AUTO;    }    while (--i >= 0) {struct resource *r = &pdev->resource[i];        if (r->parent)            release_resource(r);    } err_out:    return ret;}

动态分配

struct platform_device *pdev;pdev = platform_device_alloc("demo-uart", -1);platform_device_add_resources(pdev, demo_resources,                              ARRAY_SIZE(demo_resources));platform_device_add(pdev);

4. 驱动

驱动注册的函数是platform_driver_register(drv),它是个宏定义,等价于__platform_driver_register(drv, THIS_MODULE)

int __platform_driver_register(struct platform_driver *drv,                struct module *owner){    drv->driver.owner = owner;    drv->driver.bus = &platform_bus_type;    drv->driver.probe = platform_drv_probe;    drv->driver.remove = platform_drv_remove;    drv->driver.shutdown = platform_drv_shutdown;    return driver_register(&drv->driver);}

最新文章

随机文章