[RFC PATCH 09/15] dm: core: Integrate adjuncts into existing code paths

Simon Glass sjg at chromium.org
Thu Mar 19 22:35:39 CET 2026


Wire adjunct support into the existing driver-model lifecycle and
uclass iteration.

Use the shared ucm_alloc_plat() and ucm_alloc_priv() helpers in the
primary bind and probe paths. Call device_init_adj() at bind,
device_probe_adjs() at probe, device_free_adjs() at remove and
device_unbind_adjs() at unbind.

Update uclass0iteration to handle the combined dev_head list, which now
contains both primary and adjunct entries. Function-based iterators use
ucm->dev

Some iterators use container_of() so cannot handle adjuncts.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 drivers/core/device-remove.c |   9 +-
 drivers/core/device.c        |   9 +-
 drivers/core/uclass.c        | 286 ++++++++++++++++++++++++++++++++---
 include/dm/uclass-internal.h |   8 +
 include/dm/uclass.h          | 210 ++++++++++++++++++++++---
 5 files changed, 483 insertions(+), 39 deletions(-)

diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
index 2a3253b34a5..7d07317f344 100644
--- a/drivers/core/device-remove.c
+++ b/drivers/core/device-remove.c
@@ -109,6 +109,10 @@ int device_unbind(struct udevice *dev)
 		free(dev_get_parent_plat(dev));
 		dev_set_parent_plat(dev, NULL);
 	}
+	ret = device_unbind_adjs(dev);
+	if (ret)
+		return log_msg_ret("adj", ret);
+
 	ret = uclass_unbind_device(dev);
 	if (ret)
 		return log_msg_ret("uc", ret);
@@ -131,12 +135,15 @@ int device_unbind(struct udevice *dev)
  */
 void device_free(struct udevice *dev)
 {
-	int size;
+	int size, ret;
 
 	if (dev->driver->priv_auto) {
 		free(dev_get_priv(dev));
 		dev_set_priv(dev, NULL);
 	}
+	ret = device_remove_adjs(dev);
+	if (ret)
+		log_debug("Failed to remove adjuncts: %d\n", ret);
 	size = dev->ucm.uc->uc_drv->per_device_auto;
 	if (size) {
 		free(dev_get_uclass_priv(dev));
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 1de392238f8..ba3c5b9a28e 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -80,7 +80,9 @@ static int device_bind_common(struct udevice *parent, const struct driver *drv,
 	dev_set_ofnode(dev, node);
 	dev->parent = parent;
 	dev->driver = drv;
+	device_init_adj(dev);
 	ucm->uc = uc;
+	ucm->dev = dev;
 
 	ucm->seq = -1;
 	if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) &&
@@ -351,6 +353,7 @@ static int device_alloc_priv(struct udevice *dev)
 	const struct driver *drv;
 	void *ptr;
 	int size;
+	int ret;
 
 	drv = dev->driver;
 	assert(drv);
@@ -363,7 +366,7 @@ static int device_alloc_priv(struct udevice *dev)
 		dev_set_priv(dev, ptr);
 	}
 
-	/* Allocate private data if requested and not reentered */
+	/* Allocate uclass-private data if requested and not reentered */
 	size = dev->ucm.uc->uc_drv->per_device_auto;
 	if (size && !dev_get_uclass_priv(dev)) {
 		ptr = alloc_priv(size, dev->ucm.uc->uc_drv->flags);
@@ -372,6 +375,10 @@ static int device_alloc_priv(struct udevice *dev)
 		dev_set_uclass_priv(dev, ptr);
 	}
 
+	ret = device_probe_adjs_(dev);
+	if (ret)
+		return ret;
+
 	/* Allocate parent data for this child */
 	if (dev->parent) {
 		size = dev->parent->driver->per_child_auto;
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 276aec855a0..9abca7a530f 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -114,6 +114,9 @@ int uclass_destroy(struct uclass *uc)
 	struct udevice *dev;
 	int ret;
 
+	/* Remove adjunct entries first, leaving only primary devices */
+	uclass_destroy_adjs(uc);
+
 	/*
 	 * We cannot use list_for_each_entry_safe() here. If a device in this
 	 * uclass has a child device also in this uclass, it will be also be
@@ -253,10 +256,23 @@ int uclass_find_first_device(enum uclass_id id, struct udevice **devp)
 	ret = uclass_get(id, &uc);
 	if (ret)
 		return ret;
-	if (list_empty(&uc->dev_head))
-		return 0;
 
-	*devp = list_first_entry(&uc->dev_head, struct udevice, ucm.uc_node);
+	if (CONFIG_IS_ENABLED(DM_UC_ADJUNCT)) {
+		struct uclass_member *ucm;
+
+		/* Skip adjunct entries - only return primary devices */
+		list_for_each_entry(ucm, &uc->dev_head, uc_node) {
+			if (ucm_is_primary(ucm)) {
+				*devp = ucm->dev;
+				return 0;
+			}
+		}
+	} else {
+		if (!list_empty(&uc->dev_head))
+			*devp = list_first_entry(&uc->dev_head,
+						 struct udevice,
+						 ucm.uc_node);
+	}
 
 	return 0;
 }
