aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ubicom32/files/arch/ubicom32/include/asm/atomic.h
blob: aaf7726020c8b8aa60afccd2e480d2b0d01e0848 (plain)
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
/*
 * arch/ubicom32/include/asm/atomic.h
 *   Atomic operations definitions for Ubicom32 architecture.
 *
 * (C) Copyright 2009, Ubicom, Inc.
 *
 * This file is part of the Ubicom32 Linux Kernel Port.
 *
 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the Ubicom32 Linux Kernel Port.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * Ubicom32 implementation derived from (with many thanks):
 *   arch/m68knommu
 *   arch/blackfin
 *   arch/parisc
 */
#ifndef _ASM_UBICOM32_ATOMIC_H
#define _ASM_UBICOM32_ATOMIC_H

#include <asm/system.h>
#include <asm/ubicom32-common.h>
#include <asm/types.h>

/*
 * Most instructions on the Ubicom32 processor are atomic in that they
 * execute in one clock cycle.  However, Linux has several operations
 * (e.g. compare and swap) which will require more than a single instruction
 * to perform.   To achieve this, the Ubicom32 processor uses a single
 * global bit in a scratchpad register as a critical section lock. All
 * atomic operations acquire this lock.
 *
 * NOTE: To AVOID DEADLOCK(s), the atomic lock must only be used for atomic
 * operations or by the ldsr to avoid disabling a thread performing an atomic
 * operation.
 *
 * Do not attempt to disable interrupts while holding the atomic operations
 * lock or you will DEADLOCK the system.
 */

#define ATOMIC_INIT(i)	{ (i) }

/*
 * __atomic_add()
 * 	Add i to v and return the result.
 */
static inline void __atomic_add(int i, atomic_t *v)
{
	atomic_t *vt = v;

	__atomic_lock_acquire();
	vt->counter += i;
	__atomic_lock_release();
}

/*
 * __atomic_sub()
 * 	Subtract i from v and return the result.
 */
static inline void __atomic_sub(int i, atomic_t *v)
{
	atomic_t *vt = v;

	__atomic_lock_acquire();
	vt->counter -= i;
	__atomic_lock_release();
}

/*
 * __atomic_add_return()
 * 	Add i to v and return the result.
 *
 * The implementation here looks rather odd because we appear to be doing
 * the addition twice.  In fact that's exactly what we're doing but with
 * the ubicom32 instruction set we can do the inner load and add with two
 * instructions whereas generating both the atomic result and the "ret"
 * result requires three instructions.  The second add is generally only as
 * costly as a move instruction and in cases where we compare the result
 * with a constant the compiler can fold two constant values and do a
 * single instruction, thus saving an instruction overall!
 *
 * At the worst we save one instruction inside the atomic lock.
 */
static inline int __atomic_add_return(int i, atomic_t *v)
{
	int ret;
	atomic_t *vt = v;

	__atomic_lock_acquire();
	ret = vt->counter;
	vt->counter = ret + i;
	__atomic_lock_release();

	return ret + i;
}

/*
 * __atomic_sub_return()
 * 	Subtract i from v and return the result.
 *
 * The implementation here looks rather odd because we appear to be doing
 * the subtraction twice.  In fact that's exactly what we're doing but with
 * the ubicom32 instruction set we can do the inner load and sub with two
 * instructions whereas generating both the atomic result and the "ret"
 * result requires three instructions.  The second sub is generally only as
 * costly as a move instruction and in cases where we compare the result
 * with a constant the compiler can fold two constant values and do a
 * single instruction, thus saving an instruction overall!
 *
 * At the worst we save one instruction inside the atomic lock.
 */
static inline int __atomic_sub_return(int i, atomic_t *v)
{
	int ret;
	atomic_t *vt = v;

	__atomic_lock_acquire();
	ret = vt->counter;
	vt->counter = ret - i;
	__atomic_lock_release();

	return ret - i;
}

/*
 * PUBLIC API FOR ATOMIC!
 */
#define atomic_add(i,v)	(__atomic_add( ((int)i),(v)))
#define atomic_sub(i,v)	(__atomic_sub( ((int)i),(v)))
#define atomic_inc(v)	(__atomic_add(   1,(v)))
#define atomic_dec(v)	(__atomic_sub(   1,(v)))
#define atomic_add_return(i,v)	(__atomic_add_return( ((int)i),(v)))
#define atomic_sub_return(i,v)	(__atomic_sub_return( ((int)i),(v)))
#define atomic_inc_return(v)	(__atomic_add_return(   1,(v)))
#define atomic_dec_return(v)	(__atomic_sub_return(   1,(v)))
#define atomic_inc_and_test(v)	(atomic_inc_return(v) == 0)
#define atomic_dec_and_test(v)	(atomic_dec_return(v) == 0)
#define atomic_add_negative(a, v)	(atomic_add_return((a), (v)) < 0)
#define atomic_sub_and_test(i,v)	(atomic_sub_return((i),(v)) == 0)

