platform_device的生成过程

kernel在引入dts前后,platform_device的生成方式是不同的。
在引入设备树之前,platform_device变量是静态定义的。
而引入设备树之后,kernel通过解析设备节点的信息生成platform_device。
一、引入dts之前
 add a platform-level device

int platform_device_register(struct platform_device *pdev)
{
        device_initialize(&pdev->dev);
        arch_setup_pdev_archdata(pdev);
        return platform_device_add(pdev);
}
 
 add a platform device to device hierarchy
 This is part 2 of platform_device_register(), though may be called
 separately _iff_ pdev was allocated by platform_device_alloc().
int platform_device_add(struct platform_device *pdev)
{
        int i, ret;

        if (!pdev)
                return -EINVAL;

        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:
                /*
                 * Automatically allocated device ID. We mark it as such so
                 * that we remember it must be freed, and we append a suffix
                 * to avoid namespace collision with explicit IDs.
                 */
                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)) {
                        dev_err(&pdev->dev, "failed to claim resource %d\n", i);
                        ret = -EBUSY;
                        goto failed;
                }
        }

        pr_debug("Registering platform device '%s'. Parent at %s\n",
                 dev_name(&pdev->dev), dev_name(pdev->dev.parent));

        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;
}

该函数最终会调用device_add添加设备
二、引入设备树之后
引入设备树之后,主要是通过解析设备树节点生成platform_device。
kernel中有两个阶段会生成platform_device:
第一阶段是在kernel初始化解析设备树时,kernel会为所有包含compatible属性名的第一级node创建platform_device。
如果第一级node的compatible属性值为“simple-bus”、“simple-mfd”或者"arm,amba-bus",kernel会继续为当前node
的包含compatible属性的节点创建platform_device。
一般SOC直接子设备节点对应的platform_device会被生成,而间接子节点对应的platform_device不会生成。

第二阶段是生成第一阶段中未满足match要求的间接子节点对应的platform_device。通常是在父节点设备驱动的probe函数中
遍历子节点生成对应的platform_device。

(1)第一阶段
通过调用of_platform_populate解析设备树
//Populate platform_devices from device tree data
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;

        for_each_child_of_node(root, child) {
                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;
}


kernel中有个of_platform_bus_probe函数,该函数和of_platform_populate有点类似,在新的board中最好使
用of_platform_populate来代替of_platform_bus_probe函数。


Create a device for a node and its children.
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)
{
        const struct of_dev_auxdata *auxdata;
        struct device_node *child;
        struct platform_device *dev;
        const char *bus_id = NULL;
        void *platform_data = NULL;
        int rc = 0;

        /* Make sure it has a compatible property */
        if (strict && (!of_get_property(bus, "compatible", NULL))) {
                pr_debug("%s() - skipping %s, no compatible prop\n",
                         __func__, bus->full_name);
                return 0;
        }

        auxdata = of_dev_lookup(lookup, bus);
        if (auxdata) {
                bus_id = auxdata->name;
                platform_data = auxdata->platform_data;
        }

        if (of_device_is_compatible(bus, "arm,primecell")) {
                /*
                 * Don't return an error here to keep compatibility with older
                 * device tree files.
                 */
                of_amba_device_create(bus, bus_id, platform_data, parent);
                return 0;
        }

        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
        if (!dev || !of_match_node(matches, bus))
                return 0;

        for_each_child_of_node(bus, child) {
                pr_debug("   create child: %s\n", child->full_name);
                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;
}


Alloc, initialize and register an of_device
static struct platform_device *of_platform_device_create_pdata(
                                        struct device_node *np,
                                        const char *bus_id,
                                        void *platform_data,
                                        struct device *parent)
{
        struct platform_device *dev;

        if (!of_device_is_available(np) ||
            of_node_test_and_set_flag(np, OF_POPULATED))
                return NULL;

        dev = of_device_alloc(np, bus_id, parent);
        if (!dev)
                goto err_clear_flag;

        dev->dev.bus = &platform_bus_type;
        dev->dev.platform_data = platform_data;
        of_dma_configure(&dev->dev, dev->dev.of_node);
        of_msi_configure(&dev->dev, dev->dev.of_node);

        if (of_device_add(dev) != 0) {
                of_dma_deconfigure(&dev->dev);
                platform_device_put(dev);
                goto err_clear_flag;
        }

        return dev;

err_clear_flag:
        of_node_clear_flag(np, OF_POPULATED);
        return NULL;
}
of_platform_device_create_pdata函数真正完成了创建platform_device的工作。
调用它的函数主要是做一些准备工作,如查找设备节点、检查兼容性等。

int of_device_add(struct platform_device *ofdev)
{
        BUG_ON(ofdev->dev.of_node == NULL);

        /* name and id have to be set so that the platform bus doesn't get
         * confused on matching */
        ofdev->name = dev_name(&ofdev->dev);
        ofdev->id = -1;

        /*
         * If this device has not binding numa node in devicetree, that is
         * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this
         * device is on the same node as the parent.
         */
        set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));

        return device_add(&ofdev->dev);
}

