summaryrefslogtreecommitdiff
path: root/arch/riscv/include/asm/atomic.h
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-11-15 10:49:15 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-11-15 10:49:15 -0800
commitb293fca43be544483b6488d33ad4b3ed55881064 (patch)
treebf9f51967cd3a9fae3a8c1254b715b9c31aa56a6 /arch/riscv/include/asm/atomic.h
parent0ef76878cfcf4d6b64972b283021f576a95d9216 (diff)
parentfbe934d69eb7ed22b59514e9c1fe8871b8b198ec (diff)
Merge tag 'riscv-for-linus-4.15-arch-v9-premerge' of git://git.kernel.org/pub/scm/linux/kernel/git/palmer/linux
Pull RISC-V architecture support from Palmer Dabbelt: "This contains the core RISC-V Linux port, which has been through nine rounds of review on various mailing lists. The port is not complete: there's some cleanup patches moving through the review process, a whole bunch of drivers that need some work, and a lot of feature additions that will be needed. The patches contained in this tag have been through nine rounds of review on the various mailing lists. I have some outstanding cleanup patches, but since there's been so much review on these patches I thought it would be best to submit them as-is and then submit explicit cleanup patches so everyone can review them. This first patch set is big enough that it's a bit of a pain to constantly rewrite, and it's caused a few headaches with various contributors. The port is definately a work in progress. While what's there builds and boots with 4.14, it's a bit hard to actually see anything happen because there are no device drivers yet. I maintain a staging branch that contains all the device drivers and cleanup that actually works, but those patches won't all be ready for a while. I'd like to get what we currently have into your tree so everyone can start working from a single base -- of particular importance is allowing the glibc upstreaming process to proceed so we can sort out any possibly lingering user-visible ABI problems we might have. Copied below is the ChangeLog that contains the history of this patch set: (v9) As per suggestions on our v8 patch set, I've split the core architecture code out from our drivers and would like to submit this patch set to be included into linux-next, with the goal being to be merged in during the next merge window. This patch set is based on 4.14-rc2, but if it's better to have it based on something else then I can change it around. This patch set contains just the core arch code for RISC-V, so while it builds an nominally boots, you can't print or take an interrupt so it's not that useful. If you're looking to actually boot a system it would probably be better to use the full patch set listed below. We've collected a handful of tags from reviewers, and the remainder of the patch set only got minimal feedback last time. Here's what changed: - We now use the device tree to initialize the timer driver so it's less tighly coupled with the arch port. - I cleaned up the defconfigs -- there's actually now just one, and it's empty. For now I think we're OK with what the kernel sets as defaults, but I anticipate we'll begin to expand this as people start to use the port more. - The VDSO symbols version is sane. - We WFI while spinning in the boot loop. - A handful of comments have been added. While there are still a handful of FIXMEs in this patch set, we've started to get enough interest from various users and contributors that maintaining an out of tree patch set is starting to become a big burden. Hopefully the patches are good enough to merge now, which will at least get everyone working in a more reasonable manner as we clean up the remaining issues. (v8) I know it may not be the ideal time to submit a patch set right now, as it's the middle of the merge window, but things have calmed down quite a bit in the last month so I thought it would be good to get everyone on the same page. There's been a handful of changes since the last patch set, but most of them are fairly minor: - We changed PAGE_OFFSET to allowing mapping more physical memory on 64-bit systems. This is user configurable, as it triggers a different code model that generates slightly less efficient code. - The device tree binding documentation is back, I'd managed to lose it at some point. - We now pass the atomic64 test suite - The SBI timer driver has been refactored. (v7) It's been a while since my last patch set, but the changes han been fairly minimal: - The PCI cleanup patches have been dropped, we'll do them as a separate patch set later. - We've the Kconfig entries from CONFIG_ISA_* to CONFIG_RISCV_ISA_*, to make grep easier. - There have been a handful of memory model related tweaks in I/O land, particularly relating the PCI and the upcoming platform specification. There are significant comments in the relevant files. This is still a WIP, but I think we're close to getting as good as we're going to get until we end up with some more specifications. (v6) As it's been only a day since the v5 patch set, the changes are pretty minimal: - The patch set is now based on linux-next/master, which I believe is a better base now that we're getting closer to upstream. - EARLY_PRINTK is no longer an option. Since the SBI console is reasonable, there's no penalty to enabling it (and thus no benefit to disabling it). - The mmap syscalls were refactored a bit. (v5) Things have really started to calm down, so this is fairly similar to the v4 patch set. The most interesting changes include: - We've moved back to a single patch set. - SMP support has been fixed, I was accidentally running on a non-SMP configuration. There were various mistakes all over the tree as a result of this. - The cmpxchg syscalls have been removed, as they were deemed a bad idea. As a result, RISC-V Linux systems mandate the A extension. The corresponding Kconfig entry to enable builds on non-A systems has been removed. - A few more atomic fixes: mostly fence changes, but those resulted in a handful of additional macros that were no longer necessary. - riscv_early_sie has been removed. (v4) There have only been a few changes since the v3 patch set: - The cmpxchg64 syscall is no longer enabled on 32-bit systems. It's not possible to provide this on SMP systems, and it's not necessary as glibc knows not to call it. - We provide a ELF_HWCAP so users can determine the ISA of the machine the kernel is running on. - The multi-line comments are in a better form. - There were a handful of headers that could be replaced with the asm-generic versions, and a few unnecessary definitions. - We no longer use printk, but instead use pr_*. - A few Kconfig and defconfig entries have been cleaned up. (v3) A highlight of the changes since the v2 patch set includes: - We've split out all our drivers into separate patch sets, which I've already sent out to the relevant maintainers. I haven't included those patches in this patch set, but some of them are necessary to build our port. - The patch set is now split up differently: rather than being split per directory it is split per topic. Hopefully this will make it easier to review the port on the mailing list. The split is a bit rough, so you probably still want to look at the patch set as a whole. - atomic.h has been completely rewritten and is hopefully now correct. I've attempted to sanitize the various other memory model related code as well, and I think it should all be sane now aside from a handful of FIXMEs commented in the code. - We've changed the cmpexchg syscall to always exist and to not be multiplexed. There is also a VDSO entry for compare and exchange, which allows kernels with the A extension to execute user code without the A extension reasonably fast. - Our user-visible register state now contains enough space for the Q extension for 128-bit floating point, as well as a few words to allow extensibility to future ISA extensions like the eventual V extension for vectors. - A handful of driver cleanups, but these have been split into separate patch sets now so I won't duplicate them here. (v2) A highlight of the changes since the v1 patch set includes: - We've split out our drivers into the right places, which means now there's a lot more patches. I'll be submitting these patches to various subsystem maintainers and including them in any future RISC-V patch sets until they've been merged. - The SBI console driver has been completely rewritten to use the HVC helpers and is now significantly smaller. - We've begun to use weaker barriers as opposed to just the big "fence". There's still some work to do here, specifically: - We need fences in the relaxed MMIO functions. - The non-relaxed MMIO functions are missing R/W bits on their fences. - Many AMOs need the aq and rl bits set. - We now have thread_info in task_struct. As a result, sscratch now contains TP instead of SP. This was necessary because thread_info is no longer on the stack. - A few shared routines have been added that we use instead of creating another arch copy" Reviewed-by: Arnd Bergmann <arnd@arndb.de> * tag 'riscv-for-linus-4.15-arch-v9-premerge' of git://git.kernel.org/pub/scm/linux/kernel/git/palmer/linux: RISC-V: Build Infrastructure RISC-V: User-facing API RISC-V: Paging and MMU RISC-V: Device, timer, IRQs, and the SBI RISC-V: Task implementation RISC-V: ELF and module implementation RISC-V: Generic library routines and assembly RISC-V: Atomic and Locking Code RISC-V: Init and Halt Code dt-bindings: RISC-V CPU Bindings lib: Add shared copies of some GCC library routines MAINTAINERS: Add RISC-V
Diffstat (limited to 'arch/riscv/include/asm/atomic.h')
-rw-r--r--arch/riscv/include/asm/atomic.h375
1 files changed, 375 insertions, 0 deletions
diff --git a/arch/riscv/include/asm/atomic.h b/arch/riscv/include/asm/atomic.h
new file mode 100644
index 000000000000..e2e37c57cbeb
--- /dev/null
+++ b/arch/riscv/include/asm/atomic.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _ASM_RISCV_ATOMIC_H
+#define _ASM_RISCV_ATOMIC_H
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+# include <asm-generic/atomic64.h>
+#else
+# if (__riscv_xlen < 64)
+# error "64-bit atomics require XLEN to be at least 64"
+# endif
+#endif
+
+#include <asm/cmpxchg.h>
+#include <asm/barrier.h>
+
+#define ATOMIC_INIT(i) { (i) }
+static __always_inline int atomic_read(const atomic_t *v)
+{
+ return READ_ONCE(v->counter);
+}
+static __always_inline void atomic_set(atomic_t *v, int i)
+{
+ WRITE_ONCE(v->counter, i);
+}
+
+#ifndef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC64_INIT(i) { (i) }
+static __always_inline long atomic64_read(const atomic64_t *v)
+{
+ return READ_ONCE(v->counter);
+}
+static __always_inline void atomic64_set(atomic64_t *v, long i)
+{
+ WRITE_ONCE(v->counter, i);
+}
+#endif
+
+/*
+ * First, the atomic ops that have no ordering constraints and therefor don't
+ * have the AQ or RL bits set. These don't return anything, so there's only
+ * one version to worry about.
+ */
+#define ATOMIC_OP(op, asm_op, c_op, I, asm_type, c_type, prefix) \
+static __always_inline void atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \
+{ \
+ __asm__ __volatile__ ( \
+ "amo" #asm_op "." #asm_type " zero, %1, %0" \
+ : "+A" (v->counter) \
+ : "r" (I) \
+ : "memory"); \
+}
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC_OPS(op, asm_op, c_op, I) \
+ ATOMIC_OP (op, asm_op, c_op, I, w, int, )
+#else
+#define ATOMIC_OPS(op, asm_op, c_op, I) \
+ ATOMIC_OP (op, asm_op, c_op, I, w, int, ) \
+ ATOMIC_OP (op, asm_op, c_op, I, d, long, 64)
+#endif
+
+ATOMIC_OPS(add, add, +, i)
+ATOMIC_OPS(sub, add, +, -i)
+ATOMIC_OPS(and, and, &, i)
+ATOMIC_OPS( or, or, |, i)
+ATOMIC_OPS(xor, xor, ^, i)
+
+#undef ATOMIC_OP
+#undef ATOMIC_OPS
+
+/*
+ * Atomic ops that have ordered, relaxed, acquire, and relese variants.
+ * There's two flavors of these: the arithmatic ops have both fetch and return
+ * versions, while the logical ops only have fetch versions.
+ */
+#define ATOMIC_FETCH_OP(op, asm_op, c_op, I, asm_or, c_or, asm_type, c_type, prefix) \
+static __always_inline c_type atomic##prefix##_fetch_##op##c_or(c_type i, atomic##prefix##_t *v) \
+{ \
+ register c_type ret; \
+ __asm__ __volatile__ ( \
+ "amo" #asm_op "." #asm_type #asm_or " %1, %2, %0" \
+ : "+A" (v->counter), "=r" (ret) \
+ : "r" (I) \
+ : "memory"); \
+ return ret; \
+}
+
+#define ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_or, c_or, asm_type, c_type, prefix) \
+static __always_inline c_type atomic##prefix##_##op##_return##c_or(c_type i, atomic##prefix##_t *v) \
+{ \
+ return atomic##prefix##_fetch_##op##c_or(i, v) c_op I; \
+}
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC_OPS(op, asm_op, c_op, I, asm_or, c_or) \
+ ATOMIC_FETCH_OP (op, asm_op, c_op, I, asm_or, c_or, w, int, ) \
+ ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_or, c_or, w, int, )
+#else
+#define ATOMIC_OPS(op, asm_op, c_op, I, asm_or, c_or) \
+ ATOMIC_FETCH_OP (op, asm_op, c_op, I, asm_or, c_or, w, int, ) \
+ ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_or, c_or, w, int, ) \
+ ATOMIC_FETCH_OP (op, asm_op, c_op, I, asm_or, c_or, d, long, 64) \
+ ATOMIC_OP_RETURN(op, asm_op, c_op, I, asm_or, c_or, d, long, 64)
+#endif
+
+ATOMIC_OPS(add, add, +, i, , _relaxed)
+ATOMIC_OPS(add, add, +, i, .aq , _acquire)
+ATOMIC_OPS(add, add, +, i, .rl , _release)
+ATOMIC_OPS(add, add, +, i, .aqrl, )
+
+ATOMIC_OPS(sub, add, +, -i, , _relaxed)
+ATOMIC_OPS(sub, add, +, -i, .aq , _acquire)
+ATOMIC_OPS(sub, add, +, -i, .rl , _release)
+ATOMIC_OPS(sub, add, +, -i, .aqrl, )
+
+#undef ATOMIC_OPS
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC_OPS(op, asm_op, c_op, I, asm_or, c_or) \
+ ATOMIC_FETCH_OP(op, asm_op, c_op, I, asm_or, c_or, w, int, )
+#else
+#define ATOMIC_OPS(op, asm_op, c_op, I, asm_or, c_or) \
+ ATOMIC_FETCH_OP(op, asm_op, c_op, I, asm_or, c_or, w, int, ) \
+ ATOMIC_FETCH_OP(op, asm_op, c_op, I, asm_or, c_or, d, long, 64)
+#endif
+
+ATOMIC_OPS(and, and, &, i, , _relaxed)
+ATOMIC_OPS(and, and, &, i, .aq , _acquire)
+ATOMIC_OPS(and, and, &, i, .rl , _release)
+ATOMIC_OPS(and, and, &, i, .aqrl, )
+
+ATOMIC_OPS( or, or, |, i, , _relaxed)
+ATOMIC_OPS( or, or, |, i, .aq , _acquire)
+ATOMIC_OPS( or, or, |, i, .rl , _release)
+ATOMIC_OPS( or, or, |, i, .aqrl, )
+
+ATOMIC_OPS(xor, xor, ^, i, , _relaxed)
+ATOMIC_OPS(xor, xor, ^, i, .aq , _acquire)
+ATOMIC_OPS(xor, xor, ^, i, .rl , _release)
+ATOMIC_OPS(xor, xor, ^, i, .aqrl, )
+
+#undef ATOMIC_OPS
+
+#undef ATOMIC_FETCH_OP
+#undef ATOMIC_OP_RETURN
+
+/*
+ * The extra atomic operations that are constructed from one of the core
+ * AMO-based operations above (aside from sub, which is easier to fit above).
+ * These are required to perform a barrier, but they're OK this way because
+ * atomic_*_return is also required to perform a barrier.
+ */
+#define ATOMIC_OP(op, func_op, comp_op, I, c_type, prefix) \
+static __always_inline bool atomic##prefix##_##op(c_type i, atomic##prefix##_t *v) \
+{ \
+ return atomic##prefix##_##func_op##_return(i, v) comp_op I; \
+}
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC_OPS(op, func_op, comp_op, I) \
+ ATOMIC_OP (op, func_op, comp_op, I, int, )
+#else
+#define ATOMIC_OPS(op, func_op, comp_op, I) \
+ ATOMIC_OP (op, func_op, comp_op, I, int, ) \
+ ATOMIC_OP (op, func_op, comp_op, I, long, 64)
+#endif
+
+ATOMIC_OPS(add_and_test, add, ==, 0)
+ATOMIC_OPS(sub_and_test, sub, ==, 0)
+ATOMIC_OPS(add_negative, add, <, 0)
+
+#undef ATOMIC_OP
+#undef ATOMIC_OPS
+
+#define ATOMIC_OP(op, func_op, c_op, I, c_type, prefix) \
+static __always_inline void atomic##prefix##_##op(atomic##prefix##_t *v) \
+{ \
+ atomic##prefix##_##func_op(I, v); \
+}
+
+#define ATOMIC_FETCH_OP(op, func_op, c_op, I, c_type, prefix) \
+static __always_inline c_type atomic##prefix##_fetch_##op(atomic##prefix##_t *v) \
+{ \
+ return atomic##prefix##_fetch_##func_op(I, v); \
+}
+
+#define ATOMIC_OP_RETURN(op, asm_op, c_op, I, c_type, prefix) \
+static __always_inline c_type atomic##prefix##_##op##_return(atomic##prefix##_t *v) \
+{ \
+ return atomic##prefix##_fetch_##op(v) c_op I; \
+}
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC_OPS(op, asm_op, c_op, I) \
+ ATOMIC_OP (op, asm_op, c_op, I, int, ) \
+ ATOMIC_FETCH_OP (op, asm_op, c_op, I, int, ) \
+ ATOMIC_OP_RETURN(op, asm_op, c_op, I, int, )
+#else
+#define ATOMIC_OPS(op, asm_op, c_op, I) \
+ ATOMIC_OP (op, asm_op, c_op, I, int, ) \
+ ATOMIC_FETCH_OP (op, asm_op, c_op, I, int, ) \
+ ATOMIC_OP_RETURN(op, asm_op, c_op, I, int, ) \
+ ATOMIC_OP (op, asm_op, c_op, I, long, 64) \
+ ATOMIC_FETCH_OP (op, asm_op, c_op, I, long, 64) \
+ ATOMIC_OP_RETURN(op, asm_op, c_op, I, long, 64)
+#endif
+
+ATOMIC_OPS(inc, add, +, 1)
+ATOMIC_OPS(dec, add, +, -1)
+
+#undef ATOMIC_OPS
+#undef ATOMIC_OP
+#undef ATOMIC_FETCH_OP
+#undef ATOMIC_OP_RETURN
+
+#define ATOMIC_OP(op, func_op, comp_op, I, prefix) \
+static __always_inline bool atomic##prefix##_##op(atomic##prefix##_t *v) \
+{ \
+ return atomic##prefix##_##func_op##_return(v) comp_op I; \
+}
+
+ATOMIC_OP(inc_and_test, inc, ==, 0, )
+ATOMIC_OP(dec_and_test, dec, ==, 0, )
+#ifndef CONFIG_GENERIC_ATOMIC64
+ATOMIC_OP(inc_and_test, inc, ==, 0, 64)
+ATOMIC_OP(dec_and_test, dec, ==, 0, 64)
+#endif
+
+#undef ATOMIC_OP
+
+/* This is required to provide a barrier on success. */
+static __always_inline int __atomic_add_unless(atomic_t *v, int a, int u)
+{
+ int prev, rc;
+
+ __asm__ __volatile__ (
+ "0:\n\t"
+ "lr.w.aqrl %[p], %[c]\n\t"
+ "beq %[p], %[u], 1f\n\t"
+ "add %[rc], %[p], %[a]\n\t"
+ "sc.w.aqrl %[rc], %[rc], %[c]\n\t"
+ "bnez %[rc], 0b\n\t"
+ "1:"
+ : [p]"=&r" (prev), [rc]"=&r" (rc), [c]"+A" (v->counter)
+ : [a]"r" (a), [u]"r" (u)
+ : "memory");
+ return prev;
+}
+
+#ifndef CONFIG_GENERIC_ATOMIC64
+static __always_inline long __atomic64_add_unless(atomic64_t *v, long a, long u)
+{
+ long prev, rc;
+
+ __asm__ __volatile__ (
+ "0:\n\t"
+ "lr.d.aqrl %[p], %[c]\n\t"
+ "beq %[p], %[u], 1f\n\t"
+ "add %[rc], %[p], %[a]\n\t"
+ "sc.d.aqrl %[rc], %[rc], %[c]\n\t"
+ "bnez %[rc], 0b\n\t"
+ "1:"
+ : [p]"=&r" (prev), [rc]"=&r" (rc), [c]"+A" (v->counter)
+ : [a]"r" (a), [u]"r" (u)
+ : "memory");
+ return prev;
+}
+
+static __always_inline int atomic64_add_unless(atomic64_t *v, long a, long u)
+{
+ return __atomic64_add_unless(v, a, u) != u;
+}
+#endif
+
+/*
+ * The extra atomic operations that are constructed from one of the core
+ * LR/SC-based operations above.
+ */
+static __always_inline int atomic_inc_not_zero(atomic_t *v)
+{
+ return __atomic_add_unless(v, 1, 0);
+}
+
+#ifndef CONFIG_GENERIC_ATOMIC64
+static __always_inline long atomic64_inc_not_zero(atomic64_t *v)
+{
+ return atomic64_add_unless(v, 1, 0);
+}
+#endif
+
+/*
+ * atomic_{cmp,}xchg is required to have exactly the same ordering semantics as
+ * {cmp,}xchg and the operations that return, so they need a barrier. We just
+ * use the other implementations directly.
+ */
+#define ATOMIC_OP(c_t, prefix, c_or, size, asm_or) \
+static __always_inline c_t atomic##prefix##_cmpxchg##c_or(atomic##prefix##_t *v, c_t o, c_t n) \
+{ \
+ return __cmpxchg(&(v->counter), o, n, size, asm_or, asm_or); \
+} \
+static __always_inline c_t atomic##prefix##_xchg##c_or(atomic##prefix##_t *v, c_t n) \
+{ \
+ return __xchg(n, &(v->counter), size, asm_or); \
+}
+
+#ifdef CONFIG_GENERIC_ATOMIC64
+#define ATOMIC_OPS(c_or, asm_or) \
+ ATOMIC_OP( int, , c_or, 4, asm_or)
+#else
+#define ATOMIC_OPS(c_or, asm_or) \
+ ATOMIC_OP( int, , c_or, 4, asm_or) \
+ ATOMIC_OP(long, 64, c_or, 8, asm_or)
+#endif
+
+ATOMIC_OPS( , .aqrl)
+ATOMIC_OPS(_acquire, .aq)
+ATOMIC_OPS(_release, .rl)
+ATOMIC_OPS(_relaxed, )
+
+#undef ATOMIC_OPS
+#undef ATOMIC_OP
+
+static __always_inline int atomic_sub_if_positive(atomic_t *v, int offset)
+{
+ int prev, rc;
+
+ __asm__ __volatile__ (
+ "0:\n\t"
+ "lr.w.aqrl %[p], %[c]\n\t"
+ "sub %[rc], %[p], %[o]\n\t"
+ "bltz %[rc], 1f\n\t"
+ "sc.w.aqrl %[rc], %[rc], %[c]\n\t"
+ "bnez %[rc], 0b\n\t"
+ "1:"
+ : [p]"=&r" (prev), [rc]"=&r" (rc), [c]"+A" (v->counter)
+ : [o]"r" (offset)
+ : "memory");
+ return prev - offset;
+}
+
+#define atomic_dec_if_positive(v) atomic_sub_if_positive(v, 1)
+
+#ifndef CONFIG_GENERIC_ATOMIC64
+static __always_inline long atomic64_sub_if_positive(atomic64_t *v, int offset)
+{
+ long prev, rc;
+
+ __asm__ __volatile__ (
+ "0:\n\t"
+ "lr.d.aqrl %[p], %[c]\n\t"
+ "sub %[rc], %[p], %[o]\n\t"
+ "bltz %[rc], 1f\n\t"
+ "sc.d.aqrl %[rc], %[rc], %[c]\n\t"
+ "bnez %[rc], 0b\n\t"
+ "1:"
+ : [p]"=&r" (prev), [rc]"=&r" (rc), [c]"+A" (v->counter)
+ : [o]"r" (offset)
+ : "memory");
+ return prev - offset;
+}
+
+#define atomic64_dec_if_positive(v) atomic64_sub_if_positive(v, 1)
+#endif
+
+#endif /* _ASM_RISCV_ATOMIC_H */