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 接口。