[PATCHv4 4/7] hwspinlock/core: add common OF helpers

Suman Anna s-anna at ti.com
Mon Jan 13 19:19:21 EST 2014


This patch adds three new OF helper functions to use/request
locks from a hwspinlock device instantiated through a
device-tree blob.

1. The of_hwspin_lock_get_num_locks() is a common helper
   function to read the common 'hwlock-num-locks' property.
2. The of_hwspin_lock_simple_xlate() is a simple default
   translator function for hwspinlock provider implementations
   that use a single cell number for requesting a specific lock
   (relatively indexed) within a hwlock bank.
3. The of_hwspin_lock_request_specific() API can be used by
   hwspinlock clients to request a specific lock using the
   phandle + args specifier. This function relies on the
   implementation providing back a relative hwlock id within
   the bank from the args specifier.

Signed-off-by: Suman Anna <s-anna at ti.com>
---
 Documentation/hwspinlock.txt             |  34 +++++++++-
 drivers/hwspinlock/hwspinlock_core.c     | 108 ++++++++++++++++++++++++++++++-
 drivers/hwspinlock/hwspinlock_internal.h |   4 ++
 include/linux/hwspinlock.h               |  20 +++++-
 4 files changed, 161 insertions(+), 5 deletions(-)

diff --git a/Documentation/hwspinlock.txt b/Documentation/hwspinlock.txt
index 640ae47..903d477 100644
--- a/Documentation/hwspinlock.txt
+++ b/Documentation/hwspinlock.txt
@@ -48,6 +48,15 @@ independent, drivers.
      ids for predefined purposes.
      Should be called from a process context (might sleep).
 
+  struct hwspinlock *of_hwspin_lock_request_specific(
+		struct device_node *np,	const char *propname, int index);
+   - assign a specific hwspinlock id and return its address, or NULL
+     if that hwspinlock is already in use. This function is the OF
+     equivalent of hwspin_lock_request_specific() function, and provides
+     a means for users of the hwspinlock module to request a specific
+     hwspinlock using the phandle of the hwspinlock device.
+     Should be called from a process context (might sleep).
+
   int hwspin_lock_free(struct hwspinlock *hwlock);
    - free a previously-assigned hwspinlock; returns 0 on success, or an
      appropriate error code on failure (e.g. -EINVAL if the hwspinlock
@@ -243,6 +252,23 @@ int hwspinlock_example2(void)
      Returns the address of hwspinlock on success, or NULL on error (e.g.
      if the hwspinlock is still in use).
 
+  int of_hwspin_lock_simple_xlate(struct hwspinlock_device *bank,
+				  const struct of_phandle_args *hwlock_spec);
+   - is a simple default OF translate helper function that can be plugged in
+     as the underlying vendor-specific implementation's of_xlate ops function.
+     This can be used by implementations that use a single integer argument in
+     the DT node argument specifier, that indicates the hwlock index number.
+     Returns a hwlock index within a bank, or appropriate error code on
+     failure.
+
+  int of_hwspin_lock_get_num_locks(struct device_node *dn);
+   - is a common OF helper function that can be used by some underlying
+     vendor-specific implementations. This can be used by implementations
+     that require and define the number of locks supported within a hwspinlock
+     bank as a device tree node property. This function should be called by
+     needed implementations before registering a hwspinlock device with the
+     core.
+
 5. Important structs
 
 struct hwspinlock_device is a device which usually contains a bank
@@ -288,12 +314,14 @@ initialized by the hwspinlock core itself.
 
 6. Implementation callbacks
 
-There are three possible callbacks defined in 'struct hwspinlock_ops':
+There are four possible callbacks defined in 'struct hwspinlock_ops':
 
 struct hwspinlock_ops {
 	int (*trylock)(struct hwspinlock *lock);
 	void (*unlock)(struct hwspinlock *lock);
 	void (*relax)(struct hwspinlock *lock);
+	int (*of_xlate)(struct hwspinlock_device *bank,
+			const struct of_phandle_args *hwlock_spec);
 };
 
 The first two callbacks are mandatory:
@@ -307,3 +335,7 @@ may _not_ sleep.
 The ->relax() callback is optional. It is called by hwspinlock core while
 spinning on a lock, and can be used by the underlying implementation to force
 a delay between two successive invocations of ->trylock(). It may _not_ sleep.
+
+The ->of_xlate() callback is mandatory to support requesting hwlocks through
+device-tree nodes. It is called by hwspinlock core to retrieve the relative
+lock index within a bank from the underlying implementation.
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 48f7866..1e299f7 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -27,6 +27,7 @@
 #include <linux/hwspinlock.h>
 #include <linux/pm_runtime.h>
 #include <linux/mutex.h>
+#include <linux/of.h>
 
 #include "hwspinlock_internal.h"
 
@@ -262,6 +263,33 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
 }
 EXPORT_SYMBOL_GPL(__hwspin_unlock);
 
