mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-01-02 18:51:14 +00:00
The "RISC-V C API" [1] defines architecture extension test macros says naming rule for the test macros is __riscv_<ext_name>, where <ext_name> is all lower-case. Three extensions dealing with atomics implementation are: "zaamo" consists of AMO instructions, "zalrsc" - LR/SC, "a" extension means both "zaamo" and "zalrsc" Built-in test macros are __riscv_a, __riscv_zaamo and __riscv_zalrsc. Alternative to the __riscv_a macro name, __riscv_atomic, is deprecated. Use correct test macro __riscv_zaamo for the AMO variant of atomics. It used to be __riscv_atomic that is both deprecated and incorrect because it tests for the "a" extension; i.e. both "zaamo" and "zalrsc" If ISA enables only zaamo but not zalrsc, code as it was would not compile. Older toolchains may have neither __riscv_zaamo nor __riscv_zalrsc, so query __riscv_atomic - it should be treated as both __riscv_zaamo and __riscv_zalrsc, in all present cases __riscv_zaamo is more favorable so take is as alternative for __riscv_zaamo [1] https://github.com/riscv-non-isa/riscv-c-api-doc Signed-off-by: Vladimir Kondratiev <vladimir.kondratiev@mobileye.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20251228073321.1533844-1-vladimir.kondratiev@mobileye.com Signed-off-by: Anup Patel <anup@brainfault.org>
192 lines
4.7 KiB
C
192 lines
4.7 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
|
|
*
|
|
* Authors:
|
|
* Anup Patel <anup.patel@wdc.com>
|
|
*/
|
|
|
|
#include <sbi/sbi_bitops.h>
|
|
#include <sbi/riscv_asm.h>
|
|
#include <sbi/riscv_atomic.h>
|
|
#include <sbi/riscv_barrier.h>
|
|
|
|
#if !defined(__riscv_atomic) && !defined(__riscv_zaamo) && !defined(__riscv_zalrsc)
|
|
#error "opensbi strongly relies on the Zaamo or Zalrsc extensions of RISC-V"
|
|
#endif
|
|
|
|
long atomic_read(atomic_t *atom)
|
|
{
|
|
long ret = atom->counter;
|
|
rmb();
|
|
return ret;
|
|
}
|
|
|
|
void atomic_write(atomic_t *atom, long value)
|
|
{
|
|
atom->counter = value;
|
|
wmb();
|
|
}
|
|
|
|
long atomic_add_return(atomic_t *atom, long value)
|
|
{
|
|
#if defined(__riscv_atomic) || defined(__riscv_zaamo)
|
|
long ret;
|
|
#if __SIZEOF_LONG__ == 4
|
|
__asm__ __volatile__(" amoadd.w.aqrl %1, %2, %0"
|
|
: "+A"(atom->counter), "=r"(ret)
|
|
: "r"(value)
|
|
: "memory");
|
|
#elif __SIZEOF_LONG__ == 8
|
|
__asm__ __volatile__(" amoadd.d.aqrl %1, %2, %0"
|
|
: "+A"(atom->counter), "=r"(ret)
|
|
: "r"(value)
|
|
: "memory");
|
|
#endif
|
|
#elif defined(__riscv_zalrsc)
|
|
long ret, temp;
|
|
#if __SIZEOF_LONG__ == 4
|
|
__asm__ __volatile__("1:lr.w.aqrl %1,%0\n"
|
|
" addw %2,%1,%3\n"
|
|
" sc.w.aqrl %2,%2,%0\n"
|
|
" bnez %2,1b"
|
|
: "+A"(atom->counter), "=&r"(ret), "=&r"(temp)
|
|
: "r"(value)
|
|
: "memory");
|
|
#elif __SIZEOF_LONG__ == 8
|
|
__asm__ __volatile__("1:lr.d.aqrl %1,%0\n"
|
|
" add %2,%1,%3\n"
|
|
" sc.d.aqrl %2,%2,%0\n"
|
|
" bnez %2,1b"
|
|
: "+A"(atom->counter), "=&r"(ret), "=&r"(temp)
|
|
: "r"(value)
|
|
: "memory");
|
|
#endif
|
|
#else
|
|
#error "need A or Zaamo or Zalrsc"
|
|
#endif
|
|
|
|
return ret + value;
|
|
}
|
|
|
|
long atomic_sub_return(atomic_t *atom, long value)
|
|
{
|
|
return atomic_add_return(atom, -value);
|
|
}
|
|
|
|
#if defined(__riscv_atomic) || defined(__riscv_zaamo)
|
|
#define __axchg(ptr, new, size) \
|
|
({ \
|
|
__typeof__(ptr) __ptr = (ptr); \
|
|
__typeof__(new) __new = (new); \
|
|
__typeof__(*(ptr)) __ret; \
|
|
switch (size) { \
|
|
case 4: \
|
|
__asm__ __volatile__ ( \
|
|
" amoswap.w.aqrl %0, %2, %1\n" \
|
|
: "=r" (__ret), "+A" (*__ptr) \
|
|
: "r" (__new) \
|
|
: "memory"); \
|
|
break; \
|
|
case 8: \
|
|
__asm__ __volatile__ ( \
|
|
" amoswap.d.aqrl %0, %2, %1\n" \
|
|
: "=r" (__ret), "+A" (*__ptr) \
|
|
: "r" (__new) \
|
|
: "memory"); \
|
|
break; \
|
|
default: \
|
|
break; \
|
|
} \
|
|
__ret; \
|
|
})
|
|
#elif defined(__riscv_zalrsc)
|
|
#define __axchg(ptr, new, size) \
|
|
({ \
|
|
__typeof__(ptr) __ptr = (ptr); \
|
|
__typeof__(new) __new = (new); \
|
|
__typeof__(*(ptr)) __ret, __temp; \
|
|
switch (size) { \
|
|
case 4: \
|
|
__asm__ __volatile__ ( \
|
|
"1: lr.w.aqrl %0, %1\n" \
|
|
" sc.w.aqrl %2, %3, %1\n" \
|
|
" bnez %2, 1b\n" \
|
|
: "=&r" (__ret), "+A" (*__ptr), "=&r" (__temp) \
|
|
: "r" (__new) \
|
|
: "memory"); \
|
|
break; \
|
|
case 8: \
|
|
__asm__ __volatile__ ( \
|
|
"1: lr.d.aqrl %0, %1\n" \
|
|
" sc.d.aqrl %2, %3, %1\n" \
|
|
" bnez %2, 1b\n" \
|
|
: "=&r" (__ret), "+A" (*__ptr), "=&r" (__temp) \
|
|
: "r" (__new) \
|
|
: "memory"); \
|
|
break; \
|
|
default: \
|
|
break; \
|
|
} \
|
|
__ret; \
|
|
})
|
|
#else
|
|
#error "need A or Zaamo or Zalrsc"
|
|
#endif
|
|
|
|
#define axchg(ptr, x) \
|
|
({ \
|
|
__typeof__(*(ptr)) _x_ = (x); \
|
|
(__typeof__(*(ptr))) __axchg((ptr), _x_, sizeof(*(ptr))); \
|
|
})
|
|
|
|
long atomic_cmpxchg(atomic_t *atom, long oldval, long newval)
|
|
{
|
|
return __sync_val_compare_and_swap(&atom->counter, oldval, newval);
|
|
}
|
|
|
|
long atomic_xchg(atomic_t *atom, long newval)
|
|
{
|
|
/* Atomically set new value and return old value. */
|
|
return axchg(&atom->counter, newval);
|
|
}
|
|
|
|
unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr,
|
|
unsigned int newval)
|
|
{
|
|
/* Atomically set new value and return old value. */
|
|
return axchg(ptr, newval);
|
|
}
|
|
|
|
unsigned long atomic_raw_xchg_ulong(volatile unsigned long *ptr,
|
|
unsigned long newval)
|
|
{
|
|
/* Atomically set new value and return old value. */
|
|
return axchg(ptr, newval);
|
|
}
|
|
|
|
int atomic_raw_set_bit(int nr, volatile unsigned long *addr)
|
|
{
|
|
unsigned long res, mask = BIT_MASK(nr);
|
|
res = __atomic_fetch_or(&addr[BIT_WORD(nr)], mask, __ATOMIC_RELAXED);
|
|
return res & mask ? 1 : 0;
|
|
}
|
|
|
|
int atomic_raw_clear_bit(int nr, volatile unsigned long *addr)
|
|
{
|
|
unsigned long res, mask = BIT_MASK(nr);
|
|
res = __atomic_fetch_and(&addr[BIT_WORD(nr)], ~mask, __ATOMIC_RELAXED);
|
|
return res & mask ? 1 : 0;
|
|
}
|
|
|
|
int atomic_set_bit(int nr, atomic_t *atom)
|
|
{
|
|
return atomic_raw_set_bit(nr, (unsigned long *)&atom->counter);
|
|
}
|
|
|
|
int atomic_clear_bit(int nr, atomic_t *atom)
|
|
{
|
|
return atomic_raw_clear_bit(nr, (unsigned long *)&atom->counter);
|
|
}
|