diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-18 04:42:16 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-11 09:30:30 -0500 |
commit | 14e968bad788de922a755a84b92cb29f8c1342e4 (patch) | |
tree | 5d1a38b177e2b63466ce00ee0165d04f04a2bceb | |
parent | ac4c244d4e5d914f9a5642cdcc03b18780e55dbc (diff) |
ARC: Atomic/bitops/cmpxchg/barriers
This covers the UP / SMP (with no hardware assist for atomic r-m-w) as
well as ARC700 LLOCK/SCOND insns based.
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
-rw-r--r-- | arch/arc/include/asm/atomic.h | 232 | ||||
-rw-r--r-- | arch/arc/include/asm/barrier.h | 42 | ||||
-rw-r--r-- | arch/arc/include/asm/bitops.h | 516 | ||||
-rw-r--r-- | arch/arc/include/asm/cmpxchg.h | 143 | ||||
-rw-r--r-- | arch/arc/include/asm/smp.h | 34 |
5 files changed, 967 insertions, 0 deletions
diff --git a/arch/arc/include/asm/atomic.h b/arch/arc/include/asm/atomic.h new file mode 100644 index 000000000000..83f03ca6caf6 --- /dev/null +++ b/arch/arc/include/asm/atomic.h | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _ASM_ARC_ATOMIC_H | ||
10 | #define _ASM_ARC_ATOMIC_H | ||
11 | |||
12 | #ifdef __KERNEL__ | ||
13 | |||
14 | #ifndef __ASSEMBLY__ | ||
15 | |||
16 | #include <linux/types.h> | ||
17 | #include <linux/compiler.h> | ||
18 | #include <asm/cmpxchg.h> | ||
19 | #include <asm/barrier.h> | ||
20 | #include <asm/smp.h> | ||
21 | |||
22 | #define atomic_read(v) ((v)->counter) | ||
23 | |||
24 | #ifdef CONFIG_ARC_HAS_LLSC | ||
25 | |||
26 | #define atomic_set(v, i) (((v)->counter) = (i)) | ||
27 | |||
28 | static inline void atomic_add(int i, atomic_t *v) | ||
29 | { | ||
30 | unsigned int temp; | ||
31 | |||
32 | __asm__ __volatile__( | ||
33 | "1: llock %0, [%1] \n" | ||
34 | " add %0, %0, %2 \n" | ||
35 | " scond %0, [%1] \n" | ||
36 | " bnz 1b \n" | ||
37 | : "=&r"(temp) /* Early clobber, to prevent reg reuse */ | ||
38 | : "r"(&v->counter), "ir"(i) | ||
39 | : "cc"); | ||
40 | } | ||
41 | |||
42 | static inline void atomic_sub(int i, atomic_t *v) | ||
43 | { | ||
44 | unsigned int temp; | ||
45 | |||
46 | __asm__ __volatile__( | ||
47 | "1: llock %0, [%1] \n" | ||
48 | " sub %0, %0, %2 \n" | ||
49 | " scond %0, [%1] \n" | ||
50 | " bnz 1b \n" | ||
51 | : "=&r"(temp) | ||
52 | : "r"(&v->counter), "ir"(i) | ||
53 | : "cc"); | ||
54 | } | ||
55 | |||
56 | /* add and also return the new value */ | ||
57 | static inline int atomic_add_return(int i, atomic_t *v) | ||
58 | { | ||
59 | unsigned int temp; | ||
60 | |||
61 | __asm__ __volatile__( | ||
62 | "1: llock %0, [%1] \n" | ||
63 | " add %0, %0, %2 \n" | ||
64 | " scond %0, [%1] \n" | ||
65 | " bnz 1b \n" | ||
66 | : "=&r"(temp) | ||
67 | : "r"(&v->counter), "ir"(i) | ||
68 | : "cc"); | ||
69 | |||
70 | return temp; | ||
71 | } | ||
72 | |||
73 | static inline int atomic_sub_return(int i, atomic_t *v) | ||
74 | { | ||
75 | unsigned int temp; | ||
76 | |||
77 | __asm__ __volatile__( | ||
78 | "1: llock %0, [%1] \n" | ||
79 | " sub %0, %0, %2 \n" | ||
80 | " scond %0, [%1] \n" | ||
81 | " bnz 1b \n" | ||
82 | : "=&r"(temp) | ||
83 | : "r"(&v->counter), "ir"(i) | ||
84 | : "cc"); | ||
85 | |||
86 | return temp; | ||
87 | } | ||
88 | |||
89 | static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) | ||
90 | { | ||
91 | unsigned int temp; | ||
92 | |||
93 | __asm__ __volatile__( | ||
94 | "1: llock %0, [%1] \n" | ||
95 | " bic %0, %0, %2 \n" | ||
96 | " scond %0, [%1] \n" | ||
97 | " bnz 1b \n" | ||
98 | : "=&r"(temp) | ||
99 | : "r"(addr), "ir"(mask) | ||
100 | : "cc"); | ||
101 | } | ||
102 | |||
103 | #else /* !CONFIG_ARC_HAS_LLSC */ | ||
104 | |||
105 | #ifndef CONFIG_SMP | ||
106 | |||
107 | /* violating atomic_xxx API locking protocol in UP for optimization sake */ | ||
108 | #define atomic_set(v, i) (((v)->counter) = (i)) | ||
109 | |||
110 | #else | ||
111 | |||
112 | static inline void atomic_set(atomic_t *v, int i) | ||
113 | { | ||
114 | /* | ||
115 | * Independent of hardware support, all of the atomic_xxx() APIs need | ||
116 | * to follow the same locking rules to make sure that a "hardware" | ||
117 | * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn | ||
118 | * sequence | ||
119 | * | ||
120 | * Thus atomic_set() despite being 1 insn (and seemingly atomic) | ||
121 | * requires the locking. | ||
122 | */ | ||
123 | unsigned long flags; | ||
124 | |||
125 | atomic_ops_lock(flags); | ||
126 | v->counter = i; | ||
127 | atomic_ops_unlock(flags); | ||
128 | } | ||
129 | #endif | ||
130 | |||
131 | /* | ||
132 | * Non hardware assisted Atomic-R-M-W | ||
133 | * Locking would change to irq-disabling only (UP) and spinlocks (SMP) | ||
134 | */ | ||
135 | |||
136 | static inline void atomic_add(int i, atomic_t *v) | ||
137 | { | ||
138 | unsigned long flags; | ||
139 | |||
140 | atomic_ops_lock(flags); | ||
141 | v->counter += i; | ||
142 | atomic_ops_unlock(flags); | ||
143 | } | ||
144 | |||
145 | static inline void atomic_sub(int i, atomic_t *v) | ||
146 | { | ||
147 | unsigned long flags; | ||
148 | |||
149 | atomic_ops_lock(flags); | ||
150 | v->counter -= i; | ||
151 | atomic_ops_unlock(flags); | ||
152 | } | ||
153 | |||
154 | static inline int atomic_add_return(int i, atomic_t *v) | ||
155 | { | ||
156 | unsigned long flags; | ||
157 | unsigned long temp; | ||
158 | |||
159 | atomic_ops_lock(flags); | ||
160 | temp = v->counter; | ||
161 | temp += i; | ||
162 | v->counter = temp; | ||
163 | atomic_ops_unlock(flags); | ||
164 | |||
165 | return temp; | ||
166 | } | ||
167 | |||
168 | static inline int atomic_sub_return(int i, atomic_t *v) | ||
169 | { | ||
170 | unsigned long flags; | ||
171 | unsigned long temp; | ||
172 | |||
173 | atomic_ops_lock(flags); | ||
174 | temp = v->counter; | ||
175 | temp -= i; | ||
176 | v->counter = temp; | ||
177 | atomic_ops_unlock(flags); | ||
178 | |||
179 | return temp; | ||
180 | } | ||
181 | |||
182 | static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | |||
186 | atomic_ops_lock(flags); | ||
187 | *addr &= ~mask; | ||
188 | atomic_ops_unlock(flags); | ||
189 | } | ||
190 | |||
191 | #endif /* !CONFIG_ARC_HAS_LLSC */ | ||
192 | |||
193 | /** | ||
194 | * __atomic_add_unless - add unless the number is a given value | ||
195 | * @v: pointer of type atomic_t | ||
196 | * @a: the amount to add to v... | ||
197 | * @u: ...unless v is equal to u. | ||
198 | * | ||
199 | * Atomically adds @a to @v, so long as it was not @u. | ||
200 | * Returns the old value of @v | ||
201 | */ | ||
202 | #define __atomic_add_unless(v, a, u) \ | ||
203 | ({ \ | ||
204 | int c, old; \ | ||
205 | c = atomic_read(v); \ | ||
206 | while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c)\ | ||
207 | c = old; \ | ||
208 | c; \ | ||
209 | }) | ||
210 | |||
211 | #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) | ||
212 | |||
213 | #define atomic_inc(v) atomic_add(1, v) | ||
214 | #define atomic_dec(v) atomic_sub(1, v) | ||
215 | |||
216 | #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) | ||
217 | #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) | ||
218 | #define atomic_inc_return(v) atomic_add_return(1, (v)) | ||
219 | #define atomic_dec_return(v) atomic_sub_return(1, (v)) | ||
220 | #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) | ||
221 | |||
222 | #define atomic_add_negative(i, v) (atomic_add_return(i, v) < 0) | ||
223 | |||
224 | #define ATOMIC_INIT(i) { (i) } | ||
225 | |||
226 | #include <asm-generic/atomic64.h> | ||
227 | |||
228 | #endif | ||
229 | |||
230 | #endif | ||
231 | |||
232 | #endif | ||
diff --git a/arch/arc/include/asm/barrier.h b/arch/arc/include/asm/barrier.h new file mode 100644 index 000000000000..f6cb7c4ffb35 --- /dev/null +++ b/arch/arc/include/asm/barrier.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef __ASM_BARRIER_H | ||
10 | #define __ASM_BARRIER_H | ||
11 | |||
12 | #ifndef __ASSEMBLY__ | ||
13 | |||
14 | /* TODO-vineetg: Need to see what this does, don't we need sync anywhere */ | ||
15 | #define mb() __asm__ __volatile__ ("" : : : "memory") | ||
16 | #define rmb() mb() | ||
17 | #define wmb() mb() | ||
18 | #define set_mb(var, value) do { var = value; mb(); } while (0) | ||
19 | #define set_wmb(var, value) do { var = value; wmb(); } while (0) | ||
20 | #define read_barrier_depends() mb() | ||
21 | |||
22 | /* TODO-vineetg verify the correctness of macros here */ | ||
23 | #ifdef CONFIG_SMP | ||
24 | #define smp_mb() mb() | ||
25 | #define smp_rmb() rmb() | ||
26 | #define smp_wmb() wmb() | ||
27 | #else | ||
28 | #define smp_mb() barrier() | ||
29 | #define smp_rmb() barrier() | ||
30 | #define smp_wmb() barrier() | ||
31 | #endif | ||
32 | |||
33 | #define smp_mb__before_atomic_dec() barrier() | ||
34 | #define smp_mb__after_atomic_dec() barrier() | ||
35 | #define smp_mb__before_atomic_inc() barrier() | ||
36 | #define smp_mb__after_atomic_inc() barrier() | ||
37 | |||
38 | #define smp_read_barrier_depends() do { } while (0) | ||
39 | |||
40 | #endif | ||
41 | |||
42 | #endif | ||
diff --git a/arch/arc/include/asm/bitops.h b/arch/arc/include/asm/bitops.h new file mode 100644 index 000000000000..647a83a8e756 --- /dev/null +++ b/arch/arc/include/asm/bitops.h | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _ASM_BITOPS_H | ||
10 | #define _ASM_BITOPS_H | ||
11 | |||
12 | #ifndef _LINUX_BITOPS_H | ||
13 | #error only <linux/bitops.h> can be included directly | ||
14 | #endif | ||
15 | |||
16 | #ifdef __KERNEL__ | ||
17 | |||
18 | #ifndef __ASSEMBLY__ | ||
19 | |||
20 | #include <linux/types.h> | ||
21 | #include <linux/compiler.h> | ||
22 | |||
23 | /* | ||
24 | * Hardware assisted read-modify-write using ARC700 LLOCK/SCOND insns. | ||
25 | * The Kconfig glue ensures that in SMP, this is only set if the container | ||
26 | * SoC/platform has cross-core coherent LLOCK/SCOND | ||
27 | */ | ||
28 | #if defined(CONFIG_ARC_HAS_LLSC) | ||
29 | |||
30 | static inline void set_bit(unsigned long nr, volatile unsigned long *m) | ||
31 | { | ||
32 | unsigned int temp; | ||
33 | |||
34 | m += nr >> 5; | ||
35 | |||
36 | if (__builtin_constant_p(nr)) | ||
37 | nr &= 0x1f; | ||
38 | |||
39 | __asm__ __volatile__( | ||
40 | "1: llock %0, [%1] \n" | ||
41 | " bset %0, %0, %2 \n" | ||
42 | " scond %0, [%1] \n" | ||
43 | " bnz 1b \n" | ||
44 | : "=&r"(temp) | ||
45 | : "r"(m), "ir"(nr) | ||
46 | : "cc"); | ||
47 | } | ||
48 | |||
49 | static inline void clear_bit(unsigned long nr, volatile unsigned long *m) | ||
50 | { | ||
51 | unsigned int temp; | ||
52 | |||
53 | m += nr >> 5; | ||
54 | |||
55 | if (__builtin_constant_p(nr)) | ||
56 | nr &= 0x1f; | ||
57 | |||
58 | __asm__ __volatile__( | ||
59 | "1: llock %0, [%1] \n" | ||
60 | " bclr %0, %0, %2 \n" | ||
61 | " scond %0, [%1] \n" | ||
62 | " bnz 1b \n" | ||
63 | : "=&r"(temp) | ||
64 | : "r"(m), "ir"(nr) | ||
65 | : "cc"); | ||
66 | } | ||
67 | |||
68 | static inline void change_bit(unsigned long nr, volatile unsigned long *m) | ||
69 | { | ||
70 | unsigned int temp; | ||
71 | |||
72 | m += nr >> 5; | ||
73 | |||
74 | if (__builtin_constant_p(nr)) | ||
75 | nr &= 0x1f; | ||
76 | |||
77 | __asm__ __volatile__( | ||
78 | "1: llock %0, [%1] \n" | ||
79 | " bxor %0, %0, %2 \n" | ||
80 | " scond %0, [%1] \n" | ||
81 | " bnz 1b \n" | ||
82 | : "=&r"(temp) | ||
83 | : "r"(m), "ir"(nr) | ||
84 | : "cc"); | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Semantically: | ||
89 | * Test the bit | ||
90 | * if clear | ||
91 | * set it and return 0 (old value) | ||
92 | * else | ||
93 | * return 1 (old value). | ||
94 | * | ||
95 | * Since ARC lacks a equivalent h/w primitive, the bit is set unconditionally | ||
96 | * and the old value of bit is returned | ||
97 | */ | ||
98 | static inline int test_and_set_bit(unsigned long nr, volatile unsigned long *m) | ||
99 | { | ||
100 | unsigned long old, temp; | ||
101 | |||
102 | m += nr >> 5; | ||
103 | |||
104 | if (__builtin_constant_p(nr)) | ||
105 | nr &= 0x1f; | ||
106 | |||
107 | __asm__ __volatile__( | ||
108 | "1: llock %0, [%2] \n" | ||
109 | " bset %1, %0, %3 \n" | ||
110 | " scond %1, [%2] \n" | ||
111 | " bnz 1b \n" | ||
112 | : "=&r"(old), "=&r"(temp) | ||
113 | : "r"(m), "ir"(nr) | ||
114 | : "cc"); | ||
115 | |||
116 | return (old & (1 << nr)) != 0; | ||
117 | } | ||
118 | |||
119 | static inline int | ||
120 | test_and_clear_bit(unsigned long nr, volatile unsigned long *m) | ||
121 | { | ||
122 | unsigned int old, temp; | ||
123 | |||
124 | m += nr >> 5; | ||
125 | |||
126 | if (__builtin_constant_p(nr)) | ||
127 | nr &= 0x1f; | ||
128 | |||
129 | __asm__ __volatile__( | ||
130 | "1: llock %0, [%2] \n" | ||
131 | " bclr %1, %0, %3 \n" | ||
132 | " scond %1, [%2] \n" | ||
133 | " bnz 1b \n" | ||
134 | : "=&r"(old), "=&r"(temp) | ||
135 | : "r"(m), "ir"(nr) | ||
136 | : "cc"); | ||
137 | |||
138 | return (old & (1 << nr)) != 0; | ||
139 | } | ||
140 | |||
141 | static inline int | ||
142 | test_and_change_bit(unsigned long nr, volatile unsigned long *m) | ||
143 | { | ||
144 | unsigned int old, temp; | ||
145 | |||
146 | m += nr >> 5; | ||
147 | |||
148 | if (__builtin_constant_p(nr)) | ||
149 | nr &= 0x1f; | ||
150 | |||
151 | __asm__ __volatile__( | ||
152 | "1: llock %0, [%2] \n" | ||
153 | " bxor %1, %0, %3 \n" | ||
154 | " scond %1, [%2] \n" | ||
155 | " bnz 1b \n" | ||
156 | : "=&r"(old), "=&r"(temp) | ||
157 | : "r"(m), "ir"(nr) | ||
158 | : "cc"); | ||
159 | |||
160 | return (old & (1 << nr)) != 0; | ||
161 | } | ||
162 | |||
163 | #else /* !CONFIG_ARC_HAS_LLSC */ | ||
164 | |||
165 | #include <asm/smp.h> | ||
166 | |||
167 | /* | ||
168 | * Non hardware assisted Atomic-R-M-W | ||
169 | * Locking would change to irq-disabling only (UP) and spinlocks (SMP) | ||
170 | * | ||
171 | * There's "significant" micro-optimization in writing our own variants of | ||
172 | * bitops (over generic variants) | ||
173 | * | ||
174 | * (1) The generic APIs have "signed" @nr while we have it "unsigned" | ||
175 | * This avoids extra code to be generated for pointer arithmatic, since | ||
176 | * is "not sure" that index is NOT -ve | ||
177 | * (2) Utilize the fact that ARCompact bit fidding insn (BSET/BCLR/ASL) etc | ||
178 | * only consider bottom 5 bits of @nr, so NO need to mask them off. | ||
179 | * (GCC Quirk: however for constant @nr we still need to do the masking | ||
180 | * at compile time) | ||
181 | */ | ||
182 | |||
183 | static inline void set_bit(unsigned long nr, volatile unsigned long *m) | ||
184 | { | ||
185 | unsigned long temp, flags; | ||
186 | m += nr >> 5; | ||
187 | |||
188 | if (__builtin_constant_p(nr)) | ||
189 | nr &= 0x1f; | ||
190 | |||
191 | bitops_lock(flags); | ||
192 | |||
193 | temp = *m; | ||
194 | *m = temp | (1UL << nr); | ||
195 | |||
196 | bitops_unlock(flags); | ||
197 | } | ||
198 | |||
199 | static inline void clear_bit(unsigned long nr, volatile unsigned long *m) | ||
200 | { | ||
201 | unsigned long temp, flags; | ||
202 | m += nr >> 5; | ||
203 | |||
204 | if (__builtin_constant_p(nr)) | ||
205 | nr &= 0x1f; | ||
206 | |||
207 | bitops_lock(flags); | ||
208 | |||
209 | temp = *m; | ||
210 | *m = temp & ~(1UL << nr); | ||
211 | |||
212 | bitops_unlock(flags); | ||
213 | } | ||
214 | |||
215 | static inline void change_bit(unsigned long nr, volatile unsigned long *m) | ||
216 | { | ||
217 | unsigned long temp, flags; | ||
218 | m += nr >> 5; | ||
219 | |||
220 | if (__builtin_constant_p(nr)) | ||
221 | nr &= 0x1f; | ||
222 | |||
223 | bitops_lock(flags); | ||
224 | |||
225 | temp = *m; | ||
226 | *m = temp ^ (1UL << nr); | ||
227 | |||
228 | bitops_unlock(flags); | ||
229 | } | ||
230 | |||
231 | static inline int test_and_set_bit(unsigned long nr, volatile unsigned long *m) | ||
232 | { | ||
233 | unsigned long old, flags; | ||
234 | m += nr >> 5; | ||
235 | |||
236 | if (__builtin_constant_p(nr)) | ||
237 | nr &= 0x1f; | ||
238 | |||
239 | bitops_lock(flags); | ||
240 | |||
241 | old = *m; | ||
242 | *m = old | (1 << nr); | ||
243 | |||
244 | bitops_unlock(flags); | ||
245 | |||
246 | return (old & (1 << nr)) != 0; | ||
247 | } | ||
248 | |||
249 | static inline int | ||
250 | test_and_clear_bit(unsigned long nr, volatile unsigned long *m) | ||
251 | { | ||
252 | unsigned long old, flags; | ||
253 | m += nr >> 5; | ||
254 | |||
255 | if (__builtin_constant_p(nr)) | ||
256 | nr &= 0x1f; | ||
257 | |||
258 | bitops_lock(flags); | ||
259 | |||
260 | old = *m; | ||
261 | *m = old & ~(1 << nr); | ||
262 | |||
263 | bitops_unlock(flags); | ||
264 | |||
265 | return (old & (1 << nr)) != 0; | ||
266 | } | ||
267 | |||
268 | static inline int | ||
269 | test_and_change_bit(unsigned long nr, volatile unsigned long *m) | ||
270 | { | ||
271 | unsigned long old, flags; | ||
272 | m += nr >> 5; | ||
273 | |||
274 | if (__builtin_constant_p(nr)) | ||
275 | nr &= 0x1f; | ||
276 | |||
277 | bitops_lock(flags); | ||
278 | |||
279 | old = *m; | ||
280 | *m = old ^ (1 << nr); | ||
281 | |||
282 | bitops_unlock(flags); | ||
283 | |||
284 | return (old & (1 << nr)) != 0; | ||
285 | } | ||
286 | |||
287 | #endif /* CONFIG_ARC_HAS_LLSC */ | ||
288 | |||
289 | /*************************************** | ||
290 | * Non atomic variants | ||
291 | **************************************/ | ||
292 | |||
293 | static inline void __set_bit(unsigned long nr, volatile unsigned long *m) | ||
294 | { | ||
295 | unsigned long temp; | ||
296 | m += nr >> 5; | ||
297 | |||
298 | if (__builtin_constant_p(nr)) | ||
299 | nr &= 0x1f; | ||
300 | |||
301 | temp = *m; | ||
302 | *m = temp | (1UL << nr); | ||
303 | } | ||
304 | |||
305 | static inline void __clear_bit(unsigned long nr, volatile unsigned long *m) | ||
306 | { | ||
307 | unsigned long temp; | ||
308 | m += nr >> 5; | ||
309 | |||
310 | if (__builtin_constant_p(nr)) | ||
311 | nr &= 0x1f; | ||
312 | |||
313 | temp = *m; | ||
314 | *m = temp & ~(1UL << nr); | ||
315 | } | ||
316 | |||
317 | static inline void __change_bit(unsigned long nr, volatile unsigned long *m) | ||
318 | { | ||
319 | unsigned long temp; | ||
320 | m += nr >> 5; | ||
321 | |||
322 | if (__builtin_constant_p(nr)) | ||
323 | nr &= 0x1f; | ||
324 | |||
325 | temp = *m; | ||
326 | *m = temp ^ (1UL << nr); | ||
327 | } | ||
328 | |||
329 | static inline int | ||
330 | __test_and_set_bit(unsigned long nr, volatile unsigned long *m) | ||
331 | { | ||
332 | unsigned long old; | ||
333 | m += nr >> 5; | ||
334 | |||
335 | if (__builtin_constant_p(nr)) | ||
336 | nr &= 0x1f; | ||
337 | |||
338 | old = *m; | ||
339 | *m = old | (1 << nr); | ||
340 | |||
341 | return (old & (1 << nr)) != 0; | ||
342 | } | ||
343 | |||
344 | static inline int | ||
345 | __test_and_clear_bit(unsigned long nr, volatile unsigned long *m) | ||
346 | { | ||
347 | unsigned long old; | ||
348 | m += nr >> 5; | ||
349 | |||
350 | if (__builtin_constant_p(nr)) | ||
351 | nr &= 0x1f; | ||
352 | |||
353 | old = *m; | ||
354 | *m = old & ~(1 << nr); | ||
355 | |||
356 | return (old & (1 << nr)) != 0; | ||
357 | } | ||
358 | |||
359 | static inline int | ||
360 | __test_and_change_bit(unsigned long nr, volatile unsigned long *m) | ||
361 | { | ||
362 | unsigned long old; | ||
363 | m += nr >> 5; | ||
364 | |||
365 | if (__builtin_constant_p(nr)) | ||
366 | nr &= 0x1f; | ||
367 | |||
368 | old = *m; | ||
369 | *m = old ^ (1 << nr); | ||
370 | |||
371 | return (old & (1 << nr)) != 0; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * This routine doesn't need to be atomic. | ||
376 | */ | ||
377 | static inline int | ||
378 | __constant_test_bit(unsigned int nr, const volatile unsigned long *addr) | ||
379 | { | ||
380 | return ((1UL << (nr & 31)) & | ||
381 | (((const volatile unsigned int *)addr)[nr >> 5])) != 0; | ||
382 | } | ||
383 | |||
384 | static inline int | ||
385 | __test_bit(unsigned int nr, const volatile unsigned long *addr) | ||
386 | { | ||
387 | unsigned long mask; | ||
388 | |||
389 | addr += nr >> 5; | ||
390 | |||
391 | /* ARC700 only considers 5 bits in bit-fiddling insn */ | ||
392 | mask = 1 << nr; | ||
393 | |||
394 | return ((mask & *addr) != 0); | ||
395 | } | ||
396 | |||
397 | #define test_bit(nr, addr) (__builtin_constant_p(nr) ? \ | ||
398 | __constant_test_bit((nr), (addr)) : \ | ||
399 | __test_bit((nr), (addr))) | ||
400 | |||
401 | /* | ||
402 | * Count the number of zeros, starting from MSB | ||
403 | * Helper for fls( ) friends | ||
404 | * This is a pure count, so (1-32) or (0-31) doesn't apply | ||
405 | * It could be 0 to 32, based on num of 0's in there | ||
406 | * clz(0x8000_0000) = 0, clz(0xFFFF_FFFF)=0, clz(0) = 32, clz(1) = 31 | ||
407 | */ | ||
408 | static inline __attribute__ ((const)) int clz(unsigned int x) | ||
409 | { | ||
410 | unsigned int res; | ||
411 | |||
412 | __asm__ __volatile__( | ||
413 | " norm.f %0, %1 \n" | ||
414 | " mov.n %0, 0 \n" | ||
415 | " add.p %0, %0, 1 \n" | ||
416 | : "=r"(res) | ||
417 | : "r"(x) | ||
418 | : "cc"); | ||
419 | |||
420 | return res; | ||
421 | } | ||
422 | |||
423 | static inline int constant_fls(int x) | ||
424 | { | ||
425 | int r = 32; | ||
426 | |||
427 | if (!x) | ||
428 | return 0; | ||
429 | if (!(x & 0xffff0000u)) { | ||
430 | x <<= 16; | ||
431 | r -= 16; | ||
432 | } | ||
433 | if (!(x & 0xff000000u)) { | ||
434 | x <<= 8; | ||
435 | r -= 8; | ||
436 | } | ||
437 | if (!(x & 0xf0000000u)) { | ||
438 | x <<= 4; | ||
439 | r -= 4; | ||
440 | } | ||
441 | if (!(x & 0xc0000000u)) { | ||
442 | x <<= 2; | ||
443 | r -= 2; | ||
444 | } | ||
445 | if (!(x & 0x80000000u)) { | ||
446 | x <<= 1; | ||
447 | r -= 1; | ||
448 | } | ||
449 | return r; | ||
450 | } | ||
451 | |||
452 | /* | ||
453 | * fls = Find Last Set in word | ||
454 | * @result: [1-32] | ||
455 | * fls(1) = 1, fls(0x80000000) = 32, fls(0) = 0 | ||
456 | */ | ||
457 | static inline __attribute__ ((const)) int fls(unsigned long x) | ||
458 | { | ||
459 | if (__builtin_constant_p(x)) | ||
460 | return constant_fls(x); | ||
461 | |||
462 | return 32 - clz(x); | ||
463 | } | ||
464 | |||
465 | /* | ||
466 | * __fls: Similar to fls, but zero based (0-31) | ||
467 | */ | ||
468 | static inline __attribute__ ((const)) int __fls(unsigned long x) | ||
469 | { | ||
470 | if (!x) | ||
471 | return 0; | ||
472 | else | ||
473 | return fls(x) - 1; | ||
474 | } | ||
475 | |||
476 | /* | ||
477 | * ffs = Find First Set in word (LSB to MSB) | ||
478 | * @result: [1-32], 0 if all 0's | ||
479 | */ | ||
480 | #define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) | ||
481 | |||
482 | /* | ||
483 | * __ffs: Similar to ffs, but zero based (0-31) | ||
484 | */ | ||
485 | static inline __attribute__ ((const)) int __ffs(unsigned long word) | ||
486 | { | ||
487 | if (!word) | ||
488 | return word; | ||
489 | |||
490 | return ffs(word) - 1; | ||
491 | } | ||
492 | |||
493 | /* | ||
494 | * ffz = Find First Zero in word. | ||
495 | * @return:[0-31], 32 if all 1's | ||
496 | */ | ||
497 | #define ffz(x) __ffs(~(x)) | ||
498 | |||
499 | /* TODO does this affect uni-processor code */ | ||
500 | #define smp_mb__before_clear_bit() barrier() | ||
501 | #define smp_mb__after_clear_bit() barrier() | ||
502 | |||
503 | #include <asm-generic/bitops/hweight.h> | ||
504 | #include <asm-generic/bitops/fls64.h> | ||
505 | #include <asm-generic/bitops/sched.h> | ||
506 | #include <asm-generic/bitops/lock.h> | ||
507 | |||
508 | #include <asm-generic/bitops/find.h> | ||
509 | #include <asm-generic/bitops/le.h> | ||
510 | #include <asm-generic/bitops/ext2-atomic-setbit.h> | ||
511 | |||
512 | #endif /* !__ASSEMBLY__ */ | ||
513 | |||
514 | #endif /* __KERNEL__ */ | ||
515 | |||
516 | #endif | ||
diff --git a/arch/arc/include/asm/cmpxchg.h b/arch/arc/include/asm/cmpxchg.h new file mode 100644 index 000000000000..03cd6894855d --- /dev/null +++ b/arch/arc/include/asm/cmpxchg.h | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef __ASM_ARC_CMPXCHG_H | ||
10 | #define __ASM_ARC_CMPXCHG_H | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <asm/smp.h> | ||
14 | |||
15 | #ifdef CONFIG_ARC_HAS_LLSC | ||
16 | |||
17 | static inline unsigned long | ||
18 | __cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new) | ||
19 | { | ||
20 | unsigned long prev; | ||
21 | |||
22 | __asm__ __volatile__( | ||
23 | "1: llock %0, [%1] \n" | ||
24 | " brne %0, %2, 2f \n" | ||
25 | " scond %3, [%1] \n" | ||
26 | " bnz 1b \n" | ||
27 | "2: \n" | ||
28 | : "=&r"(prev) | ||
29 | : "r"(ptr), "ir"(expected), | ||
30 | "r"(new) /* can't be "ir". scond can't take limm for "b" */ | ||
31 | : "cc"); | ||
32 | |||
33 | return prev; | ||
34 | } | ||
35 | |||
36 | #else | ||
37 | |||
38 | static inline unsigned long | ||
39 | __cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new) | ||
40 | { | ||
41 | unsigned long flags; | ||
42 | int prev; | ||
43 | volatile unsigned long *p = ptr; | ||
44 | |||
45 | atomic_ops_lock(flags); | ||
46 | prev = *p; | ||
47 | if (prev == expected) | ||
48 | *p = new; | ||
49 | atomic_ops_unlock(flags); | ||
50 | return prev; | ||
51 | } | ||
52 | |||
53 | #endif /* CONFIG_ARC_HAS_LLSC */ | ||
54 | |||
55 | #define cmpxchg(ptr, o, n) ((typeof(*(ptr)))__cmpxchg((ptr), \ | ||
56 | (unsigned long)(o), (unsigned long)(n))) | ||
57 | |||
58 | /* | ||
59 | * Since not supported natively, ARC cmpxchg() uses atomic_ops_lock (UP/SMP) | ||
60 | * just to gaurantee semantics. | ||
61 | * atomic_cmpxchg() needs to use the same locks as it's other atomic siblings | ||
62 | * which also happens to be atomic_ops_lock. | ||
63 | * | ||
64 | * Thus despite semantically being different, implementation of atomic_cmpxchg() | ||
65 | * is same as cmpxchg(). | ||
66 | */ | ||
67 | #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) | ||
68 | |||
69 | |||
70 | /* | ||
71 | * xchg (reg with memory) based on "Native atomic" EX insn | ||
72 | */ | ||
73 | static inline unsigned long __xchg(unsigned long val, volatile void *ptr, | ||
74 | int size) | ||
75 | { | ||
76 | extern unsigned long __xchg_bad_pointer(void); | ||
77 | |||
78 | switch (size) { | ||
79 | case 4: | ||
80 | __asm__ __volatile__( | ||
81 | " ex %0, [%1] \n" | ||
82 | : "+r"(val) | ||
83 | : "r"(ptr) | ||
84 | : "memory"); | ||
85 | |||
86 | return val; | ||
87 | } | ||
88 | return __xchg_bad_pointer(); | ||
89 | } | ||
90 | |||
91 | #define _xchg(ptr, with) ((typeof(*(ptr)))__xchg((unsigned long)(with), (ptr), \ | ||
92 | sizeof(*(ptr)))) | ||
93 | |||
94 | /* | ||
95 | * On ARC700, EX insn is inherently atomic, so by default "vanilla" xchg() need | ||
96 | * not require any locking. However there's a quirk. | ||
97 | * ARC lacks native CMPXCHG, thus emulated (see above), using external locking - | ||
98 | * incidently it "reuses" the same atomic_ops_lock used by atomic APIs. | ||
99 | * Now, llist code uses cmpxchg() and xchg() on same data, so xchg() needs to | ||
100 | * abide by same serializing rules, thus ends up using atomic_ops_lock as well. | ||
101 | * | ||
102 | * This however is only relevant if SMP and/or ARC lacks LLSC | ||
103 | * if (UP or LLSC) | ||
104 | * xchg doesn't need serialization | ||
105 | * else <==> !(UP or LLSC) <==> (!UP and !LLSC) <==> (SMP and !LLSC) | ||
106 | * xchg needs serialization | ||
107 | */ | ||
108 | |||
109 | #if !defined(CONFIG_ARC_HAS_LLSC) && defined(CONFIG_SMP) | ||
110 | |||
111 | #define xchg(ptr, with) \ | ||
112 | ({ \ | ||
113 | unsigned long flags; \ | ||
114 | typeof(*(ptr)) old_val; \ | ||
115 | \ | ||
116 | atomic_ops_lock(flags); \ | ||
117 | old_val = _xchg(ptr, with); \ | ||
118 | atomic_ops_unlock(flags); \ | ||
119 | old_val; \ | ||
120 | }) | ||
121 | |||
122 | #else | ||
123 | |||
124 | #define xchg(ptr, with) _xchg(ptr, with) | ||
125 | |||
126 | #endif | ||
127 | |||
128 | /* | ||
129 | * "atomic" variant of xchg() | ||
130 | * REQ: It needs to follow the same serialization rules as other atomic_xxx() | ||
131 | * Since xchg() doesn't always do that, it would seem that following defintion | ||
132 | * is incorrect. But here's the rationale: | ||
133 | * SMP : Even xchg() takes the atomic_ops_lock, so OK. | ||
134 | * LLSC: atomic_ops_lock are not relevent at all (even if SMP, since LLSC | ||
135 | * is natively "SMP safe", no serialization required). | ||
136 | * UP : other atomics disable IRQ, so no way a difft ctxt atomic_xchg() | ||
137 | * could clobber them. atomic_xchg() itself would be 1 insn, so it | ||
138 | * can't be clobbered by others. Thus no serialization required when | ||
139 | * atomic_xchg is involved. | ||
140 | */ | ||
141 | #define atomic_xchg(v, new) (xchg(&((v)->counter), new)) | ||
142 | |||
143 | #endif | ||
diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h new file mode 100644 index 000000000000..4341f3ba7d92 --- /dev/null +++ b/arch/arc/include/asm/smp.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef __ASM_ARC_SMP_H | ||
10 | #define __ASM_ARC_SMP_H | ||
11 | |||
12 | /* | ||
13 | * ARC700 doesn't support atomic Read-Modify-Write ops. | ||
14 | * Originally Interrupts had to be disabled around code to gaurantee atomicity. | ||
15 | * The LLOCK/SCOND insns allow writing interrupt-hassle-free based atomic ops | ||
16 | * based on retry-if-irq-in-atomic (with hardware assist). | ||
17 | * However despite these, we provide the IRQ disabling variant | ||
18 | * | ||
19 | * (1) These insn were introduced only in 4.10 release. So for older released | ||
20 | * support needed. | ||
21 | */ | ||
22 | #ifndef CONFIG_ARC_HAS_LLSC | ||
23 | |||
24 | #include <linux/irqflags.h> | ||
25 | |||
26 | #define atomic_ops_lock(flags) local_irq_save(flags) | ||
27 | #define atomic_ops_unlock(flags) local_irq_restore(flags) | ||
28 | |||
29 | #define bitops_lock(flags) local_irq_save(flags) | ||
30 | #define bitops_unlock(flags) local_irq_restore(flags) | ||
31 | |||
32 | #endif /* !CONFIG_ARC_HAS_LLSC */ | ||
33 | |||
34 | #endif | ||