root/arch/arc/include/asm/atomic-spinlock.h
/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef _ASM_ARC_ATOMIC_SPLOCK_H
#define _ASM_ARC_ATOMIC_SPLOCK_H

/*
 * Non hardware assisted Atomic-R-M-W
 * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
 */

static inline void arch_atomic_set(atomic_t *v, int i)
{
        /*
         * Independent of hardware support, all of the atomic_xxx() APIs need
         * to follow the same locking rules to make sure that a "hardware"
         * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
         * sequence
         *
         * Thus atomic_set() despite being 1 insn (and seemingly atomic)
         * requires the locking.
         */
        unsigned long flags;

        atomic_ops_lock(flags);
        WRITE_ONCE(v->counter, i);
        atomic_ops_unlock(flags);
}

#define arch_atomic_set_release(v, i)   arch_atomic_set((v), (i))

#define ATOMIC_OP(op, c_op, asm_op)                                     \
static inline void arch_atomic_##op(int i, atomic_t *v)                 \
{                                                                       \
        unsigned long flags;                                            \
                                                                        \
        atomic_ops_lock(flags);                                         \
        v->counter c_op i;                                              \
        atomic_ops_unlock(flags);                                       \
}

#define ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
static inline int arch_atomic_##op##_return(int i, atomic_t *v)         \
{                                                                       \
        unsigned long flags;                                            \
        unsigned int temp;                                              \
                                                                        \
        /*                                                              \
         * spin lock/unlock provides the needed smp_mb() before/after   \
         */                                                             \
        atomic_ops_lock(flags);                                         \
        temp = v->counter;                                              \
        temp c_op i;                                                    \
        v->counter = temp;                                              \
        atomic_ops_unlock(flags);                                       \
                                                                        \
        return temp;                                                    \
}

#define ATOMIC_FETCH_OP(op, c_op, asm_op)                               \
static inline int arch_atomic_fetch_##op(int i, atomic_t *v)            \
{                                                                       \
        unsigned long flags;                                            \
        unsigned int orig;                                              \
                                                                        \
        /*                                                              \
         * spin lock/unlock provides the needed smp_mb() before/after   \
         */                                                             \
        atomic_ops_lock(flags);                                         \
        orig = v->counter;                                              \
        v->counter c_op i;                                              \
        atomic_ops_unlock(flags);                                       \
                                                                        \
        return orig;                                                    \
}

#define ATOMIC_OPS(op, c_op, asm_op)                                    \
        ATOMIC_OP(op, c_op, asm_op)                                     \
        ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
        ATOMIC_FETCH_OP(op, c_op, asm_op)

ATOMIC_OPS(add, +=, add)
ATOMIC_OPS(sub, -=, sub)

#define arch_atomic_fetch_add           arch_atomic_fetch_add
#define arch_atomic_fetch_sub           arch_atomic_fetch_sub
#define arch_atomic_add_return          arch_atomic_add_return
#define arch_atomic_sub_return          arch_atomic_sub_return

#undef ATOMIC_OPS
#define ATOMIC_OPS(op, c_op, asm_op)                                    \
        ATOMIC_OP(op, c_op, asm_op)                                     \
        ATOMIC_FETCH_OP(op, c_op, asm_op)

ATOMIC_OPS(and, &=, and)
ATOMIC_OPS(andnot, &= ~, bic)
ATOMIC_OPS(or, |=, or)
ATOMIC_OPS(xor, ^=, xor)

#define arch_atomic_andnot              arch_atomic_andnot

#define arch_atomic_fetch_and           arch_atomic_fetch_and
#define arch_atomic_fetch_andnot        arch_atomic_fetch_andnot
#define arch_atomic_fetch_or            arch_atomic_fetch_or
#define arch_atomic_fetch_xor           arch_atomic_fetch_xor

#undef ATOMIC_OPS
#undef ATOMIC_FETCH_OP
#undef ATOMIC_OP_RETURN
#undef ATOMIC_OP

#endif