2 * 1,2 and 4 byte cmpxchg and xchg implementations for OpenRISC.
4 * Copyright (C) 2014 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
5 * Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
7 * This file is licensed under the terms of the GNU General Public License
8 * version 2. This program is licensed "as is" without any warranty of any
9 * kind, whether express or implied.
12 * The portable implementations of 1 and 2 byte xchg and cmpxchg using a 4
13 * byte cmpxchg is sourced heavily from the sh and mips implementations.
16 #ifndef __ASM_OPENRISC_CMPXCHG_H
17 #define __ASM_OPENRISC_CMPXCHG_H
19 #include <linux/bits.h>
20 #include <linux/compiler.h>
21 #include <linux/types.h>
23 #define __HAVE_ARCH_CMPXCHG 1
25 static inline unsigned long cmpxchg_u32(volatile void *ptr
,
26 unsigned long old
, unsigned long new)
29 "1: l.lwa %0, 0(%1) \n"
38 : "r"(ptr
), "r"(old
), "r"(new)
44 static inline unsigned long xchg_u32(volatile void *ptr
,
48 "1: l.lwa %0, 0(%1) \n"
59 static inline u32
cmpxchg_small(volatile void *ptr
, u32 old
, u32
new,
62 int off
= (unsigned long)ptr
% sizeof(u32
);
63 volatile u32
*p
= ptr
- off
;
65 int bitoff
= (sizeof(u32
) - size
- off
) * BITS_PER_BYTE
;
67 int bitoff
= off
* BITS_PER_BYTE
;
69 u32 bitmask
= ((0x1 << size
* BITS_PER_BYTE
) - 1) << bitoff
;
70 u32 load32
, old32
, new32
;
73 load32
= READ_ONCE(*p
);
76 ret
= (load32
& bitmask
) >> bitoff
;
80 old32
= (load32
& ~bitmask
) | (old
<< bitoff
);
81 new32
= (load32
& ~bitmask
) | (new << bitoff
);
83 /* Do 32 bit cmpxchg */
84 load32
= cmpxchg_u32(p
, old32
, new32
);
92 static inline u32
xchg_small(volatile void *ptr
, u32 x
, int size
)
94 int off
= (unsigned long)ptr
% sizeof(u32
);
95 volatile u32
*p
= ptr
- off
;
97 int bitoff
= (sizeof(u32
) - size
- off
) * BITS_PER_BYTE
;
99 int bitoff
= off
* BITS_PER_BYTE
;
101 u32 bitmask
= ((0x1 << size
* BITS_PER_BYTE
) - 1) << bitoff
;
106 oldv
= READ_ONCE(*p
);
107 ret
= (oldv
& bitmask
) >> bitoff
;
108 newv
= (oldv
& ~bitmask
) | (x
<< bitoff
);
109 } while (cmpxchg_u32(p
, oldv
, newv
) != oldv
);
115 * This function doesn't exist, so you'll get a linker error
116 * if something tries to do an invalid cmpxchg().
118 extern unsigned long __cmpxchg_called_with_bad_pointer(void)
119 __compiletime_error("Bad argument size for cmpxchg");
121 static inline unsigned long __cmpxchg(volatile void *ptr
, unsigned long old
,
122 unsigned long new, int size
)
127 return cmpxchg_small(ptr
, old
, new, size
);
129 return cmpxchg_u32(ptr
, old
, new);
131 return __cmpxchg_called_with_bad_pointer();
135 #define arch_cmpxchg(ptr, o, n) \
137 (__typeof__(*(ptr))) __cmpxchg((ptr), \
138 (unsigned long)(o), \
139 (unsigned long)(n), \
144 * This function doesn't exist, so you'll get a linker error if
145 * something tries to do an invalidly-sized xchg().
147 extern unsigned long __xchg_called_with_bad_pointer(void)
148 __compiletime_error("Bad argument size for xchg");
150 static inline unsigned long
151 __arch_xchg(volatile void *ptr
, unsigned long with
, int size
)
156 return xchg_small(ptr
, with
, size
);
158 return xchg_u32(ptr
, with
);
160 return __xchg_called_with_bad_pointer();
164 #define arch_xchg(ptr, with) \
166 (__typeof__(*(ptr))) __arch_xchg((ptr), \
167 (unsigned long)(with), \
171 #endif /* __ASM_OPENRISC_CMPXCHG_H */