162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#ifndef _ASM_POWERPC_HW_IRQ_H 662306a36Sopenharmony_ci#define _ASM_POWERPC_HW_IRQ_H 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#ifdef __KERNEL__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/compiler.h> 1262306a36Sopenharmony_ci#include <asm/ptrace.h> 1362306a36Sopenharmony_ci#include <asm/processor.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#ifdef CONFIG_PPC64 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci * PACA flags in paca->irq_happened. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * This bits are set when interrupts occur while soft-disabled 2162306a36Sopenharmony_ci * and allow a proper replay. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * The PACA_IRQ_HARD_DIS is set whenever we hard disable. It is almost 2462306a36Sopenharmony_ci * always in synch with the MSR[EE] state, except: 2562306a36Sopenharmony_ci * - A window in interrupt entry, where hardware disables MSR[EE] and that 2662306a36Sopenharmony_ci * must be "reconciled" with the soft mask state. 2762306a36Sopenharmony_ci * - NMI interrupts that hit in awkward places, until they fix the state. 2862306a36Sopenharmony_ci * - When local irqs are being enabled and state is being fixed up. 2962306a36Sopenharmony_ci * - When returning from an interrupt there are some windows where this 3062306a36Sopenharmony_ci * can become out of synch, but gets fixed before the RFI or before 3162306a36Sopenharmony_ci * executing the next user instruction (see arch/powerpc/kernel/interrupt.c). 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#define PACA_IRQ_HARD_DIS 0x01 3462306a36Sopenharmony_ci#define PACA_IRQ_DBELL 0x02 3562306a36Sopenharmony_ci#define PACA_IRQ_EE 0x04 3662306a36Sopenharmony_ci#define PACA_IRQ_DEC 0x08 /* Or FIT */ 3762306a36Sopenharmony_ci#define PACA_IRQ_HMI 0x10 3862306a36Sopenharmony_ci#define PACA_IRQ_PMI 0x20 3962306a36Sopenharmony_ci#define PACA_IRQ_REPLAYING 0x40 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Some soft-masked interrupts must be hard masked until they are replayed 4362306a36Sopenharmony_ci * (e.g., because the soft-masked handler does not clear the exception). 4462306a36Sopenharmony_ci * Interrupt replay itself must remain hard masked too. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S 4762306a36Sopenharmony_ci#define PACA_IRQ_MUST_HARD_MASK (PACA_IRQ_EE|PACA_IRQ_PMI|PACA_IRQ_REPLAYING) 4862306a36Sopenharmony_ci#else 4962306a36Sopenharmony_ci#define PACA_IRQ_MUST_HARD_MASK (PACA_IRQ_EE|PACA_IRQ_REPLAYING) 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#endif /* CONFIG_PPC64 */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * flags for paca->irq_soft_mask 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define IRQS_ENABLED 0 5862306a36Sopenharmony_ci#define IRQS_DISABLED 1 /* local_irq_disable() interrupts */ 5962306a36Sopenharmony_ci#define IRQS_PMI_DISABLED 2 6062306a36Sopenharmony_ci#define IRQS_ALL_DISABLED (IRQS_DISABLED | IRQS_PMI_DISABLED) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#ifndef __ASSEMBLY__ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic inline void __hard_irq_enable(void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BOOKE_OR_40x)) 6762306a36Sopenharmony_ci wrtee(MSR_EE); 6862306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_8xx)) 6962306a36Sopenharmony_ci wrtspr(SPRN_EIE); 7062306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) 7162306a36Sopenharmony_ci __mtmsrd(MSR_EE | MSR_RI, 1); 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci mtmsr(mfmsr() | MSR_EE); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic inline void __hard_irq_disable(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BOOKE_OR_40x)) 7962306a36Sopenharmony_ci wrtee(0); 8062306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_8xx)) 8162306a36Sopenharmony_ci wrtspr(SPRN_EID); 8262306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) 8362306a36Sopenharmony_ci __mtmsrd(MSR_RI, 1); 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci mtmsr(mfmsr() & ~MSR_EE); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic inline void __hard_EE_RI_disable(void) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BOOKE_OR_40x)) 9162306a36Sopenharmony_ci wrtee(0); 9262306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_8xx)) 9362306a36Sopenharmony_ci wrtspr(SPRN_NRI); 9462306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) 9562306a36Sopenharmony_ci __mtmsrd(0, 1); 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci mtmsr(mfmsr() & ~(MSR_EE | MSR_RI)); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic inline void __hard_RI_enable(void) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BOOKE_OR_40x)) 10362306a36Sopenharmony_ci return; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_8xx)) 10662306a36Sopenharmony_ci wrtspr(SPRN_EID); 10762306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) 10862306a36Sopenharmony_ci __mtmsrd(MSR_RI, 1); 10962306a36Sopenharmony_ci else 11062306a36Sopenharmony_ci mtmsr(mfmsr() | MSR_RI); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#ifdef CONFIG_PPC64 11462306a36Sopenharmony_ci#include <asm/paca.h> 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic inline notrace unsigned long irq_soft_mask_return(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned long flags; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci asm volatile( 12162306a36Sopenharmony_ci "lbz %0,%1(13)" 12262306a36Sopenharmony_ci : "=r" (flags) 12362306a36Sopenharmony_ci : "i" (offsetof(struct paca_struct, irq_soft_mask))); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return flags; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * The "memory" clobber acts as both a compiler barrier 13062306a36Sopenharmony_ci * for the critical section and as a clobber because 13162306a36Sopenharmony_ci * we changed paca->irq_soft_mask 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic inline notrace void irq_soft_mask_set(unsigned long mask) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * The irq mask must always include the STD bit if any are set. 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * and interrupts don't get replayed until the standard 13962306a36Sopenharmony_ci * interrupt (local_irq_disable()) is unmasked. 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Other masks must only provide additional masking beyond 14262306a36Sopenharmony_ci * the standard, and they are also not replayed until the 14362306a36Sopenharmony_ci * standard interrupt becomes unmasked. 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * This could be changed, but it will require partial 14662306a36Sopenharmony_ci * unmasks to be replayed, among other things. For now, take 14762306a36Sopenharmony_ci * the simple approach. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) 15062306a36Sopenharmony_ci WARN_ON(mask && !(mask & IRQS_DISABLED)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci asm volatile( 15362306a36Sopenharmony_ci "stb %0,%1(13)" 15462306a36Sopenharmony_ci : 15562306a36Sopenharmony_ci : "r" (mask), 15662306a36Sopenharmony_ci "i" (offsetof(struct paca_struct, irq_soft_mask)) 15762306a36Sopenharmony_ci : "memory"); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline notrace unsigned long irq_soft_mask_set_return(unsigned long mask) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned long flags = irq_soft_mask_return(); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci irq_soft_mask_set(mask); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return flags; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic inline notrace unsigned long irq_soft_mask_or_return(unsigned long mask) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci unsigned long flags = irq_soft_mask_return(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci irq_soft_mask_set(flags | mask); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return flags; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic inline notrace unsigned long irq_soft_mask_andc_return(unsigned long mask) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci unsigned long flags = irq_soft_mask_return(); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci irq_soft_mask_set(flags & ~mask); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return flags; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline unsigned long arch_local_save_flags(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return irq_soft_mask_return(); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline void arch_local_irq_disable(void) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci irq_soft_mask_set(IRQS_DISABLED); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciextern void arch_local_irq_restore(unsigned long); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic inline void arch_local_irq_enable(void) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci arch_local_irq_restore(IRQS_ENABLED); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline unsigned long arch_local_irq_save(void) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return irq_soft_mask_or_return(IRQS_DISABLED); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline bool arch_irqs_disabled_flags(unsigned long flags) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci return flags & IRQS_DISABLED; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic inline bool arch_irqs_disabled(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci return arch_irqs_disabled_flags(arch_local_save_flags()); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic inline void set_pmi_irq_pending(void) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Invoked from PMU callback functions to set PMI bit in the paca. 22362306a36Sopenharmony_ci * This has to be called with irq's disabled (via hard_irq_disable()). 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) 22662306a36Sopenharmony_ci WARN_ON_ONCE(mfmsr() & MSR_EE); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci get_paca()->irq_happened |= PACA_IRQ_PMI; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline void clear_pmi_irq_pending(void) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * Invoked from PMU callback functions to clear the pending PMI bit 23562306a36Sopenharmony_ci * in the paca. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) 23862306a36Sopenharmony_ci WARN_ON_ONCE(mfmsr() & MSR_EE); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci get_paca()->irq_happened &= ~PACA_IRQ_PMI; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic inline bool pmi_irq_pending(void) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci /* 24662306a36Sopenharmony_ci * Invoked from PMU callback functions to check if there is a pending 24762306a36Sopenharmony_ci * PMI bit in the paca. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci if (get_paca()->irq_happened & PACA_IRQ_PMI) 25062306a36Sopenharmony_ci return true; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return false; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S 25662306a36Sopenharmony_ci/* 25762306a36Sopenharmony_ci * To support disabling and enabling of irq with PMI, set of 25862306a36Sopenharmony_ci * new powerpc_local_irq_pmu_save() and powerpc_local_irq_restore() 25962306a36Sopenharmony_ci * functions are added. These macros are implemented using generic 26062306a36Sopenharmony_ci * linux local_irq_* code from include/linux/irqflags.h. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci#define raw_local_irq_pmu_save(flags) \ 26362306a36Sopenharmony_ci do { \ 26462306a36Sopenharmony_ci typecheck(unsigned long, flags); \ 26562306a36Sopenharmony_ci flags = irq_soft_mask_or_return(IRQS_DISABLED | \ 26662306a36Sopenharmony_ci IRQS_PMI_DISABLED); \ 26762306a36Sopenharmony_ci } while(0) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define raw_local_irq_pmu_restore(flags) \ 27062306a36Sopenharmony_ci do { \ 27162306a36Sopenharmony_ci typecheck(unsigned long, flags); \ 27262306a36Sopenharmony_ci arch_local_irq_restore(flags); \ 27362306a36Sopenharmony_ci } while(0) 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci#ifdef CONFIG_TRACE_IRQFLAGS 27662306a36Sopenharmony_ci#define powerpc_local_irq_pmu_save(flags) \ 27762306a36Sopenharmony_ci do { \ 27862306a36Sopenharmony_ci raw_local_irq_pmu_save(flags); \ 27962306a36Sopenharmony_ci if (!raw_irqs_disabled_flags(flags)) \ 28062306a36Sopenharmony_ci trace_hardirqs_off(); \ 28162306a36Sopenharmony_ci } while(0) 28262306a36Sopenharmony_ci#define powerpc_local_irq_pmu_restore(flags) \ 28362306a36Sopenharmony_ci do { \ 28462306a36Sopenharmony_ci if (!raw_irqs_disabled_flags(flags)) \ 28562306a36Sopenharmony_ci trace_hardirqs_on(); \ 28662306a36Sopenharmony_ci raw_local_irq_pmu_restore(flags); \ 28762306a36Sopenharmony_ci } while(0) 28862306a36Sopenharmony_ci#else 28962306a36Sopenharmony_ci#define powerpc_local_irq_pmu_save(flags) \ 29062306a36Sopenharmony_ci do { \ 29162306a36Sopenharmony_ci raw_local_irq_pmu_save(flags); \ 29262306a36Sopenharmony_ci } while(0) 29362306a36Sopenharmony_ci#define powerpc_local_irq_pmu_restore(flags) \ 29462306a36Sopenharmony_ci do { \ 29562306a36Sopenharmony_ci raw_local_irq_pmu_restore(flags); \ 29662306a36Sopenharmony_ci } while (0) 29762306a36Sopenharmony_ci#endif /* CONFIG_TRACE_IRQFLAGS */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci#endif /* CONFIG_PPC_BOOK3S */ 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#define hard_irq_disable() do { \ 30262306a36Sopenharmony_ci unsigned long flags; \ 30362306a36Sopenharmony_ci __hard_irq_disable(); \ 30462306a36Sopenharmony_ci flags = irq_soft_mask_set_return(IRQS_ALL_DISABLED); \ 30562306a36Sopenharmony_ci local_paca->irq_happened |= PACA_IRQ_HARD_DIS; \ 30662306a36Sopenharmony_ci if (!arch_irqs_disabled_flags(flags)) { \ 30762306a36Sopenharmony_ci asm volatile("std%X0 %1,%0" : "=m" (local_paca->saved_r1) \ 30862306a36Sopenharmony_ci : "r" (current_stack_pointer)); \ 30962306a36Sopenharmony_ci trace_hardirqs_off(); \ 31062306a36Sopenharmony_ci } \ 31162306a36Sopenharmony_ci} while(0) 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic inline bool __lazy_irq_pending(u8 irq_happened) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci return !!(irq_happened & ~PACA_IRQ_HARD_DIS); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * Check if a lazy IRQ is pending. Should be called with IRQs hard disabled. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistatic inline bool lazy_irq_pending(void) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci return __lazy_irq_pending(get_paca()->irq_happened); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci * Check if a lazy IRQ is pending, with no debugging checks. 32862306a36Sopenharmony_ci * Should be called with IRQs hard disabled. 32962306a36Sopenharmony_ci * For use in RI disabled code or other constrained situations. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_cistatic inline bool lazy_irq_pending_nocheck(void) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci return __lazy_irq_pending(local_paca->irq_happened); 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cibool power_pmu_wants_prompt_pmi(void); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/* 33962306a36Sopenharmony_ci * This is called by asynchronous interrupts to check whether to 34062306a36Sopenharmony_ci * conditionally re-enable hard interrupts after having cleared 34162306a36Sopenharmony_ci * the source of the interrupt. They are kept disabled if there 34262306a36Sopenharmony_ci * is a different soft-masked interrupt pending that requires hard 34362306a36Sopenharmony_ci * masking. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cistatic inline bool should_hard_irq_enable(struct pt_regs *regs) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) { 34862306a36Sopenharmony_ci WARN_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED); 34962306a36Sopenharmony_ci WARN_ON(!(get_paca()->irq_happened & PACA_IRQ_HARD_DIS)); 35062306a36Sopenharmony_ci WARN_ON(mfmsr() & MSR_EE); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PERF_EVENTS)) 35462306a36Sopenharmony_ci return false; 35562306a36Sopenharmony_ci /* 35662306a36Sopenharmony_ci * If the PMU is not running, there is not much reason to enable 35762306a36Sopenharmony_ci * MSR[EE] in irq handlers because any interrupts would just be 35862306a36Sopenharmony_ci * soft-masked. 35962306a36Sopenharmony_ci * 36062306a36Sopenharmony_ci * TODO: Add test for 64e 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) { 36362306a36Sopenharmony_ci if (!power_pmu_wants_prompt_pmi()) 36462306a36Sopenharmony_ci return false; 36562306a36Sopenharmony_ci /* 36662306a36Sopenharmony_ci * If PMIs are disabled then IRQs should be disabled as well, 36762306a36Sopenharmony_ci * so we shouldn't see this condition, check for it just in 36862306a36Sopenharmony_ci * case because we are about to enable PMIs. 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ci if (WARN_ON_ONCE(regs->softe & IRQS_PMI_DISABLED)) 37162306a36Sopenharmony_ci return false; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (get_paca()->irq_happened & PACA_IRQ_MUST_HARD_MASK) 37562306a36Sopenharmony_ci return false; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return true; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * Do the hard enabling, only call this if should_hard_irq_enable is true. 38262306a36Sopenharmony_ci * This allows PMI interrupts to profile irq handlers. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_cistatic inline void do_hard_irq_enable(void) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * Asynch interrupts come in with IRQS_ALL_DISABLED, 38862306a36Sopenharmony_ci * PACA_IRQ_HARD_DIS, and MSR[EE]=0. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) 39162306a36Sopenharmony_ci irq_soft_mask_andc_return(IRQS_PMI_DISABLED); 39262306a36Sopenharmony_ci get_paca()->irq_happened &= ~PACA_IRQ_HARD_DIS; 39362306a36Sopenharmony_ci __hard_irq_enable(); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic inline bool arch_irq_disabled_regs(struct pt_regs *regs) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci return (regs->softe & IRQS_DISABLED); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ciextern bool prep_irq_for_idle(void); 40262306a36Sopenharmony_ciextern bool prep_irq_for_idle_irqsoff(void); 40362306a36Sopenharmony_ciextern void irq_set_pending_from_srr1(unsigned long srr1); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci#define fini_irq_for_idle_irqsoff() trace_hardirqs_off(); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciextern void force_external_irq_replay(void); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic inline void irq_soft_mask_regs_set_state(struct pt_regs *regs, unsigned long val) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci regs->softe = val; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci#else /* CONFIG_PPC64 */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic inline notrace unsigned long irq_soft_mask_return(void) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic inline unsigned long arch_local_save_flags(void) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci return mfmsr(); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic inline void arch_local_irq_restore(unsigned long flags) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BOOKE)) 42862306a36Sopenharmony_ci wrtee(flags); 42962306a36Sopenharmony_ci else 43062306a36Sopenharmony_ci mtmsr(flags); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic inline unsigned long arch_local_irq_save(void) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci unsigned long flags = arch_local_save_flags(); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_BOOKE)) 43862306a36Sopenharmony_ci wrtee(0); 43962306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_PPC_8xx)) 44062306a36Sopenharmony_ci wrtspr(SPRN_EID); 44162306a36Sopenharmony_ci else 44262306a36Sopenharmony_ci mtmsr(flags & ~MSR_EE); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return flags; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic inline void arch_local_irq_disable(void) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci __hard_irq_disable(); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic inline void arch_local_irq_enable(void) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci __hard_irq_enable(); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic inline bool arch_irqs_disabled_flags(unsigned long flags) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci return (flags & MSR_EE) == 0; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic inline bool arch_irqs_disabled(void) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci return arch_irqs_disabled_flags(arch_local_save_flags()); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci#define hard_irq_disable() arch_local_irq_disable() 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic inline bool arch_irq_disabled_regs(struct pt_regs *regs) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci return !(regs->msr & MSR_EE); 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic __always_inline bool should_hard_irq_enable(struct pt_regs *regs) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci return false; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic inline void do_hard_irq_enable(void) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci BUILD_BUG(); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic inline void clear_pmi_irq_pending(void) { } 48562306a36Sopenharmony_cistatic inline void set_pmi_irq_pending(void) { } 48662306a36Sopenharmony_cistatic inline bool pmi_irq_pending(void) { return false; } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic inline void irq_soft_mask_regs_set_state(struct pt_regs *regs, unsigned long val) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci#endif /* CONFIG_PPC64 */ 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic inline unsigned long mtmsr_isync_irqsafe(unsigned long msr) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci#ifdef CONFIG_PPC64 49662306a36Sopenharmony_ci if (arch_irqs_disabled()) { 49762306a36Sopenharmony_ci /* 49862306a36Sopenharmony_ci * With soft-masking, MSR[EE] can change from 1 to 0 49962306a36Sopenharmony_ci * asynchronously when irqs are disabled, and we don't want to 50062306a36Sopenharmony_ci * set MSR[EE] back to 1 here if that has happened. A race-free 50162306a36Sopenharmony_ci * way to do this is ensure EE is already 0. Another way it 50262306a36Sopenharmony_ci * could be done is with a RESTART_TABLE handler, but that's 50362306a36Sopenharmony_ci * probably overkill here. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci msr &= ~MSR_EE; 50662306a36Sopenharmony_ci mtmsr_isync(msr); 50762306a36Sopenharmony_ci irq_soft_mask_set(IRQS_ALL_DISABLED); 50862306a36Sopenharmony_ci local_paca->irq_happened |= PACA_IRQ_HARD_DIS; 50962306a36Sopenharmony_ci } else 51062306a36Sopenharmony_ci#endif 51162306a36Sopenharmony_ci mtmsr_isync(msr); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return msr; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci#define ARCH_IRQ_INIT_FLAGS IRQ_NOREQUEST 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci#endif /* __ASSEMBLY__ */ 52062306a36Sopenharmony_ci#endif /* __KERNEL__ */ 52162306a36Sopenharmony_ci#endif /* _ASM_POWERPC_HW_IRQ_H */ 522