Driver core: Unified device properties interface for platform firmware
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 4 Nov 2014 00:28:56 +0000 (01:28 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 4 Nov 2014 20:58:21 +0000 (21:58 +0100)
Add a uniform interface by which device drivers can request device
properties from the platform firmware by providing a property name
and the corresponding data type.  The purpose of it is to help to
write portable code that won't depend on any particular platform
firmware interface.

The following general helper functions are added:

device_property_present()
device_property_read_u8()
device_property_read_u16()
device_property_read_u32()
device_property_read_u64()
device_property_read_string()
device_property_read_u8_array()
device_property_read_u16_array()
device_property_read_u32_array()
device_property_read_u64_array()
device_property_read_string_array()

The first one allows the caller to check if the given property is
present.  The next 5 of them allow single-valued properties of
various types to be retrieved in a uniform way.  The remaining 5 are
for reading properties with multiple values (arrays of either numbers
or strings).

The interface covers both ACPI and Device Trees.

This change set includes material from Mika Westerberg and Aaron Lu.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Grant Likely <grant.likely@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/property.c
drivers/base/Makefile
drivers/base/property.c [new file with mode: 0644]
drivers/of/base.c
include/linux/acpi.h
include/linux/of.h
include/linux/property.h [new file with mode: 0644]

index c4a3e800e82caf1c9f2f0918a0faa2f7294423e7..2541b1fd1fa5906a1ece2f43b2e8c708d93a3439 100644 (file)
@@ -362,3 +362,181 @@ int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name,
        return -EPROTO;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference);