/*
 * atomic_read()
 * 	Acquire the atomic lock and read the variable.
 */
static inline int atomic_read(const atomic_t *v)
{
	int ret;
	const atomic_t *vt = v;

	__atomic_lock_acquire();
	ret = vt->counter;
	__atomic_lock_release();

	return ret;
}

/*
 * atomic_set()
 * 	Acquire the atomic lock and set the variable.
 */
static inline void atomic_set(atomic_t *v, int i)
{
	atomic_t *vt = v;

	__atomic_lock_acquire();
	vt->counter = i;
	__atomic_lock_release();
}

/*
 * atomic_cmpxchg
 * 	Acquire the atomic lock and exchange if current == old.
 */
static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
{
	int prev;
	atomic_t *vt = v;

	__atomic_lock_acquire();
	prev = vt->counter;
	if (prev == old) {
		vt->counter = new;
	}
	__atomic_lock_release();

	return prev;
}

/*
 * atomic_xchg()
 * 	Acquire the atomic lock and exchange values.
 */
static inline int atomic_xchg(atomic_t *v, int new)
{
	int prev;
	atomic_t *vt = v;

	__atomic_lock_acquire();
	prev = vt->counter;
	vt->counter = new;
	__atomic_lock_release();

	return prev;
}

/*
 * atomic_add_unless()
 * 	Acquire the atomic lock and add a unless the value is u.
 */
static inline int atomic_add_unless(atomic_t *v, int a, int u)
{
	int prev;
	atomic_t *vt = v;

	__atomic_lock_acquire();
	prev = vt->counter;
	if (prev != u) {
		vt->counter += a;
		__atomic_lock_release();
		return 1;
	}

	__atomic_lock_release();
	return 0;
}

#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)

#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#include <asm-generic/atomic-long.h>
#else
#include <asm-generic/atomic.h>
#endif

/*
 * The following is not a real function.  The compiler should remove the function
 * call as long as the user does not pass in a size that __xchg and __cmpxchg
 * are not prepared for.  If the user does pass in an unknown size, the user
 * will get a link time error.
 *
 * The no return is to prevent a compiler error that can occur when dealing with
 * uninitialized variables. Given that the function doesn't exist there is no
 * net effect (and if it did it would not return).
 */
extern void __xchg_called_with_bad_pointer(void) __attribute__((noreturn));

/*
 * __xchg()
 *	Xchange *ptr for x atomically.
 *
 * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an
 * atomic exchange instruction so we use the global atomic_lock.
 */
static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
{
	unsigned long ret;

	__atomic_lock_acquire();

	switch (size) {
	case 1:
		ret = *(volatile unsigned char *)ptr;
		*(volatile unsigned char *)ptr = x;
		break;

	case 2:
		ret = *(volatile unsigned short *)ptr;
		*(volatile unsigned short *)ptr = x;
		break;

	case 4:
		ret = *(volatile unsigned int *)ptr;
		*(volatile unsigned int *)ptr = x;
		break;

	default:
		__xchg_called_with_bad_pointer();
		break;
	}
	__atomic_lock_release();
	return ret;
}

#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))

/*
 * __cmpxchg()
 *	Compare and Xchange *ptr for x atomically.
 *
 * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an
 * atomic exchange instruction so we use the global atomic_lock.
 */
static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long next, int size)
{
	unsigned long prev;

	__atomic_lock_acquire();
	switch (size) {
	case 1:
		prev = *(u8 *)ptr;
		if (prev == old) {
			*(u8 *)ptr = (u8)next;
		}
		break;

	case 2:
		prev = *(u16 *)ptr;
		if (prev == old) {
			*(u16 *)ptr = (u16)next;
		}
		break;

	case 4:
		prev = *(u32 *)ptr;
		if (prev == old) {
			*(u32 *)ptr = (u32)next;
		}
		break;

	default:
		__xchg_called_with_bad_pointer();
		break;
	}
	__atomic_lock_release();
	return prev;
}

/*
 * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make
 * them available.
 */
#define cmpxchg_local(ptr, o, n) \
	((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), (unsigned long)(n), sizeof(*(ptr))))

#define cmpxchg(ptr, o, n) __cmpxchg((ptr), (o), (n), sizeof(*(ptr)))

#define smp_mb__before_atomic_inc() asm volatile ("" : : : "memory")
#define smp_mb__after_atomic_inc() asm volatile ("" : : : "memory")
#define smp_mb__before_atomic_dec() asm volatile ("" : : : "memory")
#define smp_mb__after_atomic_dec() asm volatile ("" : : : "memory")

#endif /* _ASM_UBICOM32_ATOMIC_H */