162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware spinlock framework 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Contact: Ohad Ben-Cohen <ohad@wizery.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/spinlock.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/jiffies.h> 1962306a36Sopenharmony_ci#include <linux/radix-tree.h> 2062306a36Sopenharmony_ci#include <linux/hwspinlock.h> 2162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "hwspinlock_internal.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* retry delay used in atomic context */ 2862306a36Sopenharmony_ci#define HWSPINLOCK_RETRY_DELAY_US 100 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* radix tree tags */ 3162306a36Sopenharmony_ci#define HWSPINLOCK_UNUSED (0) /* tags an hwspinlock as unused */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * A radix tree is used to maintain the available hwspinlock instances. 3562306a36Sopenharmony_ci * The tree associates hwspinlock pointers with their integer key id, 3662306a36Sopenharmony_ci * and provides easy-to-use API which makes the hwspinlock core code simple 3762306a36Sopenharmony_ci * and easy to read. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Radix trees are quick on lookups, and reasonably efficient in terms of 4062306a36Sopenharmony_ci * storage, especially with high density usages such as this framework 4162306a36Sopenharmony_ci * requires (a continuous range of integer keys, beginning with zero, is 4262306a36Sopenharmony_ci * used as the ID's of the hwspinlock instances). 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * The radix tree API supports tagging items in the tree, which this 4562306a36Sopenharmony_ci * framework uses to mark unused hwspinlock instances (see the 4662306a36Sopenharmony_ci * HWSPINLOCK_UNUSED tag above). As a result, the process of querying the 4762306a36Sopenharmony_ci * tree, looking for an unused hwspinlock instance, is now reduced to a 4862306a36Sopenharmony_ci * single radix tree API call. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistatic RADIX_TREE(hwspinlock_tree, GFP_KERNEL); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Synchronization of access to the tree is achieved using this mutex, 5462306a36Sopenharmony_ci * as the radix-tree API requires that users provide all synchronisation. 5562306a36Sopenharmony_ci * A mutex is needed because we're using non-atomic radix tree allocations. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic DEFINE_MUTEX(hwspinlock_tree_lock); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/** 6162306a36Sopenharmony_ci * __hwspin_trylock() - attempt to lock a specific hwspinlock 6262306a36Sopenharmony_ci * @hwlock: an hwspinlock which we want to trylock 6362306a36Sopenharmony_ci * @mode: controls whether local interrupts are disabled or not 6462306a36Sopenharmony_ci * @flags: a pointer where the caller's interrupt state will be saved at (if 6562306a36Sopenharmony_ci * requested) 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * This function attempts to lock an hwspinlock, and will immediately 6862306a36Sopenharmony_ci * fail if the hwspinlock is already taken. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine 7162306a36Sopenharmony_ci * of getting hardware lock with mutex or spinlock. Since in some scenarios, 7262306a36Sopenharmony_ci * user need some time-consuming or sleepable operations under the hardware 7362306a36Sopenharmony_ci * lock, they need one sleepable lock (like mutex) to protect the operations. 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * If the mode is neither HWLOCK_IN_ATOMIC nor HWLOCK_RAW, upon a successful 7662306a36Sopenharmony_ci * return from this function, preemption (and possibly interrupts) is disabled, 7762306a36Sopenharmony_ci * so the caller must not sleep, and is advised to release the hwspinlock as 7862306a36Sopenharmony_ci * soon as possible. This is required in order to minimize remote cores polling 7962306a36Sopenharmony_ci * on the hardware interconnect. 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * The user decides whether local interrupts are disabled or not, and if yes, 8262306a36Sopenharmony_ci * whether he wants their previous state to be saved. It is up to the user 8362306a36Sopenharmony_ci * to choose the appropriate @mode of operation, exactly the same way users 8462306a36Sopenharmony_ci * should decide between spin_trylock, spin_trylock_irq and 8562306a36Sopenharmony_ci * spin_trylock_irqsave. 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Returns 0 if we successfully locked the hwspinlock or -EBUSY if 8862306a36Sopenharmony_ci * the hwspinlock was already taken. 8962306a36Sopenharmony_ci * This function will never sleep. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ciint __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int ret; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE))) 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * This spin_lock{_irq, _irqsave} serves three purposes: 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * 1. Disable preemption, in order to minimize the period of time 10262306a36Sopenharmony_ci * in which the hwspinlock is taken. This is important in order 10362306a36Sopenharmony_ci * to minimize the possible polling on the hardware interconnect 10462306a36Sopenharmony_ci * by a remote user of this lock. 10562306a36Sopenharmony_ci * 2. Make the hwspinlock SMP-safe (so we can take it from 10662306a36Sopenharmony_ci * additional contexts on the local host). 10762306a36Sopenharmony_ci * 3. Ensure that in_atomic/might_sleep checks catch potential 10862306a36Sopenharmony_ci * problems with hwspinlock usage (e.g. scheduler checks like 10962306a36Sopenharmony_ci * 'scheduling while atomic' etc.) 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci switch (mode) { 11262306a36Sopenharmony_ci case HWLOCK_IRQSTATE: 11362306a36Sopenharmony_ci ret = spin_trylock_irqsave(&hwlock->lock, *flags); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case HWLOCK_IRQ: 11662306a36Sopenharmony_ci ret = spin_trylock_irq(&hwlock->lock); 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case HWLOCK_RAW: 11962306a36Sopenharmony_ci case HWLOCK_IN_ATOMIC: 12062306a36Sopenharmony_ci ret = 1; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci ret = spin_trylock(&hwlock->lock); 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* is lock already taken by another context on the local cpu ? */ 12862306a36Sopenharmony_ci if (!ret) 12962306a36Sopenharmony_ci return -EBUSY; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* try to take the hwspinlock device */ 13262306a36Sopenharmony_ci ret = hwlock->bank->ops->trylock(hwlock); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* if hwlock is already taken, undo spin_trylock_* and exit */ 13562306a36Sopenharmony_ci if (!ret) { 13662306a36Sopenharmony_ci switch (mode) { 13762306a36Sopenharmony_ci case HWLOCK_IRQSTATE: 13862306a36Sopenharmony_ci spin_unlock_irqrestore(&hwlock->lock, *flags); 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci case HWLOCK_IRQ: 14162306a36Sopenharmony_ci spin_unlock_irq(&hwlock->lock); 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case HWLOCK_RAW: 14462306a36Sopenharmony_ci case HWLOCK_IN_ATOMIC: 14562306a36Sopenharmony_ci /* Nothing to do */ 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci default: 14862306a36Sopenharmony_ci spin_unlock(&hwlock->lock); 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return -EBUSY; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * We can be sure the other core's memory operations 15762306a36Sopenharmony_ci * are observable to us only _after_ we successfully take 15862306a36Sopenharmony_ci * the hwspinlock, and we must make sure that subsequent memory 15962306a36Sopenharmony_ci * operations (both reads and writes) will not be reordered before 16062306a36Sopenharmony_ci * we actually took the hwspinlock. 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Note: the implicit memory barrier of the spinlock above is too 16362306a36Sopenharmony_ci * early, so we need this additional explicit memory barrier. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci mb(); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__hwspin_trylock); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/** 17262306a36Sopenharmony_ci * __hwspin_lock_timeout() - lock an hwspinlock with timeout limit 17362306a36Sopenharmony_ci * @hwlock: the hwspinlock to be locked 17462306a36Sopenharmony_ci * @timeout: timeout value in msecs 17562306a36Sopenharmony_ci * @mode: mode which controls whether local interrupts are disabled or not 17662306a36Sopenharmony_ci * @flags: a pointer to where the caller's interrupt state will be saved at (if 17762306a36Sopenharmony_ci * requested) 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * This function locks the given @hwlock. If the @hwlock 18062306a36Sopenharmony_ci * is already taken, the function will busy loop waiting for it to 18162306a36Sopenharmony_ci * be released, but give up after @timeout msecs have elapsed. 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine 18462306a36Sopenharmony_ci * of getting hardware lock with mutex or spinlock. Since in some scenarios, 18562306a36Sopenharmony_ci * user need some time-consuming or sleepable operations under the hardware 18662306a36Sopenharmony_ci * lock, they need one sleepable lock (like mutex) to protect the operations. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * If the mode is HWLOCK_IN_ATOMIC (called from an atomic context) the timeout 18962306a36Sopenharmony_ci * is handled with busy-waiting delays, hence shall not exceed few msecs. 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * If the mode is neither HWLOCK_IN_ATOMIC nor HWLOCK_RAW, upon a successful 19262306a36Sopenharmony_ci * return from this function, preemption (and possibly interrupts) is disabled, 19362306a36Sopenharmony_ci * so the caller must not sleep, and is advised to release the hwspinlock as 19462306a36Sopenharmony_ci * soon as possible. This is required in order to minimize remote cores polling 19562306a36Sopenharmony_ci * on the hardware interconnect. 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * The user decides whether local interrupts are disabled or not, and if yes, 19862306a36Sopenharmony_ci * whether he wants their previous state to be saved. It is up to the user 19962306a36Sopenharmony_ci * to choose the appropriate @mode of operation, exactly the same way users 20062306a36Sopenharmony_ci * should decide between spin_lock, spin_lock_irq and spin_lock_irqsave. 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * Returns 0 when the @hwlock was successfully taken, and an appropriate 20362306a36Sopenharmony_ci * error code otherwise (most notably -ETIMEDOUT if the @hwlock is still 20462306a36Sopenharmony_ci * busy after @timeout msecs). The function will never sleep. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ciint __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to, 20762306a36Sopenharmony_ci int mode, unsigned long *flags) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int ret; 21062306a36Sopenharmony_ci unsigned long expire, atomic_delay = 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci expire = msecs_to_jiffies(to) + jiffies; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci for (;;) { 21562306a36Sopenharmony_ci /* Try to take the hwspinlock */ 21662306a36Sopenharmony_ci ret = __hwspin_trylock(hwlock, mode, flags); 21762306a36Sopenharmony_ci if (ret != -EBUSY) 21862306a36Sopenharmony_ci break; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * The lock is already taken, let's check if the user wants 22262306a36Sopenharmony_ci * us to try again 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci if (mode == HWLOCK_IN_ATOMIC) { 22562306a36Sopenharmony_ci udelay(HWSPINLOCK_RETRY_DELAY_US); 22662306a36Sopenharmony_ci atomic_delay += HWSPINLOCK_RETRY_DELAY_US; 22762306a36Sopenharmony_ci if (atomic_delay > to * 1000) 22862306a36Sopenharmony_ci return -ETIMEDOUT; 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci if (time_is_before_eq_jiffies(expire)) 23162306a36Sopenharmony_ci return -ETIMEDOUT; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * Allow platform-specific relax handlers to prevent 23662306a36Sopenharmony_ci * hogging the interconnect (no sleeping, though) 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (hwlock->bank->ops->relax) 23962306a36Sopenharmony_ci hwlock->bank->ops->relax(hwlock); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__hwspin_lock_timeout); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/** 24762306a36Sopenharmony_ci * __hwspin_unlock() - unlock a specific hwspinlock 24862306a36Sopenharmony_ci * @hwlock: a previously-acquired hwspinlock which we want to unlock 24962306a36Sopenharmony_ci * @mode: controls whether local interrupts needs to be restored or not 25062306a36Sopenharmony_ci * @flags: previous caller's interrupt state to restore (if requested) 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * This function will unlock a specific hwspinlock, enable preemption and 25362306a36Sopenharmony_ci * (possibly) enable interrupts or restore their previous state. 25462306a36Sopenharmony_ci * @hwlock must be already locked before calling this function: it is a bug 25562306a36Sopenharmony_ci * to call unlock on a @hwlock that is already unlocked. 25662306a36Sopenharmony_ci * 25762306a36Sopenharmony_ci * The user decides whether local interrupts should be enabled or not, and 25862306a36Sopenharmony_ci * if yes, whether he wants their previous state to be restored. It is up 25962306a36Sopenharmony_ci * to the user to choose the appropriate @mode of operation, exactly the 26062306a36Sopenharmony_ci * same way users decide between spin_unlock, spin_unlock_irq and 26162306a36Sopenharmony_ci * spin_unlock_irqrestore. 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * The function will never sleep. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_civoid __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE))) 26862306a36Sopenharmony_ci return; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* 27162306a36Sopenharmony_ci * We must make sure that memory operations (both reads and writes), 27262306a36Sopenharmony_ci * done before unlocking the hwspinlock, will not be reordered 27362306a36Sopenharmony_ci * after the lock is released. 27462306a36Sopenharmony_ci * 27562306a36Sopenharmony_ci * That's the purpose of this explicit memory barrier. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * Note: the memory barrier induced by the spin_unlock below is too 27862306a36Sopenharmony_ci * late; the other core is going to access memory soon after it will 27962306a36Sopenharmony_ci * take the hwspinlock, and by then we want to be sure our memory 28062306a36Sopenharmony_ci * operations are already observable. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci mb(); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci hwlock->bank->ops->unlock(hwlock); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Undo the spin_trylock{_irq, _irqsave} called while locking */ 28762306a36Sopenharmony_ci switch (mode) { 28862306a36Sopenharmony_ci case HWLOCK_IRQSTATE: 28962306a36Sopenharmony_ci spin_unlock_irqrestore(&hwlock->lock, *flags); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case HWLOCK_IRQ: 29262306a36Sopenharmony_ci spin_unlock_irq(&hwlock->lock); 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case HWLOCK_RAW: 29562306a36Sopenharmony_ci case HWLOCK_IN_ATOMIC: 29662306a36Sopenharmony_ci /* Nothing to do */ 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci spin_unlock(&hwlock->lock); 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__hwspin_unlock); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/** 30662306a36Sopenharmony_ci * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id 30762306a36Sopenharmony_ci * @bank: the hwspinlock device bank 30862306a36Sopenharmony_ci * @hwlock_spec: hwlock specifier as found in the device tree 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * This is a simple translation function, suitable for hwspinlock platform 31162306a36Sopenharmony_ci * drivers that only has a lock specifier length of 1. 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * Returns a relative index of the lock within a specified bank on success, 31462306a36Sopenharmony_ci * or -EINVAL on invalid specifier cell count. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_cistatic inline int 31762306a36Sopenharmony_ciof_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci if (WARN_ON(hwlock_spec->args_count != 1)) 32062306a36Sopenharmony_ci return -EINVAL; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return hwlock_spec->args[0]; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/** 32662306a36Sopenharmony_ci * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock 32762306a36Sopenharmony_ci * @np: device node from which to request the specific hwlock 32862306a36Sopenharmony_ci * @index: index of the hwlock in the list of values 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * This function provides a means for DT users of the hwspinlock module to 33162306a36Sopenharmony_ci * get the global lock id of a specific hwspinlock using the phandle of the 33262306a36Sopenharmony_ci * hwspinlock device, so that it can be requested using the normal 33362306a36Sopenharmony_ci * hwspin_lock_request_specific() API. 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock 33662306a36Sopenharmony_ci * device is not yet registered, -EINVAL on invalid args specifier value or an 33762306a36Sopenharmony_ci * appropriate error as returned from the OF parsing of the DT client node. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ciint of_hwspin_lock_get_id(struct device_node *np, int index) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct of_phandle_args args; 34262306a36Sopenharmony_ci struct hwspinlock *hwlock; 34362306a36Sopenharmony_ci struct radix_tree_iter iter; 34462306a36Sopenharmony_ci void **slot; 34562306a36Sopenharmony_ci int id; 34662306a36Sopenharmony_ci int ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index, 34962306a36Sopenharmony_ci &args); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!of_device_is_available(args.np)) { 35462306a36Sopenharmony_ci ret = -ENOENT; 35562306a36Sopenharmony_ci goto out; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Find the hwspinlock device: we need its base_id */ 35962306a36Sopenharmony_ci ret = -EPROBE_DEFER; 36062306a36Sopenharmony_ci rcu_read_lock(); 36162306a36Sopenharmony_ci radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) { 36262306a36Sopenharmony_ci hwlock = radix_tree_deref_slot(slot); 36362306a36Sopenharmony_ci if (unlikely(!hwlock)) 36462306a36Sopenharmony_ci continue; 36562306a36Sopenharmony_ci if (radix_tree_deref_retry(hwlock)) { 36662306a36Sopenharmony_ci slot = radix_tree_iter_retry(&iter); 36762306a36Sopenharmony_ci continue; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (device_match_of_node(hwlock->bank->dev, args.np)) { 37162306a36Sopenharmony_ci ret = 0; 37262306a36Sopenharmony_ci break; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci rcu_read_unlock(); 37662306a36Sopenharmony_ci if (ret < 0) 37762306a36Sopenharmony_ci goto out; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci id = of_hwspin_lock_simple_xlate(&args); 38062306a36Sopenharmony_ci if (id < 0 || id >= hwlock->bank->num_locks) { 38162306a36Sopenharmony_ci ret = -EINVAL; 38262306a36Sopenharmony_ci goto out; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci id += hwlock->bank->base_id; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ciout: 38762306a36Sopenharmony_ci of_node_put(args.np); 38862306a36Sopenharmony_ci return ret ? ret : id; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_hwspin_lock_get_id); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * of_hwspin_lock_get_id_byname() - get lock id for an specified hwlock name 39462306a36Sopenharmony_ci * @np: device node from which to request the specific hwlock 39562306a36Sopenharmony_ci * @name: hwlock name 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * This function provides a means for DT users of the hwspinlock module to 39862306a36Sopenharmony_ci * get the global lock id of a specific hwspinlock using the specified name of 39962306a36Sopenharmony_ci * the hwspinlock device, so that it can be requested using the normal 40062306a36Sopenharmony_ci * hwspin_lock_request_specific() API. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock 40362306a36Sopenharmony_ci * device is not yet registered, -EINVAL on invalid args specifier value or an 40462306a36Sopenharmony_ci * appropriate error as returned from the OF parsing of the DT client node. 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ciint of_hwspin_lock_get_id_byname(struct device_node *np, const char *name) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int index; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!name) 41162306a36Sopenharmony_ci return -EINVAL; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci index = of_property_match_string(np, "hwlock-names", name); 41462306a36Sopenharmony_ci if (index < 0) 41562306a36Sopenharmony_ci return index; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return of_hwspin_lock_get_id(np, index); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_hwspin_lock_get_id_byname); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct hwspinlock *tmp; 42462306a36Sopenharmony_ci int ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mutex_lock(&hwspinlock_tree_lock); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = radix_tree_insert(&hwspinlock_tree, id, hwlock); 42962306a36Sopenharmony_ci if (ret) { 43062306a36Sopenharmony_ci if (ret == -EEXIST) 43162306a36Sopenharmony_ci pr_err("hwspinlock id %d already exists!\n", id); 43262306a36Sopenharmony_ci goto out; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* mark this hwspinlock as available */ 43662306a36Sopenharmony_ci tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* self-sanity check which should never fail */ 43962306a36Sopenharmony_ci WARN_ON(tmp != hwlock); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ciout: 44262306a36Sopenharmony_ci mutex_unlock(&hwspinlock_tree_lock); 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic struct hwspinlock *hwspin_lock_unregister_single(unsigned int id) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct hwspinlock *hwlock = NULL; 44962306a36Sopenharmony_ci int ret; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci mutex_lock(&hwspinlock_tree_lock); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* make sure the hwspinlock is not in use (tag is set) */ 45462306a36Sopenharmony_ci ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); 45562306a36Sopenharmony_ci if (ret == 0) { 45662306a36Sopenharmony_ci pr_err("hwspinlock %d still in use (or not present)\n", id); 45762306a36Sopenharmony_ci goto out; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci hwlock = radix_tree_delete(&hwspinlock_tree, id); 46162306a36Sopenharmony_ci if (!hwlock) { 46262306a36Sopenharmony_ci pr_err("failed to delete hwspinlock %d\n", id); 46362306a36Sopenharmony_ci goto out; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciout: 46762306a36Sopenharmony_ci mutex_unlock(&hwspinlock_tree_lock); 46862306a36Sopenharmony_ci return hwlock; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/** 47262306a36Sopenharmony_ci * hwspin_lock_register() - register a new hw spinlock device 47362306a36Sopenharmony_ci * @bank: the hwspinlock device, which usually provides numerous hw locks 47462306a36Sopenharmony_ci * @dev: the backing device 47562306a36Sopenharmony_ci * @ops: hwspinlock handlers for this device 47662306a36Sopenharmony_ci * @base_id: id of the first hardware spinlock in this bank 47762306a36Sopenharmony_ci * @num_locks: number of hwspinlocks provided by this device 47862306a36Sopenharmony_ci * 47962306a36Sopenharmony_ci * This function should be called from the underlying platform-specific 48062306a36Sopenharmony_ci * implementation, to register a new hwspinlock device instance. 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * Should be called from a process context (might sleep) 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code on failure 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ciint hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, 48762306a36Sopenharmony_ci const struct hwspinlock_ops *ops, int base_id, int num_locks) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct hwspinlock *hwlock; 49062306a36Sopenharmony_ci int ret = 0, i; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (!bank || !ops || !dev || !num_locks || !ops->trylock || 49362306a36Sopenharmony_ci !ops->unlock) { 49462306a36Sopenharmony_ci pr_err("invalid parameters\n"); 49562306a36Sopenharmony_ci return -EINVAL; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci bank->dev = dev; 49962306a36Sopenharmony_ci bank->ops = ops; 50062306a36Sopenharmony_ci bank->base_id = base_id; 50162306a36Sopenharmony_ci bank->num_locks = num_locks; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci for (i = 0; i < num_locks; i++) { 50462306a36Sopenharmony_ci hwlock = &bank->lock[i]; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci spin_lock_init(&hwlock->lock); 50762306a36Sopenharmony_ci hwlock->bank = bank; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ret = hwspin_lock_register_single(hwlock, base_id + i); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci goto reg_failed; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cireg_failed: 51762306a36Sopenharmony_ci while (--i >= 0) 51862306a36Sopenharmony_ci hwspin_lock_unregister_single(base_id + i); 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwspin_lock_register); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci/** 52462306a36Sopenharmony_ci * hwspin_lock_unregister() - unregister an hw spinlock device 52562306a36Sopenharmony_ci * @bank: the hwspinlock device, which usually provides numerous hw locks 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * This function should be called from the underlying platform-specific 52862306a36Sopenharmony_ci * implementation, to unregister an existing (and unused) hwspinlock. 52962306a36Sopenharmony_ci * 53062306a36Sopenharmony_ci * Should be called from a process context (might sleep) 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code on failure 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ciint hwspin_lock_unregister(struct hwspinlock_device *bank) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct hwspinlock *hwlock, *tmp; 53762306a36Sopenharmony_ci int i; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci for (i = 0; i < bank->num_locks; i++) { 54062306a36Sopenharmony_ci hwlock = &bank->lock[i]; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci tmp = hwspin_lock_unregister_single(bank->base_id + i); 54362306a36Sopenharmony_ci if (!tmp) 54462306a36Sopenharmony_ci return -EBUSY; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* self-sanity check that should never fail */ 54762306a36Sopenharmony_ci WARN_ON(tmp != hwlock); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwspin_lock_unregister); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void devm_hwspin_lock_unreg(struct device *dev, void *res) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci hwspin_lock_unregister(*(struct hwspinlock_device **)res); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int devm_hwspin_lock_device_match(struct device *dev, void *res, 56062306a36Sopenharmony_ci void *data) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct hwspinlock_device **bank = res; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (WARN_ON(!bank || !*bank)) 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return *bank == data; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/** 57162306a36Sopenharmony_ci * devm_hwspin_lock_unregister() - unregister an hw spinlock device for 57262306a36Sopenharmony_ci * a managed device 57362306a36Sopenharmony_ci * @dev: the backing device 57462306a36Sopenharmony_ci * @bank: the hwspinlock device, which usually provides numerous hw locks 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * This function should be called from the underlying platform-specific 57762306a36Sopenharmony_ci * implementation, to unregister an existing (and unused) hwspinlock. 57862306a36Sopenharmony_ci * 57962306a36Sopenharmony_ci * Should be called from a process context (might sleep) 58062306a36Sopenharmony_ci * 58162306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code on failure 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ciint devm_hwspin_lock_unregister(struct device *dev, 58462306a36Sopenharmony_ci struct hwspinlock_device *bank) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci int ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = devres_release(dev, devm_hwspin_lock_unreg, 58962306a36Sopenharmony_ci devm_hwspin_lock_device_match, bank); 59062306a36Sopenharmony_ci WARN_ON(ret); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hwspin_lock_unregister); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci/** 59762306a36Sopenharmony_ci * devm_hwspin_lock_register() - register a new hw spinlock device for 59862306a36Sopenharmony_ci * a managed device 59962306a36Sopenharmony_ci * @dev: the backing device 60062306a36Sopenharmony_ci * @bank: the hwspinlock device, which usually provides numerous hw locks 60162306a36Sopenharmony_ci * @ops: hwspinlock handlers for this device 60262306a36Sopenharmony_ci * @base_id: id of the first hardware spinlock in this bank 60362306a36Sopenharmony_ci * @num_locks: number of hwspinlocks provided by this device 60462306a36Sopenharmony_ci * 60562306a36Sopenharmony_ci * This function should be called from the underlying platform-specific 60662306a36Sopenharmony_ci * implementation, to register a new hwspinlock device instance. 60762306a36Sopenharmony_ci * 60862306a36Sopenharmony_ci * Should be called from a process context (might sleep) 60962306a36Sopenharmony_ci * 61062306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code on failure 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ciint devm_hwspin_lock_register(struct device *dev, 61362306a36Sopenharmony_ci struct hwspinlock_device *bank, 61462306a36Sopenharmony_ci const struct hwspinlock_ops *ops, 61562306a36Sopenharmony_ci int base_id, int num_locks) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct hwspinlock_device **ptr; 61862306a36Sopenharmony_ci int ret; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ptr = devres_alloc(devm_hwspin_lock_unreg, sizeof(*ptr), GFP_KERNEL); 62162306a36Sopenharmony_ci if (!ptr) 62262306a36Sopenharmony_ci return -ENOMEM; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci ret = hwspin_lock_register(bank, dev, ops, base_id, num_locks); 62562306a36Sopenharmony_ci if (!ret) { 62662306a36Sopenharmony_ci *ptr = bank; 62762306a36Sopenharmony_ci devres_add(dev, ptr); 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci devres_free(ptr); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hwspin_lock_register); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci/** 63762306a36Sopenharmony_ci * __hwspin_lock_request() - tag an hwspinlock as used and power it up 63862306a36Sopenharmony_ci * 63962306a36Sopenharmony_ci * This is an internal function that prepares an hwspinlock instance 64062306a36Sopenharmony_ci * before it is given to the user. The function assumes that 64162306a36Sopenharmony_ci * hwspinlock_tree_lock is taken. 64262306a36Sopenharmony_ci * 64362306a36Sopenharmony_ci * Returns 0 or positive to indicate success, and a negative value to 64462306a36Sopenharmony_ci * indicate an error (with the appropriate error code) 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_cistatic int __hwspin_lock_request(struct hwspinlock *hwlock) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct device *dev = hwlock->bank->dev; 64962306a36Sopenharmony_ci struct hwspinlock *tmp; 65062306a36Sopenharmony_ci int ret; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* prevent underlying implementation from being removed */ 65362306a36Sopenharmony_ci if (!try_module_get(dev->driver->owner)) { 65462306a36Sopenharmony_ci dev_err(dev, "%s: can't get owner\n", __func__); 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* notify PM core that power is now needed */ 65962306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 66062306a36Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 66162306a36Sopenharmony_ci dev_err(dev, "%s: can't power on device\n", __func__); 66262306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 66362306a36Sopenharmony_ci module_put(dev->driver->owner); 66462306a36Sopenharmony_ci return ret; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci ret = 0; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* mark hwspinlock as used, should not fail */ 67062306a36Sopenharmony_ci tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock), 67162306a36Sopenharmony_ci HWSPINLOCK_UNUSED); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* self-sanity check that should never fail */ 67462306a36Sopenharmony_ci WARN_ON(tmp != hwlock); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return ret; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/** 68062306a36Sopenharmony_ci * hwspin_lock_get_id() - retrieve id number of a given hwspinlock 68162306a36Sopenharmony_ci * @hwlock: a valid hwspinlock instance 68262306a36Sopenharmony_ci * 68362306a36Sopenharmony_ci * Returns the id number of a given @hwlock, or -EINVAL if @hwlock is invalid. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ciint hwspin_lock_get_id(struct hwspinlock *hwlock) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci if (!hwlock) { 68862306a36Sopenharmony_ci pr_err("invalid hwlock\n"); 68962306a36Sopenharmony_ci return -EINVAL; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return hwlock_to_id(hwlock); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwspin_lock_get_id); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/** 69762306a36Sopenharmony_ci * hwspin_lock_request() - request an hwspinlock 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci * This function should be called by users of the hwspinlock device, 70062306a36Sopenharmony_ci * in order to dynamically assign them an unused hwspinlock. 70162306a36Sopenharmony_ci * Usually the user of this lock will then have to communicate the lock's id 70262306a36Sopenharmony_ci * to the remote core before it can be used for synchronization (to get the 70362306a36Sopenharmony_ci * id of a given hwlock, use hwspin_lock_get_id()). 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * Should be called from a process context (might sleep) 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * Returns the address of the assigned hwspinlock, or NULL on error 70862306a36Sopenharmony_ci */ 70962306a36Sopenharmony_cistruct hwspinlock *hwspin_lock_request(void) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct hwspinlock *hwlock; 71262306a36Sopenharmony_ci int ret; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mutex_lock(&hwspinlock_tree_lock); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* look for an unused lock */ 71762306a36Sopenharmony_ci ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock, 71862306a36Sopenharmony_ci 0, 1, HWSPINLOCK_UNUSED); 71962306a36Sopenharmony_ci if (ret == 0) { 72062306a36Sopenharmony_ci pr_warn("a free hwspinlock is not available\n"); 72162306a36Sopenharmony_ci hwlock = NULL; 72262306a36Sopenharmony_ci goto out; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* sanity check that should never fail */ 72662306a36Sopenharmony_ci WARN_ON(ret > 1); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* mark as used and power up */ 72962306a36Sopenharmony_ci ret = __hwspin_lock_request(hwlock); 73062306a36Sopenharmony_ci if (ret < 0) 73162306a36Sopenharmony_ci hwlock = NULL; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ciout: 73462306a36Sopenharmony_ci mutex_unlock(&hwspinlock_tree_lock); 73562306a36Sopenharmony_ci return hwlock; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwspin_lock_request); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci/** 74062306a36Sopenharmony_ci * hwspin_lock_request_specific() - request for a specific hwspinlock 74162306a36Sopenharmony_ci * @id: index of the specific hwspinlock that is requested 74262306a36Sopenharmony_ci * 74362306a36Sopenharmony_ci * This function should be called by users of the hwspinlock module, 74462306a36Sopenharmony_ci * in order to assign them a specific hwspinlock. 74562306a36Sopenharmony_ci * Usually early board code will be calling this function in order to 74662306a36Sopenharmony_ci * reserve specific hwspinlock ids for predefined purposes. 74762306a36Sopenharmony_ci * 74862306a36Sopenharmony_ci * Should be called from a process context (might sleep) 74962306a36Sopenharmony_ci * 75062306a36Sopenharmony_ci * Returns the address of the assigned hwspinlock, or NULL on error 75162306a36Sopenharmony_ci */ 75262306a36Sopenharmony_cistruct hwspinlock *hwspin_lock_request_specific(unsigned int id) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct hwspinlock *hwlock; 75562306a36Sopenharmony_ci int ret; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci mutex_lock(&hwspinlock_tree_lock); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* make sure this hwspinlock exists */ 76062306a36Sopenharmony_ci hwlock = radix_tree_lookup(&hwspinlock_tree, id); 76162306a36Sopenharmony_ci if (!hwlock) { 76262306a36Sopenharmony_ci pr_warn("hwspinlock %u does not exist\n", id); 76362306a36Sopenharmony_ci goto out; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* sanity check (this shouldn't happen) */ 76762306a36Sopenharmony_ci WARN_ON(hwlock_to_id(hwlock) != id); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* make sure this hwspinlock is unused */ 77062306a36Sopenharmony_ci ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); 77162306a36Sopenharmony_ci if (ret == 0) { 77262306a36Sopenharmony_ci pr_warn("hwspinlock %u is already in use\n", id); 77362306a36Sopenharmony_ci hwlock = NULL; 77462306a36Sopenharmony_ci goto out; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* mark as used and power up */ 77862306a36Sopenharmony_ci ret = __hwspin_lock_request(hwlock); 77962306a36Sopenharmony_ci if (ret < 0) 78062306a36Sopenharmony_ci hwlock = NULL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ciout: 78362306a36Sopenharmony_ci mutex_unlock(&hwspinlock_tree_lock); 78462306a36Sopenharmony_ci return hwlock; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwspin_lock_request_specific); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/** 78962306a36Sopenharmony_ci * hwspin_lock_free() - free a specific hwspinlock 79062306a36Sopenharmony_ci * @hwlock: the specific hwspinlock to free 79162306a36Sopenharmony_ci * 79262306a36Sopenharmony_ci * This function mark @hwlock as free again. 79362306a36Sopenharmony_ci * Should only be called with an @hwlock that was retrieved from 79462306a36Sopenharmony_ci * an earlier call to hwspin_lock_request{_specific}. 79562306a36Sopenharmony_ci * 79662306a36Sopenharmony_ci * Should be called from a process context (might sleep) 79762306a36Sopenharmony_ci * 79862306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code on failure 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ciint hwspin_lock_free(struct hwspinlock *hwlock) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct device *dev; 80362306a36Sopenharmony_ci struct hwspinlock *tmp; 80462306a36Sopenharmony_ci int ret; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!hwlock) { 80762306a36Sopenharmony_ci pr_err("invalid hwlock\n"); 80862306a36Sopenharmony_ci return -EINVAL; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci dev = hwlock->bank->dev; 81262306a36Sopenharmony_ci mutex_lock(&hwspinlock_tree_lock); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci /* make sure the hwspinlock is used */ 81562306a36Sopenharmony_ci ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock), 81662306a36Sopenharmony_ci HWSPINLOCK_UNUSED); 81762306a36Sopenharmony_ci if (ret == 1) { 81862306a36Sopenharmony_ci dev_err(dev, "%s: hwlock is already free\n", __func__); 81962306a36Sopenharmony_ci dump_stack(); 82062306a36Sopenharmony_ci ret = -EINVAL; 82162306a36Sopenharmony_ci goto out; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* notify the underlying device that power is not needed */ 82562306a36Sopenharmony_ci pm_runtime_put(dev); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* mark this hwspinlock as available */ 82862306a36Sopenharmony_ci tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock), 82962306a36Sopenharmony_ci HWSPINLOCK_UNUSED); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* sanity check (this shouldn't happen) */ 83262306a36Sopenharmony_ci WARN_ON(tmp != hwlock); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci module_put(dev->driver->owner); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ciout: 83762306a36Sopenharmony_ci mutex_unlock(&hwspinlock_tree_lock); 83862306a36Sopenharmony_ci return ret; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hwspin_lock_free); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic int devm_hwspin_lock_match(struct device *dev, void *res, void *data) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct hwspinlock **hwlock = res; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (WARN_ON(!hwlock || !*hwlock)) 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return *hwlock == data; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void devm_hwspin_lock_release(struct device *dev, void *res) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci hwspin_lock_free(*(struct hwspinlock **)res); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci/** 85862306a36Sopenharmony_ci * devm_hwspin_lock_free() - free a specific hwspinlock for a managed device 85962306a36Sopenharmony_ci * @dev: the device to free the specific hwspinlock 86062306a36Sopenharmony_ci * @hwlock: the specific hwspinlock to free 86162306a36Sopenharmony_ci * 86262306a36Sopenharmony_ci * This function mark @hwlock as free again. 86362306a36Sopenharmony_ci * Should only be called with an @hwlock that was retrieved from 86462306a36Sopenharmony_ci * an earlier call to hwspin_lock_request{_specific}. 86562306a36Sopenharmony_ci * 86662306a36Sopenharmony_ci * Should be called from a process context (might sleep) 86762306a36Sopenharmony_ci * 86862306a36Sopenharmony_ci * Returns 0 on success, or an appropriate error code on failure 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ciint devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci int ret; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci ret = devres_release(dev, devm_hwspin_lock_release, 87562306a36Sopenharmony_ci devm_hwspin_lock_match, hwlock); 87662306a36Sopenharmony_ci WARN_ON(ret); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return ret; 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hwspin_lock_free); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/** 88362306a36Sopenharmony_ci * devm_hwspin_lock_request() - request an hwspinlock for a managed device 88462306a36Sopenharmony_ci * @dev: the device to request an hwspinlock 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * This function should be called by users of the hwspinlock device, 88762306a36Sopenharmony_ci * in order to dynamically assign them an unused hwspinlock. 88862306a36Sopenharmony_ci * Usually the user of this lock will then have to communicate the lock's id 88962306a36Sopenharmony_ci * to the remote core before it can be used for synchronization (to get the 89062306a36Sopenharmony_ci * id of a given hwlock, use hwspin_lock_get_id()). 89162306a36Sopenharmony_ci * 89262306a36Sopenharmony_ci * Should be called from a process context (might sleep) 89362306a36Sopenharmony_ci * 89462306a36Sopenharmony_ci * Returns the address of the assigned hwspinlock, or NULL on error 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_cistruct hwspinlock *devm_hwspin_lock_request(struct device *dev) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci struct hwspinlock **ptr, *hwlock; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ptr = devres_alloc(devm_hwspin_lock_release, sizeof(*ptr), GFP_KERNEL); 90162306a36Sopenharmony_ci if (!ptr) 90262306a36Sopenharmony_ci return NULL; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci hwlock = hwspin_lock_request(); 90562306a36Sopenharmony_ci if (hwlock) { 90662306a36Sopenharmony_ci *ptr = hwlock; 90762306a36Sopenharmony_ci devres_add(dev, ptr); 90862306a36Sopenharmony_ci } else { 90962306a36Sopenharmony_ci devres_free(ptr); 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return hwlock; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hwspin_lock_request); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/** 91762306a36Sopenharmony_ci * devm_hwspin_lock_request_specific() - request for a specific hwspinlock for 91862306a36Sopenharmony_ci * a managed device 91962306a36Sopenharmony_ci * @dev: the device to request the specific hwspinlock 92062306a36Sopenharmony_ci * @id: index of the specific hwspinlock that is requested 92162306a36Sopenharmony_ci * 92262306a36Sopenharmony_ci * This function should be called by users of the hwspinlock module, 92362306a36Sopenharmony_ci * in order to assign them a specific hwspinlock. 92462306a36Sopenharmony_ci * Usually early board code will be calling this function in order to 92562306a36Sopenharmony_ci * reserve specific hwspinlock ids for predefined purposes. 92662306a36Sopenharmony_ci * 92762306a36Sopenharmony_ci * Should be called from a process context (might sleep) 92862306a36Sopenharmony_ci * 92962306a36Sopenharmony_ci * Returns the address of the assigned hwspinlock, or NULL on error 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_cistruct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev, 93262306a36Sopenharmony_ci unsigned int id) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct hwspinlock **ptr, *hwlock; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci ptr = devres_alloc(devm_hwspin_lock_release, sizeof(*ptr), GFP_KERNEL); 93762306a36Sopenharmony_ci if (!ptr) 93862306a36Sopenharmony_ci return NULL; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci hwlock = hwspin_lock_request_specific(id); 94162306a36Sopenharmony_ci if (hwlock) { 94262306a36Sopenharmony_ci *ptr = hwlock; 94362306a36Sopenharmony_ci devres_add(dev, ptr); 94462306a36Sopenharmony_ci } else { 94562306a36Sopenharmony_ci devres_free(ptr); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return hwlock; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_hwspin_lock_request_specific); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ciMODULE_DESCRIPTION("Hardware spinlock interface"); 95362306a36Sopenharmony_ciMODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>"); 954