1/*
2 *  linux/include/asm-arm/proc-armv/system.h
3 *
4 *  Copyright (C) 1996 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#ifndef __ASM_PROC_SYSTEM_H
11#define __ASM_PROC_SYSTEM_H
12
13/*
14 * Save the current interrupt enable state & disable IRQs
15 */
16#ifdef CONFIG_ARM64
17
18/*
19 * Save the current interrupt enable state
20 * and disable IRQs/FIQs
21 */
22#define local_irq_save(flags)					\
23	({							\
24	asm volatile(						\
25	"mrs	%0, daif\n"					\
26	"msr	daifset, #3"					\
27	: "=r" (flags)						\
28	:							\
29	: "memory");						\
30	})
31
32/*
33 * restore saved IRQ & FIQ state
34 */
35#define local_irq_restore(flags)				\
36	({							\
37	asm volatile(						\
38	"msr	daif, %0"					\
39	:							\
40	: "r" (flags)						\
41	: "memory");						\
42	})
43
44/*
45 * Enable IRQs/FIQs
46 */
47#define local_irq_enable()					\
48	({							\
49	asm volatile(						\
50	"msr	daifclr, #3"					\
51	:							\
52	:							\
53	: "memory");						\
54	})
55
56/*
57 * Disable IRQs/FIQs
58 */
59#define local_irq_disable()					\
60	({							\
61	asm volatile(						\
62	"msr	daifset, #3"					\
63	:							\
64	:							\
65	: "memory");						\
66	})
67
68#else	/* CONFIG_ARM64 */
69
70#define local_irq_save(x)					\
71	({							\
72		unsigned long temp;				\
73	__asm__ __volatile__(					\
74	"mrs	%0, cpsr		@ local_irq_save\n"	\
75"	orr	%1, %0, #128\n"					\
76"	msr	cpsr_c, %1"					\
77	: "=r" (x), "=r" (temp)					\
78	:							\
79	: "memory");						\
80	})
81
82/*
83 * Enable IRQs
84 */
85#define local_irq_enable()					\
86	({							\
87		unsigned long temp;				\
88	__asm__ __volatile__(					\
89	"mrs	%0, cpsr		@ local_irq_enable\n"	\
90"	bic	%0, %0, #128\n"					\
91"	msr	cpsr_c, %0"					\
92	: "=r" (temp)						\
93	:							\
94	: "memory");						\
95	})
96
97/*
98 * Disable IRQs
99 */
100#define local_irq_disable()					\
101	({							\
102		unsigned long temp;				\
103	__asm__ __volatile__(					\
104	"mrs	%0, cpsr		@ local_irq_disable\n"	\
105"	orr	%0, %0, #128\n"					\
106"	msr	cpsr_c, %0"					\
107	: "=r" (temp)						\
108	:							\
109	: "memory");						\
110	})
111
112/*
113 * Enable FIQs
114 */
115#define __stf()							\
116	({							\
117		unsigned long temp;				\
118	__asm__ __volatile__(					\
119	"mrs	%0, cpsr		@ stf\n"		\
120"	bic	%0, %0, #64\n"					\
121"	msr	cpsr_c, %0"					\
122	: "=r" (temp)						\
123	:							\
124	: "memory");						\
125	})
126
127/*
128 * Disable FIQs
129 */
130#define __clf()							\
131	({							\
132		unsigned long temp;				\
133	__asm__ __volatile__(					\
134	"mrs	%0, cpsr		@ clf\n"		\
135"	orr	%0, %0, #64\n"					\
136"	msr	cpsr_c, %0"					\
137	: "=r" (temp)						\
138	:							\
139	: "memory");						\
140	})
141
142/*
143 * Save the current interrupt enable state.
144 */
145#define local_save_flags(x)					\
146	({							\
147	__asm__ __volatile__(					\
148	"mrs	%0, cpsr		@ local_save_flags\n"	\
149	  : "=r" (x)						\
150	  :							\
151	  : "memory");						\
152	})
153
154/*
155 * restore saved IRQ & FIQ state
156 */
157#define local_irq_restore(x)					\
158	__asm__ __volatile__(					\
159	"msr	cpsr_c, %0		@ local_irq_restore\n"	\
160	:							\
161	: "r" (x)						\
162	: "memory")
163
164#endif	/* CONFIG_ARM64 */
165
166#if defined(CONFIG_ARM64)
167/*
168 * On the StrongARM, "swp" is terminally broken since it bypasses the
169 * cache totally.  This means that the cache becomes inconsistent, and,
170 * since we use normal loads/stores as well, this is really bad.
171 * Typically, this causes oopsen in filp_close, but could have other,
172 * more disasterous effects.  There are two work-arounds:
173 *  1. Disable interrupts and emulate the atomic swap
174 *  2. Clean the cache, perform atomic swap, flush the cache
175 *
176 * We choose (1) since its the "easiest" to achieve here and is not
177 * dependent on the processor type.
178 */
179#define swp_is_buggy
180#endif
181
182static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
183{
184	extern void __bad_xchg(volatile void *, int);
185	unsigned long ret;
186#ifdef swp_is_buggy
187	unsigned long flags;
188#endif
189
190	switch (size) {
191#ifdef swp_is_buggy
192		case 1:
193			local_irq_save(flags);
194			ret = *(volatile unsigned char *)ptr;
195			*(volatile unsigned char *)ptr = x;
196			local_irq_restore(flags);
197			break;
198
199		case 4:
200			local_irq_save(flags);
201			ret = *(volatile unsigned long *)ptr;
202			*(volatile unsigned long *)ptr = x;
203			local_irq_restore(flags);
204			break;
205#else
206		case 1:	__asm__ __volatile__ ("swpb %0, %1, [%2]"
207					: "=&r" (ret)
208					: "r" (x), "r" (ptr)
209					: "memory");
210			break;
211		case 4:	__asm__ __volatile__ ("swp %0, %1, [%2]"
212					: "=&r" (ret)
213					: "r" (x), "r" (ptr)
214					: "memory");
215			break;
216#endif
217		default: __bad_xchg(ptr, size), ret = 0;
218	}
219
220	return ret;
221}
222
223#endif
224