@@ -266,11 +282,215 @@ void uclass_find_next_device(struct udevice **devp)
 	struct udevice *dev = *devp;
 
 	*devp = NULL;
-	if (!list_is_last(&dev->ucm.uc_node, &dev->ucm.uc->dev_head))
-		*devp = list_entry(dev->ucm.uc_node.next, struct udevice,
-				   ucm.uc_node);
+
+	if (CONFIG_IS_ENABLED(DM_UC_ADJUNCT)) {
+		struct uclass_member *ucm;
+
+		/* Skip adjunct entries - only return primary devices */
+		ucm = &dev->ucm;
+		while (ucm->uc_node.next != &ucm->uc->dev_head) {
+			ucm = list_next_entry(ucm, uc_node);
+			if (ucm_is_primary(ucm)) {
+				*devp = ucm->dev;
+				return;
+			}
+		}
+	} else {
+		if (!list_is_last(&dev->ucm.uc_node,
+				  &dev->ucm.uc->dev_head))
+			*devp = list_entry(dev->ucm.uc_node.next,
+					   struct udevice, ucm.uc_node);
+	}
+}
+
+#if CONFIG_IS_ENABLED(DM_UC_ADJUNCT)
+int ucm_find_first(enum uclass_id id, struct uclass_member **ucmp)
+{
+	struct uclass *uc;
+	int ret;
+
+	*ucmp = NULL;
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return ret;
+
+	if (!list_empty(&uc->dev_head))
+		*ucmp = list_first_entry(&uc->dev_head,
+					 struct uclass_member, uc_node);
+
+	return 0;
+}
+
+void ucm_find_next(struct uclass_member **ucmp)
+{
+	struct uclass_member *ucm = *ucmp;
+
+	if (ucm->uc_node.next == &ucm->uc->dev_head)
+		*ucmp = NULL;
+	else
+		*ucmp = list_next_entry(ucm, uc_node);
+}
+
+struct uclass_member *_ucm_first_primary(struct uclass *uc)
+{
+	struct uclass_member *ucm;
+
+	list_for_each_entry(ucm, &uc->dev_head, uc_node) {
+		if (ucm_is_primary(ucm))
+			return ucm;
+	}
+
+	return NULL;
+}
+
+void _ucm_next_primary(struct uclass_member **ucmp)
+{
+	struct uclass_member *ucm = *ucmp;
+
+	while (ucm->uc_node.next != &ucm->uc->dev_head) {
+		ucm = list_next_entry(ucm, uc_node);
+		if (ucm_is_primary(ucm)) {
+			*ucmp = ucm;
+			return;
+		}
+	}
+	*ucmp = NULL;
+}
+
+/*
+ * Starting from the given member @ucm, return the first member whose device
+ * probes successfully.
+ */
+static struct uclass_member *_ucm_next(struct uclass_member *ucm)
+{
+	for (; ucm; ucm_find_next(&ucm)) {
+		if (!device_probe(ucm->dev))
+			break;
+	}
+
+	return ucm;
+}
+
+struct uclass_member *ucm_first(enum uclass_id id)
+{
+	struct uclass_member *ucm;
+
+	ucm_find_first(id, &ucm);
+
+	return _ucm_next(ucm);
+}
+
+struct uclass_member *ucm_next(struct uclass_member *ucm)
+{
+	ucm_find_next(&ucm);
+
+	return _ucm_next(ucm);
+}
+
+int ucm_first_check(enum uclass_id id, struct uclass_member **ucmp)
+{
+	int ret;
+
+	*ucmp = NULL;
+	ret = ucm_find_first(id, ucmp);
+	if (ret)
+		return ret;
+	if (!*ucmp)
+		return 0;
+
+	return device_probe((*ucmp)->dev);
+}
+
+int ucm_next_check(struct uclass_member **ucmp)
+{
+	ucm_find_next(ucmp);
+
+	if (!*ucmp)
+		return 0;
+
+	return device_probe((*ucmp)->dev);
+}
+
+int ucm_first_drvdata(enum uclass_id id, ulong driver_data,
+		      struct uclass_member **ucmp)
+{
+	struct uclass_member *ucm;
+	int ret;
+
+	ret = ucm_find_first(id, &ucm);
+	if (ret)
+		return ret;
+
+	for (; ucm; ucm_find_next(&ucm)) {
+		if (dev_get_driver_data(ucm->dev) == driver_data) {
+			*ucmp = ucm;
+
+			return device_probe(ucm->dev);
+		}
+	}
+
+	return -ENODEV;
 }
 
