driver core: Introduce device_{add,remove}_of_node()
authorHerve Codina <herve.codina@bootlin.com>
Mon, 24 Feb 2025 14:13:51 +0000 (15:13 +0100)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 28 Feb 2025 21:12:58 +0000 (15:12 -0600)
An of_node can be set to a device using device_set_node(), which does not
prevent any of_node and/or fwnode overwrites.

When adding an of_node on an already present device, the following
operations need to be done:

  - Attach the of_node only if no of_node is already attached

  - Attach the of_node as a fwnode if no fwnode were already attached

This is the purpose of device_add_of_node().  device_remove_of_node()
reverts the operations done by device_add_of_node().

Link: https://lore.kernel.org/r/20250224141356.36325-2-herve.codina@bootlin.com
Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/base/core.c
include/linux/device.h

index 5a1f051981149dc5b5eee4fb69c0ab748a85956d..49eb5aaf19cc93174a1a249cc8f215e82db3d606 100644 (file)
@@ -5170,6 +5170,67 @@ void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
 }
 EXPORT_SYMBOL_GPL(set_secondary_fwnode);
 
+/**
+ * device_remove_of_node - Remove an of_node from a device
+ * @dev: device whose device tree node is being removed
+ */
+void device_remove_of_node(struct device *dev)
+{
+       dev = get_device(dev);
+       if (!dev)
+               return;
+
+       if (!dev->of_node)
+               goto end;
+
+       if (dev->fwnode == of_fwnode_handle(dev->of_node))
+               dev->fwnode = NULL;
+
+       of_node_put(dev->of_node);
+       dev->of_node = NULL;
+
+end:
+       put_device(dev);
+}
+EXPORT_SYMBOL_GPL(device_remove_of_node);
+
+/**
+ * device_add_of_node - Add an of_node to an existing device
+ * @dev: device whose device tree node is being added
+ * @of_node: of_node to add
+ *
+ * Return: 0 on success or error code on failure.
+ */
+int device_add_of_node(struct device *dev, struct device_node *of_node)
+{
+       int ret;
+
+       if (!of_node)
+               return -EINVAL;
+
+       dev = get_device(dev);
+       if (!dev)
+               return -EINVAL;
+
+       if (dev->of_node) {
+               dev_err(dev, "Cannot replace node %pOF with %pOF\n",
+                       dev->of_node, of_node);
+               ret = -EBUSY;
+               goto end;
+       }
+
+       dev->of_node = of_node_get(of_node);
+
+       if (!dev->fwnode)
+               dev->fwnode = of_fwnode_handle(of_node);
+
+       ret = 0;
+end:
+       put_device(dev);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(device_add_of_node);
+
 /**
  * device_set_of_node_from_dev - reuse device-tree node of another device
  * @dev: device whose device-tree node is being set
index 80a5b32689866c39128b4a564f8521cec663de01..1244e58922925edc2b6119c195cd1e6a0839d80e 100644 (file)
@@ -1191,6 +1191,8 @@ int device_online(struct device *dev);
 void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
 void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
 void device_set_node(struct device *dev, struct fwnode_handle *fwnode);
+int device_add_of_node(struct device *dev, struct device_node *of_node);
+void device_remove_of_node(struct device *dev);
 void device_set_of_node_from_dev(struct device *dev, const struct device *dev2);
 
 static inline struct device_node *dev_of_node(struct device *dev)