Provide a structure and a set of functions allowing to set up automatic
secondary firmware node assignment for platform devices. It uses
a behind-the-scenes bus notifier for a group of named software nodes. It
will wait for bus events and when a device is added, it will check its
name against the software node's name and - on match - assign the
software node as the secondary firmware node of the device's *real*
firmware node.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
drivers/base/swnode.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/property.h | 18 ++++++++
2 files changed, 123 insertions(+)
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 51320837f3a9f1bf4f65aa161d9b941affc74936..97e3354cdafd94e175d29acb697a0dc61186a9c8 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -6,6 +6,7 @@
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/
+#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -1088,6 +1089,110 @@ int device_create_managed_software_node(struct device *dev,
}
EXPORT_SYMBOL_GPL(device_create_managed_software_node);
+static struct software_node_auto_secondary *to_auto_sec(struct notifier_block *nb)
+{
+ return container_of(nb, struct software_node_auto_secondary, nb);
+}
+
+static void swnode_set_secondary_fwnode(struct device *dev,
+ const struct software_node *swnode)
+{
+ struct fwnode_handle *fwnode;
+
+ fwnode = software_node_fwnode(swnode);
+ if (WARN(!fwnode, "Software node %s should have been registered before", swnode->name))
+ return;
+
+ set_secondary_fwnode(dev, fwnode);
+}
+
+static int swnode_auto_secondary_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct software_node_auto_secondary *auto_sec = to_auto_sec(nb);
+ const struct software_node * const *swnode;
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ for (swnode = auto_sec->node_group; *swnode; swnode++) {
+ if (strcmp(dev_name(dev), (*swnode)->name))
+ continue;
+
+ swnode_set_secondary_fwnode(dev, *swnode);
+ return NOTIFY_OK;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+/**
+ * software_node_register_auto_secondary() - set up automatic assignment of
+ * secondary firmware nodes
+ * @auto_sec: Context data to use.
+ *
+ * NOTE: All software nodes passed in @auto_sec must be named.
+ *
+ * Returns:
+ * 0 on success, negative error number on failure.
+ *
+ * This registers the software node group passed in @auto_sec and sets up
+ * automatic assignment of them as secondary firmware nodes of real nodes
+ * attached to appropriate devices on the bus specified in @auto_sec. The
+ * software nodes must be named and their names must be the same as the
+ * devices they should reference. The assignment happens when the device is
+ * first added to the bus.
+ */
+int software_node_register_auto_secondary(struct software_node_auto_secondary *auto_sec)
+{
+ const struct software_node * const *swnode;
+ int ret;
+
+ if (!auto_sec->node_group || !auto_sec->bus)
+ return -EINVAL;
+
+ for (swnode = auto_sec->node_group; *swnode; swnode++) {
+ if (!(*swnode)->name)
+ return -EINVAL;
+ }
+
+ ret = software_node_register_node_group(auto_sec->node_group);
+ if (ret)
+ return ret;
+
+ auto_sec->nb.notifier_call = swnode_auto_secondary_notifier;
+ ret = bus_register_notifier(auto_sec->bus, &auto_sec->nb);
+ if (ret)
+ software_node_unregister_node_group(auto_sec->node_group);
+
+ /* Device may have been already added. */
+ for (swnode = auto_sec->node_group; *swnode; swnode++) {
+ struct device *dev __free(put_device) =
+ bus_find_device_by_name(auto_sec->bus, NULL, (*swnode)->name);
+ if (dev)
+ swnode_set_secondary_fwnode(dev, *swnode);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(software_node_register_auto_secondary);
+
+/**
+ * software_node_unregister_auto_secondary() - unregister automatic assignment
+ * of secondary firmware nodes
+ * @auto_sec: Address of the context structure used at registration
+ */
+void software_node_unregister_auto_secondary(struct software_node_auto_secondary *auto_sec)
+{
+ bus_unregister_notifier(auto_sec->bus, &auto_sec->nb);
+ software_node_unregister_node_group(auto_sec->node_group);
+}
+EXPORT_SYMBOL_GPL(software_node_unregister_auto_secondary);
+
void software_node_notify(struct device *dev)
{
struct swnode *swnode;
diff --git a/include/linux/property.h b/include/linux/property.h
index e30ef23a9af3396455d5bb19bb6d41089d81d77f..2a7af60cbb8ecc2ba83819ce92562db42705b82a 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -15,10 +15,12 @@
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/fwnode.h>
+#include <linux/notifier.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/util_macros.h>
+struct bus_type;
struct device;
enum dev_prop_type {
@@ -610,4 +612,20 @@ int device_create_managed_software_node(struct device *dev,
const struct property_entry *properties,
const struct software_node *parent);
+/**
+ * struct software_node_auto_secondary - context data for automatic secondary
+ * fwnode assignment
+ * @nb: Private bus notifier data - don't use
+ * @node_group: NULL-terminated array of software node addresses
+ * @bus: Bus on which to wait for devices
+ */
+struct software_node_auto_secondary {
+ struct notifier_block nb;
+ const struct software_node * const *node_group;
+ const struct bus_type *bus;
+};
+
+int software_node_register_auto_secondary(struct software_node_auto_secondary *auto_sec);
+void software_node_unregister_auto_secondary(struct software_node_auto_secondary *auto_sec);
+
#endif /* _LINUX_PROPERTY_H_ */
--
2.47.3
On Thu, Mar 19, 2026 at 05:10:54PM +0100, Bartosz Golaszewski wrote:
> Provide a structure and a set of functions allowing to set up automatic
> secondary firmware node assignment for platform devices. It uses
> a behind-the-scenes bus notifier for a group of named software nodes. It
> will wait for bus events and when a device is added, it will check its
> name against the software node's name and - on match - assign the
> software node as the secondary firmware node of the device's *real*
> firmware node.
...
> +/**
> + * software_node_register_auto_secondary() - set up automatic assignment of
> + * secondary firmware nodes
> + * @auto_sec: Context data to use.
> + *
> + * NOTE: All software nodes passed in @auto_sec must be named.
> + * Returns:
Is it with 's' in other kernel-doc? The official is "Return", the 's' variant
is supported, but not documented.
> + * 0 on success, negative error number on failure.
The Return section must be last in the kernel-doc description. This is documented.
> + * This registers the software node group passed in @auto_sec and sets up
> + * automatic assignment of them as secondary firmware nodes of real nodes
> + * attached to appropriate devices on the bus specified in @auto_sec. The
> + * software nodes must be named and their names must be the same as the
> + * devices they should reference. The assignment happens when the device is
> + * first added to the bus.
> + */
...
> +/**
> + * struct software_node_auto_secondary - context data for automatic secondary
> + * fwnode assignment
> + * @nb: Private bus notifier data - don't use
Mark it with __private then.
> + * @node_group: NULL-terminated array of software node addresses
> + * @bus: Bus on which to wait for devices
If bus is not compiled into kernel, this optionally has to support NULL
in the code (I haven't checked the code, though).
> + */
> +struct software_node_auto_secondary {
> + struct notifier_block nb;
struct notifier_block __private nb;
> + const struct software_node * const *node_group;
> + const struct bus_type *bus;
> +};
--
With Best Regards,
Andy Shevchenko
On Fri, Mar 20, 2026 at 8:39 AM Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote: > > On Thu, Mar 19, 2026 at 05:10:54PM +0100, Bartosz Golaszewski wrote: > > Provide a structure and a set of functions allowing to set up automatic > > secondary firmware node assignment for platform devices. It uses > > a behind-the-scenes bus notifier for a group of named software nodes. It > > will wait for bus events and when a device is added, it will check its > > name against the software node's name and - on match - assign the > > software node as the secondary firmware node of the device's *real* > > firmware node. > > ... > > > +/** > > + * software_node_register_auto_secondary() - set up automatic assignment of > > + * secondary firmware nodes > > + * @auto_sec: Context data to use. > > + * > > + * NOTE: All software nodes passed in @auto_sec must be named. > > > + * Returns: > > Is it with 's' in other kernel-doc? The official is "Return", the 's' variant > is supported, but not documented. > > > + * 0 on success, negative error number on failure. > > The Return section must be last in the kernel-doc description. This is documented. > > > + * This registers the software node group passed in @auto_sec and sets up > > + * automatic assignment of them as secondary firmware nodes of real nodes > > + * attached to appropriate devices on the bus specified in @auto_sec. The > > + * software nodes must be named and their names must be the same as the > > + * devices they should reference. The assignment happens when the device is > > + * first added to the bus. > > + */ > > ... > > > +/** > > + * struct software_node_auto_secondary - context data for automatic secondary > > + * fwnode assignment > > + * @nb: Private bus notifier data - don't use > > Mark it with __private then. Ok for this and the ones above. > > > + * @node_group: NULL-terminated array of software node addresses > > + * @bus: Bus on which to wait for devices > > If bus is not compiled into kernel, this optionally has to support NULL > in the code (I haven't checked the code, though). > The bus type object for given bus is typically defined in whatever compilation unit contains the implementation of that bus. It's not possible to access its address if it's not enabled. Bart
© 2016 - 2026 Red Hat, Inc.