162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifndef _ASM_ARC_ATOMIC_SPLOCK_H
462306a36Sopenharmony_ci#define _ASM_ARC_ATOMIC_SPLOCK_H
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/*
762306a36Sopenharmony_ci * Non hardware assisted Atomic-R-M-W
862306a36Sopenharmony_ci * Locking would change to irq-disabling only (UP) and spinlocks (SMP)
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic inline void arch_atomic_set(atomic_t *v, int i)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	/*
1462306a36Sopenharmony_ci	 * Independent of hardware support, all of the atomic_xxx() APIs need
1562306a36Sopenharmony_ci	 * to follow the same locking rules to make sure that a "hardware"
1662306a36Sopenharmony_ci	 * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn
1762306a36Sopenharmony_ci	 * sequence
1862306a36Sopenharmony_ci	 *
1962306a36Sopenharmony_ci	 * Thus atomic_set() despite being 1 insn (and seemingly atomic)
2062306a36Sopenharmony_ci	 * requires the locking.
2162306a36Sopenharmony_ci	 */
2262306a36Sopenharmony_ci	unsigned long flags;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	atomic_ops_lock(flags);
2562306a36Sopenharmony_ci	WRITE_ONCE(v->counter, i);
2662306a36Sopenharmony_ci	atomic_ops_unlock(flags);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define arch_atomic_set_release(v, i)	arch_atomic_set((v), (i))
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define ATOMIC_OP(op, c_op, asm_op)					\
3262306a36Sopenharmony_cistatic inline void arch_atomic_##op(int i, atomic_t *v)			\
3362306a36Sopenharmony_ci{									\
3462306a36Sopenharmony_ci	unsigned long flags;						\
3562306a36Sopenharmony_ci									\
3662306a36Sopenharmony_ci	atomic_ops_lock(flags);						\
3762306a36Sopenharmony_ci	v->counter c_op i;						\
3862306a36Sopenharmony_ci	atomic_ops_unlock(flags);					\
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define ATOMIC_OP_RETURN(op, c_op, asm_op)				\
4262306a36Sopenharmony_cistatic inline int arch_atomic_##op##_return(int i, atomic_t *v)		\
4362306a36Sopenharmony_ci{									\
4462306a36Sopenharmony_ci	unsigned long flags;						\
4562306a36Sopenharmony_ci	unsigned int temp;						\
4662306a36Sopenharmony_ci									\
4762306a36Sopenharmony_ci	/*								\
4862306a36Sopenharmony_ci	 * spin lock/unlock provides the needed smp_mb() before/after	\
4962306a36Sopenharmony_ci	 */								\
5062306a36Sopenharmony_ci	atomic_ops_lock(flags);						\
5162306a36Sopenharmony_ci	temp = v->counter;						\
5262306a36Sopenharmony_ci	temp c_op i;							\
5362306a36Sopenharmony_ci	v->counter = temp;						\
5462306a36Sopenharmony_ci	atomic_ops_unlock(flags);					\
5562306a36Sopenharmony_ci									\
5662306a36Sopenharmony_ci	return temp;							\
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define ATOMIC_FETCH_OP(op, c_op, asm_op)				\
6062306a36Sopenharmony_cistatic inline int arch_atomic_fetch_##op(int i, atomic_t *v)		\
6162306a36Sopenharmony_ci{									\
6262306a36Sopenharmony_ci	unsigned long flags;						\
6362306a36Sopenharmony_ci	unsigned int orig;						\
6462306a36Sopenharmony_ci									\
6562306a36Sopenharmony_ci	/*								\
6662306a36Sopenharmony_ci	 * spin lock/unlock provides the needed smp_mb() before/after	\
6762306a36Sopenharmony_ci	 */								\
6862306a36Sopenharmony_ci	atomic_ops_lock(flags);						\
6962306a36Sopenharmony_ci	orig = v->counter;						\
7062306a36Sopenharmony_ci	v->counter c_op i;						\
7162306a36Sopenharmony_ci	atomic_ops_unlock(flags);					\
7262306a36Sopenharmony_ci									\
7362306a36Sopenharmony_ci	return orig;							\
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define ATOMIC_OPS(op, c_op, asm_op)					\
7762306a36Sopenharmony_ci	ATOMIC_OP(op, c_op, asm_op)					\
7862306a36Sopenharmony_ci	ATOMIC_OP_RETURN(op, c_op, asm_op)				\
7962306a36Sopenharmony_ci	ATOMIC_FETCH_OP(op, c_op, asm_op)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciATOMIC_OPS(add, +=, add)
8262306a36Sopenharmony_ciATOMIC_OPS(sub, -=, sub)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define arch_atomic_fetch_add		arch_atomic_fetch_add
8562306a36Sopenharmony_ci#define arch_atomic_fetch_sub		arch_atomic_fetch_sub
8662306a36Sopenharmony_ci#define arch_atomic_add_return		arch_atomic_add_return
8762306a36Sopenharmony_ci#define arch_atomic_sub_return		arch_atomic_sub_return
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#undef ATOMIC_OPS
9062306a36Sopenharmony_ci#define ATOMIC_OPS(op, c_op, asm_op)					\
9162306a36Sopenharmony_ci	ATOMIC_OP(op, c_op, asm_op)					\
9262306a36Sopenharmony_ci	ATOMIC_FETCH_OP(op, c_op, asm_op)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciATOMIC_OPS(and, &=, and)
9562306a36Sopenharmony_ciATOMIC_OPS(andnot, &= ~, bic)
9662306a36Sopenharmony_ciATOMIC_OPS(or, |=, or)
9762306a36Sopenharmony_ciATOMIC_OPS(xor, ^=, xor)
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define arch_atomic_andnot		arch_atomic_andnot
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define arch_atomic_fetch_and		arch_atomic_fetch_and
10262306a36Sopenharmony_ci#define arch_atomic_fetch_andnot	arch_atomic_fetch_andnot
10362306a36Sopenharmony_ci#define arch_atomic_fetch_or		arch_atomic_fetch_or
10462306a36Sopenharmony_ci#define arch_atomic_fetch_xor		arch_atomic_fetch_xor
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#undef ATOMIC_OPS
10762306a36Sopenharmony_ci#undef ATOMIC_FETCH_OP
10862306a36Sopenharmony_ci#undef ATOMIC_OP_RETURN
10962306a36Sopenharmony_ci#undef ATOMIC_OP
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#endif
112