summaryrefslogtreecommitdiff
path: root/libc/sysdeps/linux/xtensa/bits
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2020-08-08 23:28:17 -0700
committerWaldemar Brodkorb <wbx@openadk.org>2020-08-11 13:35:02 +0200
commitfc48f4fb0506b2ea6ef3bb33037be3a4da2874bc (patch)
treeaa83e91570361d19ca569ea68d279b98b69ec3b5 /libc/sysdeps/linux/xtensa/bits
parentc2e5177b97825211565150b4f9a7f253e0458619 (diff)
xtensa: add exclusive access support
Add XCHAL definitions for S32C1I and EXCLUSIVE options to xtensa-config.h, include it in places that implement atomic operations and add implementations with exclusive access option opcodes. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'libc/sysdeps/linux/xtensa/bits')
-rw-r--r--libc/sysdeps/linux/xtensa/bits/atomic.h128
-rw-r--r--libc/sysdeps/linux/xtensa/bits/xtensa-config.h6
2 files changed, 134 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: */
diff --git a/libc/sysdeps/linux/xtensa/bits/xtensa-config.h b/libc/sysdeps/linux/xtensa/bits/xtensa-config.h
index 2e60af936..b99928b1e 100644
--- a/libc/sysdeps/linux/xtensa/bits/xtensa-config.h
+++ b/libc/sysdeps/linux/xtensa/bits/xtensa-config.h
@@ -43,4 +43,10 @@
#undef XCHAL_NUM_AREGS
#define XCHAL_NUM_AREGS 64
+#undef XCHAL_HAVE_S32C1I
+#define XCHAL_HAVE_S32C1I 1
+
+#undef XCHAL_HAVE_EXCLUSIVE
+#define XCHAL_HAVE_EXCLUSIVE 0
+
#endif /* !XTENSA_CONFIG_H */