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