u-boot device_bind_common函数

static int device_bind_common(struct udevice *parent, const struct driver *drv,
                  const char *name, void *plat,
                  ulong driver_data, ofnode node,
                  uint of_plat_size, struct udevice **devp)
{
    struct udevice *dev;
    struct uclass *uc;
    int size, ret = 0;
    bool auto_seq = true;
    void *ptr;

    if (CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND))
        return -ENOSYS;

    if (devp)
        *devp = NULL;
    if (!name)
        return -EINVAL;

    ret = uclass_get(drv->id, &uc);
    if (ret) {
        debug("Missing uclass for driver %s\n", drv->name);
        return ret;
    }
//分配设备空间
    dev = calloc(1, sizeof(struct udevice));
    if (!dev)
        return -ENOMEM;
//设备初始化
    INIT_LIST_HEAD(&dev->sibling_node);
    INIT_LIST_HEAD(&dev->child_head);
    INIT_LIST_HEAD(&dev->uclass_node);
#ifdef CONFIG_DEVRES
    INIT_LIST_HEAD(&dev->devres_head);
#endif
//
dev->plat_ = plat;位置:driver/core/device.c

dev_set_plat(dev, plat);
    dev->driver_data = driver_data;
    dev->name = name;
//dev->node_ = node;位置:inlcude/dm/device.h
    dev_set_ofnode(dev, node);
dev
->parent = parent; dev->driver = drv; dev->uclass = uc; //设备别名
//def_config的配置项
DM_SEQ_ALIAS; 驱动程序flag
//def_config的配置项 OF_CONTROL OF_PLATDATA
// dev
->seq_ = -1; if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) && (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS)) { /* * Some devices, such as a SPI bus, I2C bus and serial ports * are numbered using aliases. */ if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { if (uc->uc_drv->name && ofnode_valid(node)) { if (!dev_read_alias_seq(dev, &dev->seq_)) { //获取序列号成功,auto_seq设置为false auto_seq = false; log_debug(" - seq=%d\n", dev->seq_); } } } }
//自动获取序列号
if (auto_seq && !(uc->uc_drv->flags & DM_UC_FLAG_NO_AUTO_SEQ)) dev->seq_ = uclass_find_next_free_seq(uc);

//dev-〉plat_平台数据空间
/* Check if we need to allocate plat */ if (drv->plat_auto) { //驱动程序自动分配的空间是否需要分配,如果非零需要设备需要分配相应的空间 bool alloc = !plat; /* * For of-platdata, we try use the existing data, but if * plat_auto is larger, we must allocate a new space */
//绑定数据空间 if (CONFIG_IS_ENABLED(OF_PLATDATA)) { if (of_plat_size) dev_or_flags(dev, DM_FLAG_OF_PLATDATA); if (of_plat_size < drv->plat_auto) alloc = true; }
//
if (alloc) { dev_or_flags(dev, DM_FLAG_ALLOC_PDATA); ptr = calloc(1, drv->plat_auto); if (!ptr) { ret = -ENOMEM; goto fail_alloc1; } /* * For of-platdata, copy the old plat into the new * space */ if (CONFIG_IS_ENABLED(OF_PLATDATA) && plat) memcpy(ptr, plat, of_plat_size); dev_set_plat(dev, ptr); } }

//dev中每个设备的平台数据
dev->uclass_plat_

    size = uc->uc_drv->per_device_plat_auto;
    if (size) {
        dev_or_flags(dev, DM_FLAG_ALLOC_UCLASS_PDATA);
        ptr = calloc(1, size);
        if (!ptr) {
            ret = -ENOMEM;
            goto fail_alloc2;
        }
        dev_set_uclass_plat(dev, ptr);
    }

//
dev->parent_plat_设置数据空间
if (parent) {
        size = parent->driver->per_child_plat_auto;
        if (!size)
            size = parent->uclass->uc_drv->per_child_plat_auto;
        if (size) {
            dev_or_flags(dev, DM_FLAG_ALLOC_PARENT_PDATA);
            ptr = calloc(1, size);
            if (!ptr) {
                ret = -ENOMEM;
                goto fail_alloc3;
            }
            dev_set_parent_plat(dev, ptr);
        }
        /* put dev into parent's successor list */
        list_add_tail(&dev->sibling_node, &parent->child_head);
    }

    ret = uclass_bind_device(dev);
    if (ret)
        goto fail_uclass_bind;

    /* if we fail to bind we remove device from successors and free it */
    if (drv->bind) {
        ret = drv->bind(dev);
        if (ret)
            goto fail_bind;
    }
    if (parent && parent->driver->child_post_bind) {
        ret = parent->driver->child_post_bind(dev);
        if (ret)
            goto fail_child_post_bind;
    }
    if (uc->uc_drv->post_bind) {
        ret = uc->uc_drv->post_bind(dev);
        if (ret)
            goto fail_uclass_post_bind;
    }

    if (parent)
        pr_debug("Bound device %s to %s\n", dev->name, parent->name);
    if (devp)
        *devp = dev;
//标志设备已经绑定标志DM_FLAG_BOUND dev_or_flags(dev, DM_FLAG_BOUND);
return 0; fail_uclass_post_bind: /* There is no child unbind() method, so no clean-up required */ fail_child_post_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (drv->unbind && drv->unbind(dev)) { dm_warn("unbind() method failed on dev '%s' on error path\n", dev->name); } } fail_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (uclass_unbind_device(dev)) { dm_warn("Failed to unbind dev '%s' on error path\n", dev->name); } } fail_uclass_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { list_del(&dev->sibling_node); if (dev_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) { free(dev_get_parent_plat(dev)); dev_set_parent_plat(dev, NULL); } } fail_alloc3: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (dev_get_flags(dev) & DM_FLAG_ALLOC_UCLASS_PDATA) { free(dev_get_uclass_plat(dev)); dev_set_uclass_plat(dev, NULL); } } fail_alloc2: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) { free(dev_get_plat(dev)); dev_set_plat(dev, NULL); } } fail_alloc1: devres_release_all(dev); free(dev); return ret; }

 

