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