brintos

brintos / linux-shallow public Read only

0
0
Text · 28.1 KiB · 6505261 Raw
987 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Hardware spinlock framework4 *5 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com6 *7 * Contact: Ohad Ben-Cohen <ohad@wizery.com>8 */9 10#define pr_fmt(fmt)    "%s: " fmt, __func__11 12#include <linux/delay.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/spinlock.h>16#include <linux/types.h>17#include <linux/err.h>18#include <linux/jiffies.h>19#include <linux/radix-tree.h>20#include <linux/hwspinlock.h>21#include <linux/pm_runtime.h>22#include <linux/mutex.h>23#include <linux/of.h>24 25#include "hwspinlock_internal.h"26 27/* retry delay used in atomic context */28#define HWSPINLOCK_RETRY_DELAY_US	10029 30/* radix tree tags */31#define HWSPINLOCK_UNUSED	(0) /* tags an hwspinlock as unused */32 33/*34 * A radix tree is used to maintain the available hwspinlock instances.35 * The tree associates hwspinlock pointers with their integer key id,36 * and provides easy-to-use API which makes the hwspinlock core code simple37 * and easy to read.38 *39 * Radix trees are quick on lookups, and reasonably efficient in terms of40 * storage, especially with high density usages such as this framework41 * requires (a continuous range of integer keys, beginning with zero, is42 * used as the ID's of the hwspinlock instances).43 *44 * The radix tree API supports tagging items in the tree, which this45 * framework uses to mark unused hwspinlock instances (see the46 * HWSPINLOCK_UNUSED tag above). As a result, the process of querying the47 * tree, looking for an unused hwspinlock instance, is now reduced to a48 * single radix tree API call.49 */50static RADIX_TREE(hwspinlock_tree, GFP_KERNEL);51 52/*53 * Synchronization of access to the tree is achieved using this mutex,54 * as the radix-tree API requires that users provide all synchronisation.55 * A mutex is needed because we're using non-atomic radix tree allocations.56 */57static DEFINE_MUTEX(hwspinlock_tree_lock);58 59 60/**61 * __hwspin_trylock() - attempt to lock a specific hwspinlock62 * @hwlock: an hwspinlock which we want to trylock63 * @mode: controls whether local interrupts are disabled or not64 * @flags: a pointer where the caller's interrupt state will be saved at (if65 *         requested)66 *67 * This function attempts to lock an hwspinlock, and will immediately68 * fail if the hwspinlock is already taken.69 *70 * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine71 * of getting hardware lock with mutex or spinlock. Since in some scenarios,72 * user need some time-consuming or sleepable operations under the hardware73 * lock, they need one sleepable lock (like mutex) to protect the operations.74 *75 * If the mode is neither HWLOCK_IN_ATOMIC nor HWLOCK_RAW, upon a successful76 * return from this function, preemption (and possibly interrupts) is disabled,77 * so the caller must not sleep, and is advised to release the hwspinlock as78 * soon as possible. This is required in order to minimize remote cores polling79 * on the hardware interconnect.80 *81 * The user decides whether local interrupts are disabled or not, and if yes,82 * whether he wants their previous state to be saved. It is up to the user83 * to choose the appropriate @mode of operation, exactly the same way users84 * should decide between spin_trylock, spin_trylock_irq and85 * spin_trylock_irqsave.86 *87 * Returns: %0 if we successfully locked the hwspinlock or -EBUSY if88 * the hwspinlock was already taken.89 *90 * This function will never sleep.91 */92int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)93{94	int ret;95 96	if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE)))97		return -EINVAL;98 99	/*100	 * This spin_lock{_irq, _irqsave} serves three purposes:101	 *102	 * 1. Disable preemption, in order to minimize the period of time103	 *    in which the hwspinlock is taken. This is important in order104	 *    to minimize the possible polling on the hardware interconnect105	 *    by a remote user of this lock.106	 * 2. Make the hwspinlock SMP-safe (so we can take it from107	 *    additional contexts on the local host).108	 * 3. Ensure that in_atomic/might_sleep checks catch potential109	 *    problems with hwspinlock usage (e.g. scheduler checks like110	 *    'scheduling while atomic' etc.)111	 */112	switch (mode) {113	case HWLOCK_IRQSTATE:114		ret = spin_trylock_irqsave(&hwlock->lock, *flags);115		break;116	case HWLOCK_IRQ:117		ret = spin_trylock_irq(&hwlock->lock);118		break;119	case HWLOCK_RAW:120	case HWLOCK_IN_ATOMIC:121		ret = 1;122		break;123	default:124		ret = spin_trylock(&hwlock->lock);125		break;126	}127 128	/* is lock already taken by another context on the local cpu ? */129	if (!ret)130		return -EBUSY;131 132	/* try to take the hwspinlock device */133	ret = hwlock->bank->ops->trylock(hwlock);134 135	/* if hwlock is already taken, undo spin_trylock_* and exit */136	if (!ret) {137		switch (mode) {138		case HWLOCK_IRQSTATE:139			spin_unlock_irqrestore(&hwlock->lock, *flags);140			break;141		case HWLOCK_IRQ:142			spin_unlock_irq(&hwlock->lock);143			break;144		case HWLOCK_RAW:145		case HWLOCK_IN_ATOMIC:146			/* Nothing to do */147			break;148		default:149			spin_unlock(&hwlock->lock);150			break;151		}152 153		return -EBUSY;154	}155 156	/*157	 * We can be sure the other core's memory operations158	 * are observable to us only _after_ we successfully take159	 * the hwspinlock, and we must make sure that subsequent memory160	 * operations (both reads and writes) will not be reordered before161	 * we actually took the hwspinlock.162	 *163	 * Note: the implicit memory barrier of the spinlock above is too164	 * early, so we need this additional explicit memory barrier.165	 */166	mb();167 168	return 0;169}170EXPORT_SYMBOL_GPL(__hwspin_trylock);171 172/**173 * __hwspin_lock_timeout() - lock an hwspinlock with timeout limit174 * @hwlock: the hwspinlock to be locked175 * @to: timeout value in msecs176 * @mode: mode which controls whether local interrupts are disabled or not177 * @flags: a pointer to where the caller's interrupt state will be saved at (if178 *         requested)179 *180 * This function locks the given @hwlock. If the @hwlock181 * is already taken, the function will busy loop waiting for it to182 * be released, but give up after @timeout msecs have elapsed.183 *184 * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine185 * of getting hardware lock with mutex or spinlock. Since in some scenarios,186 * user need some time-consuming or sleepable operations under the hardware187 * lock, they need one sleepable lock (like mutex) to protect the operations.188 *189 * If the mode is HWLOCK_IN_ATOMIC (called from an atomic context) the timeout190 * is handled with busy-waiting delays, hence shall not exceed few msecs.191 *192 * If the mode is neither HWLOCK_IN_ATOMIC nor HWLOCK_RAW, upon a successful193 * return from this function, preemption (and possibly interrupts) is disabled,194 * so the caller must not sleep, and is advised to release the hwspinlock as195 * soon as possible. This is required in order to minimize remote cores polling196 * on the hardware interconnect.197 *198 * The user decides whether local interrupts are disabled or not, and if yes,199 * whether he wants their previous state to be saved. It is up to the user200 * to choose the appropriate @mode of operation, exactly the same way users201 * should decide between spin_lock, spin_lock_irq and spin_lock_irqsave.202 *203 * Returns: %0 when the @hwlock was successfully taken, and an appropriate204 * error code otherwise (most notably -ETIMEDOUT if the @hwlock is still205 * busy after @timeout msecs).206 *207 * The function will never sleep.208 */209int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to,210					int mode, unsigned long *flags)211{212	int ret;213	unsigned long expire, atomic_delay = 0;214 215	expire = msecs_to_jiffies(to) + jiffies;216 217	for (;;) {218		/* Try to take the hwspinlock */219		ret = __hwspin_trylock(hwlock, mode, flags);220		if (ret != -EBUSY)221			break;222 223		/*224		 * The lock is already taken, let's check if the user wants225		 * us to try again226		 */227		if (mode == HWLOCK_IN_ATOMIC) {228			udelay(HWSPINLOCK_RETRY_DELAY_US);229			atomic_delay += HWSPINLOCK_RETRY_DELAY_US;230			if (atomic_delay > to * 1000)231				return -ETIMEDOUT;232		} else {233			if (time_is_before_eq_jiffies(expire))234				return -ETIMEDOUT;235		}236 237		/*238		 * Allow platform-specific relax handlers to prevent239		 * hogging the interconnect (no sleeping, though)240		 */241		if (hwlock->bank->ops->relax)242			hwlock->bank->ops->relax(hwlock);243	}244 245	return ret;246}247EXPORT_SYMBOL_GPL(__hwspin_lock_timeout);248 249/**250 * __hwspin_unlock() - unlock a specific hwspinlock251 * @hwlock: a previously-acquired hwspinlock which we want to unlock252 * @mode: controls whether local interrupts needs to be restored or not253 * @flags: previous caller's interrupt state to restore (if requested)254 *255 * This function will unlock a specific hwspinlock, enable preemption and256 * (possibly) enable interrupts or restore their previous state.257 * @hwlock must be already locked before calling this function: it is a bug258 * to call unlock on a @hwlock that is already unlocked.259 *260 * The user decides whether local interrupts should be enabled or not, and261 * if yes, whether he wants their previous state to be restored. It is up262 * to the user to choose the appropriate @mode of operation, exactly the263 * same way users decide between spin_unlock, spin_unlock_irq and264 * spin_unlock_irqrestore.265 *266 * The function will never sleep.267 */268void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)269{270	if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE)))271		return;272 273	/*274	 * We must make sure that memory operations (both reads and writes),275	 * done before unlocking the hwspinlock, will not be reordered276	 * after the lock is released.277	 *278	 * That's the purpose of this explicit memory barrier.279	 *280	 * Note: the memory barrier induced by the spin_unlock below is too281	 * late; the other core is going to access memory soon after it will282	 * take the hwspinlock, and by then we want to be sure our memory283	 * operations are already observable.284	 */285	mb();286 287	hwlock->bank->ops->unlock(hwlock);288 289	/* Undo the spin_trylock{_irq, _irqsave} called while locking */290	switch (mode) {291	case HWLOCK_IRQSTATE:292		spin_unlock_irqrestore(&hwlock->lock, *flags);293		break;294	case HWLOCK_IRQ:295		spin_unlock_irq(&hwlock->lock);296		break;297	case HWLOCK_RAW:298	case HWLOCK_IN_ATOMIC:299		/* Nothing to do */300		break;301	default:302		spin_unlock(&hwlock->lock);303		break;304	}305}306EXPORT_SYMBOL_GPL(__hwspin_unlock);307 308/**309 * hwspin_lock_bust() - bust a specific hwspinlock310 * @hwlock: a previously-acquired hwspinlock which we want to bust311 * @id: identifier of the remote lock holder, if applicable312 *313 * This function will bust a hwspinlock that was previously acquired as314 * long as the current owner of the lock matches the id given by the caller.315 *316 * Context: Process context.317 *318 * Returns: 0 on success, or -EINVAL if the hwspinlock does not exist, or319 * the bust operation fails, and -EOPNOTSUPP if the bust operation is not320 * defined for the hwspinlock.321 */322int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id)323{324	if (WARN_ON(!hwlock))325		return -EINVAL;326 327	if (!hwlock->bank->ops->bust) {328		pr_err("bust operation not defined\n");329		return -EOPNOTSUPP;330	}331 332	return hwlock->bank->ops->bust(hwlock, id);333}334EXPORT_SYMBOL_GPL(hwspin_lock_bust);335 336/**337 * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id338 * @hwlock_spec: hwlock specifier as found in the device tree339 *340 * This is a simple translation function, suitable for hwspinlock platform341 * drivers that only has a lock specifier length of 1.342 *343 * Returns: a relative index of the lock within a specified bank on success,344 * or -EINVAL on invalid specifier cell count.345 */346static inline int347of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec)348{349	if (WARN_ON(hwlock_spec->args_count != 1))350		return -EINVAL;351 352	return hwlock_spec->args[0];353}354 355/**356 * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock357 * @np: device node from which to request the specific hwlock358 * @index: index of the hwlock in the list of values359 *360 * This function provides a means for DT users of the hwspinlock module to361 * get the global lock id of a specific hwspinlock using the phandle of the362 * hwspinlock device, so that it can be requested using the normal363 * hwspin_lock_request_specific() API.364 *365 * Returns: the global lock id number on success, -EPROBE_DEFER if the366 * hwspinlock device is not yet registered, -EINVAL on invalid args367 * specifier value or an appropriate error as returned from the OF parsing368 * of the DT client node.369 */370int of_hwspin_lock_get_id(struct device_node *np, int index)371{372	struct of_phandle_args args;373	struct hwspinlock *hwlock;374	struct radix_tree_iter iter;375	void **slot;376	int id;377	int ret;378 379	ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index,380					 &args);381	if (ret)382		return ret;383 384	if (!of_device_is_available(args.np)) {385		ret = -ENOENT;386		goto out;387	}388 389	/* Find the hwspinlock device: we need its base_id */390	ret = -EPROBE_DEFER;391	rcu_read_lock();392	radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) {393		hwlock = radix_tree_deref_slot(slot);394		if (unlikely(!hwlock))395			continue;396		if (radix_tree_deref_retry(hwlock)) {397			slot = radix_tree_iter_retry(&iter);398			continue;399		}400 401		if (device_match_of_node(hwlock->bank->dev, args.np)) {402			ret = 0;403			break;404		}405	}406	rcu_read_unlock();407	if (ret < 0)408		goto out;409 410	id = of_hwspin_lock_simple_xlate(&args);411	if (id < 0 || id >= hwlock->bank->num_locks) {412		ret = -EINVAL;413		goto out;414	}415	id += hwlock->bank->base_id;416 417out:418	of_node_put(args.np);419	return ret ? ret : id;420}421EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id);422 423/**424 * of_hwspin_lock_get_id_byname() - get lock id for an specified hwlock name425 * @np: device node from which to request the specific hwlock426 * @name: hwlock name427 *428 * This function provides a means for DT users of the hwspinlock module to429 * get the global lock id of a specific hwspinlock using the specified name of430 * the hwspinlock device, so that it can be requested using the normal431 * hwspin_lock_request_specific() API.432 *433 * Returns: the global lock id number on success, -EPROBE_DEFER if the434 * hwspinlock device is not yet registered, -EINVAL on invalid args435 * specifier value or an appropriate error as returned from the OF parsing436 * of the DT client node.437 */438int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name)439{440	int index;441 442	if (!name)443		return -EINVAL;444 445	index = of_property_match_string(np, "hwlock-names", name);446	if (index < 0)447		return index;448 449	return of_hwspin_lock_get_id(np, index);450}451EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id_byname);452 453static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)454{455	struct hwspinlock *tmp;456	int ret;457 458	mutex_lock(&hwspinlock_tree_lock);459 460	ret = radix_tree_insert(&hwspinlock_tree, id, hwlock);461	if (ret) {462		if (ret == -EEXIST)463			pr_err("hwspinlock id %d already exists!\n", id);464		goto out;465	}466 467	/* mark this hwspinlock as available */468	tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);469 470	/* self-sanity check which should never fail */471	WARN_ON(tmp != hwlock);472 473out:474	mutex_unlock(&hwspinlock_tree_lock);475	return 0;476}477 478static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id)479{480	struct hwspinlock *hwlock = NULL;481	int ret;482 483	mutex_lock(&hwspinlock_tree_lock);484 485	/* make sure the hwspinlock is not in use (tag is set) */486	ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);487	if (ret == 0) {488		pr_err("hwspinlock %d still in use (or not present)\n", id);489		goto out;490	}491 492	hwlock = radix_tree_delete(&hwspinlock_tree, id);493	if (!hwlock) {494		pr_err("failed to delete hwspinlock %d\n", id);495		goto out;496	}497 498out:499	mutex_unlock(&hwspinlock_tree_lock);500	return hwlock;501}502 503/**504 * hwspin_lock_register() - register a new hw spinlock device505 * @bank: the hwspinlock device, which usually provides numerous hw locks506 * @dev: the backing device507 * @ops: hwspinlock handlers for this device508 * @base_id: id of the first hardware spinlock in this bank509 * @num_locks: number of hwspinlocks provided by this device510 *511 * This function should be called from the underlying platform-specific512 * implementation, to register a new hwspinlock device instance.513 *514 * Should be called from a process context (might sleep)515 *516 * Returns: %0 on success, or an appropriate error code on failure517 */518int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,519		const struct hwspinlock_ops *ops, int base_id, int num_locks)520{521	struct hwspinlock *hwlock;522	int ret = 0, i;523 524	if (!bank || !ops || !dev || !num_locks || !ops->trylock ||525							!ops->unlock) {526		pr_err("invalid parameters\n");527		return -EINVAL;528	}529 530	bank->dev = dev;531	bank->ops = ops;532	bank->base_id = base_id;533	bank->num_locks = num_locks;534 535	for (i = 0; i < num_locks; i++) {536		hwlock = &bank->lock[i];537 538		spin_lock_init(&hwlock->lock);539		hwlock->bank = bank;540 541		ret = hwspin_lock_register_single(hwlock, base_id + i);542		if (ret)543			goto reg_failed;544	}545 546	return 0;547 548reg_failed:549	while (--i >= 0)550		hwspin_lock_unregister_single(base_id + i);551	return ret;552}553EXPORT_SYMBOL_GPL(hwspin_lock_register);554 555/**556 * hwspin_lock_unregister() - unregister an hw spinlock device557 * @bank: the hwspinlock device, which usually provides numerous hw locks558 *559 * This function should be called from the underlying platform-specific560 * implementation, to unregister an existing (and unused) hwspinlock.561 *562 * Should be called from a process context (might sleep)563 *564 * Returns: %0 on success, or an appropriate error code on failure565 */566int hwspin_lock_unregister(struct hwspinlock_device *bank)567{568	struct hwspinlock *hwlock, *tmp;569	int i;570 571	for (i = 0; i < bank->num_locks; i++) {572		hwlock = &bank->lock[i];573 574		tmp = hwspin_lock_unregister_single(bank->base_id + i);575		if (!tmp)576			return -EBUSY;577 578		/* self-sanity check that should never fail */579		WARN_ON(tmp != hwlock);580	}581 582	return 0;583}584EXPORT_SYMBOL_GPL(hwspin_lock_unregister);585 586static void devm_hwspin_lock_unreg(struct device *dev, void *res)587{588	hwspin_lock_unregister(*(struct hwspinlock_device **)res);589}590 591static int devm_hwspin_lock_device_match(struct device *dev, void *res,592					 void *data)593{594	struct hwspinlock_device **bank = res;595 596	if (WARN_ON(!bank || !*bank))597		return 0;598 599	return *bank == data;600}601 602/**603 * devm_hwspin_lock_unregister() - unregister an hw spinlock device for604 *				   a managed device605 * @dev: the backing device606 * @bank: the hwspinlock device, which usually provides numerous hw locks607 *608 * This function should be called from the underlying platform-specific609 * implementation, to unregister an existing (and unused) hwspinlock.610 *611 * Should be called from a process context (might sleep)612 *613 * Returns: %0 on success, or an appropriate error code on failure614 */615int devm_hwspin_lock_unregister(struct device *dev,616				struct hwspinlock_device *bank)617{618	int ret;619 620	ret = devres_release(dev, devm_hwspin_lock_unreg,621			     devm_hwspin_lock_device_match, bank);622	WARN_ON(ret);623 624	return ret;625}626EXPORT_SYMBOL_GPL(devm_hwspin_lock_unregister);627 628/**629 * devm_hwspin_lock_register() - register a new hw spinlock device for630 *				 a managed device631 * @dev: the backing device632 * @bank: the hwspinlock device, which usually provides numerous hw locks633 * @ops: hwspinlock handlers for this device634 * @base_id: id of the first hardware spinlock in this bank635 * @num_locks: number of hwspinlocks provided by this device636 *637 * This function should be called from the underlying platform-specific638 * implementation, to register a new hwspinlock device instance.639 *640 * Should be called from a process context (might sleep)641 *642 * Returns: %0 on success, or an appropriate error code on failure643 */644int devm_hwspin_lock_register(struct device *dev,645			      struct hwspinlock_device *bank,646			      const struct hwspinlock_ops *ops,647			      int base_id, int num_locks)648{649	struct hwspinlock_device **ptr;650	int ret;651 652	ptr = devres_alloc(devm_hwspin_lock_unreg, sizeof(*ptr), GFP_KERNEL);653	if (!ptr)654		return -ENOMEM;655 656	ret = hwspin_lock_register(bank, dev, ops, base_id, num_locks);657	if (!ret) {658		*ptr = bank;659		devres_add(dev, ptr);660	} else {661		devres_free(ptr);662	}663 664	return ret;665}666EXPORT_SYMBOL_GPL(devm_hwspin_lock_register);667 668/**669 * __hwspin_lock_request() - tag an hwspinlock as used and power it up670 * @hwlock: the target hwspinlock671 *672 * This is an internal function that prepares an hwspinlock instance673 * before it is given to the user. The function assumes that674 * hwspinlock_tree_lock is taken.675 *676 * Returns: %0 or positive to indicate success, and a negative value to677 * indicate an error (with the appropriate error code)678 */679static int __hwspin_lock_request(struct hwspinlock *hwlock)680{681	struct device *dev = hwlock->bank->dev;682	struct hwspinlock *tmp;683	int ret;684 685	/* prevent underlying implementation from being removed */686	if (!try_module_get(dev->driver->owner)) {687		dev_err(dev, "%s: can't get owner\n", __func__);688		return -EINVAL;689	}690 691	/* notify PM core that power is now needed */692	ret = pm_runtime_get_sync(dev);693	if (ret < 0 && ret != -EACCES) {694		dev_err(dev, "%s: can't power on device\n", __func__);695		pm_runtime_put_noidle(dev);696		module_put(dev->driver->owner);697		return ret;698	}699 700	ret = 0;701 702	/* mark hwspinlock as used, should not fail */703	tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock),704							HWSPINLOCK_UNUSED);705 706	/* self-sanity check that should never fail */707	WARN_ON(tmp != hwlock);708 709	return ret;710}711 712/**713 * hwspin_lock_get_id() - retrieve id number of a given hwspinlock714 * @hwlock: a valid hwspinlock instance715 *716 * Returns: the id number of a given @hwlock, or -EINVAL if @hwlock is invalid.717 */718int hwspin_lock_get_id(struct hwspinlock *hwlock)719{720	if (!hwlock) {721		pr_err("invalid hwlock\n");722		return -EINVAL;723	}724 725	return hwlock_to_id(hwlock);726}727EXPORT_SYMBOL_GPL(hwspin_lock_get_id);728 729/**730 * hwspin_lock_request() - request an hwspinlock731 *732 * This function should be called by users of the hwspinlock device,733 * in order to dynamically assign them an unused hwspinlock.734 * Usually the user of this lock will then have to communicate the lock's id735 * to the remote core before it can be used for synchronization (to get the736 * id of a given hwlock, use hwspin_lock_get_id()).737 *738 * Should be called from a process context (might sleep)739 *740 * Returns: the address of the assigned hwspinlock, or %NULL on error741 */742struct hwspinlock *hwspin_lock_request(void)743{744	struct hwspinlock *hwlock;745	int ret;746 747	mutex_lock(&hwspinlock_tree_lock);748 749	/* look for an unused lock */750	ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock,751						0, 1, HWSPINLOCK_UNUSED);752	if (ret == 0) {753		pr_warn("a free hwspinlock is not available\n");754		hwlock = NULL;755		goto out;756	}757 758	/* sanity check that should never fail */759	WARN_ON(ret > 1);760 761	/* mark as used and power up */762	ret = __hwspin_lock_request(hwlock);763	if (ret < 0)764		hwlock = NULL;765 766out:767	mutex_unlock(&hwspinlock_tree_lock);768	return hwlock;769}770EXPORT_SYMBOL_GPL(hwspin_lock_request);771 772/**773 * hwspin_lock_request_specific() - request for a specific hwspinlock774 * @id: index of the specific hwspinlock that is requested775 *776 * This function should be called by users of the hwspinlock module,777 * in order to assign them a specific hwspinlock.778 * Usually early board code will be calling this function in order to779 * reserve specific hwspinlock ids for predefined purposes.780 *781 * Should be called from a process context (might sleep)782 *783 * Returns: the address of the assigned hwspinlock, or %NULL on error784 */785struct hwspinlock *hwspin_lock_request_specific(unsigned int id)786{787	struct hwspinlock *hwlock;788	int ret;789 790	mutex_lock(&hwspinlock_tree_lock);791 792	/* make sure this hwspinlock exists */793	hwlock = radix_tree_lookup(&hwspinlock_tree, id);794	if (!hwlock) {795		pr_warn("hwspinlock %u does not exist\n", id);796		goto out;797	}798 799	/* sanity check (this shouldn't happen) */800	WARN_ON(hwlock_to_id(hwlock) != id);801 802	/* make sure this hwspinlock is unused */803	ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);804	if (ret == 0) {805		pr_warn("hwspinlock %u is already in use\n", id);806		hwlock = NULL;807		goto out;808	}809 810	/* mark as used and power up */811	ret = __hwspin_lock_request(hwlock);812	if (ret < 0)813		hwlock = NULL;814 815out:816	mutex_unlock(&hwspinlock_tree_lock);817	return hwlock;818}819EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);820 821/**822 * hwspin_lock_free() - free a specific hwspinlock823 * @hwlock: the specific hwspinlock to free824 *825 * This function mark @hwlock as free again.826 * Should only be called with an @hwlock that was retrieved from827 * an earlier call to hwspin_lock_request{_specific}.828 *829 * Should be called from a process context (might sleep)830 *831 * Returns: %0 on success, or an appropriate error code on failure832 */833int hwspin_lock_free(struct hwspinlock *hwlock)834{835	struct device *dev;836	struct hwspinlock *tmp;837	int ret;838 839	if (!hwlock) {840		pr_err("invalid hwlock\n");841		return -EINVAL;842	}843 844	dev = hwlock->bank->dev;845	mutex_lock(&hwspinlock_tree_lock);846 847	/* make sure the hwspinlock is used */848	ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock),849							HWSPINLOCK_UNUSED);850	if (ret == 1) {851		dev_err(dev, "%s: hwlock is already free\n", __func__);852		dump_stack();853		ret = -EINVAL;854		goto out;855	}856 857	/* notify the underlying device that power is not needed */858	pm_runtime_put(dev);859 860	/* mark this hwspinlock as available */861	tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock),862							HWSPINLOCK_UNUSED);863 864	/* sanity check (this shouldn't happen) */865	WARN_ON(tmp != hwlock);866 867	module_put(dev->driver->owner);868 869out:870	mutex_unlock(&hwspinlock_tree_lock);871	return ret;872}873EXPORT_SYMBOL_GPL(hwspin_lock_free);874 875static int devm_hwspin_lock_match(struct device *dev, void *res, void *data)876{877	struct hwspinlock **hwlock = res;878 879	if (WARN_ON(!hwlock || !*hwlock))880		return 0;881 882	return *hwlock == data;883}884 885static void devm_hwspin_lock_release(struct device *dev, void *res)886{887	hwspin_lock_free(*(struct hwspinlock **)res);888}889 890/**891 * devm_hwspin_lock_free() - free a specific hwspinlock for a managed device892 * @dev: the device to free the specific hwspinlock893 * @hwlock: the specific hwspinlock to free894 *895 * This function mark @hwlock as free again.896 * Should only be called with an @hwlock that was retrieved from897 * an earlier call to hwspin_lock_request{_specific}.898 *899 * Should be called from a process context (might sleep)900 *901 * Returns: %0 on success, or an appropriate error code on failure902 */903int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock)904{905	int ret;906 907	ret = devres_release(dev, devm_hwspin_lock_release,908			     devm_hwspin_lock_match, hwlock);909	WARN_ON(ret);910 911	return ret;912}913EXPORT_SYMBOL_GPL(devm_hwspin_lock_free);914 915/**916 * devm_hwspin_lock_request() - request an hwspinlock for a managed device917 * @dev: the device to request an hwspinlock918 *919 * This function should be called by users of the hwspinlock device,920 * in order to dynamically assign them an unused hwspinlock.921 * Usually the user of this lock will then have to communicate the lock's id922 * to the remote core before it can be used for synchronization (to get the923 * id of a given hwlock, use hwspin_lock_get_id()).924 *925 * Should be called from a process context (might sleep)926 *927 * Returns: the address of the assigned hwspinlock, or %NULL on error928 */929struct hwspinlock *devm_hwspin_lock_request(struct device *dev)930{931	struct hwspinlock **ptr, *hwlock;932 933	ptr = devres_alloc(devm_hwspin_lock_release, sizeof(*ptr), GFP_KERNEL);934	if (!ptr)935		return NULL;936 937	hwlock = hwspin_lock_request();938	if (hwlock) {939		*ptr = hwlock;940		devres_add(dev, ptr);941	} else {942		devres_free(ptr);943	}944 945	return hwlock;946}947EXPORT_SYMBOL_GPL(devm_hwspin_lock_request);948 949/**950 * devm_hwspin_lock_request_specific() - request for a specific hwspinlock for951 *					 a managed device952 * @dev: the device to request the specific hwspinlock953 * @id: index of the specific hwspinlock that is requested954 *955 * This function should be called by users of the hwspinlock module,956 * in order to assign them a specific hwspinlock.957 * Usually early board code will be calling this function in order to958 * reserve specific hwspinlock ids for predefined purposes.959 *960 * Should be called from a process context (might sleep)961 *962 * Returns: the address of the assigned hwspinlock, or %NULL on error963 */964struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev,965						     unsigned int id)966{967	struct hwspinlock **ptr, *hwlock;968 969	ptr = devres_alloc(devm_hwspin_lock_release, sizeof(*ptr), GFP_KERNEL);970	if (!ptr)971		return NULL;972 973	hwlock = hwspin_lock_request_specific(id);974	if (hwlock) {975		*ptr = hwlock;976		devres_add(dev, ptr);977	} else {978		devres_free(ptr);979	}980 981	return hwlock;982}983EXPORT_SYMBOL_GPL(devm_hwspin_lock_request_specific);984 985MODULE_DESCRIPTION("Hardware spinlock interface");986MODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>");987