GNU Linux-libre 5.19-rc6-gnu
[releases.git] / arch / arm64 / include / asm / irqflags.h
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 /*
25  * CPU interrupt mask handling.
26  */
27 static inline void arch_local_irq_enable(void)
28 {
29         if (system_has_prio_mask_debugging()) {
30                 u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
31
32                 WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
33         }
34
35         asm volatile(ALTERNATIVE(
36                 "msr    daifclr, #3             // arch_local_irq_enable",
37                 __msr_s(SYS_ICC_PMR_EL1, "%0"),
38                 ARM64_HAS_IRQ_PRIO_MASKING)
39                 :
40                 : "r" ((unsigned long) GIC_PRIO_IRQON)
41                 : "memory");
42
43         pmr_sync();
44 }
45
46 static inline void arch_local_irq_disable(void)
47 {
48         if (system_has_prio_mask_debugging()) {
49                 u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
50
51                 WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
52         }
53
54         asm volatile(ALTERNATIVE(
55                 "msr    daifset, #3             // arch_local_irq_disable",
56                 __msr_s(SYS_ICC_PMR_EL1, "%0"),
57                 ARM64_HAS_IRQ_PRIO_MASKING)
58                 :
59                 : "r" ((unsigned long) GIC_PRIO_IRQOFF)
60                 : "memory");
61 }
62
63 /*
64  * Save the current interrupt enable state.
65  */
66 static inline unsigned long arch_local_save_flags(void)
67 {
68         unsigned long flags;
69
70         asm volatile(ALTERNATIVE(
71                 "mrs    %0, daif",
72                 __mrs_s("%0", SYS_ICC_PMR_EL1),
73                 ARM64_HAS_IRQ_PRIO_MASKING)
74                 : "=&r" (flags)
75                 :
76                 : "memory");
77
78         return flags;
79 }
80
81 static inline int arch_irqs_disabled_flags(unsigned long flags)
82 {
83         int res;
84
85         asm volatile(ALTERNATIVE(
86                 "and    %w0, %w1, #" __stringify(PSR_I_BIT),
87                 "eor    %w0, %w1, #" __stringify(GIC_PRIO_IRQON),
88                 ARM64_HAS_IRQ_PRIO_MASKING)
89                 : "=&r" (res)
90                 : "r" ((int) flags)
91                 : "memory");
92
93         return res;
94 }
95
96 static inline int arch_irqs_disabled(void)
97 {
98         return arch_irqs_disabled_flags(arch_local_save_flags());
99 }
100
101 static inline unsigned long arch_local_irq_save(void)
102 {
103         unsigned long flags;
104
105         flags = arch_local_save_flags();
106
107         /*
108          * There are too many states with IRQs disabled, just keep the current
109          * state if interrupts are already disabled/masked.
110          */
111         if (!arch_irqs_disabled_flags(flags))
112                 arch_local_irq_disable();
113
114         return flags;
115 }
116
117 /*
118  * restore saved IRQ state
119  */
120 static inline void arch_local_irq_restore(unsigned long flags)
121 {
122         asm volatile(ALTERNATIVE(
123                 "msr    daif, %0",
124                 __msr_s(SYS_ICC_PMR_EL1, "%0"),
125                 ARM64_HAS_IRQ_PRIO_MASKING)
126                 :
127                 : "r" (flags)
128                 : "memory");
129
130         pmr_sync();
131 }
132
133 #endif /* __ASM_IRQFLAGS_H */