1 /* xchg and cmpxchg operation emulation for FR-V
2  *
3  * For an explanation of how atomic ops work in this arch, see:
4  *   Documentation/frv/atomic-ops.txt
5  *
6  * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
7  * Written by David Howells (dhowells@redhat.com)
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version
12  * 2 of the License, or (at your option) any later version.
13  */
14 #ifndef _ASM_CMPXCHG_H
15 #define _ASM_CMPXCHG_H
16 
17 #include <linux/types.h>
18 
19 /*****************************************************************************/
20 /*
21  * exchange value with memory
22  */
23 extern uint64_t __xchg_64(uint64_t i, volatile void *v);
24 
25 #ifndef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
26 
27 #define xchg(ptr, x)								\
28 ({										\
29 	__typeof__(ptr) __xg_ptr = (ptr);					\
30 	__typeof__(*(ptr)) __xg_orig;						\
31 										\
32 	switch (sizeof(__xg_orig)) {						\
33 	case 4:									\
34 		asm volatile(							\
35 			"swap%I0 %M0,%1"					\
36 			: "+m"(*__xg_ptr), "=r"(__xg_orig)			\
37 			: "1"(x)						\
38 			: "memory"						\
39 			);							\
40 		break;								\
41 										\
42 	default:								\
43 		__xg_orig = (__typeof__(__xg_orig))0;				\
44 		asm volatile("break");						\
45 		break;								\
46 	}									\
47 										\
48 	__xg_orig;								\
49 })
50 
51 #else
52 
53 extern uint32_t __xchg_32(uint32_t i, volatile void *v);
54 
55 #define xchg(ptr, x)										\
56 ({												\
57 	__typeof__(ptr) __xg_ptr = (ptr);							\
58 	__typeof__(*(ptr)) __xg_orig;								\
59 												\
60 	switch (sizeof(__xg_orig)) {								\
61 	case 4: __xg_orig = (__typeof__(*(ptr))) __xchg_32((uint32_t) x, __xg_ptr);	break;	\
62 	default:										\
63 		__xg_orig = (__typeof__(__xg_orig))0;									\
64 		asm volatile("break");								\
65 		break;										\
66 	}											\
67 	__xg_orig;										\
68 })
69 
70 #endif
71 
72 #define tas(ptr) (xchg((ptr), 1))
73 
74 /*****************************************************************************/
75 /*
76  * compare and conditionally exchange value with memory
77  * - if (*ptr == test) then orig = *ptr; *ptr = test;
78  * - if (*ptr != test) then orig = *ptr;
79  */
80 extern uint64_t __cmpxchg_64(uint64_t test, uint64_t new, volatile uint64_t *v);
81 
82 #ifndef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
83 
84 #define cmpxchg(ptr, test, new)							\
85 ({										\
86 	__typeof__(ptr) __xg_ptr = (ptr);					\
87 	__typeof__(*(ptr)) __xg_orig, __xg_tmp;					\
88 	__typeof__(*(ptr)) __xg_test = (test);					\
89 	__typeof__(*(ptr)) __xg_new = (new);					\
90 										\
91 	switch (sizeof(__xg_orig)) {						\
92 	case 4:									\
93 		asm volatile(							\
94 			"0:						\n"	\
95 			"	orcc		gr0,gr0,gr0,icc3	\n"	\
96 			"	ckeq		icc3,cc7		\n"	\
97 			"	ld.p		%M0,%1			\n"	\
98 			"	orcr		cc7,cc7,cc3		\n"	\
99 			"	sub%I4cc	%1,%4,%2,icc0		\n"	\
100 			"	bne		icc0,#0,1f		\n"	\
101 			"	cst.p		%3,%M0		,cc3,#1	\n"	\
102 			"	corcc		gr29,gr29,gr0	,cc3,#1	\n"	\
103 			"	beq		icc3,#0,0b		\n"	\
104 			"1:						\n"	\
105 			: "+U"(*__xg_ptr), "=&r"(__xg_orig), "=&r"(__xg_tmp)	\
106 			: "r"(__xg_new), "NPr"(__xg_test)			\
107 			: "memory", "cc7", "cc3", "icc3", "icc0"		\
108 			);							\
109 		break;								\
110 										\
111 	default:								\
112 		__xg_orig = (__typeof__(__xg_orig))0;				\
113 		asm volatile("break");						\
114 		break;								\
115 	}									\
116 										\
117 	__xg_orig;								\
118 })
119 
120 #else
121 
122 extern uint32_t __cmpxchg_32(uint32_t *v, uint32_t test, uint32_t new);
123 
124 #define cmpxchg(ptr, test, new)							\
125 ({										\
126 	__typeof__(ptr) __xg_ptr = (ptr);					\
127 	__typeof__(*(ptr)) __xg_orig;						\
128 	__typeof__(*(ptr)) __xg_test = (test);					\
129 	__typeof__(*(ptr)) __xg_new = (new);					\
130 										\
131 	switch (sizeof(__xg_orig)) {						\
132 	case 4: __xg_orig = (__force __typeof__(*ptr))				\
133 			__cmpxchg_32((__force uint32_t *)__xg_ptr,		\
134 					 (__force uint32_t)__xg_test,		\
135 					 (__force uint32_t)__xg_new); break;	\
136 	default:								\
137 		__xg_orig = (__typeof__(__xg_orig))0;				\
138 		asm volatile("break");						\
139 		break;								\
140 	}									\
141 										\
142 	__xg_orig;								\
143 })
144 
145 #endif
146 
147 #include <asm-generic/cmpxchg-local.h>
148 
__cmpxchg_local(volatile void * ptr,unsigned long old,unsigned long new,int size)149 static inline unsigned long __cmpxchg_local(volatile void *ptr,
150 				      unsigned long old,
151 				      unsigned long new, int size)
152 {
153 	switch (size) {
154 	case 4:
155 		return cmpxchg((unsigned long *)ptr, old, new);
156 	default:
157 		return __cmpxchg_local_generic(ptr, old, new, size);
158 	}
159 
160 	return old;
161 }
162 
163 /*
164  * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make
165  * them available.
166  */
167 #define cmpxchg_local(ptr, o, n)				  	\
168 	((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o),	\
169 			(unsigned long)(n), sizeof(*(ptr))))
170 #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
171 
172 #endif /* _ASM_CMPXCHG_H */
173