xref: /linux/arch/arm64/include/asm/irqflags.h (revision 06ed6aa56ffac9241e03a24649e8d048f8f1b10c)
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 'dai'
16  * order:
17  * Masking debug exceptions causes all other exceptions to be masked too/
18  * Masking SError masks irq, but not debug exceptions. Masking irqs has no
19  * side effects for other flags. Keeping to this order makes it easier for
20  * entry.S to know which exceptions should be unmasked.
21  *
22  * FIQ is never expected, but we mask it when we disable debug exceptions, and
23  * unmask it at all other times.
24  */
25 
26 /*
27  * CPU interrupt mask handling.
28  */
29 static inline void arch_local_irq_enable(void)
30 {
31 	if (system_has_prio_mask_debugging()) {
32 		u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
33 
34 		WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
35 	}
36 
37 	asm volatile(ALTERNATIVE(
38 		"msr	daifclr, #2		// arch_local_irq_enable",
39 		__msr_s(SYS_ICC_PMR_EL1, "%0"),
40 		ARM64_HAS_IRQ_PRIO_MASKING)
41 		:
42 		: "r" ((unsigned long) GIC_PRIO_IRQON)
43 		: "memory");
44 
45 	pmr_sync();
46 }
47 
48 static inline void arch_local_irq_disable(void)
49 {
50 	if (system_has_prio_mask_debugging()) {
51 		u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
52 
53 		WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
54 	}
55 
56 	asm volatile(ALTERNATIVE(
57 		"msr	daifset, #2		// arch_local_irq_disable",
58 		__msr_s(SYS_ICC_PMR_EL1, "%0"),
59 		ARM64_HAS_IRQ_PRIO_MASKING)
60 		:
61 		: "r" ((unsigned long) GIC_PRIO_IRQOFF)
62 		: "memory");
63 }
64 
65 /*
66  * Save the current interrupt enable state.
67  */
68 static inline unsigned long arch_local_save_flags(void)
69 {
70 	unsigned long flags;
71 
72 	asm volatile(ALTERNATIVE(
73 		"mrs	%0, daif",
74 		__mrs_s("%0", SYS_ICC_PMR_EL1),
75 		ARM64_HAS_IRQ_PRIO_MASKING)
76 		: "=&r" (flags)
77 		:
78 		: "memory");
79 
80 	return flags;
81 }
82 
83 static inline int arch_irqs_disabled_flags(unsigned long flags)
84 {
85 	int res;
86 
87 	asm volatile(ALTERNATIVE(
88 		"and	%w0, %w1, #" __stringify(PSR_I_BIT),
89 		"eor	%w0, %w1, #" __stringify(GIC_PRIO_IRQON),
90 		ARM64_HAS_IRQ_PRIO_MASKING)
91 		: "=&r" (res)
92 		: "r" ((int) flags)
93 		: "memory");
94 
95 	return res;
96 }
97 
98 static inline unsigned long arch_local_irq_save(void)
99 {
100 	unsigned long flags;
101 
102 	flags = arch_local_save_flags();
103 
104 	/*
105 	 * There are too many states with IRQs disabled, just keep the current
106 	 * state if interrupts are already disabled/masked.
107 	 */
108 	if (!arch_irqs_disabled_flags(flags))
109 		arch_local_irq_disable();
110 
111 	return flags;
112 }
113 
114 /*
115  * restore saved IRQ state
116  */
117 static inline void arch_local_irq_restore(unsigned long flags)
118 {
119 	asm volatile(ALTERNATIVE(
120 		"msr	daif, %0",
121 		__msr_s(SYS_ICC_PMR_EL1, "%0"),
122 		ARM64_HAS_IRQ_PRIO_MASKING)
123 		:
124 		: "r" (flags)
125 		: "memory");
126 
127 	pmr_sync();
128 }
129 
130 #endif /* __ASM_IRQFLAGS_H */
131