18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * atomic32.c: 32-bit atomic_t implementation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Keith M Wesolowski
68c2ecf20Sopenharmony_ci * Copyright (C) 2007 Kyle McMartin
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Based on asm-parisc/atomic.h Copyright (C) 2000 Philipp Rumpf
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/atomic.h>
128c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
168c2ecf20Sopenharmony_ci#define ATOMIC_HASH_SIZE	4
178c2ecf20Sopenharmony_ci#define ATOMIC_HASH(a)	(&__atomic_hash[(((unsigned long)a)>>8) & (ATOMIC_HASH_SIZE-1)])
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cispinlock_t __atomic_hash[ATOMIC_HASH_SIZE] = {
208c2ecf20Sopenharmony_ci	[0 ... (ATOMIC_HASH_SIZE-1)] = __SPIN_LOCK_UNLOCKED(__atomic_hash)
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#else /* SMP */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(dummy);
268c2ecf20Sopenharmony_ci#define ATOMIC_HASH_SIZE	1
278c2ecf20Sopenharmony_ci#define ATOMIC_HASH(a)		(&dummy)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#endif /* SMP */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define ATOMIC_FETCH_OP(op, c_op)					\
328c2ecf20Sopenharmony_ciint atomic_fetch_##op(int i, atomic_t *v)				\
338c2ecf20Sopenharmony_ci{									\
348c2ecf20Sopenharmony_ci	int ret;							\
358c2ecf20Sopenharmony_ci	unsigned long flags;						\
368c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(v), flags);			\
378c2ecf20Sopenharmony_ci									\
388c2ecf20Sopenharmony_ci	ret = v->counter;						\
398c2ecf20Sopenharmony_ci	v->counter c_op i;						\
408c2ecf20Sopenharmony_ci									\
418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);			\
428c2ecf20Sopenharmony_ci	return ret;							\
438c2ecf20Sopenharmony_ci}									\
448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atomic_fetch_##op);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define ATOMIC_OP_RETURN(op, c_op)					\
478c2ecf20Sopenharmony_ciint atomic_##op##_return(int i, atomic_t *v)				\
488c2ecf20Sopenharmony_ci{									\
498c2ecf20Sopenharmony_ci	int ret;							\
508c2ecf20Sopenharmony_ci	unsigned long flags;						\
518c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(v), flags);			\
528c2ecf20Sopenharmony_ci									\
538c2ecf20Sopenharmony_ci	ret = (v->counter c_op i);					\
548c2ecf20Sopenharmony_ci									\
558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);			\
568c2ecf20Sopenharmony_ci	return ret;							\
578c2ecf20Sopenharmony_ci}									\
588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atomic_##op##_return);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciATOMIC_OP_RETURN(add, +=)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciATOMIC_FETCH_OP(add, +=)
638c2ecf20Sopenharmony_ciATOMIC_FETCH_OP(and, &=)
648c2ecf20Sopenharmony_ciATOMIC_FETCH_OP(or, |=)
658c2ecf20Sopenharmony_ciATOMIC_FETCH_OP(xor, ^=)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#undef ATOMIC_FETCH_OP
688c2ecf20Sopenharmony_ci#undef ATOMIC_OP_RETURN
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciint atomic_xchg(atomic_t *v, int new)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	int ret;
738c2ecf20Sopenharmony_ci	unsigned long flags;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(v), flags);
768c2ecf20Sopenharmony_ci	ret = v->counter;
778c2ecf20Sopenharmony_ci	v->counter = new;
788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
798c2ecf20Sopenharmony_ci	return ret;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atomic_xchg);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ciint atomic_cmpxchg(atomic_t *v, int old, int new)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int ret;
868c2ecf20Sopenharmony_ci	unsigned long flags;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(v), flags);
898c2ecf20Sopenharmony_ci	ret = v->counter;
908c2ecf20Sopenharmony_ci	if (likely(ret == old))
918c2ecf20Sopenharmony_ci		v->counter = new;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
948c2ecf20Sopenharmony_ci	return ret;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atomic_cmpxchg);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciint atomic_fetch_add_unless(atomic_t *v, int a, int u)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	int ret;
1018c2ecf20Sopenharmony_ci	unsigned long flags;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(v), flags);
1048c2ecf20Sopenharmony_ci	ret = v->counter;
1058c2ecf20Sopenharmony_ci	if (ret != u)
1068c2ecf20Sopenharmony_ci		v->counter += a;
1078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
1088c2ecf20Sopenharmony_ci	return ret;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atomic_fetch_add_unless);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* Atomic operations are already serializing */
1138c2ecf20Sopenharmony_civoid atomic_set(atomic_t *v, int i)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	unsigned long flags;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(v), flags);
1188c2ecf20Sopenharmony_ci	v->counter = i;
1198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atomic_set);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ciunsigned long ___set_bit(unsigned long *addr, unsigned long mask)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	unsigned long old, flags;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(addr), flags);
1288c2ecf20Sopenharmony_ci	old = *addr;
1298c2ecf20Sopenharmony_ci	*addr = old | mask;
1308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(addr), flags);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return old & mask;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(___set_bit);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciunsigned long ___clear_bit(unsigned long *addr, unsigned long mask)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	unsigned long old, flags;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(addr), flags);
1418c2ecf20Sopenharmony_ci	old = *addr;
1428c2ecf20Sopenharmony_ci	*addr = old & ~mask;
1438c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(addr), flags);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return old & mask;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(___clear_bit);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ciunsigned long ___change_bit(unsigned long *addr, unsigned long mask)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	unsigned long old, flags;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(addr), flags);
1548c2ecf20Sopenharmony_ci	old = *addr;
1558c2ecf20Sopenharmony_ci	*addr = old ^ mask;
1568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(addr), flags);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return old & mask;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(___change_bit);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciunsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	unsigned long flags;
1658c2ecf20Sopenharmony_ci	u32 prev;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
1688c2ecf20Sopenharmony_ci	if ((prev = *ptr) == old)
1698c2ecf20Sopenharmony_ci		*ptr = new;
1708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return (unsigned long)prev;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cmpxchg_u32);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciu64 __cmpxchg_u64(u64 *ptr, u64 old, u64 new)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	unsigned long flags;
1798c2ecf20Sopenharmony_ci	u64 prev;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
1828c2ecf20Sopenharmony_ci	if ((prev = *ptr) == old)
1838c2ecf20Sopenharmony_ci		*ptr = new;
1848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return prev;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cmpxchg_u64);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciunsigned long __xchg_u32(volatile u32 *ptr, u32 new)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	unsigned long flags;
1938c2ecf20Sopenharmony_ci	u32 prev;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
1968c2ecf20Sopenharmony_ci	prev = *ptr;
1978c2ecf20Sopenharmony_ci	*ptr = new;
1988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return (unsigned long)prev;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__xchg_u32);
203