device property: Make it possible to use secondary firmware nodes
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 3 Apr 2015 21:23:37 +0000 (23:23 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 3 Apr 2015 21:23:37 +0000 (23:23 +0200)
Add a secondary pointer to struct fwnode_handle so as to make it
possible for a device to have two firmware nodes associated with
it at the same time, for example, an ACPI node and a node with
a set of properties provided by platform initialization code.

In the future that will allow device property lookup to fall back
from the primary firmware node to the secondary one if the given
property is not present there to make it easier to provide defaults
for device properties used by device drivers.

Introduce two helper routines, set_primary_fwnode() and
set_secondary_fwnode() allowing callers to add a primary/secondary
firmware node to the given device in such a way that

 (1) If there's only one firmware node for that device, it will be
     pointed to by the device's firmware node pointer.
 (2) If both the primary and secondary firmware nodes are present,
     the primary one will be pointed to by the device's firmware
     node pointer, while the secondary one will be pointed to by the
     primary node's secondary pointer.
 (3) If one of these nodes is removed (by calling one of the new
     nelpers with NULL as the second argument), the other one will
     be preserved.

Make ACPI use set_primary_fwnode() for attaching its firmware nodes
to devices.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/base/core.c
include/linux/acpi.h
include/linux/device.h
include/linux/fwnode.h

index 07304a3b9ee2872d85526cba24eabd422997aa7e..c7e2a9a708655bceeb8fa517106b5d904d332db8 100644 (file)
@@ -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;
+}
index ec488d03b5186453913c9600618b973c2ae0aa4a..dd12127f171c7569fd169915d01dd24e7af97aba 100644 (file)
@@ -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)
index badef20b876a7936a518a45d03e4ff4fc121cebd..324d02add7b4d4ea0794ef4fe4adb1826dd36532 100644 (file)
@@ -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
  */
index 17bb5f039509d3d24aa2c62c00a9502d5f3776d9..fc30b84b532a08fa2d0306bed98146ec4cd4de1a 100644 (file)
@@ -20,6 +20,7 @@ enum fwnode_type {
 
 struct fwnode_handle {
        enum fwnode_type type;
+       struct fwnode_handle *secondary;
 };
 
 #endif