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