Blame view

drivers/hwspinlock/hwspinlock_core.c 27.2 KB
eebba71e1   Suman Anna   hwspinlock/core: ...
1
  // SPDX-License-Identifier: GPL-2.0
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
2
3
4
5
6
7
  /*
   * Hardware spinlock framework
   *
   * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
   *
   * Contact: Ohad Ben-Cohen <ohad@wizery.com>
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
8
9
10
   */
  
  #define pr_fmt(fmt)    "%s: " fmt, __func__
360aa640a   Fabien Dessenne   hwspinlock: add t...
11
  #include <linux/delay.h>
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
12
13
14
15
16
17
18
19
20
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/spinlock.h>
  #include <linux/types.h>
  #include <linux/err.h>
  #include <linux/jiffies.h>
  #include <linux/radix-tree.h>
  #include <linux/hwspinlock.h>
  #include <linux/pm_runtime.h>
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
21
  #include <linux/mutex.h>
fb7737e94   Suman Anna   hwspinlock/core: ...
22
  #include <linux/of.h>
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
23
24
  
  #include "hwspinlock_internal.h"
360aa640a   Fabien Dessenne   hwspinlock: add t...
25
26
  /* retry delay used in atomic context */
  #define HWSPINLOCK_RETRY_DELAY_US	100
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /* radix tree tags */
  #define HWSPINLOCK_UNUSED	(0) /* tags an hwspinlock as unused */
  
  /*
   * A radix tree is used to maintain the available hwspinlock instances.
   * The tree associates hwspinlock pointers with their integer key id,
   * and provides easy-to-use API which makes the hwspinlock core code simple
   * and easy to read.
   *
   * Radix trees are quick on lookups, and reasonably efficient in terms of
   * storage, especially with high density usages such as this framework
   * requires (a continuous range of integer keys, beginning with zero, is
   * used as the ID's of the hwspinlock instances).
   *
   * The radix tree API supports tagging items in the tree, which this
   * framework uses to mark unused hwspinlock instances (see the
   * HWSPINLOCK_UNUSED tag above). As a result, the process of querying the
   * tree, looking for an unused hwspinlock instance, is now reduced to a
   * single radix tree API call.
   */
  static RADIX_TREE(hwspinlock_tree, GFP_KERNEL);
  
  /*
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
50
   * Synchronization of access to the tree is achieved using this mutex,
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
51
   * as the radix-tree API requires that users provide all synchronisation.
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
52
   * A mutex is needed because we're using non-atomic radix tree allocations.
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
53
   */
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
54
  static DEFINE_MUTEX(hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
55
56
57
58
59
60
61
62
63
64
65
  
  /**
   * __hwspin_trylock() - attempt to lock a specific hwspinlock
   * @hwlock: an hwspinlock which we want to trylock
   * @mode: controls whether local interrupts are disabled or not
   * @flags: a pointer where the caller's interrupt state will be saved at (if
   *         requested)
   *
   * This function attempts to lock an hwspinlock, and will immediately
   * fail if the hwspinlock is already taken.
   *
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
66
67
68
69
70
   * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine
   * of getting hardware lock with mutex or spinlock. Since in some scenarios,
   * user need some time-consuming or sleepable operations under the hardware
   * lock, they need one sleepable lock (like mutex) to protect the operations.
   *
360aa640a   Fabien Dessenne   hwspinlock: add t...
71
72
73
74
75
   * If the mode is neither HWLOCK_IN_ATOMIC nor HWLOCK_RAW, upon a successful
   * return from this function, preemption (and possibly interrupts) is disabled,
   * so the caller must not sleep, and is advised to release the hwspinlock as
   * soon as possible. This is required in order to minimize remote cores polling
   * on the hardware interconnect.
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
   *
   * The user decides whether local interrupts are disabled or not, and if yes,
   * whether he wants their previous state to be saved. It is up to the user
   * to choose the appropriate @mode of operation, exactly the same way users
   * should decide between spin_trylock, spin_trylock_irq and
   * spin_trylock_irqsave.
   *
   * Returns 0 if we successfully locked the hwspinlock or -EBUSY if
   * the hwspinlock was already taken.
   * This function will never sleep.
   */
  int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
  {
  	int ret;
b2547dce4   Baolin Wang   hwspinlock: Remov...
90
91
  	if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE)))
  		return -EINVAL;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  
  	/*
  	 * This spin_lock{_irq, _irqsave} serves three purposes:
  	 *
  	 * 1. Disable preemption, in order to minimize the period of time
  	 *    in which the hwspinlock is taken. This is important in order
  	 *    to minimize the possible polling on the hardware interconnect
  	 *    by a remote user of this lock.
  	 * 2. Make the hwspinlock SMP-safe (so we can take it from
  	 *    additional contexts on the local host).
  	 * 3. Ensure that in_atomic/might_sleep checks catch potential
  	 *    problems with hwspinlock usage (e.g. scheduler checks like
  	 *    'scheduling while atomic' etc.)
  	 */
