#include <common.h>
#include <fdtdec.h>
+#include <fdt_support.h>
#include <malloc.h>
#include <dm/device.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
+#include <dm/pinctrl.h>
#include <dm/platdata.h>
#include <dm/uclass.h>
#include <dm/uclass-internal.h>
struct uclass *uc;
int size, ret = 0;
- *devp = NULL;
+ if (devp)
+ *devp = NULL;
if (!name)
return -EINVAL;
ret = uclass_get(drv->id, &uc);
- if (ret)
+ if (ret) {
+ debug("Missing uclass for driver %s\n", drv->name);
return ret;
+ }
dev = calloc(1, sizeof(struct udevice));
if (!dev)
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->platdata = platdata;
dev->name = name;
dev->of_offset = of_offset;
dev->seq = -1;
dev->req_seq = -1;
- if (IS_ENABLED(CONFIG_OF_CONTROL) && IS_ENABLED(CONFIG_DM_SEQ_ALIAS)) {
+ if (CONFIG_IS_ENABLED(OF_CONTROL) && IS_ENABLED(CONFIG_DM_SEQ_ALIAS)) {
/*
* Some devices, such as a SPI bus, I2C bus and serial ports
* are numbered using aliases.
if (parent)
dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
- *devp = dev;
+ if (devp)
+ *devp = dev;
+
+ dev->flags |= DM_FLAG_BOUND;
return 0;
fail_child_post_bind:
- if (IS_ENABLED(CONFIG_DM_DEVICE_REMOVE)) {
+ 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 (IS_ENABLED(CONFIG_DM_DEVICE_REMOVE)) {
+ 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 (IS_ENABLED(CONFIG_DM_DEVICE_REMOVE)) {
+ if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
list_del(&dev->sibling_node);
if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
free(dev->parent_platdata);
dev->platdata = NULL;
}
fail_alloc1:
+ devres_release_all(dev);
+
free(dev);
return ret;
drv = dev->driver;
assert(drv);
- /* Allocate private data if requested */
- if (drv->priv_auto_alloc_size) {
+ /* Allocate private data if requested and not reentered */
+ if (drv->priv_auto_alloc_size && !dev->priv) {
dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags);
if (!dev->priv) {
ret = -ENOMEM;
goto fail;
}
}
- /* Allocate private data if requested */
+ /* Allocate private data if requested and not reentered */
size = dev->uclass->uc_drv->per_device_auto_alloc_size;
- if (size) {
+ if (size && !dev->uclass_priv) {
dev->uclass_priv = calloc(1, size);
if (!dev->uclass_priv) {
ret = -ENOMEM;
size = dev->parent->uclass->uc_drv->
per_child_auto_alloc_size;
}
- if (size) {
+ if (size && !dev->parent_priv) {
dev->parent_priv = alloc_priv(size, drv->flags);
if (!dev->parent_priv) {
ret = -ENOMEM;
ret = device_probe(dev->parent);
if (ret)
goto fail;
+
+ /*
+ * The device might have already been probed during
+ * the call to device_probe() on its parent device
+ * (e.g. PCI bridge devices). Test the flags again
+ * so that we don't mess up the device.
+ */
+ if (dev->flags & DM_FLAG_ACTIVATED)
+ return 0;
}
seq = uclass_resolve_seq(dev);
dev->flags |= DM_FLAG_ACTIVATED;
+ /*
+ * Process pinctrl for everything except the root device, and
+ * continue regardless of the result of pinctrl.
+ */
+ if (dev->parent)
+ pinctrl_select_state(dev, "default");
+
ret = uclass_pre_probe_device(dev);
if (ret)
goto fail;
goto fail;
}
- dev->flags |= DM_FLAG_ACTIVATED;
if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
void *dev_get_parent_platdata(struct udevice *dev)
{
if (!dev) {
- dm_warn("%s: null device", __func__);
+ dm_warn("%s: null device\n", __func__);
return NULL;
}
void *dev_get_uclass_platdata(struct udevice *dev)
{
if (!dev) {
- dm_warn("%s: null device", __func__);
+ dm_warn("%s: null device\n", __func__);
return NULL;
}
return -ENODEV;
}
-int device_get_child_by_of_offset(struct udevice *parent, int seq,
+int device_get_child_by_of_offset(struct udevice *parent, int node,
struct udevice **devp)
{
struct udevice *dev;
int ret;
*devp = NULL;
- ret = device_find_child_by_of_offset(parent, seq, &dev);
+ ret = device_find_child_by_of_offset(parent, node, &dev);
return device_get_device_tail(dev, ret, devp);
}
+static struct udevice *_device_find_global_by_of_offset(struct udevice *parent,
+ int of_offset)
+{
+ struct udevice *dev, *found;
+
+ if (parent->of_offset == of_offset)
+ return parent;
+
+ list_for_each_entry(dev, &parent->child_head, sibling_node) {
+ found = _device_find_global_by_of_offset(dev, of_offset);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+int device_get_global_by_of_offset(int of_offset, struct udevice **devp)
+{
+ struct udevice *dev;
+
+ dev = _device_find_global_by_of_offset(gd->dm_root, of_offset);
+ return device_get_device_tail(dev, dev ? 0 : -ENOENT, devp);
+}
+
int device_find_first_child(struct udevice *parent, struct udevice **devp)
{
if (list_empty(&parent->child_head)) {
return dev->uclass->uc_drv->name;
}
-#ifdef CONFIG_OF_CONTROL
fdt_addr_t dev_get_addr(struct udevice *dev)
{
- return fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
-}
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ fdt_addr_t addr;
+
+ if (CONFIG_IS_ENABLED(OF_TRANSLATE)) {
+ const fdt32_t *reg;
+
+ reg = fdt_getprop(gd->fdt_blob, dev->of_offset, "reg", NULL);
+ if (!reg)
+ return FDT_ADDR_T_NONE;
+
+ /*
+ * Use the full-fledged translate function for complex
+ * bus setups.
+ */
+ return fdt_translate_address((void *)gd->fdt_blob,
+ dev->of_offset, reg);
+ }
+
+ /*
+ * Use the "simple" translate function for less complex
+ * bus setups.
+ */
+ addr = fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
+ dev->parent->of_offset,
+ dev->of_offset, "reg",
+ 0, NULL);
+ if (CONFIG_IS_ENABLED(SIMPLE_BUS) && addr != FDT_ADDR_T_NONE) {
+ if (device_get_uclass_id(dev->parent) == UCLASS_SIMPLE_BUS)
+ addr = simple_bus_translate(dev->parent, addr);
+ }
+
+ return addr;
#else
-fdt_addr_t dev_get_addr(struct udevice *dev)
-{
return FDT_ADDR_T_NONE;
-}
#endif
+}
bool device_has_children(struct udevice *dev)
{
return false;
return list_is_last(&dev->sibling_node, &parent->child_head);
}
+
+int device_set_name(struct udevice *dev, const char *name)
+{
+ name = strdup(name);
+ if (!name)
+ return -ENOMEM;
+ dev->name = name;
+
+ return 0;
+}