summaryrefslogtreecommitdiff
path: root/libc/sysdeps/linux/xtensa/bits/atomic.h
diff options
context:
space:
mode:
Diffstat (limited to 'libc/sysdeps/linux/xtensa/bits/atomic.h')
-rw-r--r--libc/sysdeps/linux/xtensa/bits/atomic.h128
1 files changed, 128 insertions, 0 deletions
diff --git a/libc/sysdeps/linux/xtensa/bits/atomic.h b/libc/sysdeps/linux/xtensa/bits/atomic.h
index efc027d1a..18b809998 100644
--- a/libc/sysdeps/linux/xtensa/bits/atomic.h
+++ b/libc/sysdeps/linux/xtensa/bits/atomic.h
@@ -18,6 +18,7 @@
#ifndef _BITS_ATOMIC_H
#define _BITS_ATOMIC_H 1
+#include <bits/xtensa-config.h>
#include <inttypes.h>
typedef int32_t atomic32_t;
@@ -50,6 +51,128 @@ typedef uintmax_t uatomic_max_t;
#define __arch_compare_and_exchange_bool_16_rel(mem, newval, oldval) \
(abort (), 0)
+#if XCHAL_HAVE_EXCLUSIVE
+
+/* Atomically store NEWVAL in *MEM if *MEM is equal to OLDVAL.
+ Return the old *MEM value. */
+
+#define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
+ ({__typeof__(*(mem)) __tmp, __value; \
+ __asm__ __volatile__( \
+ " memw \n" \
+ "1: l32ex %0, %2 \n" \
+ " bne %0, %4, 2f \n" \
+ " mov %1, %3 \n" \
+ " s32ex %1, %2 \n" \
+ " getex %1 \n" \
+ " beqz %1, 1b \n" \
+ " memw \n" \
+ "2: \n" \
+ : "=&a" (__value), "=&a" (__tmp) \
+ : "a" (mem), "a" (newval), "a" (oldval) \
+ : "memory" ); \
+ __value; \
+ })
+
+/* Atomically store NEWVAL in *MEM if *MEM is equal to OLDVAL.
+ Return zero if *MEM was changed or non-zero if no exchange happened. */
+
+#define __arch_compare_and_exchange_bool_32_acq(mem, newval, oldval) \
+ ({__typeof__(*(mem)) __tmp, __value; \
+ __asm__ __volatile__( \
+ " memw \n" \
+ "1: l32ex %0, %2 \n" \
+ " sub %0, %4, %0 \n" \
+ " bnez %0, 2f \n" \
+ " mov %1, %3 \n" \
+ " s32ex %1, %2 \n" \
+ " getex %1 \n" \
+ " beqz %1, 1b \n" \
+ " movi %0, 0 \n" \
+ " memw \n" \
+ "2: \n" \
+ : "=&a" (__value), "=&a" (__tmp) \
+ : "a" (mem), "a" (newval), "a" (oldval) \
+ : "memory" ); \
+ __value != 0; \
+ })
+
+/* Store NEWVALUE in *MEM and return the old value. */
+
+#define __arch_exchange_32_acq(mem, newval) \
+ ({__typeof__(*(mem)) __tmp, __value; \
+ __asm__ __volatile__( \
+ " memw \n" \
+ "1: l32ex %0, %2 \n" \
+ " mov %1, %3 \n" \
+ " s32ex %1, %2 \n" \
+ " getex %1 \n" \
+ " beqz %1, 1b \n" \
+ " memw \n" \
+ : "=&a" (__value), "=&a" (__tmp) \
+ : "a" (mem), "a" (newval) \
+ : "memory" ); \
+ __value; \
+ })
+
+/* Add VALUE to *MEM and return the old value of *MEM. */
+
+#define __arch_atomic_exchange_and_add_32(mem, value) \
+ ({__typeof__(*(mem)) __tmp, __value; \
+ __asm__ __volatile__( \
+ " memw \n" \
+ "1: l32ex %0, %2 \n" \
+ " add %1, %0, %3 \n" \
+ " s32ex %1, %2 \n" \
+ " getex %1 \n" \
+ " beqz %1, 1b \n" \
+ " memw \n" \
+ : "=&a" (__value), "=&a" (__tmp) \
+ : "a" (mem), "a" (value) \
+ : "memory" ); \
+ __value; \
+ })
+
+/* Subtract VALUE from *MEM and return the old value of *MEM. */
+
+#define __arch_atomic_exchange_and_sub_32(mem, value) \
+ ({__typeof__(*(mem)) __tmp, __value; \
+ __asm__ __volatile__( \
+ " memw \n" \
+ "1: l32ex %0, %2 \n" \
+ " sub %1, %0, %3 \n" \
+ " s32ex %1, %2 \n" \
+ " getex %1 \n" \
+ " beqz %1, 1b \n" \
+ " memw \n" \
+ : "=&a" (__value), "=&a" (__tmp) \
+ : "a" (mem), "a" (value) \
+ : "memory" ); \
+ __tmp; \
+ })
+
+/* Decrement *MEM if it is > 0, and return the old value. */
+
+#define __arch_atomic_decrement_if_positive_32(mem) \
+ ({__typeof__(*(mem)) __tmp, __value; \
+ __asm__ __volatile__( \
+ " memw \n" \
+ "1: l32ex %0, %2 \n" \
+ " blti %0, 1, 2f \n" \
+ " addi %1, %0, -1 \n" \
+ " s32ex %1, %2 \n" \
+ " getex %1 \n" \
+ " beqz %1, 1b \n" \
+ " memw \n" \
+ "2: \n" \
+ : "=&a" (__value), "=&a" (__tmp) \
+ : "a" (mem) \
+ : "memory" ); \
+ __value; \
+ })
+
+#elif XCHAL_HAVE_S32C1I
+
/* Atomically store NEWVAL in *MEM if *MEM is equal to OLDVAL.
Return the old *MEM value. */
@@ -156,6 +279,11 @@ typedef uintmax_t uatomic_max_t;
__value; \
})
+#else
+
+#error No hardware atomic operations
+
+#endif
/* These are the preferred public interfaces: */