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_set_plat(dev, plat); dev->driver_data = driver_data; dev->name = name; dev_set_ofnode(dev, node); dev->parent = parent; dev->driver = drv; dev->uclass = uc; 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; 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); /* 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); } } 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); } 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; 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; }
A:生成 udevice。
B:绑定 udevice 和 driver。
C:把设备挂到 uclass 的dev_head 链表下。
D:调用设备驱动的 bind 接口。