66742b19e   Baolin Wang   hwspinlock: Conve...
106
107
  	switch (mode) {
  	case HWLOCK_IRQSTATE:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
108
  		ret = spin_trylock_irqsave(&hwlock->lock, *flags);
66742b19e   Baolin Wang   hwspinlock: Conve...
109
110
  		break;
  	case HWLOCK_IRQ:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
111
  		ret = spin_trylock_irq(&hwlock->lock);
66742b19e   Baolin Wang   hwspinlock: Conve...
112
  		break;
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
113
  	case HWLOCK_RAW:
360aa640a   Fabien Dessenne   hwspinlock: add t...
114
  	case HWLOCK_IN_ATOMIC:
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
115
116
  		ret = 1;
  		break;
66742b19e   Baolin Wang   hwspinlock: Conve...
117
  	default:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
118
  		ret = spin_trylock(&hwlock->lock);
66742b19e   Baolin Wang   hwspinlock: Conve...
119
120
  		break;
  	}
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
121
122
123
124
125
126
  
  	/* is lock already taken by another context on the local cpu ? */
  	if (!ret)
  		return -EBUSY;
  
  	/* try to take the hwspinlock device */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
127
  	ret = hwlock->bank->ops->trylock(hwlock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
128
129
130
  
  	/* if hwlock is already taken, undo spin_trylock_* and exit */
  	if (!ret) {
66742b19e   Baolin Wang   hwspinlock: Conve...
131
132
  		switch (mode) {
  		case HWLOCK_IRQSTATE:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
133
  			spin_unlock_irqrestore(&hwlock->lock, *flags);
66742b19e   Baolin Wang   hwspinlock: Conve...
134
135
  			break;
  		case HWLOCK_IRQ:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
136
  			spin_unlock_irq(&hwlock->lock);
66742b19e   Baolin Wang   hwspinlock: Conve...
137
  			break;
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
138
  		case HWLOCK_RAW:
360aa640a   Fabien Dessenne   hwspinlock: add t...
139
  		case HWLOCK_IN_ATOMIC:
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
140
141
  			/* Nothing to do */
  			break;
66742b19e   Baolin Wang   hwspinlock: Conve...
142
  		default:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
143
  			spin_unlock(&hwlock->lock);
66742b19e   Baolin Wang   hwspinlock: Conve...
144
145
  			break;
  		}
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  
  		return -EBUSY;
  	}
  
  	/*
  	 * We can be sure the other core's memory operations
  	 * are observable to us only _after_ we successfully take
  	 * the hwspinlock, and we must make sure that subsequent memory
  	 * operations (both reads and writes) will not be reordered before
  	 * we actually took the hwspinlock.
  	 *
  	 * Note: the implicit memory barrier of the spinlock above is too
  	 * early, so we need this additional explicit memory barrier.
  	 */
  	mb();
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(__hwspin_trylock);
  
  /**
   * __hwspin_lock_timeout() - lock an hwspinlock with timeout limit
   * @hwlock: the hwspinlock to be locked
   * @timeout: timeout value in msecs
   * @mode: mode which controls whether local interrupts are disabled or not
   * @flags: a pointer to where the caller's interrupt state will be saved at (if
   *         requested)
   *
   * This function locks the given @hwlock. If the @hwlock
   * is already taken, the function will busy loop waiting for it to
   * be released, but give up after @timeout msecs have elapsed.
   *
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
178
179
180
181
182
   * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine
   * of getting hardware lock with mutex or spinlock. Since in some scenarios,
   * user need some time-consuming or sleepable operations under the hardware
   * lock, they need one sleepable lock (like mutex) to protect the operations.
   *
360aa640a   Fabien Dessenne   hwspinlock: add t...
183
184
185
186
187
188
189
190
   * If the mode is HWLOCK_IN_ATOMIC (called from an atomic context) the timeout
   * is handled with busy-waiting delays, hence shall not exceed few msecs.
   *
   * If the mode is neither HWLOCK_IN_ATOMIC nor HWLOCK_RAW, upon a successful
   * return from this function, preemption (and possibly interrupts) is disabled,
   * so the caller must not sleep, and is advised to release the hwspinlock as
   * soon as possible. This is required in order to minimize remote cores polling
   * on the hardware interconnect.
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
   *
   * The user decides whether local interrupts are disabled or not, and if yes,
   * whether he wants their previous state to be saved. It is up to the user
   * to choose the appropriate @mode of operation, exactly the same way users
   * should decide between spin_lock, spin_lock_irq and spin_lock_irqsave.
   *
   * Returns 0 when the @hwlock was successfully taken, and an appropriate
   * error code otherwise (most notably -ETIMEDOUT if the @hwlock is still
   * busy after @timeout msecs). The function will never sleep.
   */
  int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to,
  					int mode, unsigned long *flags)
  {
  	int ret;
360aa640a   Fabien Dessenne   hwspinlock: add t...
205
  	unsigned long expire, atomic_delay = 0;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
206
207
208
209
210
211
212
213
214
215
216
217
218
  
  	expire = msecs_to_jiffies(to) + jiffies;
  
  	for (;;) {
  		/* Try to take the hwspinlock */
  		ret = __hwspin_trylock(hwlock, mode, flags);
  		if (ret != -EBUSY)
  			break;
  
  		/*
  		 * The lock is already taken, let's check if the user wants
  		 * us to try again
  		 */
360aa640a   Fabien Dessenne   hwspinlock: add t...
219
220
221
222
223
224
225
226
227
  		if (mode == HWLOCK_IN_ATOMIC) {
  			udelay(HWSPINLOCK_RETRY_DELAY_US);
  			atomic_delay += HWSPINLOCK_RETRY_DELAY_US;
  			if (atomic_delay > to * 1000)
  				return -ETIMEDOUT;
  		} else {
  			if (time_is_before_eq_jiffies(expire))
  				return -ETIMEDOUT;
  		}
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
228
229
230
231
232
  
  		/*
  		 * Allow platform-specific relax handlers to prevent
  		 * hogging the interconnect (no sleeping, though)
  		 */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
233
234
  		if (hwlock->bank->ops->relax)
  			hwlock->bank->ops->relax(hwlock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  	}
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(__hwspin_lock_timeout);
  
  /**
   * __hwspin_unlock() - unlock a specific hwspinlock
   * @hwlock: a previously-acquired hwspinlock which we want to unlock
   * @mode: controls whether local interrupts needs to be restored or not
   * @flags: previous caller's interrupt state to restore (if requested)
   *
   * This function will unlock a specific hwspinlock, enable preemption and
   * (possibly) enable interrupts or restore their previous state.
   * @hwlock must be already locked before calling this function: it is a bug
   * to call unlock on a @hwlock that is already unlocked.
   *
   * The user decides whether local interrupts should be enabled or not, and
   * if yes, whether he wants their previous state to be restored. It is up
   * to the user to choose the appropriate @mode of operation, exactly the
   * same way users decide between spin_unlock, spin_unlock_irq and
   * spin_unlock_irqrestore.
   *
   * The function will never sleep.
   */
  void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
  {
b2547dce4   Baolin Wang   hwspinlock: Remov...
262
263
  	if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE)))
  		return;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  
  	/*
  	 * We must make sure that memory operations (both reads and writes),
  	 * done before unlocking the hwspinlock, will not be reordered
  	 * after the lock is released.
  	 *
  	 * That's the purpose of this explicit memory barrier.
  	 *
  	 * Note: the memory barrier induced by the spin_unlock below is too
  	 * late; the other core is going to access memory soon after it will
  	 * take the hwspinlock, and by then we want to be sure our memory
  	 * operations are already observable.
  	 */
  	mb();
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
278
  	hwlock->bank->ops->unlock(hwlock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
279
280
  
  	/* Undo the spin_trylock{_irq, _irqsave} called while locking */
66742b19e   Baolin Wang   hwspinlock: Conve...
281
282
  	switch (mode) {
  	case HWLOCK_IRQSTATE:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
283
  		spin_unlock_irqrestore(&hwlock->lock, *flags);
66742b19e   Baolin Wang   hwspinlock: Conve...
284
285
  		break;
  	case HWLOCK_IRQ:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
286
  		spin_unlock_irq(&hwlock->lock);
66742b19e   Baolin Wang   hwspinlock: Conve...
287
  		break;
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
288
  	case HWLOCK_RAW:
360aa640a   Fabien Dessenne   hwspinlock: add t...
289
  	case HWLOCK_IN_ATOMIC:
1e6c06a7e   Baolin Wang   hwspinlock: Intro...
290
291
  		/* Nothing to do */
  		break;
66742b19e   Baolin Wang   hwspinlock: Conve...
292
  	default:
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
293
  		spin_unlock(&hwlock->lock);
66742b19e   Baolin Wang   hwspinlock: Conve...
294
295
  		break;
  	}
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
296
297
  }
  EXPORT_SYMBOL_GPL(__hwspin_unlock);
