summaryrefslogtreecommitdiff
path: root/drivers/core/uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/core/uclass.c')
-rw-r--r--drivers/core/uclass.c135
1 files changed, 133 insertions, 2 deletions
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 34723ec42a..61ca17e564 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -23,6 +23,8 @@ struct uclass *uclass_find(enum uclass_id key)
{
struct uclass *uc;
+ if (!gd->dm_root)
+ return NULL;
/*
* TODO(sjg@chromium.org): Optimise this, perhaps moving the found
* node to the start of the list, or creating a linear array mapping
@@ -158,13 +160,72 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
return -ENODEV;
}
-int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
+int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
+ bool find_req_seq, struct udevice **devp)
{
+ struct uclass *uc;
struct udevice *dev;
int ret;
*devp = NULL;
- ret = uclass_find_device(id, index, &dev);
+ debug("%s: %d %d\n", __func__, find_req_seq, seq_or_req_seq);
+ if (seq_or_req_seq == -1)
+ return -ENODEV;
+ ret = uclass_get(id, &uc);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+ debug(" - %d %d\n", dev->req_seq, dev->seq);
+ if ((find_req_seq ? dev->req_seq : dev->seq) ==
+ seq_or_req_seq) {
+ *devp = dev;
+ debug(" - found\n");
+ return 0;
+ }
+ }
+ debug(" - not found\n");
+
+ return -ENODEV;
+}
+
+static int uclass_find_device_by_of_offset(enum uclass_id id, int node,
+ struct udevice **devp)
+{
+ struct uclass *uc;
+ struct udevice *dev;
+ int ret;
+
+ *devp = NULL;
+ if (node < 0)
+ return -ENODEV;
+ ret = uclass_get(id, &uc);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+ if (dev->of_offset == node) {
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/**
+ * uclass_get_device_tail() - handle the end of a get_device call
+ *
+ * This handles returning an error or probing a device as needed.
+ *
+ * @dev: Device that needs to be probed
+ * @ret: Error to return. If non-zero then the device is not probed
+ * @devp: Returns the value of 'dev' if there is no error
+ * @return ret, if non-zero, else the result of the device_probe() call
+ */
+static int uclass_get_device_tail(struct udevice *dev, int ret,
+ struct udevice **devp)
+{
if (ret)
return ret;
@@ -177,6 +238,44 @@ int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
return 0;
}
+int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
+{
+ struct udevice *dev;
+ int ret;
+
+ *devp = NULL;
+ ret = uclass_find_device(id, index, &dev);
+ return uclass_get_device_tail(dev, ret, devp);
+}
+
+int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
+{
+ struct udevice *dev;
+ int ret;
+
+ *devp = NULL;
+ ret = uclass_find_device_by_seq(id, seq, false, &dev);
+ if (ret == -ENODEV) {
+ /*
+ * We didn't find it in probed devices. See if there is one
+ * that will request this seq if probed.
+ */
+ ret = uclass_find_device_by_seq(id, seq, true, &dev);
+ }
+ return uclass_get_device_tail(dev, ret, devp);
+}
+
+int uclass_get_device_by_of_offset(enum uclass_id id, int node,
+ struct udevice **devp)
+{
+ struct udevice *dev;
+ int ret;
+
+ *devp = NULL;
+ ret = uclass_find_device_by_of_offset(id, node, &dev);
+ return uclass_get_device_tail(dev, ret, devp);
+}
+
int uclass_first_device(enum uclass_id id, struct udevice **devp)
{
struct uclass *uc;
@@ -254,6 +353,37 @@ int uclass_unbind_device(struct udevice *dev)
return 0;
}
+int uclass_resolve_seq(struct udevice *dev)
+{
+ struct udevice *dup;
+ int seq;
+ int ret;
+
+ assert(dev->seq == -1);
+ ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, dev->req_seq,
+ false, &dup);
+ if (!ret) {
+ dm_warn("Device '%s': seq %d is in use by '%s'\n",
+ dev->name, dev->req_seq, dup->name);
+ } else if (ret == -ENODEV) {
+ /* Our requested sequence number is available */
+ if (dev->req_seq != -1)
+ return dev->req_seq;
+ } else {
+ return ret;
+ }
+
+ for (seq = 0; seq < DM_MAX_SEQ; seq++) {
+ ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, seq,
+ false, &dup);
+ if (ret == -ENODEV)
+ break;
+ if (ret)
+ return ret;
+ }
+ return seq;
+}
+
int uclass_post_probe_device(struct udevice *dev)
{
struct uclass_driver *uc_drv = dev->uclass->uc_drv;
@@ -281,6 +411,7 @@ int uclass_pre_remove_device(struct udevice *dev)
free(dev->uclass_priv);
dev->uclass_priv = NULL;
}
+ dev->seq = -1;
return 0;
}