static int device_bind_common(struct udevice *parent, const struct driver *drv,
                  const char *name, void *plat,
                  ulong driver_data, ofnode node,
                  uint of_plat_size, struct udevice **devp)
{
    struct udevice *dev;
    struct uclass *uc;
    int size, ret = 0;
    bool auto_seq = true;
    void *ptr;

    if (CONFIG_IS_ENABLED(OF_PLATDATA_NO_BIND))
        return -ENOSYS;

    if (devp)
        *devp = NULL;
    if (!name)
        return -EINVAL;

    ret = uclass_get(drv->id, &uc);
    if (ret) {
        debug("Missing uclass for driver %s\n", drv->name);
        return ret;
    }
//分配设备空间
    dev = calloc(1, sizeof(struct udevice));
    if (!dev)
        return -ENOMEM;
//设备初始化
    INIT_LIST_HEAD(&dev->sibling_node);
    INIT_LIST_HEAD(&dev->child_head);
    INIT_LIST_HEAD(&dev->uclass_node);
#ifdef CONFIG_DEVRES
    INIT_LIST_HEAD(&dev->devres_head);
#endif
//dev->plat_ = plat;位置:driver/core/device.c

dev_set_plat(dev, plat);
    dev->driver_data = driver_data;
    dev->name = name;
//dev->node_ = node;位置:inlcude/dm/device.h
    dev_set_ofnode(dev, node);
dev->parent = parent; dev->driver = drv; dev->uclass = uc; //设备别名
//def_config的配置项
DM_SEQ_ALIAS; 驱动程序flag
//def_config的配置项 OF_CONTROL OF_PLATDATA
// dev->seq_ = -1; if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) && (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS)) { /* * Some devices, such as a SPI bus, I2C bus and serial ports * are numbered using aliases. */ if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { if (uc->uc_drv->name && ofnode_valid(node)) { if (!dev_read_alias_seq(dev, &dev->seq_)) { //获取序列号成功,auto_seq设置为false auto_seq = false; log_debug(" - seq=%d\n", dev->seq_); } } } }
//自动获取序列号 if (auto_seq && !(uc->uc_drv->flags & DM_UC_FLAG_NO_AUTO_SEQ)) dev->seq_ = uclass_find_next_free_seq(uc);

//dev-〉plat_平台数据空间 /* Check if we need to allocate plat */ if (drv->plat_auto) { bool alloc = !plat; /* * For of-platdata, we try use the existing data, but if * plat_auto is larger, we must allocate a new space */ if (CONFIG_IS_ENABLED(OF_PLATDATA)) { if (of_plat_size) dev_or_flags(dev, DM_FLAG_OF_PLATDATA); if (of_plat_size < drv->plat_auto) alloc = true; } if (alloc) { dev_or_flags(dev, DM_FLAG_ALLOC_PDATA); ptr = calloc(1, drv->plat_auto); if (!ptr) { ret = -ENOMEM; goto fail_alloc1; } /* * For of-platdata, copy the old plat into the new * space */ if (CONFIG_IS_ENABLED(OF_PLATDATA) && plat) memcpy(ptr, plat, of_plat_size); dev_set_plat(dev, ptr); } }

//dev中每个设备的平台数据dev->uclass_plat_

    size = uc->uc_drv->per_device_plat_auto;
    if (size) {
        dev_or_flags(dev, DM_FLAG_ALLOC_UCLASS_PDATA);
        ptr = calloc(1, size);
        if (!ptr) {
            ret = -ENOMEM;
            goto fail_alloc2;
        }
        dev_set_uclass_plat(dev, ptr);
    }

//dev->parent_plat_设置数据空间
if (parent) {
        size = parent->driver->per_child_plat_auto;
        if (!size)
            size = parent->uclass->uc_drv->per_child_plat_auto;
        if (size) {
            dev_or_flags(dev, DM_FLAG_ALLOC_PARENT_PDATA);
            ptr = calloc(1, size);
            if (!ptr) {
                ret = -ENOMEM;
                goto fail_alloc3;
            }
            dev_set_parent_plat(dev, ptr);
        }
        /* put dev into parent's successor list */
        list_add_tail(&dev->sibling_node, &parent->child_head);
    }

//dev->parent->uclass->uc_drv->child_post_bind(dev)函数 ret = uclass_bind_device(dev);
if (ret) goto fail_uclass_bind; /* if we fail to bind we remove device from successors and free it */ if (drv->bind) { ret = drv->bind(dev); if (ret) goto fail_bind; } if (parent && parent->driver->child_post_bind) { ret = parent->driver->child_post_bind(dev); if (ret) goto fail_child_post_bind; } if (uc->uc_drv->post_bind) { ret = uc->uc_drv->post_bind(dev); if (ret) goto fail_uclass_post_bind; } if (parent) pr_debug("Bound device %s to %s\n", dev->name, parent->name); if (devp) *devp = dev;
//标志设备已经绑定标志DM_FLAG_BOUND dev_or_flags(dev, DM_FLAG_BOUND); return 0; fail_uclass_post_bind: /* There is no child unbind() method, so no clean-up required */ fail_child_post_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (drv->unbind && drv->unbind(dev)) { dm_warn("unbind() method failed on dev '%s' on error path\n", dev->name); } } fail_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (uclass_unbind_device(dev)) { dm_warn("Failed to unbind dev '%s' on error path\n", dev->name); } } fail_uclass_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { list_del(&dev->sibling_node); if (dev_get_flags(dev) & DM_FLAG_ALLOC_PARENT_PDATA) { free(dev_get_parent_plat(dev)); dev_set_parent_plat(dev, NULL); } } fail_alloc3: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (dev_get_flags(dev) & DM_FLAG_ALLOC_UCLASS_PDATA) { free(dev_get_uclass_plat(dev)); dev_set_uclass_plat(dev, NULL); } } fail_alloc2: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (dev_get_flags(dev) & DM_FLAG_ALLOC_PDATA) { free(dev_get_plat(dev)); dev_set_plat(dev, NULL); } } fail_alloc1: devres_release_all(dev); free(dev); return ret; }

 

 

dev_read_alias_seq: 函数功能
fdtdec_get_alias_seq(gd->fdt_blob, dev->uclass->uc_drv->name,  dev_of_offset(dev), devnump);
/**
 * Get the alias sequence number of a node
 *
 * This works out whether a node is pointed to by an alias, and if so, the
 * sequence number of that alias. Aliases are of the form <base><num> where
 * <num> is the sequence number. For example spi2 would be sequence number
 * 2.
 *
 * @param blob        Device tree blob (if NULL, then error is returned)
 * @param base        Base name for alias (before the underscore)
 * @param node        Node to look up
 * @param seqp        This is set to the sequence number if one is found,
 *            but otherwise the value is left alone
 * @return 0 if a sequence was found, -ve if not
 */
int fdtdec_get_alias_seq(const void *blob, const char *base, int node,
             int *seqp);

static inline int dev_of_offset(const struct udevice *dev)
{
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
    return ofnode_to_offset(dev_ofnode(dev));
#else
    return -1;
#endif
}
 
/**
 * ofnode_to_offset() - convert an ofnode to a flat DT offset
 *
 * This cannot be called if the reference contains a node pointer.
 *
 * @node: Reference containing offset (possibly invalid)
 * @return DT offset (can be -1)
 */
static inline int ofnode_to_offset(ofnode node)
{
#ifdef OF_CHECKS
    if (of_live_active())
        return -1;
#endif
    return node.of_offset;
}
 
 
/**
 * dev_ofnode() - get the DT node reference associated with a udevice
 *
 * @dev:    device to check
 * @return reference of the the device's DT node
 */
static inline ofnode dev_ofnode(const struct udevice *dev)
{
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
    return dev->node_;
#else
    return ofnode_null();
#endif
}

 

 
 

 

 

 

A:生成 udevice。

B:绑定 udevice 和 driver。

C:把设备挂到 uclass 的dev_head 链表下。

D:调用设备驱动的 bind 接口。

 

posted @ 2022-02-18 22:21  liujunhuasd  阅读(316)  评论(0编辑  收藏  举报