1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-only */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #ifndef __ASM_ARC_CMPXCHG_H
7*4882a593Smuzhiyun #define __ASM_ARC_CMPXCHG_H
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/types.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <asm/barrier.h>
12*4882a593Smuzhiyun #include <asm/smp.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #ifdef CONFIG_ARC_HAS_LLSC
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun static inline unsigned long
__cmpxchg(volatile void * ptr,unsigned long expected,unsigned long new)17*4882a593Smuzhiyun __cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun unsigned long prev;
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun /*
22*4882a593Smuzhiyun * Explicit full memory barrier needed before/after as
23*4882a593Smuzhiyun * LLOCK/SCOND themselves don't provide any such semantics
24*4882a593Smuzhiyun */
25*4882a593Smuzhiyun smp_mb();
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun __asm__ __volatile__(
28*4882a593Smuzhiyun "1: llock %0, [%1] \n"
29*4882a593Smuzhiyun " brne %0, %2, 2f \n"
30*4882a593Smuzhiyun " scond %3, [%1] \n"
31*4882a593Smuzhiyun " bnz 1b \n"
32*4882a593Smuzhiyun "2: \n"
33*4882a593Smuzhiyun : "=&r"(prev) /* Early clobber, to prevent reg reuse */
34*4882a593Smuzhiyun : "r"(ptr), /* Not "m": llock only supports reg direct addr mode */
35*4882a593Smuzhiyun "ir"(expected),
36*4882a593Smuzhiyun "r"(new) /* can't be "ir". scond can't take LIMM for "b" */
37*4882a593Smuzhiyun : "cc", "memory"); /* so that gcc knows memory is being written here */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun smp_mb();
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun return prev;
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #else /* !CONFIG_ARC_HAS_LLSC */
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun static inline unsigned long
__cmpxchg(volatile void * ptr,unsigned long expected,unsigned long new)47*4882a593Smuzhiyun __cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun unsigned long flags;
50*4882a593Smuzhiyun int prev;
51*4882a593Smuzhiyun volatile unsigned long *p = ptr;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun /*
54*4882a593Smuzhiyun * spin lock/unlock provide the needed smp_mb() before/after
55*4882a593Smuzhiyun */
56*4882a593Smuzhiyun atomic_ops_lock(flags);
57*4882a593Smuzhiyun prev = *p;
58*4882a593Smuzhiyun if (prev == expected)
59*4882a593Smuzhiyun *p = new;
60*4882a593Smuzhiyun atomic_ops_unlock(flags);
61*4882a593Smuzhiyun return prev;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun #endif
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun #define cmpxchg(ptr, o, n) ({ \
67*4882a593Smuzhiyun (typeof(*(ptr)))__cmpxchg((ptr), \
68*4882a593Smuzhiyun (unsigned long)(o), \
69*4882a593Smuzhiyun (unsigned long)(n)); \
70*4882a593Smuzhiyun })
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /*
73*4882a593Smuzhiyun * atomic_cmpxchg is same as cmpxchg
74*4882a593Smuzhiyun * LLSC: only different in data-type, semantics are exactly same
75*4882a593Smuzhiyun * !LLSC: cmpxchg() has to use an external lock atomic_ops_lock to guarantee
76*4882a593Smuzhiyun * semantics, and this lock also happens to be used by atomic_*()
77*4882a593Smuzhiyun */
78*4882a593Smuzhiyun #define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n)))
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /*
82*4882a593Smuzhiyun * xchg (reg with memory) based on "Native atomic" EX insn
83*4882a593Smuzhiyun */
__xchg(unsigned long val,volatile void * ptr,int size)84*4882a593Smuzhiyun static inline unsigned long __xchg(unsigned long val, volatile void *ptr,
85*4882a593Smuzhiyun int size)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun extern unsigned long __xchg_bad_pointer(void);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun switch (size) {
90*4882a593Smuzhiyun case 4:
91*4882a593Smuzhiyun smp_mb();
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun __asm__ __volatile__(
94*4882a593Smuzhiyun " ex %0, [%1] \n"
95*4882a593Smuzhiyun : "+r"(val)
96*4882a593Smuzhiyun : "r"(ptr)
97*4882a593Smuzhiyun : "memory");
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun smp_mb();
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun return val;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun return __xchg_bad_pointer();
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun #define _xchg(ptr, with) ((typeof(*(ptr)))__xchg((unsigned long)(with), (ptr), \
107*4882a593Smuzhiyun sizeof(*(ptr))))
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /*
110*4882a593Smuzhiyun * xchg() maps directly to ARC EX instruction which guarantees atomicity.
111*4882a593Smuzhiyun * However in !LLSC config, it also needs to be use @atomic_ops_lock spinlock
112*4882a593Smuzhiyun * due to a subtle reason:
113*4882a593Smuzhiyun * - For !LLSC, cmpxchg() needs to use that lock (see above) and there is lot
114*4882a593Smuzhiyun * of kernel code which calls xchg()/cmpxchg() on same data (see llist.h)
115*4882a593Smuzhiyun * Hence xchg() needs to follow same locking rules.
116*4882a593Smuzhiyun *
117*4882a593Smuzhiyun * Technically the lock is also needed for UP (boils down to irq save/restore)
118*4882a593Smuzhiyun * but we can cheat a bit since cmpxchg() atomic_ops_lock() would cause irqs to
119*4882a593Smuzhiyun * be disabled thus can't possibly be interrpted/preempted/clobbered by xchg()
120*4882a593Smuzhiyun * Other way around, xchg is one instruction anyways, so can't be interrupted
121*4882a593Smuzhiyun * as such
122*4882a593Smuzhiyun */
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun #if !defined(CONFIG_ARC_HAS_LLSC) && defined(CONFIG_SMP)
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun #define xchg(ptr, with) \
127*4882a593Smuzhiyun ({ \
128*4882a593Smuzhiyun unsigned long flags; \
129*4882a593Smuzhiyun typeof(*(ptr)) old_val; \
130*4882a593Smuzhiyun \
131*4882a593Smuzhiyun atomic_ops_lock(flags); \
132*4882a593Smuzhiyun old_val = _xchg(ptr, with); \
133*4882a593Smuzhiyun atomic_ops_unlock(flags); \
134*4882a593Smuzhiyun old_val; \
135*4882a593Smuzhiyun })
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun #else
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun #define xchg(ptr, with) _xchg(ptr, with)
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun #endif
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun /*
144*4882a593Smuzhiyun * "atomic" variant of xchg()
145*4882a593Smuzhiyun * REQ: It needs to follow the same serialization rules as other atomic_xxx()
146*4882a593Smuzhiyun * Since xchg() doesn't always do that, it would seem that following defintion
147*4882a593Smuzhiyun * is incorrect. But here's the rationale:
148*4882a593Smuzhiyun * SMP : Even xchg() takes the atomic_ops_lock, so OK.
149*4882a593Smuzhiyun * LLSC: atomic_ops_lock are not relevant at all (even if SMP, since LLSC
150*4882a593Smuzhiyun * is natively "SMP safe", no serialization required).
151*4882a593Smuzhiyun * UP : other atomics disable IRQ, so no way a difft ctxt atomic_xchg()
152*4882a593Smuzhiyun * could clobber them. atomic_xchg() itself would be 1 insn, so it
153*4882a593Smuzhiyun * can't be clobbered by others. Thus no serialization required when
154*4882a593Smuzhiyun * atomic_xchg is involved.
155*4882a593Smuzhiyun */
156*4882a593Smuzhiyun #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun #endif
159