xref: /openbmc/linux/arch/ia64/include/asm/spinlock.h (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
27f30491cSTony Luck #ifndef _ASM_IA64_SPINLOCK_H
37f30491cSTony Luck #define _ASM_IA64_SPINLOCK_H
47f30491cSTony Luck 
57f30491cSTony Luck /*
67f30491cSTony Luck  * Copyright (C) 1998-2003 Hewlett-Packard Co
77f30491cSTony Luck  *	David Mosberger-Tang <davidm@hpl.hp.com>
87f30491cSTony Luck  * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
97f30491cSTony Luck  *
107f30491cSTony Luck  * This file is used for SMP configurations only.
117f30491cSTony Luck  */
127f30491cSTony Luck 
137f30491cSTony Luck #include <linux/compiler.h>
147f30491cSTony Luck #include <linux/kernel.h>
157f30491cSTony Luck #include <linux/bitops.h>
167f30491cSTony Luck 
1760063497SArun Sharma #include <linux/atomic.h>
187f30491cSTony Luck #include <asm/intrinsics.h>
19726328d9SPeter Zijlstra #include <asm/barrier.h>
20726328d9SPeter Zijlstra #include <asm/processor.h>
217f30491cSTony Luck 
220199c4e6SThomas Gleixner #define arch_spin_lock_init(x)			((x)->lock = 0)
237f30491cSTony Luck 
247f30491cSTony Luck /*
252c86963bSTony Luck  * Ticket locks are conceptually two parts, one indicating the current head of
262c86963bSTony Luck  * the queue, and the other indicating the current tail. The lock is acquired
272c86963bSTony Luck  * by atomically noting the tail and incrementing it by one (thus adding
282c86963bSTony Luck  * ourself to the queue and noting our position), then waiting until the head
29f589c67fSRandy Dunlap  * becomes equal to the initial value of the tail.
309d40ee20STony Luck  * The pad bits in the middle are used to prevent the next_ticket number
319d40ee20STony Luck  * overflowing into the now_serving number.
322c86963bSTony Luck  *
339d40ee20STony Luck  *   31             17  16    15  14                    0
342c86963bSTony Luck  *  +----------------------------------------------------+
359d40ee20STony Luck  *  |  now_serving     | padding |   next_ticket         |
362c86963bSTony Luck  *  +----------------------------------------------------+
377f30491cSTony Luck  */
387f30491cSTony Luck 
399d40ee20STony Luck #define TICKET_SHIFT	17
409d40ee20STony Luck #define TICKET_BITS	15
419d40ee20STony Luck #define	TICKET_MASK	((1 << TICKET_BITS) - 1)
427f30491cSTony Luck 
__ticket_spin_lock(arch_spinlock_t * lock)43445c8951SThomas Gleixner static __always_inline void __ticket_spin_lock(arch_spinlock_t *lock)
447f30491cSTony Luck {
459d40ee20STony Luck 	int	*p = (int *)&lock->lock, ticket, serve;
467f30491cSTony Luck 
479d40ee20STony Luck 	ticket = ia64_fetchadd(1, p, acq);
482c86963bSTony Luck 
499d40ee20STony Luck 	if (!(((ticket >> TICKET_SHIFT) ^ ticket) & TICKET_MASK))
502c86963bSTony Luck 		return;
512c86963bSTony Luck 
529d40ee20STony Luck 	ia64_invala();
539d40ee20STony Luck 
549d40ee20STony Luck 	for (;;) {
559d40ee20STony Luck 		asm volatile ("ld4.c.nc %0=[%1]" : "=r"(serve) : "r"(p) : "memory");
569d40ee20STony Luck 
579d40ee20STony Luck 		if (!(((serve >> TICKET_SHIFT) ^ ticket) & TICKET_MASK))
589d40ee20STony Luck 			return;
592c86963bSTony Luck 		cpu_relax();
609d40ee20STony Luck 	}
617f30491cSTony Luck }
627f30491cSTony Luck 
__ticket_spin_trylock(arch_spinlock_t * lock)63445c8951SThomas Gleixner static __always_inline int __ticket_spin_trylock(arch_spinlock_t *lock)
642c86963bSTony Luck {
656aa7de05SMark Rutland 	int tmp = READ_ONCE(lock->lock);
667f30491cSTony Luck 
679d40ee20STony Luck 	if (!(((tmp >> TICKET_SHIFT) ^ tmp) & TICKET_MASK))
689d40ee20STony Luck 		return ia64_cmpxchg(acq, &lock->lock, tmp, tmp + 1, sizeof (tmp)) == tmp;
692c86963bSTony Luck 	return 0;
707f30491cSTony Luck }
717f30491cSTony Luck 
__ticket_spin_unlock(arch_spinlock_t * lock)72445c8951SThomas Gleixner static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)
732c86963bSTony Luck {
749d40ee20STony Luck 	unsigned short	*p = (unsigned short *)&lock->lock + 1, tmp;
757f30491cSTony Luck 
7649ca6462SWill Deacon 	/* This could be optimised with ARCH_HAS_MMIOWB */
7749ca6462SWill Deacon 	mmiowb();
789d40ee20STony Luck 	asm volatile ("ld2.bias %0=[%1]" : "=r"(tmp) : "r"(p));
796aa7de05SMark Rutland 	WRITE_ONCE(*p, (tmp + 2) & ~1);
802c86963bSTony Luck }
812c86963bSTony Luck 
__ticket_spin_is_locked(arch_spinlock_t * lock)82445c8951SThomas Gleixner static inline int __ticket_spin_is_locked(arch_spinlock_t *lock)
832c86963bSTony Luck {
846aa7de05SMark Rutland 	long tmp = READ_ONCE(lock->lock);
852c86963bSTony Luck 
869d40ee20STony Luck 	return !!(((tmp >> TICKET_SHIFT) ^ tmp) & TICKET_MASK);
872c86963bSTony Luck }
882c86963bSTony Luck 
__ticket_spin_is_contended(arch_spinlock_t * lock)89445c8951SThomas Gleixner static inline int __ticket_spin_is_contended(arch_spinlock_t *lock)
902c86963bSTony Luck {
916aa7de05SMark Rutland 	long tmp = READ_ONCE(lock->lock);
922c86963bSTony Luck 
939d40ee20STony Luck 	return ((tmp - (tmp >> TICKET_SHIFT)) & TICKET_MASK) > 1;
942c86963bSTony Luck }
952c86963bSTony Luck 
arch_spin_value_unlocked(arch_spinlock_t lock)9671c7356fSLuck, Tony static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock)
9771c7356fSLuck, Tony {
9871c7356fSLuck, Tony 	return !(((lock.lock >> TICKET_SHIFT) ^ lock.lock) & TICKET_MASK);
9971c7356fSLuck, Tony }
10071c7356fSLuck, Tony 
arch_spin_is_locked(arch_spinlock_t * lock)1010199c4e6SThomas Gleixner static inline int arch_spin_is_locked(arch_spinlock_t *lock)
1022c86963bSTony Luck {
1032c86963bSTony Luck 	return __ticket_spin_is_locked(lock);
1042c86963bSTony Luck }
1052c86963bSTony Luck 
arch_spin_is_contended(arch_spinlock_t * lock)1060199c4e6SThomas Gleixner static inline int arch_spin_is_contended(arch_spinlock_t *lock)
1072c86963bSTony Luck {
1082c86963bSTony Luck 	return __ticket_spin_is_contended(lock);
1092c86963bSTony Luck }
1100199c4e6SThomas Gleixner #define arch_spin_is_contended	arch_spin_is_contended
1112c86963bSTony Luck 
arch_spin_lock(arch_spinlock_t * lock)1120199c4e6SThomas Gleixner static __always_inline void arch_spin_lock(arch_spinlock_t *lock)
1132c86963bSTony Luck {
1142c86963bSTony Luck 	__ticket_spin_lock(lock);
1152c86963bSTony Luck }
1162c86963bSTony Luck 
arch_spin_trylock(arch_spinlock_t * lock)1170199c4e6SThomas Gleixner static __always_inline int arch_spin_trylock(arch_spinlock_t *lock)
1182c86963bSTony Luck {
1192c86963bSTony Luck 	return __ticket_spin_trylock(lock);
1202c86963bSTony Luck }
1212c86963bSTony Luck 
arch_spin_unlock(arch_spinlock_t * lock)1220199c4e6SThomas Gleixner static __always_inline void arch_spin_unlock(arch_spinlock_t *lock)
1232c86963bSTony Luck {
1242c86963bSTony Luck 	__ticket_spin_unlock(lock);
1252c86963bSTony Luck }
1262c86963bSTony Luck 
1272d09cde9SRobin Holt #ifdef ASM_SUPPORTED
1282d09cde9SRobin Holt 
1292d09cde9SRobin Holt static __always_inline void
arch_read_lock(arch_rwlock_t * lock)130*f98a3dccSArnd Bergmann arch_read_lock(arch_rwlock_t *lock)
1312d09cde9SRobin Holt {
132*f98a3dccSArnd Bergmann 	unsigned long flags = 0;
133*f98a3dccSArnd Bergmann 
1342d09cde9SRobin Holt 	__asm__ __volatile__ (
1352d09cde9SRobin Holt 		"tbit.nz p6, p0 = %1,%2\n"
1362d09cde9SRobin Holt 		"br.few 3f\n"
1372d09cde9SRobin Holt 		"1:\n"
1382d09cde9SRobin Holt 		"fetchadd4.rel r2 = [%0], -1;;\n"
1392d09cde9SRobin Holt 		"(p6) ssm psr.i\n"
1402d09cde9SRobin Holt 		"2:\n"
1412d09cde9SRobin Holt 		"hint @pause\n"
1422d09cde9SRobin Holt 		"ld4 r2 = [%0];;\n"
1432d09cde9SRobin Holt 		"cmp4.lt p7,p0 = r2, r0\n"
1442d09cde9SRobin Holt 		"(p7) br.cond.spnt.few 2b\n"
1452d09cde9SRobin Holt 		"(p6) rsm psr.i\n"
1462d09cde9SRobin Holt 		";;\n"
1472d09cde9SRobin Holt 		"3:\n"
1482d09cde9SRobin Holt 		"fetchadd4.acq r2 = [%0], 1;;\n"
1492d09cde9SRobin Holt 		"cmp4.lt p7,p0 = r2, r0\n"
1502d09cde9SRobin Holt 		"(p7) br.cond.spnt.few 1b\n"
1512d09cde9SRobin Holt 		: : "r"(lock), "r"(flags), "i"(IA64_PSR_I_BIT)
1522d09cde9SRobin Holt 		: "p6", "p7", "r2", "memory");
1532d09cde9SRobin Holt }
1542d09cde9SRobin Holt 
1552d09cde9SRobin Holt #else /* !ASM_SUPPORTED */
1562d09cde9SRobin Holt 
157e5931943SThomas Gleixner #define arch_read_lock(rw)								\
1587f30491cSTony Luck do {											\
159fb3a6bbcSThomas Gleixner 	arch_rwlock_t *__read_lock_ptr = (rw);						\
1607f30491cSTony Luck 											\
1617f30491cSTony Luck 	while (unlikely(ia64_fetchadd(1, (int *) __read_lock_ptr, acq) < 0)) {		\
1627f30491cSTony Luck 		ia64_fetchadd(-1, (int *) __read_lock_ptr, rel);			\
1637f30491cSTony Luck 		while (*(volatile int *)__read_lock_ptr < 0)				\
1647f30491cSTony Luck 			cpu_relax();							\
1657f30491cSTony Luck 	}										\
1667f30491cSTony Luck } while (0)
1677f30491cSTony Luck 
1682d09cde9SRobin Holt #endif /* !ASM_SUPPORTED */
1692d09cde9SRobin Holt 
170e5931943SThomas Gleixner #define arch_read_unlock(rw)					\
1717f30491cSTony Luck do {								\
172fb3a6bbcSThomas Gleixner 	arch_rwlock_t *__read_lock_ptr = (rw);			\
1737f30491cSTony Luck 	ia64_fetchadd(-1, (int *) __read_lock_ptr, rel);	\
1747f30491cSTony Luck } while (0)
1757f30491cSTony Luck 
1767f30491cSTony Luck #ifdef ASM_SUPPORTED
1772d09cde9SRobin Holt 
1782d09cde9SRobin Holt static __always_inline void
arch_write_lock(arch_rwlock_t * lock)179*f98a3dccSArnd Bergmann arch_write_lock(arch_rwlock_t *lock)
1802d09cde9SRobin Holt {
181*f98a3dccSArnd Bergmann 	unsigned long flags = 0;
182*f98a3dccSArnd Bergmann 
1832d09cde9SRobin Holt 	__asm__ __volatile__ (
1842d09cde9SRobin Holt 		"tbit.nz p6, p0 = %1, %2\n"
1852d09cde9SRobin Holt 		"mov ar.ccv = r0\n"
1862d09cde9SRobin Holt 		"dep r29 = -1, r0, 31, 1\n"
1872d09cde9SRobin Holt 		"br.few 3f;;\n"
1882d09cde9SRobin Holt 		"1:\n"
1892d09cde9SRobin Holt 		"(p6) ssm psr.i\n"
1902d09cde9SRobin Holt 		"2:\n"
1912d09cde9SRobin Holt 		"hint @pause\n"
1922d09cde9SRobin Holt 		"ld4 r2 = [%0];;\n"
1932d09cde9SRobin Holt 		"cmp4.eq p0,p7 = r0, r2\n"
1942d09cde9SRobin Holt 		"(p7) br.cond.spnt.few 2b\n"
1952d09cde9SRobin Holt 		"(p6) rsm psr.i\n"
1962d09cde9SRobin Holt 		";;\n"
1972d09cde9SRobin Holt 		"3:\n"
1982d09cde9SRobin Holt 		"cmpxchg4.acq r2 = [%0], r29, ar.ccv;;\n"
1992d09cde9SRobin Holt 		"cmp4.eq p0,p7 = r0, r2\n"
2002d09cde9SRobin Holt 		"(p7) br.cond.spnt.few 1b;;\n"
2012d09cde9SRobin Holt 		: : "r"(lock), "r"(flags), "i"(IA64_PSR_I_BIT)
2022d09cde9SRobin Holt 		: "ar.ccv", "p6", "p7", "r2", "r29", "memory");
2032d09cde9SRobin Holt }
2042d09cde9SRobin Holt 
205e5931943SThomas Gleixner #define arch_write_trylock(rw)							\
2067f30491cSTony Luck ({										\
2077f30491cSTony Luck 	register long result;							\
2087f30491cSTony Luck 										\
2097f30491cSTony Luck 	__asm__ __volatile__ (							\
2107f30491cSTony Luck 		"mov ar.ccv = r0\n"						\
2117f30491cSTony Luck 		"dep r29 = -1, r0, 31, 1;;\n"					\
2127f30491cSTony Luck 		"cmpxchg4.acq %0 = [%1], r29, ar.ccv\n"				\
2137f30491cSTony Luck 		: "=r"(result) : "r"(rw) : "ar.ccv", "r29", "memory");		\
2147f30491cSTony Luck 	(result == 0);								\
2157f30491cSTony Luck })
2167f30491cSTony Luck 
arch_write_unlock(arch_rwlock_t * x)217e5931943SThomas Gleixner static inline void arch_write_unlock(arch_rwlock_t *x)
2187f30491cSTony Luck {
2197f30491cSTony Luck 	u8 *y = (u8 *)x;
2207f30491cSTony Luck 	barrier();
2217f30491cSTony Luck 	asm volatile ("st1.rel.nta [%0] = r0\n\t" :: "r"(y+3) : "memory" );
2227f30491cSTony Luck }
2237f30491cSTony Luck 
2247f30491cSTony Luck #else /* !ASM_SUPPORTED */
2257f30491cSTony Luck 
226e5931943SThomas Gleixner #define arch_write_lock(l)								\
2277f30491cSTony Luck ({											\
2287f30491cSTony Luck 	__u64 ia64_val, ia64_set_val = ia64_dep_mi(-1, 0, 31, 1);			\
2297f30491cSTony Luck 	__u32 *ia64_write_lock_ptr = (__u32 *) (l);					\
2307f30491cSTony Luck 	do {										\
2317f30491cSTony Luck 		while (*ia64_write_lock_ptr)						\
2327f30491cSTony Luck 			ia64_barrier();							\
2337f30491cSTony Luck 		ia64_val = ia64_cmpxchg4_acq(ia64_write_lock_ptr, ia64_set_val, 0);	\
2347f30491cSTony Luck 	} while (ia64_val);								\
2357f30491cSTony Luck })
2367f30491cSTony Luck 
237e5931943SThomas Gleixner #define arch_write_trylock(rw)						\
2387f30491cSTony Luck ({									\
2397f30491cSTony Luck 	__u64 ia64_val;							\
2407f30491cSTony Luck 	__u64 ia64_set_val = ia64_dep_mi(-1, 0, 31,1);			\
2417f30491cSTony Luck 	ia64_val = ia64_cmpxchg4_acq((__u32 *)(rw), ia64_set_val, 0);	\
2427f30491cSTony Luck 	(ia64_val == 0);						\
2437f30491cSTony Luck })
2447f30491cSTony Luck 
arch_write_unlock(arch_rwlock_t * x)245e5931943SThomas Gleixner static inline void arch_write_unlock(arch_rwlock_t *x)
2467f30491cSTony Luck {
2477f30491cSTony Luck 	barrier();
2487f30491cSTony Luck 	x->write_lock = 0;
2497f30491cSTony Luck }
2507f30491cSTony Luck 
2517f30491cSTony Luck #endif /* !ASM_SUPPORTED */
2527f30491cSTony Luck 
arch_read_trylock(arch_rwlock_t * x)253e5931943SThomas Gleixner static inline int arch_read_trylock(arch_rwlock_t *x)
2547f30491cSTony Luck {
2557f30491cSTony Luck 	union {
256fb3a6bbcSThomas Gleixner 		arch_rwlock_t lock;
2577f30491cSTony Luck 		__u32 word;
2587f30491cSTony Luck 	} old, new;
2597f30491cSTony Luck 	old.lock = new.lock = *x;
2607f30491cSTony Luck 	old.lock.write_lock = new.lock.write_lock = 0;
2617f30491cSTony Luck 	++new.lock.read_counter;
2627f30491cSTony Luck 	return (u32)ia64_cmpxchg4_acq((__u32 *)(x), new.word, old.word) == old.word;
2637f30491cSTony Luck }
2647f30491cSTony Luck 
2657f30491cSTony Luck #endif /*  _ASM_IA64_SPINLOCK_H */
266