78d9fcd8f228cf4172139c48748950930d7ac782
2 * arch/ubicom32/include/asm/atomic.h
3 * Atomic operations definitions for Ubicom32 architecture.
5 * (C) Copyright 2009, Ubicom, Inc.
7 * This file is part of the Ubicom32 Linux Kernel Port.
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option) any later version.
14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with the Ubicom32 Linux Kernel Port. If not,
21 * see <http://www.gnu.org/licenses/>.
23 * Ubicom32 implementation derived from (with many thanks):
28 #ifndef _ASM_UBICOM32_ATOMIC_H
29 #define _ASM_UBICOM32_ATOMIC_H
31 #include <asm/system.h>
32 #include <asm/ubicom32-common.h>
33 #include <asm/types.h>
36 * Most instructions on the Ubicom32 processor are atomic in that they
37 * execute in one clock cycle. However, Linux has several operations
38 * (e.g. compare and swap) which will require more than a single instruction
39 * to perform. To achieve this, the Ubicom32 processor uses a single
40 * global bit in a scratchpad register as a critical section lock. All
41 * atomic operations acquire this lock.
43 * NOTE: To AVOID DEADLOCK(s), the atomic lock must only be used for atomic
44 * operations or by the ldsr to avoid disabling a thread performing an atomic
47 * Do not attempt to disable interrupts while holding the atomic operations
48 * lock or you will DEADLOCK the system.
51 #define ATOMIC_INIT(i) { (i) }
55 * Add i to v and return the result.
57 static inline void __atomic_add(int i
, atomic_t
*v
)
61 __atomic_lock_acquire();
63 __atomic_lock_release();
68 * Subtract i from v and return the result.
70 static inline void __atomic_sub(int i
, atomic_t
*v
)
74 __atomic_lock_acquire();
76 __atomic_lock_release();
80 * __atomic_add_return()
81 * Add i to v and return the result.
83 * The implementation here looks rather odd because we appear to be doing
84 * the addition twice. In fact that's exactly what we're doing but with
85 * the ubicom32 instruction set we can do the inner load and add with two
86 * instructions whereas generating both the atomic result and the "ret"
87 * result requires three instructions. The second add is generally only as
88 * costly as a move instruction and in cases where we compare the result
89 * with a constant the compiler can fold two constant values and do a
90 * single instruction, thus saving an instruction overall!
92 * At the worst we save one instruction inside the atomic lock.
94 static inline int __atomic_add_return(int i
, atomic_t
*v
)
99 __atomic_lock_acquire();
101 vt
->counter
= ret
+ i
;
102 __atomic_lock_release();
108 * __atomic_sub_return()
109 * Subtract i from v and return the result.
111 * The implementation here looks rather odd because we appear to be doing
112 * the subtraction twice. In fact that's exactly what we're doing but with
113 * the ubicom32 instruction set we can do the inner load and sub with two
114 * instructions whereas generating both the atomic result and the "ret"
115 * result requires three instructions. The second sub is generally only as
116 * costly as a move instruction and in cases where we compare the result
117 * with a constant the compiler can fold two constant values and do a
118 * single instruction, thus saving an instruction overall!
120 * At the worst we save one instruction inside the atomic lock.
122 static inline int __atomic_sub_return(int i
, atomic_t
*v
)
127 __atomic_lock_acquire();
129 vt
->counter
= ret
- i
;
130 __atomic_lock_release();
136 * PUBLIC API FOR ATOMIC!
138 #define atomic_add(i,v) (__atomic_add( ((int)i),(v)))
139 #define atomic_sub(i,v) (__atomic_sub( ((int)i),(v)))
140 #define atomic_inc(v) (__atomic_add( 1,(v)))
141 #define atomic_dec(v) (__atomic_sub( 1,(v)))
142 #define atomic_add_return(i,v) (__atomic_add_return( ((int)i),(v)))
143 #define atomic_sub_return(i,v) (__atomic_sub_return( ((int)i),(v)))
144 #define atomic_inc_return(v) (__atomic_add_return( 1,(v)))
145 #define atomic_dec_return(v) (__atomic_sub_return( 1,(v)))
146 #define atomic_inc_and_test(v) (atomic_inc_return(v) == 0)
147 #define atomic_dec_and_test(v) (atomic_dec_return(v) == 0)
148 #define atomic_add_negative(a, v) (atomic_add_return((a), (v)) < 0)
149 #define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0)
153 * Acquire the atomic lock and read the variable.
155 static inline int atomic_read(const atomic_t
*v
)
158 const atomic_t
*vt
= v
;
160 __atomic_lock_acquire();
162 __atomic_lock_release();
169 * Acquire the atomic lock and set the variable.
171 static inline void atomic_set(atomic_t
*v
, int i
)
175 __atomic_lock_acquire();
177 __atomic_lock_release();
182 * Acquire the atomic lock and exchange if current == old.
184 static inline int atomic_cmpxchg(atomic_t
*v
, int old
, int new)
189 __atomic_lock_acquire();
194 __atomic_lock_release();
201 * Acquire the atomic lock and exchange values.
203 static inline int atomic_xchg(atomic_t
*v
, int new)
208 __atomic_lock_acquire();
211 __atomic_lock_release();
217 * atomic_add_unless()
218 * Acquire the atomic lock and add a unless the value is u.
220 static inline int atomic_add_unless(atomic_t
*v
, int a
, int u
)
225 __atomic_lock_acquire();
229 __atomic_lock_release();
233 __atomic_lock_release();
237 #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
239 #include <asm-generic/atomic.h>
242 * The following is not a real function. The compiler should remove the function
243 * call as long as the user does not pass in a size that __xchg and __cmpxchg
244 * are not prepared for. If the user does pass in an unknown size, the user
245 * will get a link time error.
247 * The no return is to prevent a compiler error that can occur when dealing with
248 * uninitialized variables. Given that the function doesn't exist there is no
249 * net effect (and if it did it would not return).
251 extern void __xchg_called_with_bad_pointer(void) __attribute__((noreturn
));
255 * Xchange *ptr for x atomically.
257 * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an
258 * atomic exchange instruction so we use the global atomic_lock.
260 static inline unsigned long __xchg(unsigned long x
, volatile void *ptr
, int size
)
264 __atomic_lock_acquire();
268 ret
= *(volatile unsigned char *)ptr
;
269 *(volatile unsigned char *)ptr
= x
;
273 ret
= *(volatile unsigned short *)ptr
;
274 *(volatile unsigned short *)ptr
= x
;
278 ret
= *(volatile unsigned int *)ptr
;
279 *(volatile unsigned int *)ptr
= x
;
283 __xchg_called_with_bad_pointer();
286 __atomic_lock_release();
290 #define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
294 * Compare and Xchange *ptr for x atomically.
296 * Must be both locally atomic and atomic on SMP. Ubicom32 does not have an
297 * atomic exchange instruction so we use the global atomic_lock.
299 static inline unsigned long __cmpxchg(volatile void *ptr
, unsigned long old
, unsigned long next
, int size
)
303 __atomic_lock_acquire();
308 *(u8
*)ptr
= (u8
)next
;
315 *(u16
*)ptr
= (u16
)next
;
322 *(u32
*)ptr
= (u32
)next
;
327 __xchg_called_with_bad_pointer();
330 __atomic_lock_release();
335 * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make
338 #define cmpxchg_local(ptr, o, n) \
339 ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), (unsigned long)(n), sizeof(*(ptr))))
341 #define cmpxchg(ptr, o, n) __cmpxchg((ptr), (o), (n), sizeof(*(ptr)))
343 #define smp_mb__before_atomic_inc() asm volatile ("" : : : "memory")
344 #define smp_mb__after_atomic_inc() asm volatile ("" : : : "memory")
345 #define smp_mb__before_atomic_dec() asm volatile ("" : : : "memory")
346 #define smp_mb__after_atomic_dec() asm volatile ("" : : : "memory")
348 #endif /* _ASM_UBICOM32_ATOMIC_H */
This page took 0.053034 seconds and 3 git commands to generate.