fb7737e94   Suman Anna   hwspinlock/core: ...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  /**
   * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id
   * @bank: the hwspinlock device bank
   * @hwlock_spec: hwlock specifier as found in the device tree
   *
   * This is a simple translation function, suitable for hwspinlock platform
   * drivers that only has a lock specifier length of 1.
   *
   * Returns a relative index of the lock within a specified bank on success,
   * or -EINVAL on invalid specifier cell count.
   */
  static inline int
  of_hwspin_lock_simple_xlate(const struct of_phandle_args *hwlock_spec)
  {
  	if (WARN_ON(hwlock_spec->args_count != 1))
  		return -EINVAL;
  
  	return hwlock_spec->args[0];
  }
  
  /**
   * of_hwspin_lock_get_id() - get lock id for an OF phandle-based specific lock
   * @np: device node from which to request the specific hwlock
   * @index: index of the hwlock in the list of values
   *
   * This function provides a means for DT users of the hwspinlock module to
   * get the global lock id of a specific hwspinlock using the phandle of the
   * hwspinlock device, so that it can be requested using the normal
   * hwspin_lock_request_specific() API.
   *
   * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock
   * device is not yet registered, -EINVAL on invalid args specifier value or an
   * appropriate error as returned from the OF parsing of the DT client node.
   */
  int of_hwspin_lock_get_id(struct device_node *np, int index)
  {
  	struct of_phandle_args args;
  	struct hwspinlock *hwlock;
  	struct radix_tree_iter iter;
  	void **slot;
  	int id;
  	int ret;
  
  	ret = of_parse_phandle_with_args(np, "hwlocks", "#hwlock-cells", index,
  					 &args);
  	if (ret)
  		return ret;
fbd7330c9   Fabien Dessenne   hwspinlock: ignor...
345
346
347
348
  	if (!of_device_is_available(args.np)) {
  		ret = -ENOENT;
  		goto out;
  	}
fb7737e94   Suman Anna   hwspinlock/core: ...
349
350
351
352
353
354
355
  	/* Find the hwspinlock device: we need its base_id */
  	ret = -EPROBE_DEFER;
  	rcu_read_lock();
  	radix_tree_for_each_slot(slot, &hwspinlock_tree, &iter, 0) {
  		hwlock = radix_tree_deref_slot(slot);
  		if (unlikely(!hwlock))
  			continue;
b76ba4af4   Matthew Wilcox   drivers/hwspinloc...
356
  		if (radix_tree_deref_retry(hwlock)) {
c6400ba7e   Matthew Wilcox   drivers/hwspinloc...
357
358
359
  			slot = radix_tree_iter_retry(&iter);
  			continue;
  		}
fb7737e94   Suman Anna   hwspinlock/core: ...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  
  		if (hwlock->bank->dev->of_node == args.np) {
  			ret = 0;
  			break;
  		}
  	}
  	rcu_read_unlock();
  	if (ret < 0)
  		goto out;
  
  	id = of_hwspin_lock_simple_xlate(&args);
  	if (id < 0 || id >= hwlock->bank->num_locks) {
  		ret = -EINVAL;
  		goto out;
  	}
  	id += hwlock->bank->base_id;
  
  out:
  	of_node_put(args.np);
  	return ret ? ret : id;
  }
  EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id);
