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@xxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html