summaryrefslogtreecommitdiff
path: root/include/linux/hwspinlock.h
blob: bfe7c1f1ac6d1ce7221576e38edc97878c4713c2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
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
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Hardware spinlock public header
 *
 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
 *
 * Contact: Ohad Ben-Cohen <ohad@wizery.com>
 */

#ifndef __LINUX_HWSPINLOCK_H
#define __LINUX_HWSPINLOCK_H

#include <linux/err.h>
#include <linux/sched.h>

/* hwspinlock mode argument */
#define HWLOCK_IRQSTATE		0x01 /* Disable interrupts, save state */
#define HWLOCK_IRQ		0x02 /* Disable interrupts, don't save state */
#define HWLOCK_RAW		0x03
#define HWLOCK_IN_ATOMIC	0x04 /* Called while in atomic context */

struct device;
struct device_node;
struct hwspinlock;
struct hwspinlock_device;
struct hwspinlock_ops;

/**
 * struct hwspinlock_pdata - platform data for hwspinlock drivers
 * @base_id: base id for this hwspinlock device
 *
 * hwspinlock devices provide system-wide hardware locks that are used
 * by remote processors that have no other way to achieve synchronization.
 *
 * To achieve that, each physical lock must have a system-wide id number
 * that is agreed upon, otherwise remote processors can't possibly assume
 * they're using the same hardware lock.
 *
 * Usually boards have a single hwspinlock device, which provides several
 * hwspinlocks, and in this case, they can be trivially numbered 0 to
 * (num-of-locks - 1).
 *
 * In case boards have several hwspinlocks devices, a different base id
 * should be used for each hwspinlock device (they can't all use 0 as
 * a starting id!).
 *
 * This platform data structure should be used to provide the base id
 * for each device (which is trivially 0 when only a single hwspinlock
 * device exists). It can be shared between different platforms, hence
 * its location.
 */
struct hwspinlock_pdata {
	int base_id;
};

#ifdef CONFIG_HWSPINLOCK

int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
		const struct hwspinlock_ops *ops, int base_id, int num_locks);
int hwspin_lock_unregister(struct hwspinlock_device *bank);
struct hwspinlock *hwspin_lock_request(void);
struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
int hwspin_lock_free(struct hwspinlock *hwlock);
int of_hwspin_lock_get_id(struct device_node *np, int index);
int hwspin_lock_get_id(struct hwspinlock *hwlock);
int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int,
							unsigned long *);
int __hwspin_trylock(struct hwspinlock *, int, unsigned long *);
void __hwspin_unlock(struct hwspinlock *, int, unsigned long *);
int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name);
int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock);
struct hwspinlock *devm_hwspin_lock_request(struct device *dev);
struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev,
						     unsigned int id);
int devm_hwspin_lock_unregister(struct device *dev,
				struct hwspinlock_device *bank);
int devm_hwspin_lock_register(struct device *dev,
			      struct hwspinlock_device *bank,
			      const struct hwspinlock_ops *ops,
			      int base_id, int num_locks);

#else /* !CONFIG_HWSPINLOCK */

/*
 * We don't want these functions to fail if CONFIG_HWSPINLOCK is not
 * enabled. We prefer to silently succeed in this case, and let the
 * code path get compiled away. This way, if CONFIG_HWSPINLOCK is not
 * required on a given setup, users will still work.
 *
 * The only exception is hwspin_lock_register/hwspin_lock_unregister, with which
 * we _do_ want users to fail (no point in registering hwspinlock instances if
 * the framework is not available).
 *
 * Note: ERR_PTR(-ENODEV) will still be considered a success for NULL-checking
 * users. Others, which care, can still check this with IS_ERR.
 */
static inline struct hwspinlock *hwspin_lock_request(void)
{
	return ERR_PTR(-ENODEV);
}

static inline struct hwspinlock *hwspin_lock_request_specific(unsigned int id)
{
	return ERR_PTR(-ENODEV);
}

