xref: /linux/arch/arm64/include/asm/irqflags.h (revision 7a92fc8b4d20680e4c20289a670d8fca2d1f2c1b)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Copyright (C) 2012 ARM Ltd.
4  */
5 #ifndef __ASM_IRQFLAGS_H
6 #define __ASM_IRQFLAGS_H
7 
8 #include <asm/alternative.h>
9 #include <asm/barrier.h>
10 #include <asm/ptrace.h>
11 #include <asm/sysreg.h>
12 
13 /*
14  * Aarch64 has flags for masking: Debug, Asynchronous (serror), Interrupts and
15  * FIQ exceptions, in the 'daif' register. We mask and unmask them in 'daif'
16  * order:
17  * Masking debug exceptions causes all other exceptions to be masked too/
18  * Masking SError masks IRQ/FIQ, but not debug exceptions. IRQ and FIQ are
19  * always masked and unmasked together, and have no side effects for other
20  * flags. Keeping to this order makes it easier for entry.S to know which
21  * exceptions should be unmasked.
22  */
23 
24 static __always_inline void __daif_local_irq_enable(void)
25 {
26 	barrier();
27 	asm volatile("msr daifclr, #3");
28 	barrier();
29 }
30 
31 static __always_inline void __pmr_local_irq_enable(void)
32 {
33 	if (IS_ENABLED(CONFIG_ARM64_DEBUG_PRIORITY_MASKING)) {
34 		u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
35 		WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
36 	}
37 
38 	barrier();
39 	write_sysreg_s(GIC_PRIO_IRQON, SYS_ICC_PMR_EL1);
40 	pmr_sync();
41 	barrier();
42 }
43 
44 static inline void arch_local_irq_enable(void)
45 {
46 	if (system_uses_irq_prio_masking()) {
47 		__pmr_local_irq_enable();
48 	} else {
49 		__daif_local_irq_enable();
50 	}
51 }
52 
53 static __always_inline void __daif_local_irq_disable(void)
54 {
55 	barrier();
56 	asm volatile("msr daifset, #3");
57 	barrier();
58 }
59 
60 static __always_inline void __pmr_local_irq_disable(void)
61 {
62 	if (IS_ENABLED(CONFIG_ARM64_DEBUG_PRIORITY_MASKING)) {
63 		u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
64 		WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
65 	}
66 
67 	barrier();
68 	write_sysreg_s(GIC_PRIO_IRQOFF, SYS_ICC_PMR_EL1);
69 	barrier();
70 }
71 
72 static inline void arch_local_irq_disable(void)
73 {
74 	if (system_uses_irq_prio_masking()) {
75 		__pmr_local_irq_disable();
76 	} else {
77 		__daif_local_irq_disable();
78 	}
79 }
80 
81 static __always_inline unsigned long __daif_local_save_flags(void)
82 {
83 	return read_sysreg(daif);
84 }
85 
86 static __always_inline unsigned long __pmr_local_save_flags(void)
87 {
88 	return read_sysreg_s(SYS_ICC_PMR_EL1);
89 }
90 
91 /*
92  * Save the current interrupt enable state.
93  */
94 static inline unsigned long arch_local_save_flags(void)
95 {
96 	if (system_uses_irq_prio_masking()) {
97 		return __pmr_local_save_flags();
98 	} else {
99 		return __daif_local_save_flags();
100 	}
101 }
102 
103 static __always_inline bool __daif_irqs_disabled_flags(unsigned long flags)
104 {
105 	return flags & PSR_I_BIT;
106 }
107 
108 static __always_inline bool __pmr_irqs_disabled_flags(unsigned long flags)
109 {
110 	return flags != GIC_PRIO_IRQON;
111 }
112 
113 static inline bool arch_irqs_disabled_flags(unsigned long flags)
114 {
115 	if (system_uses_irq_prio_masking()) {
116 		return __pmr_irqs_disabled_flags(flags);
117 	} else {
118 		return __daif_irqs_disabled_flags(flags);
119 	}
120 }
121 
122 static __always_inline bool __daif_irqs_disabled(void)
123 {
124 	return __daif_irqs_disabled_flags(__daif_local_save_flags());
125 }
126 
127 static __always_inline bool __pmr_irqs_disabled(void)
128 {
129 	return __pmr_irqs_disabled_flags(__pmr_local_save_flags());
130 }
131 
132 static inline bool arch_irqs_disabled(void)
133 {
134 	if (system_uses_irq_prio_masking()) {
135 		return __pmr_irqs_disabled();
136 	} else {
137 		return __daif_irqs_disabled();
138 	}
139 }
140 
141 static __always_inline unsigned long __daif_local_irq_save(void)
142 {
143 	unsigned long flags = __daif_local_save_flags();
144 
145 	__daif_local_irq_disable();
146 
147 	return flags;
148 }
149 
150 static __always_inline unsigned long __pmr_local_irq_save(void)
151 {
152 	unsigned long flags = __pmr_local_save_flags();
153 
154 	/*
155 	 * There are too many states with IRQs disabled, just keep the current
156 	 * state if interrupts are already disabled/masked.
157 	 */
158 	if (!__pmr_irqs_disabled_flags(flags))
159 		__pmr_local_irq_disable();
160 
161 	return flags;
162 }
163 
164 static inline unsigned long arch_local_irq_save(void)
165 {
166 	if (system_uses_irq_prio_masking()) {
167 		return __pmr_local_irq_save();
168 	} else {
169 		return __daif_local_irq_save();
170 	}
171 }
172 
173 static __always_inline void __daif_local_irq_restore(unsigned long flags)
174 {
175 	barrier();
176 	write_sysreg(flags, daif);
177 	barrier();
178 }
179 
180 static __always_inline void __pmr_local_irq_restore(unsigned long flags)
181 {
182 	barrier();
183 	write_sysreg_s(flags, SYS_ICC_PMR_EL1);
184 	pmr_sync();
185 	barrier();
186 }
187 
188 /*
189  * restore saved IRQ state
190  */
191 static inline void arch_local_irq_restore(unsigned long flags)
192 {
193 	if (system_uses_irq_prio_masking()) {
194 		__pmr_local_irq_restore(flags);
195 	} else {
196 		__daif_local_irq_restore(flags);
197 	}
198 }
199 
200 #endif /* __ASM_IRQFLAGS_H */
201