5560f70ca   Baolin Wang   hwspinlock: Add o...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
  /**
   * of_hwspin_lock_get_id_byname() - get lock id for an specified hwlock name
   * @np: device node from which to request the specific hwlock
   * @name: hwlock name
   *
   * This function provides a means for DT users of the hwspinlock module to
   * get the global lock id of a specific hwspinlock using the specified name of
   * the hwspinlock device, so that it can be requested using the normal
   * hwspin_lock_request_specific() API.
   *
   * Returns the global lock id number on success, -EPROBE_DEFER if the hwspinlock
   * device is not yet registered, -EINVAL on invalid args specifier value or an
   * appropriate error as returned from the OF parsing of the DT client node.
   */
  int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name)
  {
  	int index;
  
  	if (!name)
  		return -EINVAL;
  
  	index = of_property_match_string(np, "hwlock-names", name);
  	if (index < 0)
  		return index;
  
  	return of_hwspin_lock_get_id(np, index);
  }
  EXPORT_SYMBOL_GPL(of_hwspin_lock_get_id_byname);
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
410
  static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id)
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
411
412
413
  {
  	struct hwspinlock *tmp;
  	int ret;
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
414
  	mutex_lock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
415

300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
416
417
418
419
420
  	ret = radix_tree_insert(&hwspinlock_tree, id, hwlock);
  	if (ret) {
  		if (ret == -EEXIST)
  			pr_err("hwspinlock id %d already exists!
  ", id);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
421
  		goto out;
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
422
  	}
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
423
424
  
  	/* mark this hwspinlock as available */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
425
  	tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
426
427
428
429
430
  
  	/* self-sanity check which should never fail */
  	WARN_ON(tmp != hwlock);
  
  out:
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
431
  	mutex_unlock(&hwspinlock_tree_lock);
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
432
  	return 0;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
433
  }
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
434

300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
435
  static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id)
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
436
437
438
  {
  	struct hwspinlock *hwlock = NULL;
  	int ret;
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
439
  	mutex_lock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  
  	/* make sure the hwspinlock is not in use (tag is set) */
  	ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
  	if (ret == 0) {
  		pr_err("hwspinlock %d still in use (or not present)
  ", id);
  		goto out;
  	}
  
  	hwlock = radix_tree_delete(&hwspinlock_tree, id);
  	if (!hwlock) {
  		pr_err("failed to delete hwspinlock %d
  ", id);
  		goto out;
  	}
  
  out:
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
457
  	mutex_unlock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
458
459
  	return hwlock;
  }
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  
  /**
   * hwspin_lock_register() - register a new hw spinlock device
   * @bank: the hwspinlock device, which usually provides numerous hw locks
   * @dev: the backing device
   * @ops: hwspinlock handlers for this device
   * @base_id: id of the first hardware spinlock in this bank
   * @num_locks: number of hwspinlocks provided by this device
   *
   * This function should be called from the underlying platform-specific
   * implementation, to register a new hwspinlock device instance.
   *
   * Should be called from a process context (might sleep)
   *
   * Returns 0 on success, or an appropriate error code on failure
   */
  int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
  		const struct hwspinlock_ops *ops, int base_id, int num_locks)
  {
  	struct hwspinlock *hwlock;
  	int ret = 0, i;
  
  	if (!bank || !ops || !dev || !num_locks || !ops->trylock ||
  							!ops->unlock) {
  		pr_err("invalid parameters
  ");
  		return -EINVAL;
  	}
  
  	bank->dev = dev;
  	bank->ops = ops;
  	bank->base_id = base_id;
  	bank->num_locks = num_locks;
  
  	for (i = 0; i < num_locks; i++) {
  		hwlock = &bank->lock[i];
  
  		spin_lock_init(&hwlock->lock);
  		hwlock->bank = bank;
476a7eeb6   Shinya Kuribayashi   hwspinlock/core: ...
499
  		ret = hwspin_lock_register_single(hwlock, base_id + i);
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
500
501
502
503
504
505
506
507
  		if (ret)
  			goto reg_failed;
  	}
  
  	return 0;
  
  reg_failed:
  	while (--i >= 0)
476a7eeb6   Shinya Kuribayashi   hwspinlock/core: ...
508
  		hwspin_lock_unregister_single(base_id + i);
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
  	return ret;
  }
  EXPORT_SYMBOL_GPL(hwspin_lock_register);
  
  /**
   * hwspin_lock_unregister() - unregister an hw spinlock device
   * @bank: the hwspinlock device, which usually provides numerous hw locks
   *
   * This function should be called from the underlying platform-specific
   * implementation, to unregister an existing (and unused) hwspinlock.
   *
   * Should be called from a process context (might sleep)
   *
   * Returns 0 on success, or an appropriate error code on failure
   */
  int hwspin_lock_unregister(struct hwspinlock_device *bank)
  {
  	struct hwspinlock *hwlock, *tmp;
  	int i;
  
  	for (i = 0; i < bank->num_locks; i++) {
  		hwlock = &bank->lock[i];
  
  		tmp = hwspin_lock_unregister_single(bank->base_id + i);
  		if (!tmp)
  			return -EBUSY;
  
  		/* self-sanity check that should never fail */
  		WARN_ON(tmp != hwlock);
  	}
  
  	return 0;
  }
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
542
  EXPORT_SYMBOL_GPL(hwspin_lock_unregister);
c102780ac   Baolin Wang   hwspinlock: Add d...
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
  static void devm_hwspin_lock_unreg(struct device *dev, void *res)
  {
  	hwspin_lock_unregister(*(struct hwspinlock_device **)res);
  }
  
  static int devm_hwspin_lock_device_match(struct device *dev, void *res,
  					 void *data)
  {
  	struct hwspinlock_device **bank = res;
  
  	if (WARN_ON(!bank || !*bank))
  		return 0;
  
  	return *bank == data;
  }
  
  /**
   * devm_hwspin_lock_unregister() - unregister an hw spinlock device for
   *				   a managed device
   * @dev: the backing device
   * @bank: the hwspinlock device, which usually provides numerous hw locks
   *
   * This function should be called from the underlying platform-specific
   * implementation, to unregister an existing (and unused) hwspinlock.
   *
   * Should be called from a process context (might sleep)
   *
   * Returns 0 on success, or an appropriate error code on failure
   */
  int devm_hwspin_lock_unregister(struct device *dev,
  				struct hwspinlock_device *bank)
  {
  	int ret;
  
  	ret = devres_release(dev, devm_hwspin_lock_unreg,
  			     devm_hwspin_lock_device_match, bank);
  	WARN_ON(ret);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(devm_hwspin_lock_unregister);
  
  /**
   * devm_hwspin_lock_register() - register a new hw spinlock device for
   *				 a managed device
   * @dev: the backing device
   * @bank: the hwspinlock device, which usually provides numerous hw locks
   * @ops: hwspinlock handlers for this device
   * @base_id: id of the first hardware spinlock in this bank
   * @num_locks: number of hwspinlocks provided by this device
   *
   * This function should be called from the underlying platform-specific
   * implementation, to register a new hwspinlock device instance.
   *
   * Should be called from a process context (might sleep)
   *
   * Returns 0 on success, or an appropriate error code on failure
   */
  int devm_hwspin_lock_register(struct device *dev,
  			      struct hwspinlock_device *bank,
  			      const struct hwspinlock_ops *ops,
  			      int base_id, int num_locks)
  {
  	struct hwspinlock_device **ptr;
  	int ret;
  
  	ptr = devres_alloc(devm_hwspin_lock_unreg, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return -ENOMEM;
  
  	ret = hwspin_lock_register(bank, dev, ops, base_id, num_locks);
  	if (!ret) {
  		*ptr = bank;
  		devres_add(dev, ptr);
  	} else {
  		devres_free(ptr);
  	}
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(devm_hwspin_lock_register);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
624
625
626
627
628
629
630
631
632
633
634
635
  /**
   * __hwspin_lock_request() - tag an hwspinlock as used and power it up
   *
   * This is an internal function that prepares an hwspinlock instance
   * before it is given to the user. The function assumes that
   * hwspinlock_tree_lock is taken.
   *
   * Returns 0 or positive to indicate success, and a negative value to
   * indicate an error (with the appropriate error code)
   */
  static int __hwspin_lock_request(struct hwspinlock *hwlock)
  {
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
636
  	struct device *dev = hwlock->bank->dev;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
637
638
639
640
  	struct hwspinlock *tmp;
  	int ret;
  
  	/* prevent underlying implementation from being removed */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
641
642
643
  	if (!try_module_get(dev->driver->owner)) {
  		dev_err(dev, "%s: can't get owner
  ", __func__);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
644
645
646
647
  		return -EINVAL;
  	}
  
  	/* notify PM core that power is now needed */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
648
  	ret = pm_runtime_get_sync(dev);
a6fc49051   Baolin Wang   hwspinlock: Let t...
649
  	if (ret < 0 && ret != -EACCES) {
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
650
651
  		dev_err(dev, "%s: can't power on device
  ", __func__);
c10b90d85   Li Fei   hwspinlock: fix _...
652
653
  		pm_runtime_put_noidle(dev);
  		module_put(dev->driver->owner);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
654
655
  		return ret;
  	}
a6fc49051   Baolin Wang   hwspinlock: Let t...
656
  	ret = 0;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
657
  	/* mark hwspinlock as used, should not fail */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
658
  	tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock),
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  							HWSPINLOCK_UNUSED);
  
  	/* self-sanity check that should never fail */
  	WARN_ON(tmp != hwlock);
  
  	return ret;
  }
  
  /**
   * hwspin_lock_get_id() - retrieve id number of a given hwspinlock
   * @hwlock: a valid hwspinlock instance
   *
   * Returns the id number of a given @hwlock, or -EINVAL if @hwlock is invalid.
   */
  int hwspin_lock_get_id(struct hwspinlock *hwlock)
  {
  	if (!hwlock) {
  		pr_err("invalid hwlock
  ");
  		return -EINVAL;
  	}
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
680
  	return hwlock_to_id(hwlock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
681
682
683
684
685
686
687
688
689
690
691
692
  }
  EXPORT_SYMBOL_GPL(hwspin_lock_get_id);
  
  /**
   * hwspin_lock_request() - request an hwspinlock
   *
   * This function should be called by users of the hwspinlock device,
   * in order to dynamically assign them an unused hwspinlock.
   * Usually the user of this lock will then have to communicate the lock's id
   * to the remote core before it can be used for synchronization (to get the
   * id of a given hwlock, use hwspin_lock_get_id()).
   *
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
693
   * Should be called from a process context (might sleep)
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
694
695
696
697
698
699
700
   *
   * Returns the address of the assigned hwspinlock, or NULL on error
   */
  struct hwspinlock *hwspin_lock_request(void)
  {
  	struct hwspinlock *hwlock;
  	int ret;
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
701
  	mutex_lock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
  
  	/* look for an unused lock */
  	ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock,
  						0, 1, HWSPINLOCK_UNUSED);
  	if (ret == 0) {
  		pr_warn("a free hwspinlock is not available
  ");
  		hwlock = NULL;
  		goto out;
  	}
  
  	/* sanity check that should never fail */
  	WARN_ON(ret > 1);
  
  	/* mark as used and power up */
  	ret = __hwspin_lock_request(hwlock);
  	if (ret < 0)
  		hwlock = NULL;
  
  out:
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
722
  	mutex_unlock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
723
724
725
726
727
728
729
730
731
732
733
734
735
  	return hwlock;
  }
  EXPORT_SYMBOL_GPL(hwspin_lock_request);
  
  /**
   * hwspin_lock_request_specific() - request for a specific hwspinlock
   * @id: index of the specific hwspinlock that is requested
   *
   * This function should be called by users of the hwspinlock module,
   * in order to assign them a specific hwspinlock.
   * Usually early board code will be calling this function in order to
   * reserve specific hwspinlock ids for predefined purposes.
   *
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
736
   * Should be called from a process context (might sleep)
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
737
738
739
740
741
742
743
   *
   * Returns the address of the assigned hwspinlock, or NULL on error
   */
  struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
  {
  	struct hwspinlock *hwlock;
  	int ret;
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
744
  	mutex_lock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
745
746
747
748
749
750
751
752
753
754
  
  	/* make sure this hwspinlock exists */
  	hwlock = radix_tree_lookup(&hwspinlock_tree, id);
  	if (!hwlock) {
  		pr_warn("hwspinlock %u does not exist
  ", id);
  		goto out;
  	}
  
  	/* sanity check (this shouldn't happen) */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
755
  	WARN_ON(hwlock_to_id(hwlock) != id);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  
  	/* make sure this hwspinlock is unused */
  	ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED);
  	if (ret == 0) {
  		pr_warn("hwspinlock %u is already in use
  ", id);
  		hwlock = NULL;
  		goto out;
  	}
  
  	/* mark as used and power up */
  	ret = __hwspin_lock_request(hwlock);
  	if (ret < 0)
  		hwlock = NULL;
  
  out:
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
772
  	mutex_unlock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
773
774
775
776
777
778
779
780
781
782
  	return hwlock;
  }
  EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
  
  /**
   * hwspin_lock_free() - free a specific hwspinlock
   * @hwlock: the specific hwspinlock to free
   *
   * This function mark @hwlock as free again.
   * Should only be called with an @hwlock that was retrieved from
38ce60653   Baolin Wang   hwspinlock: Fix o...
783
   * an earlier call to hwspin_lock_request{_specific}.
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
784
   *
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
785
   * Should be called from a process context (might sleep)
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
786
787
788
789
790
   *
   * Returns 0 on success, or an appropriate error code on failure
   */
  int hwspin_lock_free(struct hwspinlock *hwlock)
  {
e352614cd   Wei Yongjun   hwspinlock/core: ...
791
  	struct device *dev;
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
792
793
794
795
796
797
798
799
  	struct hwspinlock *tmp;
  	int ret;
  
  	if (!hwlock) {
  		pr_err("invalid hwlock
  ");
  		return -EINVAL;
  	}
e352614cd   Wei Yongjun   hwspinlock/core: ...
800
  	dev = hwlock->bank->dev;
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
801
  	mutex_lock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
802
803
  
  	/* make sure the hwspinlock is used */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
804
  	ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock),
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
805
806
  							HWSPINLOCK_UNUSED);
  	if (ret == 1) {
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
807
808
  		dev_err(dev, "%s: hwlock is already free
  ", __func__);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
809
810
811
812
813
814
  		dump_stack();
  		ret = -EINVAL;
  		goto out;
  	}
  
  	/* notify the underlying device that power is not needed */
a6fc49051   Baolin Wang   hwspinlock: Let t...
815
  	pm_runtime_put(dev);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
816
817
  
  	/* mark this hwspinlock as available */
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
818
  	tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock),
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
819
820
821
822
  							HWSPINLOCK_UNUSED);
  
  	/* sanity check (this shouldn't happen) */
  	WARN_ON(tmp != hwlock);
300bab977   Ohad Ben-Cohen   hwspinlock/core: ...
823
  	module_put(dev->driver->owner);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
824
825
  
  out:
93b465c2e   Juan Gutierrez   hwspinlock/core: ...
826
  	mutex_unlock(&hwspinlock_tree_lock);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
827
828
829
  	return ret;
  }
  EXPORT_SYMBOL_GPL(hwspin_lock_free);
4f1acd758   Baolin Wang   hwspinlock: Add d...
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
  static int devm_hwspin_lock_match(struct device *dev, void *res, void *data)
  {
  	struct hwspinlock **hwlock = res;
  
  	if (WARN_ON(!hwlock || !*hwlock))
  		return 0;
  
  	return *hwlock == data;
  }
  
  static void devm_hwspin_lock_release(struct device *dev, void *res)
  {
  	hwspin_lock_free(*(struct hwspinlock **)res);
  }
  
  /**
   * devm_hwspin_lock_free() - free a specific hwspinlock for a managed device
   * @dev: the device to free the specific hwspinlock
   * @hwlock: the specific hwspinlock to free
   *
   * This function mark @hwlock as free again.
   * Should only be called with an @hwlock that was retrieved from
   * an earlier call to hwspin_lock_request{_specific}.
   *
   * Should be called from a process context (might sleep)
   *
   * Returns 0 on success, or an appropriate error code on failure
   */
  int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock)
  {
  	int ret;
  
  	ret = devres_release(dev, devm_hwspin_lock_release,
  			     devm_hwspin_lock_match, hwlock);
  	WARN_ON(ret);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(devm_hwspin_lock_free);
  
  /**
   * devm_hwspin_lock_request() - request an hwspinlock for a managed device
   * @dev: the device to request an hwspinlock
   *
   * This function should be called by users of the hwspinlock device,
   * in order to dynamically assign them an unused hwspinlock.
   * Usually the user of this lock will then have to communicate the lock's id
   * to the remote core before it can be used for synchronization (to get the
   * id of a given hwlock, use hwspin_lock_get_id()).
   *
   * Should be called from a process context (might sleep)
   *
   * Returns the address of the assigned hwspinlock, or NULL on error
   */
  struct hwspinlock *devm_hwspin_lock_request(struct device *dev)
  {
  	struct hwspinlock **ptr, *hwlock;
  
  	ptr = devres_alloc(devm_hwspin_lock_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
ddb34f480   Baolin Wang   hwspinlock: Fix i...
890
  		return NULL;
4f1acd758   Baolin Wang   hwspinlock: Add d...
891
892
  
  	hwlock = hwspin_lock_request();
ddb34f480   Baolin Wang   hwspinlock: Fix i...
893
  	if (hwlock) {
4f1acd758   Baolin Wang   hwspinlock: Add d...
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
  		*ptr = hwlock;
  		devres_add(dev, ptr);
  	} else {
  		devres_free(ptr);
  	}
  
  	return hwlock;
  }
  EXPORT_SYMBOL_GPL(devm_hwspin_lock_request);
  
  /**
   * devm_hwspin_lock_request_specific() - request for a specific hwspinlock for
   *					 a managed device
   * @dev: the device to request the specific hwspinlock
   * @id: index of the specific hwspinlock that is requested
   *
   * This function should be called by users of the hwspinlock module,
   * in order to assign them a specific hwspinlock.
   * Usually early board code will be calling this function in order to
   * reserve specific hwspinlock ids for predefined purposes.
   *
   * Should be called from a process context (might sleep)
   *
   * Returns the address of the assigned hwspinlock, or NULL on error
   */
  struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev,
  						     unsigned int id)
  {
  	struct hwspinlock **ptr, *hwlock;
  
  	ptr = devres_alloc(devm_hwspin_lock_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
ddb34f480   Baolin Wang   hwspinlock: Fix i...
926
  		return NULL;
4f1acd758   Baolin Wang   hwspinlock: Add d...
927
928
  
  	hwlock = hwspin_lock_request_specific(id);
ddb34f480   Baolin Wang   hwspinlock: Fix i...
929
  	if (hwlock) {
4f1acd758   Baolin Wang   hwspinlock: Add d...
930
931
932
933
934
935
936
937
938
  		*ptr = hwlock;
  		devres_add(dev, ptr);
  	} else {
  		devres_free(ptr);
  	}
  
  	return hwlock;
  }
  EXPORT_SYMBOL_GPL(devm_hwspin_lock_request_specific);
bd9a4c7df   Ohad Ben-Cohen   drivers: hwspinlo...
939
940
941
  MODULE_LICENSE("GPL v2");
  MODULE_DESCRIPTION("Hardware spinlock interface");
  MODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>");