static inline int hwspin_lock_free(struct hwspinlock *hwlock)
{
	return 0;
}

static inline
int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to,
					int mode, unsigned long *flags)
{
	return 0;
}

static inline
int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
{
	return 0;
}

static inline
void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags)
{
}

static inline int of_hwspin_lock_get_id(struct device_node *np, int index)
{
	return 0;
}

static inline int hwspin_lock_get_id(struct hwspinlock *hwlock)
{
	return 0;
}

static inline
int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name)
{
	return 0;
}

static inline
int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock)
{
	return 0;
}

static inline struct hwspinlock *devm_hwspin_lock_request(struct device *dev)
{
	return ERR_PTR(-ENODEV);
}

static inline
struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev,
						     unsigned int id)
{
	return ERR_PTR(-ENODEV);
}

#endif /* !CONFIG_HWSPINLOCK */

/**
 * hwspin_trylock_irqsave() - try to lock an hwspinlock, disable interrupts
 * @hwlock: an hwspinlock which we want to trylock
 * @flags: a pointer to where the caller's interrupt state will be saved at
 *
 * This function attempts to lock the underlying hwspinlock, and will
 * immediately fail if the hwspinlock is already locked.
 *
 * Upon a successful return from this function, preemption and local
 * interrupts are disabled (previous interrupts state is saved at @flags),
 * so the caller must not sleep, and is advised to release the hwspinlock
 * as soon as possible.
 *
 * Returns 0 if we successfully locked the hwspinlock, -EBUSY if
 * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid.
 */
static inline
int hwspin_trylock_irqsave(struct hwspinlock *hwlock, unsigned long *flags)
{
	return __hwspin_trylock(hwlock, HWLOCK_IRQSTATE, flags);
}

/**
 * hwspin_trylock_irq() - try to lock an hwspinlock, disable interrupts
 * @hwlock: an hwspinlock which we want to trylock
 *
 * This function attempts to lock the underlying hwspinlock, and will
 * immediately fail if the hwspinlock is already locked.
 *
 * Upon a successful return from this function, preemption and local
 * interrupts are disabled, so the caller must not sleep, and is advised
 * to release the hwspinlock as soon as possible.
 *
 * Returns 0 if we successfully locked the hwspinlock, -EBUSY if
 * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid.
 */
static inline int hwspin_trylock_irq(struct hwspinlock *hwlock)
{
	return __hwspin_trylock(hwlock, HWLOCK_IRQ, NULL);
}

/**
 * hwspin_trylock_raw() - attempt to lock a specific hwspinlock
 * @hwlock: an hwspinlock which we want to trylock
 *
 * This function attempts to lock an hwspinlock, and will immediately fail
 * if the hwspinlock is already taken.
 *
 * Caution: User must protect the routine of getting hardware lock with mutex
 * or spinlock to avoid dead-lock, that will let user can do some time-consuming
 * or sleepable operations under the hardware lock.
 *
 * Returns 0 if we successfully locked the hwspinlock, -EBUSY if
 * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid.
 */
static inline int hwspin_trylock_raw(struct hwspinlock *hwlock)
{
	return __hwspin_trylock(hwlock, HWLOCK_RAW, NULL);
}

/**
 * hwspin_trylock_in_atomic() - attempt to lock a specific hwspinlock
 * @hwlock: an hwspinlock which we want to trylock
 *
 * This function attempts to lock an hwspinlock, and will immediately fail
 * if the hwspinlock is already taken.
 *
 * This function shall be called only from an atomic context.
 *
 * Returns 0 if we successfully locked the hwspinlock, -EBUSY if
 * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid.
 */
static inline int hwspin_trylock_in_atomic(struct hwspinlock *hwlock)
{
	return __hwspin_trylock(hwlock, HWLOCK_IN_ATOMIC, NULL);
}