+
+int acpi_dev_prop_get(struct acpi_device *adev, const char *propname,
+                     void **valptr)
+{
+       return acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY,
+                                    (const union acpi_object **)valptr);
+}
+
+int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
+                             enum dev_prop_type proptype, void *val)
+{
+       const union acpi_object *obj;
+       int ret;
+
+       if (!val)
+               return -EINVAL;
+
+       if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) {
+               ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_INTEGER, &obj);
+               if (ret)
+                       return ret;
+
+               switch (proptype) {
+               case DEV_PROP_U8:
+                       if (obj->integer.value > U8_MAX)
+                               return -EOVERFLOW;
+                       *(u8 *)val = obj->integer.value;
+                       break;
+               case DEV_PROP_U16:
+                       if (obj->integer.value > U16_MAX)
+                               return -EOVERFLOW;
+                       *(u16 *)val = obj->integer.value;
+                       break;
+               case DEV_PROP_U32:
+                       if (obj->integer.value > U32_MAX)
+                               return -EOVERFLOW;
+                       *(u32 *)val = obj->integer.value;
+                       break;
+               default:
+                       *(u64 *)val = obj->integer.value;
+                       break;
+               }
+       } else if (proptype == DEV_PROP_STRING) {
+               ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_STRING, &obj);
+               if (ret)
+                       return ret;
+
+               *(char **)val = obj->string.pointer;
+       } else {
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val,
+                                      size_t nval)
+{
+       int i;
+
+       for (i = 0; i < nval; i++) {
+               if (items[i].type != ACPI_TYPE_INTEGER)
+                       return -EPROTO;
+               if (items[i].integer.value > U8_MAX)
+                       return -EOVERFLOW;
+
+               val[i] = items[i].integer.value;
+       }
+       return 0;
+}
+
+static int acpi_copy_property_array_u16(const union acpi_object *items,
+                                       u16 *val, size_t nval)
+{
+       int i;
+
+       for (i = 0; i < nval; i++) {
+               if (items[i].type != ACPI_TYPE_INTEGER)
+                       return -EPROTO;
+               if (items[i].integer.value > U16_MAX)
+                       return -EOVERFLOW;
+
+               val[i] = items[i].integer.value;
+       }
+       return 0;
+}
+
+static int acpi_copy_property_array_u32(const union acpi_object *items,
+                                       u32 *val, size_t nval)
+{
+       int i;
+
+       for (i = 0; i < nval; i++) {
+               if (items[i].type != ACPI_TYPE_INTEGER)
+                       return -EPROTO;
+               if (items[i].integer.value > U32_MAX)
+                       return -EOVERFLOW;
+
+               val[i] = items[i].integer.value;
+       }
+       return 0;
+}
+
+static int acpi_copy_property_array_u64(const union acpi_object *items,
+                                       u64 *val, size_t nval)
+{
+       int i;
+
+       for (i = 0; i < nval; i++) {
+               if (items[i].type != ACPI_TYPE_INTEGER)
+                       return -EPROTO;
+
+               val[i] = items[i].integer.value;
+       }
+       return 0;
+}
+
+static int acpi_copy_property_array_string(const union acpi_object *items,
+                                          char **val, size_t nval)
+{
+       int i;
+
+       for (i = 0; i < nval; i++) {
+               if (items[i].type != ACPI_TYPE_STRING)
+                       return -EPROTO;
+
+               val[i] = items[i].string.pointer;
+       }
+       return 0;
+}
+
+int acpi_dev_prop_read(struct acpi_device *adev, const char *propname,
+                      enum dev_prop_type proptype, void *val, size_t nval)
+{
+       const union acpi_object *obj;
+       const union acpi_object *items;
+       int ret;
+
+       if (val && nval == 1) {
+               ret = acpi_dev_prop_read_single(adev, propname, proptype, val);
+               if (!ret)
+                       return ret;
+       }
+
+       ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj);
+       if (ret)
+               return ret;
+
+       if (!val)
+               return obj->package.count;
+       else if (nval <= 0)
+               return -EINVAL;
+
+       if (nval > obj->package.count)
+               return -EOVERFLOW;
+
+       items = obj->package.elements;
+       switch (proptype) {
+       case DEV_PROP_U8:
+               ret = acpi_copy_property_array_u8(items, (u8 *)val, nval);
+               break;
+       case DEV_PROP_U16:
+               ret = acpi_copy_property_array_u16(items, (u16 *)val, nval);
+               break;
+       case DEV_PROP_U32:
+               ret = acpi_copy_property_array_u32(items, (u32 *)val, nval);
+               break;
+       case DEV_PROP_U64:
+               ret = acpi_copy_property_array_u64(items, (u64 *)val, nval);
+               break;
+       case DEV_PROP_STRING:
+               ret = acpi_copy_property_array_string(items, (char **)val, nval);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
index 6922cd6850a20621f08168011df84d63385ef282..53c3fe1aeb29ceb5c31cc0e0ecaa569d91f8b891 100644 (file)
@@ -4,7 +4,7 @@ obj-y                   := component.o core.o bus.o dd.o syscore.o \
                           driver.o class.o platform.o \
                           cpu.o firmware.o init.o map.o devres.o \
                           attribute_container.o transport_class.o \
-                          topology.o container.o
+                          topology.o container.o property.o
 obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
 obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
 obj-y                  += power/
diff --git a/drivers/base/property.c b/drivers/base/property.c
new file mode 100644 (file)
index 0000000..6a94ef6
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * property.c - Unified device property interface.
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *          Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/property.h>
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+/**
+ * device_property_present - check if a property of a device is present
+ * @dev: Device whose property is being checked
+ * @propname: Name of the property
+ *
+ * Check if property @propname is present in the device firmware description.
+ */
+bool device_property_present(struct device *dev, const char *propname)
+{
+       if (IS_ENABLED(CONFIG_OF) && dev->of_node)
+               return of_property_read_bool(dev->of_node, propname);
+
+       return !acpi_dev_prop_get(ACPI_COMPANION(dev), propname, NULL);
+}
+EXPORT_SYMBOL_GPL(device_property_present);
+
+#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
+       (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
+             : of_property_count_elems_of_size((node), (propname), sizeof(type))
+
+#define DEV_PROP_READ_ARRAY(_dev_, _propname_, _type_, _proptype_, _val_, _nval_) \
+       IS_ENABLED(CONFIG_OF) && _dev_->of_node ? \
+               (OF_DEV_PROP_READ_ARRAY(_dev_->of_node, _propname_, _type_, \
+                                       _val_, _nval_)) : \
+               acpi_dev_prop_read(ACPI_COMPANION(_dev_), _propname_, \
+                                  _proptype_, _val_, _nval_)
+
+/**
+ * device_property_read_u8_array - return a u8 array property of a device
+ * @dev: Device to get the property of
+ * @propname: Name of the property
+ * @val: The values are stored here
+ * @nval: Size of the @val array
+ *
+ * Function reads an array of u8 properties with @propname from the device
+ * firmware description and stores them to @val if found.
+ *
+ * Return: %0 if the property was found (success),
+ *        %-EINVAL if given arguments are not valid,
+ *        %-ENODATA if the property does not have a value,
+ *        %-EPROTO if the property is not an array of numbers,
+ *        %-EOVERFLOW if the size of the property is not as expected.
+ */
+int device_property_read_u8_array(struct device *dev, const char *propname,
+                                 u8 *val, size_t nval)
+{
+       return DEV_PROP_READ_ARRAY(dev, propname, u8, DEV_PROP_U8, val, nval);
+}
+EXPORT_SYMBOL_GPL(device_property_read_u8_array);
+
+/**
+ * device_property_read_u16_array - return a u16 array property of a device
+ * @dev: Device to get the property of
+ * @propname: Name of the property
+ * @val: The values are stored here
+ * @nval: Size of the @val array
+ *
+ * Function reads an array of u16 properties with @propname from the device
+ * firmware description and stores them to @val if found.
+ *
+ * Return: %0 if the property was found (success),
+ *        %-EINVAL if given arguments are not valid,
+ *        %-ENODATA if the property does not have a value,
+ *        %-EPROTO if the property is not an array of numbers,
+ *        %-EOVERFLOW if the size of the property is not as expected.
+ */
+int device_property_read_u16_array(struct device *dev, const char *propname,
+                                  u16 *val, size_t nval)
+{
+       return DEV_PROP_READ_ARRAY(dev, propname, u16, DEV_PROP_U16, val, nval);
+}
+EXPORT_SYMBOL_GPL(device_property_read_u16_array);
+
+/**
+ * device_property_read_u32_array - return a u32 array property of a device
+ * @dev: Device to get the property of
+ * @propname: Name of the property
+ * @val: The values are stored here
+ * @nval: Size of the @val array
+ *
+ * Function reads an array of u32 properties with @propname from the device
+ * firmware description and stores them to @val if found.
+ *
+ * Return: %0 if the property was found (success),
+ *        %-EINVAL if given arguments are not valid,
+ *        %-ENODATA if the property does not have a value,
+ *        %-EPROTO if the property is not an array of numbers,
+ *        %-EOVERFLOW if the size of the property is not as expected.
+ */
+int device_property_read_u32_array(struct device *dev, const char *propname,
+                                  u32 *val, size_t nval)
+{
+       return DEV_PROP_READ_ARRAY(dev, propname, u32, DEV_PROP_U32, val, nval);
+}
+EXPORT_SYMBOL_GPL(device_property_read_u32_array);
+
+/**
+ * device_property_read_u64_array - return a u64 array property of a device
+ * @dev: Device to get the property of
+ * @propname: Name of the property
+ * @val: The values are stored here
+ * @nval: Size of the @val array
+ *
+ * Function reads an array of u64 properties with @propname from the device
+ * firmware description and stores them to @val if found.
+ *
+ * Return: %0 if the property was found (success),
+ *        %-EINVAL if given arguments are not valid,
+ *        %-ENODATA if the property does not have a value,
+ *        %-EPROTO if the property is not an array of numbers,
+ *        %-EOVERFLOW if the size of the property is not as expected.
+ */
+int device_property_read_u64_array(struct device *dev, const char *propname,
+                                  u64 *val, size_t nval)
+{
+       return DEV_PROP_READ_ARRAY(dev, propname, u64, DEV_PROP_U64, val, nval);
+}
+EXPORT_SYMBOL_GPL(device_property_read_u64_array);
+
+/**
+ * device_property_read_string_array - return a string array property of device
+ * @dev: Device to get the property of
+ * @propname: Name of the property
+ * @val: The values are stored here
+ * @nval: Size of the @val array
+ *
+ * Function reads an array of string properties with @propname from the device
+ * firmware description and stores them to @val if found.
+ *
+ * Return: %0 if the property was found (success),
+ *        %-EINVAL if given arguments are not valid,
+ *        %-ENODATA if the property does not have a value,
+ *        %-EPROTO or %-EILSEQ if the property is not an array of strings,
+ *        %-EOVERFLOW if the size of the property is not as expected.
+ */
+int device_property_read_string_array(struct device *dev, const char *propname,
+                                     const char **val, size_t nval)
+{
+       return IS_ENABLED(CONFIG_OF) && dev->of_node ?
+               of_property_read_string_array(dev->of_node, propname, val, nval) :
+               acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
+                                  DEV_PROP_STRING, val, nval);
+}
+EXPORT_SYMBOL_GPL(device_property_read_string_array);
+
+/**
+ * device_property_read_string - return a string property of a device
+ * @dev: Device to get the property of
+ * @propname: Name of the property
+ * @val: The value is stored here
+ *
+ * Function reads property @propname from the device firmware description and
+ * stores the value into @val if found. The value is checked to be a string.
+ *
+ * Return: %0 if the property was found (success),
+ *        %-EINVAL if given arguments are not valid,
+ *        %-ENODATA if the property does not have a value,
+ *        %-EPROTO or %-EILSEQ if the property type is not a string.
+ */
+int device_property_read_string(struct device *dev, const char *propname,
+                               const char **val)
+{
+       return IS_ENABLED(CONFIG_OF) && dev->of_node ?
+               of_property_read_string(dev->of_node, propname, val) :
+               acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
+                                  DEV_PROP_STRING, val, 1);
+}
+EXPORT_SYMBOL_GPL(device_property_read_string);
index 3823edf2d0120d9e89a8195f4845c7d144bcefcf..4c2ccde42427b864c570dd2b106b9ec35eaf95c5 100644 (file)
@@ -1249,6 +1249,39 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
 }
 EXPORT_SYMBOL_GPL(of_property_read_u64);
 
