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