1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
7 */
8#ifndef __ASM_CMPXCHG_H
9#define __ASM_CMPXCHG_H
10
11#include <linux/irqflags.h>
12
13#define __HAVE_ARCH_CMPXCHG 1
14
15#define __cmpxchg_asm(ld, st, m, old, new)				\
16({									\
17	__typeof(*(m)) __ret;						\
18									\
19	if (cpu_has_llsc && R10000_LLSC_WAR) {				\
20		__asm__ __volatile__(					\
21		"	.set	push				\n"	\
22		"	.set	noat				\n"	\
23		"	.set	mips3				\n"	\
24		"1:	" ld "	%0, %2		# __cmpxchg_asm	\n"	\
25		"	bne	%0, %z3, 2f			\n"	\
26		"	.set	mips0				\n"	\
27		"	move	$1, %z4				\n"	\
28		"	.set	mips3				\n"	\
29		"	" st "	$1, %1				\n"	\
30		"	beqzl	$1, 1b				\n"	\
31		"2:						\n"	\
32		"	.set	pop				\n"	\
33		: "=&r" (__ret), "=R" (*m)				\
34		: "R" (*m), "Jr" (old), "Jr" (new)			\
35		: "memory");						\
36	} else if (cpu_has_llsc) {					\
37		__asm__ __volatile__(					\
38		"	.set	push				\n"	\
39		"	.set	noat				\n"	\
40		"	.set	mips3				\n"	\
41		"1:	" ld "	%0, %2		# __cmpxchg_asm	\n"	\
42		"	bne	%0, %z3, 2f			\n"	\
43		"	.set	mips0				\n"	\
44		"	move	$1, %z4				\n"	\
45		"	.set	mips3				\n"	\
46		"	" st "	$1, %1				\n"	\
47		"	beqz	$1, 3f				\n"	\
48		"2:						\n"	\
49		"	.subsection 2				\n"	\
50		"3:	b	1b				\n"	\
51		"	.previous				\n"	\
52		"	.set	pop				\n"	\
53		: "=&r" (__ret), "=R" (*m)				\
54		: "R" (*m), "Jr" (old), "Jr" (new)			\
55		: "memory");						\
56	} else {							\
57		unsigned long __flags;					\
58									\
59		raw_local_irq_save(__flags);				\
60		__ret = *m;						\
61		if (__ret == old)					\
62			*m = new;					\
63		raw_local_irq_restore(__flags);				\
64	}								\
65									\
66	__ret;								\
67})
68
69/*
70 * This function doesn't exist, so you'll get a linker error
71 * if something tries to do an invalid cmpxchg().
72 */
73extern void __cmpxchg_called_with_bad_pointer(void);
74
75#define __cmpxchg(ptr, old, new, barrier)				\
76({									\
77	__typeof__(ptr) __ptr = (ptr);					\
78	__typeof__(*(ptr)) __old = (old);				\
79	__typeof__(*(ptr)) __new = (new);				\
80	__typeof__(*(ptr)) __res = 0;					\
81									\
82	barrier;							\
83									\
84	switch (sizeof(*(__ptr))) {					\
85	case 4:								\
86		__res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new);	\
87		break;							\
88	case 8:								\
89		if (sizeof(long) == 8) {				\
90			__res = __cmpxchg_asm("lld", "scd", __ptr,	\
91					   __old, __new);		\
92			break;						\
93		}							\
94	default:							\
95		__cmpxchg_called_with_bad_pointer();			\
96		break;							\
97	}								\
98									\
99	barrier;							\
100									\
101	__res;								\
102})
103
104#define cmpxchg(ptr, old, new)		__cmpxchg(ptr, old, new, smp_llsc_mb())
105#define cmpxchg_local(ptr, old, new)	__cmpxchg(ptr, old, new, )
106
107#define cmpxchg64(ptr, o, n)						\
108  ({									\
109	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
110	cmpxchg((ptr), (o), (n));					\
111  })
112
113#ifdef CONFIG_64BIT
114#define cmpxchg64_local(ptr, o, n)					\
115  ({									\
116	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
117	cmpxchg_local((ptr), (o), (n));					\
118  })
119#else
120#include <asm-generic/cmpxchg-local.h>
121#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n))
122#endif
123
124#endif /* __ASM_CMPXCHG_H */
125