+/**
+ * of_property_read_u64_array - Find and read an array of 64 bit integers
+ * from a property.
+ *
+ * @np:                device node from which the property value is to be read.
+ * @propname:  name of the property to be searched.
+ * @out_values:        pointer to return value, modified only if return value is 0.
+ * @sz:                number of array elements to read
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64_array(const struct device_node *np,
+                              const char *propname, u64 *out_values,
+                              size_t sz)
+{
+       const __be32 *val = of_find_property_value_of_size(np, propname,
+                                               (sz * sizeof(*out_values)));
+
+       if (IS_ERR(val))
+               return PTR_ERR(val);
+
+       while (sz--) {
+               *out_values++ = of_read_number(val, 2);
+               val += 2;
+       }
+       return 0;
+}
+
 /**
  * of_property_read_string - Find and read a string from a property
  * @np:                device node from which the property value is to be read.
index dcdf8738898ca14b0fd08024f3e51f62867c9a95..76d64d6a903a8ce41bd0e9ff4b02a8213945a775 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/errno.h>
 #include <linux/ioport.h>      /* for struct resource */
 #include <linux/device.h>
+#include <linux/property.h>
 
 #ifndef _LINUX
 #define _LINUX
@@ -677,6 +678,13 @@ int acpi_dev_get_property_array(struct acpi_device *adev, const char *name,
 int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name,
                                    const char *cells_name, size_t index,
                                    struct acpi_reference_args *args);
