xref: /freebsd/sys/powerpc/include/atomic.h (revision ebccf1e3a6b11b97cbf5f813dd76636e892a9035)
1 /*-
2  * Copyright (c) 2001 Benno Rice
3  * Copyright (c) 2001 David E. O'Brien
4  * Copyright (c) 1998 Doug Rabson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #ifndef _MACHINE_ATOMIC_H_
32 #define	_MACHINE_ATOMIC_H_
33 
34 #include <machine/cpufunc.h>
35 
36 #ifndef _SYS_CDEFS_H_
37 #error this file needs sys/cdefs.h as a prerequisite
38 #endif
39 
40 /*
41  * Various simple arithmetic on memory which is atomic in the presence
42  * of interrupts and SMP safe.
43  */
44 
45 void	atomic_set_8(volatile uint8_t *, uint8_t);
46 void	atomic_clear_8(volatile uint8_t *, uint8_t);
47 void	atomic_add_8(volatile uint8_t *, uint8_t);
48 void	atomic_subtract_8(volatile uint8_t *, uint8_t);
49 
50 void	atomic_set_16(volatile uint16_t *, uint16_t);
51 void	atomic_clear_16(volatile uint16_t *, uint16_t);
52 void	atomic_add_16(volatile uint16_t *, uint16_t);
53 void	atomic_subtract_16(volatile uint16_t *, uint16_t);
54 
55 static __inline void
56 atomic_set_32(volatile uint32_t *p, uint32_t v)
57 {
58 	uint32_t temp;
59 
60 #ifdef __GNUCLIKE_ASM
61 	__asm __volatile (
62 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
63 		"or %0, %3, %0\n\t"		/* calculate new value */
64 		"stwcx. %0, 0, %2\n\t"      	/* attempt to store */
65 		"bne- 1b\n\t"			/* spin if failed */
66 		: "=&r" (temp), "+m" (*p)
67 		: "r" (p), "r" (v)
68 		: "cc", "memory");
69 #endif
70 }
71 
72 static __inline void
73 atomic_clear_32(volatile uint32_t *p, uint32_t v)
74 {
75 	uint32_t temp;
76 
77 #ifdef __GNUCLIKE_ASM
78 	__asm __volatile (
79 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
80 		"andc %0, %0, %3\n\t"		/* calculate new value */
81 		"stwcx. %0, 0, %2\n\t"      	/* attempt to store */
82 		"bne- 1b\n\t"			/* spin if failed */
83 		: "=&r" (temp), "+m" (*p)
84 		: "r" (p), "r" (v)
85 		: "cc", "memory");
86 #endif
87 }
88 
89 static __inline void
90 atomic_add_32(volatile uint32_t *p, uint32_t v)
91 {
92 	uint32_t temp;
93 
94 #ifdef __GNUCLIKE_ASM
95 	__asm __volatile (
96 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
97 		"add %0, %3, %0\n\t"		/* calculate new value */
98 		"stwcx. %0, 0, %2\n\t"      	/* attempt to store */
99 		"bne- 1b\n\t"			/* spin if failed */
100 		: "=&r" (temp), "+m" (*p)
101 		: "r" (p), "r" (v)
102 		: "cc", "memory");
103 #endif
104 }
105 
106 static __inline void
107 atomic_subtract_32(volatile uint32_t *p, uint32_t v)
108 {
109 	uint32_t temp;
110 
111 #ifdef __GNUCLIKE_ASM
112 	__asm __volatile (
113 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
114 		"subf %0, %3, %0\n\t"		/* calculate new value */
115 		"stwcx. %0, 0, %2\n\t"      	/* attempt to store */
116 		"bne- 1b\n\t"			/* spin if failed */
117 		: "=&r" (temp), "+m" (*p)
118 		: "r" (p), "r" (v)
119 		: "cc", "memory");
120 #endif
121 }
122 
123 static __inline uint32_t
124 atomic_readandclear_32(volatile uint32_t *addr)
125 {
126 	uint32_t result,temp;
127 
128 #ifdef __GNUCLIKE_ASM
129 	__asm __volatile (
130 		"\tsync\n"			/* drain writes */
131 		"1:\tlwarx %0, 0, %3\n\t"	/* load old value */
132 		"li %1, 0\n\t"			/* load new value */
133 		"stwcx. %1, 0, %3\n\t"      	/* attempt to store */
134 		"bne- 1b\n\t"			/* spin if failed */
135 		: "=&r"(result), "=&r"(temp), "+m" (*addr)
136 		: "r" (addr)
137 		: "cc", "memory");
138 #endif
139 
140 	return (result);
141 }
142 
143 #if 0
144 
145 /*
146  * So far I haven't found a way to implement atomic 64-bit ops on the
147  * 32-bit PowerPC without involving major headaches.  If anyone has
148  * any ideas, please let me know. =)
149  * 	- benno@FreeBSD.org
150  */
151 
152 static __inline void
153 atomic_set_64(volatile u_int64_t *p, u_int64_t v)
154 {
155 	u_int64_t temp;
156 
157 	__asm __volatile (
158 		: "=&r" (temp), "=r" (*p)
159 		: "r" (*p), "r" (v)
160 		: "memory");
161 }
162 
163 static __inline void
164 atomic_clear_64(volatile u_int64_t *p, u_int64_t v)
165 {
166 	u_int64_t temp;
167 
168 	__asm __volatile (
169 		: "=&r" (temp), "=r" (*p)
170 		: "r" (*p), "r" (v)
171 		: "memory");
172 }
173 
174 static __inline void
175 atomic_add_64(volatile u_int64_t *p, u_int64_t v)
176 {
177 	u_int64_t temp;
178 
179 	__asm __volatile (
180 		: "=&r" (temp), "=r" (*p)
181 		: "r" (*p), "r" (v)
182 		: "memory");
183 }
184 
185 static __inline void
186 atomic_subtract_64(volatile u_int64_t *p, u_int64_t v)
187 {
188 	u_int64_t temp;
189 
190 	__asm __volatile (
191 		: "=&r" (temp), "=r" (*p)
192 		: "r" (*p), "r" (v)
193 		: "memory");
194 }
195 
196 static __inline u_int64_t
197 atomic_readandclear_64(volatile u_int64_t *addr)
198 {
199 	u_int64_t result,temp;
200 
201 	__asm __volatile (
202 		: "=&r"(result), "=&r"(temp), "=r" (*addr)
203 		: "r"(*addr)
204 		: "memory");
205 
206 	return result;
207 }
208 
209 #endif /* 0 */
210 
211 #define	atomic_set_char			atomic_set_8
212 #define	atomic_clear_char		atomic_clear_8
213 #define	atomic_add_char			atomic_add_8
214 #define	atomic_subtract_char		atomic_subtract_8
215 
216 #define	atomic_set_short		atomic_set_16
217 #define	atomic_clear_short		atomic_clear_16
218 #define	atomic_add_short		atomic_add_16
219 #define	atomic_subtract_short		atomic_subtract_16
220 
221 #define	atomic_set_int			atomic_set_32
222 #define	atomic_clear_int		atomic_clear_32
223 #define	atomic_add_int			atomic_add_32
224 #define	atomic_subtract_int		atomic_subtract_32
225 #define	atomic_readandclear_int		atomic_readandclear_32
226 
227 #define	atomic_set_long			atomic_set_32
228 #define	atomic_clear_long		atomic_clear_32
229 #define	atomic_add_long(p, v)		atomic_add_32((uint32_t *)p, (uint32_t)v)
230 #define	atomic_subtract_long(p, v)     	atomic_subtract_32((uint32_t *)p, (uint32_t)v)
231 #define	atomic_readandclear_long	atomic_readandclear_32
232 
233 #if 0
234 
235 /* See above. */
236 
237 #define	atomic_set_long_long		atomic_set_64
238 #define	atomic_clear_long_long		atomic_clear_64
239 #define	atomic_add_long_long		atomic_add_64
240 #define	atomic_subtract_long_long	atomic_subtract_64
241 #define	atomic_readandclear_long_long	atomic_readandclear_64
242 
243 #endif /* 0 */
244 
245 #define	ATOMIC_ACQ_REL(NAME, WIDTH, TYPE)				\
246 static __inline void							\
247 atomic_##NAME##_acq_##WIDTH(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \
248 {									\
249 	atomic_##NAME##_##WIDTH(p, v);					\
250 	powerpc_mb();							\
251 }									\
252 									\
253 static __inline void							\
254 atomic_##NAME##_rel_##WIDTH(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \
255 {									\
256 	powerpc_mb();							\
257 	atomic_##NAME##_##WIDTH(p, v);					\
258 }									\
259 									\
260 static __inline void							\
261 atomic_##NAME##_acq_##TYPE(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \
262 {									\
263 	atomic_##NAME##_##WIDTH(p, v);					\
264 	powerpc_mb();							\
265 }									\
266 									\
267 static __inline void							\
268 atomic_##NAME##_rel_##TYPE(volatile u_int##WIDTH##_t *p, u_int##WIDTH##_t v) \
269 {									\
270 	powerpc_mb();							\
271 	atomic_##NAME##_##WIDTH(p, v);					\
272 }
273 
274 ATOMIC_ACQ_REL(set, 8, char)
275 ATOMIC_ACQ_REL(clear, 8, char)
276 ATOMIC_ACQ_REL(add, 8, char)
277 ATOMIC_ACQ_REL(subtract, 8, char)
278 ATOMIC_ACQ_REL(set, 16, short)
279 ATOMIC_ACQ_REL(clear, 16, short)
280 ATOMIC_ACQ_REL(add, 16, short)
281 ATOMIC_ACQ_REL(subtract, 16, short)
282 ATOMIC_ACQ_REL(set, 32, int)
283 ATOMIC_ACQ_REL(clear, 32, int)
284 ATOMIC_ACQ_REL(add, 32, int)
285 ATOMIC_ACQ_REL(subtract, 32, int)
286 
287 #define	atomic_set_acq_long		atomic_set_acq_32
288 #define	atomic_set_rel_long		atomic_set_rel_32
289 #define	atomic_clear_acq_long		atomic_clear_acq_32
290 #define	atomic_clear_rel_long		atomic_clear_rel_32
291 #define	atomic_add_acq_long		atomic_add_acq_32
292 #define	atomic_add_rel_long		atomic_add_rel_32
293 #define	atomic_subtract_acq_long	atomic_subtract_acq_32
294 #define	atomic_subtract_rel_long	atomic_subtract_rel_32
295 
296 #undef ATOMIC_ACQ_REL
297 
298 /*
299  * We assume that a = b will do atomic loads and stores.
300  */
301 #define	ATOMIC_STORE_LOAD(TYPE, WIDTH)				\
302 static __inline u_##TYPE					\
303 atomic_load_acq_##WIDTH(volatile u_##TYPE *p)			\
304 {								\
305 	u_##TYPE v;						\
306 								\
307 	v = *p;							\
308 	powerpc_mb();						\
309 	return (v);						\
310 }								\
311 								\
312 static __inline void						\
313 atomic_store_rel_##WIDTH(volatile u_##TYPE *p, u_##TYPE v)	\
314 {								\
315 	powerpc_mb();						\
316 	*p = v;							\
317 }								\
318 								\
319 static __inline u_##TYPE					\
320 atomic_load_acq_##TYPE(volatile u_##TYPE *p)			\
321 {								\
322 	u_##TYPE v;						\
323 								\
324 	v = *p;							\
325 	powerpc_mb();						\
326 	return (v);						\
327 }								\
328 								\
329 static __inline void						\
330 atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v)	\
331 {								\
332 	powerpc_mb();						\
333 	*p = v;							\
334 }
335 
336 ATOMIC_STORE_LOAD(char,		8)
337 ATOMIC_STORE_LOAD(short,	16)
338 ATOMIC_STORE_LOAD(int,		32)
339 
340 #define	atomic_load_acq_long	atomic_load_acq_32
341 #define	atomic_store_rel_long	atomic_store_rel_32
342 
343 #undef ATOMIC_STORE_LOAD
344 
345 /*
346  * Atomically compare the value stored at *p with cmpval and if the
347  * two values are equal, update the value of *p with newval. Returns
348  * zero if the compare failed, nonzero otherwise.
349  */
350 static __inline uint32_t
351 atomic_cmpset_32(volatile uint32_t* p, uint32_t cmpval, uint32_t newval)
352 {
353 	uint32_t	ret;
354 
355 #ifdef __GNUCLIKE_ASM
356 	__asm __volatile (
357 		"1:\tlwarx %0, 0, %2\n\t"	/* load old value */
358 		"cmplw %3, %0\n\t"		/* compare */
359 		"bne 2f\n\t"			/* exit if not equal */
360 		"stwcx. %4, 0, %2\n\t"      	/* attempt to store */
361 		"bne- 1b\n\t"			/* spin if failed */
362 		"li %0, 1\n\t"			/* success - retval = 1 */
363 		"b 3f\n\t"			/* we've succeeded */
364 		"2:\n\t"
365 		"stwcx. %0, 0, %2\n\t"       	/* clear reservation (74xx) */
366 		"li %0, 0\n\t"			/* failure - retval = 0 */
367 		"3:\n\t"
368 		: "=&r" (ret), "+m" (*p)
369 		: "r" (p), "r" (cmpval), "r" (newval)
370 		: "cc", "memory");
371 #endif
372 
373 	return (ret);
374 }
375 
376 #if 0
377 
378 /*
379  * Atomically compare the value stored at *p with cmpval and if the
380  * two values are equal, update the value of *p with newval. Returns
381  * zero if the compare failed, nonzero otherwise.
382  */
383 static __inline u_int64_t
384 atomic_cmpset_64(volatile u_int64_t* p, u_int64_t cmpval, u_int64_t newval)
385 {
386 	u_int64_t ret;
387 
388 	__asm __volatile (
389 		: "=&r" (ret), "=r" (*p)
390 		: "r" (cmpval), "r" (newval), "r" (*p)
391 		: "memory");
392 
393 	return ret;
394 }
395 
396 #endif /* 0 */
397 
398 #define	atomic_cmpset_int	atomic_cmpset_32
399 #define	atomic_cmpset_long	atomic_cmpset_32
400 
401 #if 0
402 #define	atomic_cmpset_long_long	atomic_cmpset_64
403 #endif /* 0 */
404 
405 static __inline int
406 atomic_cmpset_ptr(volatile void *dst, void *exp, void *src)
407 {
408 
409 	return (atomic_cmpset_32((volatile uint32_t *)dst, (uint32_t)exp,
410 	    (uint32_t)src));
411 }
412 
413 static __inline uint32_t
414 atomic_cmpset_acq_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
415 {
416 	int retval;
417 
418 	retval = atomic_cmpset_32(p, cmpval, newval);
419 	powerpc_mb();
420 	return (retval);
421 }
422 
423 static __inline uint32_t
424 atomic_cmpset_rel_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
425 {
426 	powerpc_mb();
427 	return (atomic_cmpset_32(p, cmpval, newval));
428 }
429 
430 #define	atomic_cmpset_acq_int	atomic_cmpset_acq_32
431 #define	atomic_cmpset_rel_int	atomic_cmpset_rel_32
432 #define	atomic_cmpset_acq_long	atomic_cmpset_acq_32
433 #define	atomic_cmpset_rel_long	atomic_cmpset_rel_32
434 
435 static __inline int
436 atomic_cmpset_acq_ptr(volatile void *dst, void *exp, void *src)
437 {
438 
439         return (atomic_cmpset_acq_32((volatile uint32_t *)dst,
440 	    (uint32_t)exp, (uint32_t)src));
441 }
442 
443 static __inline int
444 atomic_cmpset_rel_ptr(volatile void *dst, void *exp, void *src)
445 {
446 
447         return (atomic_cmpset_rel_32((volatile uint32_t *)dst,
448 	    (uint32_t)exp, (uint32_t)src));
449 }
450 
451 static __inline void *
452 atomic_load_acq_ptr(volatile void *p)
453 {
454 
455 	return (void *)atomic_load_acq_32((volatile uint32_t *)p);
456 }
457 
458 static __inline void
459 atomic_store_rel_ptr(volatile void *p, void *v)
460 {
461 
462 	atomic_store_rel_32((volatile uint32_t *)p, (uint32_t)v);
463 }
464 
465 #define	ATOMIC_PTR(NAME)					\
466 static __inline void						\
467 atomic_##NAME##_ptr(volatile void *p, uintptr_t v)		\
468 {								\
469 	atomic_##NAME##_32((volatile uint32_t *)p, v);	\
470 }								\
471 								\
472 static __inline void						\
473 atomic_##NAME##_acq_ptr(volatile void *p, uintptr_t v)		\
474 {								\
475 	atomic_##NAME##_acq_32((volatile uint32_t *)p, v);	\
476 }								\
477 								\
478 static __inline void						\
479 atomic_##NAME##_rel_ptr(volatile void *p, uintptr_t v)		\
480 {								\
481 	atomic_##NAME##_rel_32((volatile uint32_t *)p, v);	\
482 }
483 
484 ATOMIC_PTR(set)
485 ATOMIC_PTR(clear)
486 ATOMIC_PTR(add)
487 ATOMIC_PTR(subtract)
488 
489 #undef ATOMIC_PTR
490 #endif /* ! _MACHINE_ATOMIC_H_ */
491