summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/core.c51
-rw-r--r--include/linux/acpi.h4
-rw-r--r--include/linux/device.h3
-rw-r--r--include/linux/fwnode.h1
4 files changed, 57 insertions, 2 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 07304a3b9ee2..c7e2a9a70865 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/fwnode.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -2133,3 +2134,53 @@ define_dev_printk_level(dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO);
#endif
+
+static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
+{
+ return fwnode && !IS_ERR(fwnode->secondary);
+}
+
+/**
+ * set_primary_fwnode - Change the primary firmware node of a given device.
+ * @dev: Device to handle.
+ * @fwnode: New primary firmware node of the device.
+ *
+ * Set the device's firmware node pointer to @fwnode, but if a secondary
+ * firmware node of the device is present, preserve it.
+ */
+void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
+{
+ if (fwnode) {
+ struct fwnode_handle *fn = dev->fwnode;
+
+ if (fwnode_is_primary(fn))
+ fn = fn->secondary;
+
+ fwnode->secondary = fn;
+ dev->fwnode = fwnode;
+ } else {
+ dev->fwnode = fwnode_is_primary(dev->fwnode) ?
+ dev->fwnode->secondary : NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(set_primary_fwnode);
+
+/**
+ * set_secondary_fwnode - Change the secondary firmware node of a given device.
+ * @dev: Device to handle.
+ * @fwnode: New secondary firmware node of the device.
+ *
+ * If a primary firmware node of the device is present, set its secondary
+ * pointer to @fwnode. Otherwise, set the device's firmware node pointer to
+ * @fwnode.
+ */
+void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
+{
+ if (fwnode)
+ fwnode->secondary = ERR_PTR(-ENODEV);
+
+ if (fwnode_is_primary(dev->fwnode))
+ dev->fwnode->secondary = fwnode;
+ else
+ dev->fwnode = fwnode;
+}
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index ec488d03b518..dd12127f171c 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -54,8 +54,8 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
}
#define ACPI_COMPANION(dev) acpi_node((dev)->fwnode)
-#define ACPI_COMPANION_SET(dev, adev) (dev)->fwnode = (adev) ? \
- acpi_fwnode_handle(adev) : NULL
+#define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \
+ acpi_fwnode_handle(adev) : NULL)
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
static inline bool has_acpi_companion(struct device *dev)
diff --git a/include/linux/device.h b/include/linux/device.h
index badef20b876a..324d02add7b4 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -940,6 +940,9 @@ extern void unlock_device_hotplug(void);
extern int lock_device_hotplug_sysfs(void);
extern int device_offline(struct device *dev);
extern int device_online(struct device *dev);
+extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
+extern void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
+
/*
* Root device objects for grouping under /sys/devices
*/
diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 17bb5f039509..fc30b84b532a 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -20,6 +20,7 @@ enum fwnode_type {
struct fwnode_handle {
enum fwnode_type type;
+ struct fwnode_handle *secondary;
};
#endif