+/**
+ * of_hwspin_lock_get_num_locks() - OF helper to retrieve number of locks
+ * @dn: device node pointer
+ *
+ * This is an OF helper function that can be called by the underlying
+ * platform-specific implementations, to retrieve the number of locks
+ * present within a hwspinlock device instance. The hwlock-num-locks
+ * DT property may be optional for some platforms, while mandatory for
+ * some others, so this function is typically called only by needed
+ * platform-specific implementations.
+ *
+ * Returns a positive number of locks on success, -ENODEV on generic
+ * failure or an appropriate error code as returned by the OF layer
+ */
+int of_hwspin_lock_get_num_locks(struct device_node *dn)
+{
+	unsigned int val;
+	int ret = -ENODEV;
+
+	ret = of_property_read_u32(dn, "hwlock-num-locks", &val);
+	if (!ret)
+		ret = val ? val : -ENODEV;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(of_hwspin_lock_get_num_locks);
+
 static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
 {
 	struct hwspinlock *tmp;
@@ -312,6 +340,31 @@ out:
 	return hwlock;
 }
 
+/**
+ * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
+ * @bank: the hwspinlock device bank
+ * @hwlock_spec: hwlock specifier as found in the device tree
+ *
+ * This is a simple translation function, suitable for hwspinlock platform
+ * drivers that only has a lock specifier length of 1.
+ *
+ * Returns a negative value on error, and a relative index of the lock within
+ * a specified bank on success.
+ */
+int of_hwspin_lock_simple_xlate(struct hwspinlock_device *bank,
+				const struct of_phandle_args *hwlock_spec)
+{
+	/* sanity check (these shouldn't happen) */
+	if (WARN_ON(!bank->dev->of_node))
+		return -EINVAL;
+
+	if (WARN_ON(hwlock_spec->args_count != 1))
+		return -EINVAL;
+
+	return hwlock_spec->args[0];
+}
+EXPORT_SYMBOL_GPL(of_hwspin_lock_simple_xlate);
+
 /*
  * Add a new hwspinlock device to the global list, keeping the list of
  * devices sorted by base order.
@@ -368,7 +421,7 @@ int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
 	int ret = 0, i;
 
 	if (!bank || !ops || !dev || !num_locks || !ops->trylock ||
-							!ops->unlock) {
+	    !ops->unlock || (dev->of_node && !ops->of_xlate)) {
 		pr_err("invalid parameters\n");
 		return -EINVAL;
 	}
@@ -592,6 +645,59 @@ out:
 EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
 
 /**
+ * of_hwspin_lock_request_specific() - request a OF phandle-based specific lock
+ * @np: device node from which to request the specific hwlock
+ * @propname: property name containing hwlock specifier(s)
+ * @index: index of the hwlock
+ *
+ * This function is the OF equivalent of hwspin_lock_request_specific(). This
+ * function provides a means for users of the hwspinlock module to request a
+ * specific hwspinlock using the phandle of the hwspinlock device. The requested
+ * lock number is indexed relative to the hwspinlock device, unlike the
+ * hwspin_lock_request_specific() which is an absolute lock number.
+ *
+ * Returns the address of the assigned hwspinlock, or NULL on error
+ */
+struct hwspinlock *of_hwspin_lock_request_specific(struct device_node *np,
+					const char *propname, int index)
+{
+	struct hwspinlock_device *bank;
+	struct of_phandle_args args;
+	int id;
+	int ret;
+
+	ret = of_parse_phandle_with_args(np, propname, "#hwlock-cells", index,
+					 &args);
+	if (ret) {
+		pr_warn("%s: can't parse hwlocks property of node '%s[%d]' ret = %d\n",
+			__func__, np->full_name, index, ret);
+		return NULL;
+	}
+
+	mutex_lock(&hwspinlock_tree_lock);
+	list_for_each_entry(bank, &hwspinlock_devices, list)
+		if (bank->dev->of_node == args.np)
+			break;
+	mutex_unlock(&hwspinlock_tree_lock);
+	if (&bank->list == &hwspinlock_devices) {
+		pr_warn("%s: requested hwspinlock device %s is not registered\n",
+			__func__, args.np->full_name);
+		return NULL;
+	}
+
+	id = bank->ops->of_xlate(bank, &args);
+	if (id < 0 || id >= bank->num_locks) {
+		pr_warn("%s: requested lock %d is either out of range [0, %d] or failed translation\n",
+			__func__, id, bank->num_locks - 1);
+		return NULL;
+	}
+
+	id += bank->base_id;
+	return hwspin_lock_request_specific(id);
+}
+EXPORT_SYMBOL_GPL(of_hwspin_lock_request_specific);
+
+/**
  * hwspin_lock_free() - free a specific hwspinlock
  * @hwlock: the specific hwspinlock to free
  *
diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h
index aff560c..5e42613 100644
--- a/drivers/hwspinlock/hwspinlock_internal.h
+++ b/drivers/hwspinlock/hwspinlock_internal.h
@@ -32,11 +32,15 @@ struct hwspinlock_device;
  * @relax:   optional, platform-specific relax handler, called by hwspinlock
  *	     core while spinning on a lock, between two successive
  *	     invocations of @trylock. may _not_ sleep.
+ * @of_xlate: platform-specific hwlock specifier translate function, to
+ *	      return the relative index of the lock within a bank
  */
 struct hwspinlock_ops {
 	int (*trylock)(struct hwspinlock *lock);
 	void (*unlock)(struct hwspinlock *lock);
 	void (*relax)(struct hwspinlock *lock);
+	int (*of_xlate)(struct hwspinlock_device *bank,
+			const struct of_phandle_args *hwlock_spec);
 };
 
 /**
diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h
index 3343298..2453057 100644
--- a/include/linux/hwspinlock.h
+++ b/include/linux/hwspinlock.h
@@ -26,6 +26,8 @@
 #define HWLOCK_IRQ	0x02	/* Disable interrupts, don't save state */
 
 struct device;
+struct device_node;
+struct of_phandle_args;
 struct hwspinlock;
 struct hwspinlock_device;
 struct hwspinlock_ops;
@@ -60,11 +62,16 @@ struct hwspinlock_pdata {
 
 #if defined(CONFIG_HWSPINLOCK) || defined(CONFIG_HWSPINLOCK_MODULE)
 
+int of_hwspin_lock_simple_xlate(struct hwspinlock_device *bank,
+				const struct of_phandle_args *hwlock_spec);
+int of_hwspin_lock_get_num_locks(struct device_node *dn);
 int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
 		const struct hwspinlock_ops *ops, int base_id, int num_locks);
 int hwspin_lock_unregister(struct hwspinlock_device *bank);
 struct hwspinlock *hwspin_lock_request(void);
 struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
+struct hwspinlock *of_hwspin_lock_request_specific(struct device_node *np,
+		const char *propname, int index);
 int hwspin_lock_free(struct hwspinlock *hwlock);
 int hwspin_lock_get_id(struct hwspinlock *hwlock);
 int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
@@ -80,9 +87,9 @@ void __hwspin_unlock(struct hwspinlock *, int, unsigned long *);
  * code path get compiled away. This way, if CONFIG_HWSPINLOCK is not
  * required on a given setup, users will still work.
  *
- * The only exception is hwspin_lock_register/hwspin_lock_unregister, with which
- * we _do_ want users to fail (no point in registering hwspinlock instances if
- * the framework is not available).
+ * The only exception is hwspin_lock_register/hwspin_lock_unregister and
+ * associated OF helpers, with which we _do_ want users to fail (no point
+ * in registering hwspinlock instances if the framework is not available).
  *
  * Note: ERR_PTR(-ENODEV) will still be considered a success for NULL-checking
  * users. Others, which care, can still check this with IS_ERR.
@@ -97,6 +104,13 @@ static inline struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
 	return ERR_PTR(-ENODEV);
 }
 
+static inline
+struct hwspinlock *of_hwspin_lock_request_specific(struct device_node *np,
+						const char *propname, int index)
+{
+	return ERR_PTR(-ENODEV);
+}
+
 static inline int hwspin_lock_free(struct hwspinlock *hwlock)
 {
 	return 0;
-- 
1.8.4.3




More information about the linux-arm-kernel mailing list