/**
 * hwspin_trylock() - attempt to lock a specific hwspinlock
 * @hwlock: an hwspinlock which we want to trylock
 *
 * This function attempts to lock an hwspinlock, and will immediately fail
 * if the hwspinlock is already taken.
 *
 * Upon a successful return from this function, preemption 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.
 *
 * Returns 0 if we successfully locked the hwspinlock, -EBUSY if
 * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid.
 */
static inline int hwspin_trylock(struct hwspinlock *hwlock)
{
	return __hwspin_trylock(hwlock, 0, NULL);
}

/**
 * hwspin_lock_timeout_irqsave() - lock hwspinlock, with timeout, disable irqs
 * @hwlock: the hwspinlock to be locked
 * @to: timeout value in msecs
 * @flags: a pointer to where the caller's interrupt state will be saved at
 *
 * This function locks the underlying @hwlock. If the @hwlock
 * is already taken, the function will busy loop waiting for it to
 * be released, but give up when @timeout msecs have elapsed.
 *
 * Upon a successful return from this function, preemption and local interrupts
 * are disabled (plus previous interrupt state is saved), so the caller must
 * not sleep, and is advised to release the hwspinlock as soon as possible.
 *
 * Returns 0 when the @hwlock was successfully taken, and an appropriate
 * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still
 * busy after @timeout msecs). The function will never sleep.
 */
static inline int hwspin_lock_timeout_irqsave(struct hwspinlock *hwlock,
				unsigned int to, unsigned long *flags)
{
	return __hwspin_lock_timeout(hwlock, to, HWLOCK_IRQSTATE, flags);
}

/**
 * hwspin_lock_timeout_irq() - lock hwspinlock, with timeout, disable irqs
 * @hwlock: the hwspinlock to be locked
 * @to: timeout value in msecs
 *
 * This function locks the underlying @hwlock. If the @hwlock
 * is already taken, the function will busy loop waiting for it to
 * be released, but give up when @timeout msecs have elapsed.
 *
 * Upon a successful return from this function, preemption and local interrupts
 * are disabled so the caller must not sleep, and is advised to release the
 * hwspinlock as soon as possible.
 *
 * Returns 0 when the @hwlock was successfully taken, and an appropriate
 * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still
 * busy after @timeout msecs). The function will never sleep.
 */
static inline
int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int to)
{
	return __hwspin_lock_timeout(hwlock, to, HWLOCK_IRQ, NULL);
}

/**
 * hwspin_lock_timeout_raw() - lock an hwspinlock with timeout limit
 * @hwlock: the hwspinlock to be locked
 * @to: timeout value in msecs
 *
 * This function locks the underlying @hwlock. If the @hwlock
 * is already taken, the function will busy loop waiting for it to
 * be released, but give up when @timeout msecs have elapsed.
 *
 * Caution: User must protect the routine of getting hardware lock with mutex
 * or spinlock to avoid dead-lock, that will let user can do some time-consuming
 * or sleepable operations under the hardware lock.
 *
 * Returns 0 when the @hwlock was successfully taken, and an appropriate
 * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still
 * busy after @timeout msecs). The function will never sleep.
 */
static inline
int hwspin_lock_timeout_raw(struct hwspinlock *hwlock, unsigned int to)
{
	return __hwspin_lock_timeout(hwlock, to, HWLOCK_RAW, NULL);
}

/**
 * hwspin_lock_timeout_in_atomic() - lock an hwspinlock with timeout limit
 * @hwlock: the hwspinlock to be locked
 * @to: timeout value in msecs
 *
 * This function locks the underlying @hwlock. If the @hwlock
 * is already taken, the function will busy loop waiting for it to
 * be released, but give up when @timeout msecs have elapsed.
 *
 * This function shall be called only from an atomic context and the timeout
 * value shall not exceed a few msecs.
 *
 * Returns 0 when the @hwlock was successfully taken, and an appropriate
 * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still
 * busy after @timeout msecs). The function will never sleep.
 */