(2)第二阶段
在父设备节点的驱动probe函数中调用of_platform_device_create生成子设备节点对应的platforma_device。
 struct platform_device *of_platform_device_create(struct device_node *np,
                                             const char *bus_id,
                                             struct device *parent)
{
        return of_platform_device_create_pdata(np, bus_id, NULL, parent);
}
上述函数实际上调用of_platform_device_create_pdata完成生成platforma_device的工作的。




“simple-bus”、“simple-mfd”或者"arm,amba-bus",kernel会继续为当前node的包含compatible属性的节点创建platform_device。一般SOC直接子设备节点对应的platform_device会被生成,而间接子节点对应的platform_device不会生成。
第二阶段是生成第一阶段中未满足match要求的间接子节点对应的platform_device。通常是在父节点设备驱动的probe函数中遍历子节点生成对应的platform_device。

(1)第一阶段
通过调用of_platform_populate解析设备树
//Populate platform_devices from device tree data
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;

        for_each_child_of_node(root, child) {
                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;
}


kernel中有个of_platform_bus_probe函数,该函数和of_platform_populate有点类似,在新的board中最好使
用of_platform_populate来代替of_platform_bus_probe函数。


Create a device for a node and its children.
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)
{
        const struct of_dev_auxdata *auxdata;
        struct device_node *child;
        struct platform_device *dev;
        const char *bus_id = NULL;
        void *platform_data = NULL;
        int rc = 0;

        /* Make sure it has a compatible property */
        if (strict && (!of_get_property(bus, "compatible", NULL))) {
                pr_debug("%s() - skipping %s, no compatible prop\n",
                         __func__, bus->full_name);
                return 0;
        }

        auxdata = of_dev_lookup(lookup, bus);
        if (auxdata) {
                bus_id = auxdata->name;
                platform_data = auxdata->platform_data;
        }

        if (of_device_is_compatible(bus, "arm,primecell")) {
                /*
                 * Don't return an error here to keep compatibility with older
                 * device tree files.
                 */
                of_amba_device_create(bus, bus_id, platform_data, parent);
                return 0;
        }

        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
        if (!dev || !of_match_node(matches, bus))
                return 0;

        for_each_child_of_node(bus, child) {
                pr_debug("   create child: %s\n", child->full_name);
                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;
}


Alloc, initialize and register an of_device
static struct platform_device *of_platform_device_create_pdata(
                                        struct device_node *np,
                                        const char *bus_id,
                                        void *platform_data,
                                        struct device *parent)
{
        struct platform_device *dev;

        if (!of_device_is_available(np) ||
            of_node_test_and_set_flag(np, OF_POPULATED))
                return NULL;

        dev = of_device_alloc(np, bus_id, parent);
        if (!dev)
                goto err_clear_flag;

        dev->dev.bus = &platform_bus_type;
        dev->dev.platform_data = platform_data;
        of_dma_configure(&dev->dev, dev->dev.of_node);
        of_msi_configure(&dev->dev, dev->dev.of_node);

        if (of_device_add(dev) != 0) {
                of_dma_deconfigure(&dev->dev);
                platform_device_put(dev);
                goto err_clear_flag;
        }

        return dev;

err_clear_flag:
        of_node_clear_flag(np, OF_POPULATED);
        return NULL;
}
of_platform_device_create_pdata函数真正完成了创建platform_device的工作。
调用它的函数主要是做一些准备工作,如查找设备节点、检查兼容性等。

int of_device_add(struct platform_device *ofdev)
{
        BUG_ON(ofdev->dev.of_node == NULL);

        /* name and id have to be set so that the platform bus doesn't get
         * confused on matching */
        ofdev->name = dev_name(&ofdev->dev);
        ofdev->id = -1;

        /*
         * If this device has not binding numa node in devicetree, that is
         * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this
         * device is on the same node as the parent.
         */
        set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));

        return device_add(&ofdev->dev);
}

(2)第二阶段
在父设备节点的驱动probe函数中调用of_platform_device_create生成子设备节点对应的platforma_device。
 struct platform_device *of_platform_device_create(struct device_node *np,
                                             const char *bus_id,
                                             struct device *parent)
{
        return of_platform_device_create_pdata(np, bus_id, NULL, parent);
}
上述函数实际上调用of_platform_device_create_pdata完成生成platforma_device的工作的。




 



 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值