• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  linux/include/asm-arm/atomic.h
3  *
4  *  Copyright (C) 1996 Russell King.
5  *  Copyright (C) 2002 Deep Blue Solutions Ltd.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 #ifndef __ASM_ARM_ATOMIC_H
12 #define __ASM_ARM_ATOMIC_H
13 
14 #include <linux/compiler.h>
15 
16 typedef struct { volatile int counter; } atomic_t;
17 
18 #define ATOMIC_INIT(i)	{ (i) }
19 
20 #ifdef __KERNEL__
21 
22 #define atomic_read(v)	((v)->counter)
23 
24 #if __LINUX_ARM_ARCH__ >= 6
25 
26 /*
27  * ARMv6 UP and SMP safe atomic ops.  We use load exclusive and
28  * store exclusive to ensure that these are atomic.  We may loop
29  * to ensure that the update happens.  Writing to 'v->counter'
30  * without using the following operations WILL break the atomic
31  * nature of these ops.
32  */
atomic_set(atomic_t * v,int i)33 static inline void atomic_set(atomic_t *v, int i)
34 {
35 	unsigned long tmp;
36 
37 	__asm__ __volatile__("@ atomic_set\n"
38 "1:	ldrex	%0, [%1]\n"
39 "	strex	%0, %2, [%1]\n"
40 "	teq	%0, #0\n"
41 "	bne	1b"
42 	: "=&r" (tmp)
43 	: "r" (&v->counter), "r" (i)
44 	: "cc");
45 }
46 
atomic_add_return(int i,atomic_t * v)47 static inline int atomic_add_return(int i, atomic_t *v)
48 {
49 	unsigned long tmp;
50 	int result;
51 
52 	__asm__ __volatile__("@ atomic_add_return\n"
53 "1:	ldrex	%0, [%2]\n"
54 "	add	%0, %0, %3\n"
55 "	strex	%1, %0, [%2]\n"
56 "	teq	%1, #0\n"
57 "	bne	1b"
58 	: "=&r" (result), "=&r" (tmp)
59 	: "r" (&v->counter), "Ir" (i)
60 	: "cc");
61 
62 	return result;
63 }
64 
atomic_sub_return(int i,atomic_t * v)65 static inline int atomic_sub_return(int i, atomic_t *v)
66 {
67 	unsigned long tmp;
68 	int result;
69 
70 	__asm__ __volatile__("@ atomic_sub_return\n"
71 "1:	ldrex	%0, [%2]\n"
72 "	sub	%0, %0, %3\n"
73 "	strex	%1, %0, [%2]\n"
74 "	teq	%1, #0\n"
75 "	bne	1b"
76 	: "=&r" (result), "=&r" (tmp)
77 	: "r" (&v->counter), "Ir" (i)
78 	: "cc");
79 
80 	return result;
81 }
82 
atomic_cmpxchg(atomic_t * ptr,int old,int new)83 static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
84 {
85 	unsigned long oldval, res;
86 
87 	do {
88 		__asm__ __volatile__("@ atomic_cmpxchg\n"
89 		"ldrex	%1, [%2]\n"
90 		"mov	%0, #0\n"
91 		"teq	%1, %3\n"
92 		"strexeq %0, %4, [%2]\n"
93 		    : "=&r" (res), "=&r" (oldval)
94 		    : "r" (&ptr->counter), "Ir" (old), "r" (new)
95 		    : "cc");
96 	} while (res);
97 
98 	return oldval;
99 }
100 
atomic_clear_mask(unsigned long mask,unsigned long * addr)101 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
102 {
103 	unsigned long tmp, tmp2;
104 
105 	__asm__ __volatile__("@ atomic_clear_mask\n"
106 "1:	ldrex	%0, %2\n"
107 "	bic	%0, %0, %3\n"
108 "	strex	%1, %0, %2\n"
109 "	teq	%1, #0\n"
110 "	bne	1b"
111 	: "=&r" (tmp), "=&r" (tmp2)
112 	: "r" (addr), "Ir" (mask)
113 	: "cc");
114 }
115 
116 #else /* ARM_ARCH_6 */
117 
118 #include <asm/system.h>
119 
120 #ifdef CONFIG_SMP
121 #error SMP not supported on pre-ARMv6 CPUs
122 #endif
123 
124 #define atomic_set(v,i)	(((v)->counter) = (i))
125 
atomic_add_return(int i,atomic_t * v)126 static inline int atomic_add_return(int i, atomic_t *v)
127 {
128 	unsigned long flags;
129 	int val;
130 
131 	local_irq_save(flags);
132 	val = v->counter;
133 	v->counter = val += i;
134 	local_irq_restore(flags);
135 
136 	return val;
137 }
138 
atomic_sub_return(int i,atomic_t * v)139 static inline int atomic_sub_return(int i, atomic_t *v)
140 {
141 	unsigned long flags;
142 	int val;
143 
144 	local_irq_save(flags);
145 	val = v->counter;
146 	v->counter = val -= i;
147 	local_irq_restore(flags);
148 
149 	return val;
150 }
151 
atomic_cmpxchg(atomic_t * v,int old,int new)152 static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
153 {
154 	int ret;
155 	unsigned long flags;
156 
157 	local_irq_save(flags);
158 	ret = v->counter;
159 	if (likely(ret == old))
160 		v->counter = new;
161 	local_irq_restore(flags);
162 
163 	return ret;
164 }
165 
atomic_clear_mask(unsigned long mask,unsigned long * addr)166 static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
167 {
168 	unsigned long flags;
169 
170 	local_irq_save(flags);
171 	*addr &= ~mask;
172 	local_irq_restore(flags);
173 }
174 
175 #endif /* __LINUX_ARM_ARCH__ */
176 
177 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
178 
atomic_add_unless(atomic_t * v,int a,int u)179 static inline int atomic_add_unless(atomic_t *v, int a, int u)
180 {
181 	int c, old;
182 
183 	c = atomic_read(v);
184 	while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
185 		c = old;
186 	return c != u;
187 }
188 #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
189 
190 #define atomic_add(i, v)	(void) atomic_add_return(i, v)
191 #define atomic_inc(v)		(void) atomic_add_return(1, v)
192 #define atomic_sub(i, v)	(void) atomic_sub_return(i, v)
193 #define atomic_dec(v)		(void) atomic_sub_return(1, v)
194 
195 #define atomic_inc_and_test(v)	(atomic_add_return(1, v) == 0)
196 #define atomic_dec_and_test(v)	(atomic_sub_return(1, v) == 0)
197 #define atomic_inc_return(v)    (atomic_add_return(1, v))
198 #define atomic_dec_return(v)    (atomic_sub_return(1, v))
199 #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
200 
201 #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
202 
203 /* Atomic operations are already serializing on ARM */
204 #define smp_mb__before_atomic_dec()	barrier()
205 #define smp_mb__after_atomic_dec()	barrier()
206 #define smp_mb__before_atomic_inc()	barrier()
207 #define smp_mb__after_atomic_inc()	barrier()
208 
209 #include <asm-generic/atomic.h>
210 #endif
211 #endif
212