static inline
int hwspin_lock_timeout_in_atomic(struct hwspinlock *hwlock, unsigned int to)
{
	return __hwspin_lock_timeout(hwlock, to, HWLOCK_IN_ATOMIC, NULL);
}

/**
 * hwspin_lock_timeout() - lock an hwspinlock with timeout limit
 * @hwlock: the hwspinlock to be locked
 * @to: timeout value in msecs
 *
 * This function locks the underlying @hwlock. If the @hwlock
 * is already taken, the function will busy loop waiting for it to
 * be released, but give up when @timeout msecs have elapsed.
 *
 * Upon a successful return from this function, preemption 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.
 *
 * Returns 0 when the @hwlock was successfully taken, and an appropriate
 * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still
 * busy after @timeout msecs). The function will never sleep.
 */
static inline
int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to)
{
	return __hwspin_lock_timeout(hwlock, to, 0, NULL);
}

/**
 * hwspin_unlock_irqrestore() - unlock hwspinlock, restore irq state
 * @hwlock: a previously-acquired hwspinlock which we want to unlock
 * @flags: previous caller's interrupt state to restore
 *
 * This function will unlock a specific hwspinlock, enable preemption and
 * restore the previous state of the local interrupts. It should be used
 * to undo, e.g., hwspin_trylock_irqsave().
 *
 * @hwlock must be already locked before calling this function: it is a bug
 * to call unlock on a @hwlock that is already unlocked.
 */
static inline void hwspin_unlock_irqrestore(struct hwspinlock *hwlock,
							unsigned long *flags)
{
	__hwspin_unlock(hwlock, HWLOCK_IRQSTATE, flags);
}

/**
 * hwspin_unlock_irq() - unlock hwspinlock, enable interrupts
 * @hwlock: a previously-acquired hwspinlock which we want to unlock
 *
 * This function will unlock a specific hwspinlock, enable preemption and
 * enable local interrupts. Should be used to undo hwspin_lock_irq().
 *
 * @hwlock must be already locked (e.g. by hwspin_trylock_irq()) before
 * calling this function: it is a bug to call unlock on a @hwlock that is
 * already unlocked.
 */
static inline void hwspin_unlock_irq(struct hwspinlock *hwlock)
{
	__hwspin_unlock(hwlock, HWLOCK_IRQ, NULL);
}

/**
 * hwspin_unlock_raw() - unlock hwspinlock
 * @hwlock: a previously-acquired hwspinlock which we want to unlock
 *
 * This function will unlock a specific hwspinlock.
 *
 * @hwlock must be already locked (e.g. by hwspin_trylock()) before calling
 * this function: it is a bug to call unlock on a @hwlock that is already
 * unlocked.
 */
static inline void hwspin_unlock_raw(struct hwspinlock *hwlock)
{
	__hwspin_unlock(hwlock, HWLOCK_RAW, NULL);
}

/**
 * hwspin_unlock_in_atomic() - unlock hwspinlock
 * @hwlock: a previously-acquired hwspinlock which we want to unlock
 *
 * This function will unlock a specific hwspinlock.
 *
 * @hwlock must be already locked (e.g. by hwspin_trylock()) before calling
 * this function: it is a bug to call unlock on a @hwlock that is already
 * unlocked.
 */
static inline void hwspin_unlock_in_atomic(struct hwspinlock *hwlock)
{
	__hwspin_unlock(hwlock, HWLOCK_IN_ATOMIC, NULL);
}

/**
 * hwspin_unlock() - unlock hwspinlock
 * @hwlock: a previously-acquired hwspinlock which we want to unlock
 *
 * This function will unlock a specific hwspinlock and enable preemption
 * back.
 *
 * @hwlock must be already locked (e.g. by hwspin_trylock()) before calling
 * this function: it is a bug to call unlock on a @hwlock that is already
 * unlocked.
 */
static inline void hwspin_unlock(struct hwspinlock *hwlock)
{
	__hwspin_unlock(hwlock, 0, NULL);
}

#endif /* __LINUX_HWSPINLOCK_H */