+struct uclass_member *ucm_try_first(enum uclass_id id)
+{
+	struct uclass *uc;
+
+	uc = uclass_find(id);
+	if (!uc || list_empty(&uc->dev_head))
+		return NULL;
+
+	return list_first_entry(&uc->dev_head, struct uclass_member, uc_node);
+}
+
+int ucm_find_by_ofnode(enum uclass_id id, ofnode node,
+		       struct uclass_member **ucmp)
+{
+	struct uclass_member *ucm;
+	int ret;
+
+	*ucmp = NULL;
+	if (!ofnode_valid(node))
+		return -ENODEV;
+
+	ret = ucm_find_first(id, &ucm);
+	if (ret)
+		return ret;
+
+	for (; ucm; ucm_find_next(&ucm)) {
+		if (ofnode_equal(dev_ofnode(ucm->dev), node)) {
+			*ucmp = ucm;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+int ucm_find_seq(enum uclass_id id, int seq, struct uclass_member **ucmp)
+{
+	struct uclass_member *ucm;
+	int ret;
+
+	*ucmp = NULL;
+	if (seq == -1)
+		return -ENODEV;
+
+	ret = ucm_find_first(id, &ucm);
+	if (ret)
+		return ret;
+
+	for (; ucm; ucm_find_next(&ucm)) {
+		if (ucm->seq == seq) {
+			*ucmp = ucm;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+#endif /* DM_UC_ADJUNCT */
+
 int uclass_find_device_by_namelen(enum uclass_id id, const char *name, int len,
 				  struct udevice **devp)
 {
@@ -310,12 +530,15 @@ struct udevice *uclass_try_first_device(enum uclass_id id)
 	if (!uc || list_empty(&uc->dev_head))
 		return NULL;
 
+	if (CONFIG_IS_ENABLED(DM_UC_ADJUNCT))
+		return list_first_entry(&uc->dev_head,
+					struct uclass_member, uc_node)->dev;
+
 	return list_first_entry(&uc->dev_head, struct udevice, ucm.uc_node);
 }
 
 int uclass_find_next_free_seq(struct uclass *uc)
 {
-	struct udevice *dev;
 	int max = -1;
 
 	/* If using aliases, start with the highest alias value */
@@ -323,11 +546,23 @@ int uclass_find_next_free_seq(struct uclass *uc)
 	    (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS))
 		max = dev_read_alias_highest_id(uc->uc_drv->name);
 
-	/* Avoid conflict with existing devices */
-	list_for_each_entry(dev, &uc->dev_head, ucm.uc_node) {
-		if (dev->ucm.seq > max)
-			max = dev->ucm.seq;
+	/* Avoid conflict with existing devices and adjuncts */
+	if (CONFIG_IS_ENABLED(DM_UC_ADJUNCT)) {
+		struct uclass_member *ucm;
+
+		list_for_each_entry(ucm, &uc->dev_head, uc_node) {
+			if (ucm->seq > max)
+				max = ucm->seq;
+		}
+	} else {
+		struct udevice *dev;
+
+		list_for_each_entry(dev, &uc->dev_head, ucm.uc_node) {
+			if (dev->ucm.seq > max)
+				max = dev->ucm.seq;
+		}
 	}
+
 	/*
 	 * At this point, max will be -1 if there are no existing aliases or
 	 * devices
@@ -339,7 +574,6 @@ int uclass_find_next_free_seq(struct uclass *uc)
 int uclass_find_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
 {
 	struct uclass *uc;
-	struct udevice *dev;
 	int ret;
 
 	*devp = NULL;
@@ -350,12 +584,27 @@ int uclass_find_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
 	if (ret)
 		return ret;
 
-	uclass_foreach_dev(dev, uc) {
-		log_debug("   - %d '%s'\n", dev->ucm.seq, dev->name);
-		if (dev->ucm.seq == seq) {
-			*devp = dev;
-			log_debug("   - found\n");
-			return 0;
+	if (CONFIG_IS_ENABLED(DM_UC_ADJUNCT)) {
+		struct uclass_member *ucm;
+
+		list_for_each_entry(ucm, &uc->dev_head, uc_node) {
+			log_debug("   - %d '%s'\n", ucm->seq, ucm->dev->name);
+			if (ucm->seq == seq) {
+				*devp = ucm->dev;
+				log_debug("   - found\n");
+				return 0;
+			}
+		}
+	} else {
+		struct udevice *dev;
+
+		uclass_foreach_dev(dev, uc) {
+			log_debug("   - %d '%s'\n", dev->ucm.seq, dev->name);
+			if (dev->ucm.seq == seq) {
+				*devp = dev;
+				log_debug("   - found\n");
+				return 0;
+			}
 		}
 	}
 	log_debug("   - not found\n");
@@ -410,6 +659,7 @@ int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node,
 			goto done;
 		}
 	}
+
 	ret = -ENODEV;
 
 done:
diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h
index 9cb3f090271..4328419f6e6 100644
--- a/include/dm/uclass-internal.h
+++ b/include/dm/uclass-internal.h
@@ -138,6 +138,10 @@ int uclass_find_device(enum uclass_id id, int index, struct udevice **devp);
  * The device is not prepared for use - this is an internal function.
  * The function uclass_get_device_tail() can be used to probe the device.
  *
+ * Note: this skips adjunct members because it uses struct udevice * as
+ * the cursor. Use uclass_foreach_dev() or ucm_find_first() to include
+ * adjuncts.
+ *
  * Return: 0 if OK (found or not found), -ve on error
  */
 int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
@@ -149,6 +153,10 @@ int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
  *
  * The device is not prepared for use - this is an internal function.
  * The function uclass_get_device_tail() can be used to probe the device.
+ *
+ * Note: this skips adjunct members because it uses struct udevice * as
+ * the cursor. Use uclass_foreach_dev() or ucm_find_next() to include
+ * adjuncts.
  */
 void uclass_find_next_device(struct udevice **devp);
 
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
index ad49d675570..d99b76c9404 100644
--- a/include/dm/uclass.h
+++ b/include/dm/uclass.h
@@ -362,12 +362,15 @@ int uclass_get_device_by_endpoint(enum uclass_id class_id, struct udevice *dev,
 /**
  * uclass_first_device() - Get the first device in a uclass
  *
- * The device returned is probed if necessary, and ready for use
- * Devices that fail to probe are skipped
+ * The device returned is probed if necessary, and ready for use.
+ * Devices that fail to probe are skipped.
  *
  * This function is useful to start iterating through a list of devices which
  * are functioning correctly and can be probed.
  *
+ * Note: this skips adjunct members. Use uclass_foreach_dev_probe() or
+ * ucm_first() to include adjuncts.
+ *
  * @id: Uclass ID to look up
  * @devp: Returns pointer to the first device in that uclass if no error
  * occurred, or NULL if there is no usable device
@@ -377,12 +380,15 @@ void uclass_first_device(enum uclass_id id, struct udevice **devp);
 /**
  * uclass_next_device() - Get the next device in a uclass
  *
- * The device returned is probed if necessary, and ready for use
- * Devices that fail to probe are skipped
+ * The device returned is probed if necessary, and ready for use.
+ * Devices that fail to probe are skipped.
  *
  * This function is useful to iterate through a list of devices which
  * are functioning correctly and can be probed.
  *
+ * Note: this skips adjunct members. Use uclass_foreach_dev_probe() or
+ * ucm_next() to include adjuncts.
+ *
  * @devp: On entry, pointer to device to lookup. On exit, returns pointer
  * to the next device in the uclass if no error occurred, or NULL if there is
  * no next device
@@ -393,7 +399,10 @@ void uclass_next_device(struct udevice **devp);
  * uclass_first_device_err() - Get the first device in a uclass
  *
  * The device returned is probed if necessary, and ready for use if no error is
- * returned
+ * returned.
+ *
+ * Note: this skips adjunct members. Use ucm_first_check() to include
+ * adjuncts.
  *
  * @id: Uclass ID to look up
  * @devp: Returns pointer to the first device in that uclass, or NULL if none
@@ -405,7 +414,10 @@ int uclass_first_device_err(enum uclass_id id, struct udevice **devp);
  * uclass_next_device_err() - Get the next device in a uclass
  *
  * The device returned is probed if necessary, and ready for use if no error is
- * returned
+ * returned.
+ *
+ * Note: this skips adjunct members. Use ucm_next_check() to include
+ * adjuncts.
  *
  * @devp: On entry, pointer to device to lookup. On exit, returns pointer
  * to the next device in the uclass if no error occurred, or NULL if
@@ -418,11 +430,14 @@ int uclass_next_device_err(struct udevice **devp);
  * uclass_first_device_check() - Get the first device in a uclass
  *
  * The device returned is probed if necessary, and ready for use if no error is
- * returned
+ * returned.
  *
  * This function is useful to start iterating through a list of devices which
  * are functioning correctly and can be probed.
  *
+ * Note: this skips adjunct members. Use ucm_first_check() to include
+ * adjuncts.
+ *
  * @id: Uclass ID to look up
  * @devp: Returns pointer to the first device in that uclass, or NULL if there
  * is no first device
@@ -435,11 +450,14 @@ int uclass_first_device_check(enum uclass_id id, struct udevice **devp);
  * uclass_next_device_check() - Get the next device in a uclass
  *
  * The device returned is probed if necessary, and ready for use if no error is
- * returned
+ * returned.
  *
  * This function is useful to start iterating through a list of devices which
  * are functioning correctly and can be probed.
  *
+ * Note: this skips adjunct members. Use ucm_next_check() to include
+ * adjuncts.
+ *
  * @devp: On entry, pointer to device to lookup. On exit, returns pointer
  * to the next device in the uclass if any
  * Return: 0 if OK (found or not found), other -ve on error. If an error occurs
@@ -461,6 +479,120 @@ int uclass_next_device_check(struct udevice **devp);
 int uclass_first_device_drvdata(enum uclass_id id, ulong driver_data,
 				struct udevice **devp);
 
+#if CONFIG_IS_ENABLED(DM_UC_ADJUNCT)
+/**
+ * ucm_find_first() - Find the first uclass member in a uclass
+ *
+ * This returns the first entry in the uclass's member list, including
+ * adjunct registrations. Use ucm->dev to get the device. Unlike
+ * uclass_find_first_device(), this sees adjuncts and provides a cursor
+ * that ucm_find_next() can advance correctly.
+ *
+ * @id: Uclass ID to look up
+ * @ucmp: Returns pointer to the first uclass member, or NULL if none
+ * Return: 0 if OK, -ve on error
+ */
+int ucm_find_first(enum uclass_id id, struct uclass_member **ucmp);
+
+/**
+ * ucm_find_next() - Advance to the next uclass member
+ *
+ * @ucmp: On entry, pointer to current member. On exit, pointer to next
+ * member, or NULL if at end
+ */
+void ucm_find_next(struct uclass_member **ucmp);
+
+/**
+ * ucm_first() - Get the first probed member of a uclass
+ *
+ * Members that fail to probe are skipped.
+ *
+ * @id: Uclass ID to look up
+ * Return: Pointer to the first usable member, or NULL if none
+ */
+struct uclass_member *ucm_first(enum uclass_id id);
+
+/**
+ * ucm_next() - Get the next probed member of a uclass
+ *
+ * Members that fail to probe are skipped.
+ *
+ * @ucm: Current member
+ * Return: Pointer to the next usable member, or NULL if at end
+ */
+struct uclass_member *ucm_next(struct uclass_member *ucm);
+
+/**
+ * ucm_first_check() - Get the first probed member of a uclass
+ *
+ * The device is probed if necessary. On probe error the caller can still
+ * advance to the next member with ucm_next_check().
+ *
+ * @id: Uclass ID to look up
+ * @ucmp: Returns pointer to the first member, or NULL if none
+ * Return: 0 if OK (found or not found), other -ve on error
+ */
+int ucm_first_check(enum uclass_id id, struct uclass_member **ucmp);
+
+/**
+ * ucm_next_check() - Get the next probed member of a uclass
+ *
+ * @ucmp: On entry, pointer to current member. On exit, pointer to next
+ * member, or NULL if at end
+ * Return: 0 if OK (found or not found), other -ve on error
+ */
+int ucm_next_check(struct uclass_member **ucmp);
+
+/**
+ * ucm_first_drvdata() - Find the first member with given driver data
+ *
+ * @id: Uclass ID to look up
+ * @driver_data: Driver data to search for
+ * @ucmp: Returns pointer to the first matching member, if found
+ * Return: 0 if found, -ENODEV if not found, other -ve on error
+ */
+int ucm_first_drvdata(enum uclass_id id, ulong driver_data,
+		      struct uclass_member **ucmp);
+
+/**
+ * ucm_try_first() - See if there is a member for a uclass
+ *
+ * Returns the first member without probing. If the uclass does not exist,
+ * returns NULL.
+ *
+ * @id: Uclass ID to check
+ * Return: Pointer to first member, or NULL if none
+ */
+struct uclass_member *ucm_try_first(enum uclass_id id);
+
+/**
+ * ucm_find_seq() - Find a uclass member by sequence number
+ *
+ * Searches through all members of the given uclass (including adjuncts)
+ * for one with the specified sequence number.
+ *
+ * @id: Uclass ID to look up
+ * @seq: Sequence number to find
+ * @ucmp: Returns pointer to the matching member, or NULL if not found
+ * Return: 0 if found, -ENODEV if not found, other -ve on error
+ */
+int ucm_find_seq(enum uclass_id id, int seq, struct uclass_member **ucmp);
+
+/**
+ * ucm_find_by_ofnode() - Find a uclass member by device-tree node
+ *
+ * Searches through all members of the given uclass (including adjuncts)
+ * for one whose device matches the specified ofnode.
+ *
+ * @id: Uclass ID to look up
+ * @node: Device-tree node to find
+ * @ucmp: Returns pointer to the matching member, or NULL if not found
+ * Return: 0 if found, -ENODEV if not found, other -ve on error
+ */
+int ucm_find_by_ofnode(enum uclass_id id, ofnode node,
+		       struct uclass_member **ucmp);
+#endif /* DM_UC_ADJUNCT */
+
 /**
  * uclass_try_first_device()- See if there is a device for a uclass
  *
@@ -478,6 +610,9 @@ struct udevice *uclass_try_first_device(enum uclass_id id);
  * This function probes all devices associated with a uclass by
  * looking for its ID.
  *
+ * Note: this skips adjunct members. Use uclass_foreach_dev_probe() to
+ * include adjuncts.
+ *
  * @id: uclass ID to look up
  * Return: 0 if OK, other -ve on error
  */
@@ -506,49 +641,86 @@ int uclass_id_count(enum uclass_id id);
  */
 #define uclass_id_foreach_dev(id, pos, uc) \
 	if (!uclass_get(id, &uc)) \
-		list_for_each_entry(pos, &uc->dev_head, ucm.uc_node)
+		uclass_foreach_dev(pos, uc)
+
+#if CONFIG_IS_ENABLED(DM_UC_ADJUNCT)
+/* Get the first uclass_member from a uclass, or NULL if empty */
+#define _ucm_first(uc)							\
+	(list_empty(&(uc)->dev_head) ? NULL :				\
+	 list_first_entry(&(uc)->dev_head, struct uclass_member, uc_node))
+
+/* Get the device from a uclass_member pointer, or NULL */
+#define _ucm_to_dev(_ucm)	((_ucm) ? (_ucm)->dev : NULL)
+
+/* Get the first primary uclass_member, skipping adjuncts */
+struct uclass_member *_ucm_first_primary(struct uclass *uc);
+
+/* Advance to the next primary uclass_member, skipping adjuncts */
+void _ucm_next_primary(struct uclass_member **ucmp);
 
 /**
  * uclass_foreach_dev() - iterate through devices of a given uclass
  *
  * This creates a for() loop which works through the available devices in
- * a uclass in order from start to end.
+ * a uclass in order from start to end, including adjunct memberships.
  *
  * @pos: struct udevice * to hold the current device. Set to NULL when there
  * are no more devices.
  * @uc: uclass to scan (`struct uclass *`)
  */
-#define uclass_foreach_dev(pos, uc)	\
-	list_for_each_entry(pos, &uc->dev_head, ucm.uc_node)
+#define uclass_foreach_dev(pos, uc)					\
+	for (struct uclass_member *_ucm = _ucm_first(uc);		\
+	     (pos) = _ucm_to_dev(_ucm), pos;				\
+	     ucm_find_next(&_ucm))
 
 /**
  * uclass_foreach_dev_safe() - safely iterate through devices of a given uclass
  *
  * This creates a for() loop which works through the available devices in
- * a uclass in order from start to end. Inside the loop, it is safe to remove
- * @pos if required.
+ * a uclass in order from start to end, including adjunct memberships.
+ * Inside the loop, it is safe to remove @pos if required.
  *
  * @pos: struct udevice * to hold the current device. Set to NULL when there
  * are no more devices.
- * @next: struct udevice * to hold the next next
+ * @next: struct udevice * to hold the next device
  * @uc: uclass to scan (`struct uclass *`)
  */
-#define uclass_foreach_dev_safe(pos, next, uc)	\
-	list_for_each_entry_safe(pos, next, &uc->dev_head, ucm.uc_node)
+#define uclass_foreach_dev_safe(pos, next, uc)				\
+	for (struct uclass_member *_ucm = _ucm_first(uc), *_ucm_n;	\
+	     (pos) = _ucm_to_dev(_ucm),				\
+	     (next) = pos ? (_ucm_n = _ucm, ucm_find_next(&_ucm_n),	\
+			     _ucm_to_dev(_ucm_n)) : NULL,		\
+	     pos;							\
+	     _ucm = _ucm_n)
 
 /**
  * uclass_foreach_dev_probe() - iterate through devices of a given uclass ID
  *
  * This creates a for() loop which works through the available devices in
- * a uclass in order from start to end. Devices are probed if necessary,
- * and ready for use.
+ * a uclass in order from start to end, including adjunct memberships.
+ * Devices are probed if necessary, and ready for use.
  *
  * @id: Uclass ID
  * @dev: struct udevice * to hold the current device. Set to NULL when there
  * are no more devices.
  */
+#define uclass_foreach_dev_probe(id, dev)				\
+	for (struct uclass_member *_ucm = ucm_first(id);		\
+	     (dev) = _ucm_to_dev(_ucm), dev;				\
+	     _ucm = ucm_next(_ucm))
+
+#else /* !DM_UC_ADJUNCT */
+
+#define uclass_foreach_dev(pos, uc)	\
+	list_for_each_entry(pos, &(uc)->dev_head, ucm.uc_node)
+
+#define uclass_foreach_dev_safe(pos, next, uc)	\
+	list_for_each_entry_safe(pos, next, &(uc)->dev_head, ucm.uc_node)
+
 #define uclass_foreach_dev_probe(id, dev)	\
 	for (uclass_first_device(id, &dev); dev; \
 	     uclass_next_device(&dev))
 
+#endif /* DM_UC_ADJUNCT */
+
 #endif
-- 
2.43.0



More information about the U-Boot mailing list