+
+int acpi_dev_prop_get(struct acpi_device *adev, const char *propname,
+                     void **valptr);
+int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
+                             enum dev_prop_type proptype, void *val);
+int acpi_dev_prop_read(struct acpi_device *adev, const char *propname,
+                      enum dev_prop_type proptype, void *val, size_t nval);
 #else
 static inline int acpi_dev_get_property(struct acpi_device *adev,
                                        const char *name, acpi_object_type type,
@@ -697,6 +705,30 @@ static inline int acpi_dev_get_property_reference(struct acpi_device *adev,
 {
        return -ENXIO;
 }
+
+static inline int acpi_dev_prop_get(struct acpi_device *adev,
+                                   const char *propname,
+                                   void **valptr)
+{
+       return -ENXIO;
+}
+
+static inline int acpi_dev_prop_read_single(struct acpi_device *adev,
+                                           const char *propname,
+                                           enum dev_prop_type proptype,
+                                           void *val)
+{
+       return -ENXIO;
+}
+
+static inline int acpi_dev_prop_read(struct acpi_device *adev,
+                                    const char *propname,
+                                    enum dev_prop_type proptype,
+                                    void *val, size_t nval)
+{
+       return -ENXIO;
+}
+
 #endif
 
 #endif /*_LINUX_ACPI_H*/
index 29f0adc5f3e47e7c97c4e3aed816437ea9a4fa17..ce9f6a2b3532f4a8e16ec462d0145449ec9a655b 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/spinlock.h>
 #include <linux/topology.h>
 #include <linux/notifier.h>
+#include <linux/property.h>
 
 #include <asm/byteorder.h>
 #include <asm/errno.h>
@@ -263,6 +264,10 @@ extern int of_property_read_u32_array(const struct device_node *np,
                                      size_t sz);
 extern int of_property_read_u64(const struct device_node *np,
                                const char *propname, u64 *out_value);
+extern int of_property_read_u64_array(const struct device_node *np,
+                                     const char *propname,
+                                     u64 *out_values,
+                                     size_t sz);
 
 extern int of_property_read_string(struct device_node *np,
                                   const char *propname,
@@ -477,6 +482,13 @@ static inline int of_property_read_u32_array(const struct device_node *np,
        return -ENOSYS;
 }
 
+static inline int of_property_read_u64_array(const struct device_node *np,
+                                            const char *propname,
+                                            u64 *out_values, size_t sz)
+{
+       return -ENOSYS;
+}
+
 static inline int of_property_read_string(struct device_node *np,
                                          const char *propname,
                                          const char **out_string)
diff --git a/include/linux/property.h b/include/linux/property.h
new file mode 100644 (file)
index 0000000..9242fb0
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * property.h - Unified device property interface.
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *          Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _LINUX_PROPERTY_H_
+#define _LINUX_PROPERTY_H_
+
+#include <linux/types.h>
+
+struct device;
+
+enum dev_prop_type {
+       DEV_PROP_U8,
+       DEV_PROP_U16,
+       DEV_PROP_U32,
+       DEV_PROP_U64,
+       DEV_PROP_STRING,
+       DEV_PROP_MAX,
+};
+
+bool device_property_present(struct device *dev, const char *propname);
+int device_property_read_u8_array(struct device *dev, const char *propname,
+                                 u8 *val, size_t nval);
+int device_property_read_u16_array(struct device *dev, const char *propname,
+                                  u16 *val, size_t nval);
+int device_property_read_u32_array(struct device *dev, const char *propname,
+                                  u32 *val, size_t nval);
+int device_property_read_u64_array(struct device *dev, const char *propname,
+                                  u64 *val, size_t nval);
+int device_property_read_string_array(struct device *dev, const char *propname,
+                                     const char **val, size_t nval);
+int device_property_read_string(struct device *dev, const char *propname,
+                               const char **val);
+
+static inline bool device_property_read_bool(struct device *dev,
+                                            const char *propname)
+{
+       return device_property_present(dev, propname);
+}
+
+static inline int device_property_read_u8(struct device *dev,
+                                         const char *propname, u8 *val)
+{
+       return device_property_read_u8_array(dev, propname, val, 1);
+}
+
+static inline int device_property_read_u16(struct device *dev,
+                                          const char *propname, u16 *val)
+{
+       return device_property_read_u16_array(dev, propname, val, 1);
+}
+
+static inline int device_property_read_u32(struct device *dev,
+                                          const char *propname, u32 *val)
+{
+       return device_property_read_u32_array(dev, propname, val, 1);
+}
+
+static inline int device_property_read_u64(struct device *dev,
+                                          const char *propname, u64 *val)
+{
+       return device_property_read_u64_array(dev, propname, val, 1);
+}
+
+#endif /* _LINUX_PROPERTY_H_ */