1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Author: Kumar Gala <galak@kernel.crashing.org> 4 * 5 * Copyright 2009 Freescale Semiconductor Inc. 6 */ 7 8 #include <linux/stddef.h> 9 #include <linux/kernel.h> 10 #include <linux/smp.h> 11 #include <linux/threads.h> 12 #include <linux/hardirq.h> 13 14 #include <asm/dbell.h> 15 #include <asm/irq_regs.h> 16 #include <asm/kvm_ppc.h> 17 #include <asm/trace.h> 18 19 #ifdef CONFIG_SMP 20 21 /* 22 * Doorbells must only be used if CPU_FTR_DBELL is available. 23 * msgsnd is used in HV, and msgsndp is used in !HV. 24 * 25 * These should be used by platform code that is aware of restrictions. 26 * Other arch code should use ->cause_ipi. 27 * 28 * doorbell_global_ipi() sends a dbell to any target CPU. 29 * Must be used only by architectures that address msgsnd target 30 * by PIR/get_hard_smp_processor_id. 31 */ 32 void doorbell_global_ipi(int cpu) 33 { 34 u32 tag = get_hard_smp_processor_id(cpu); 35 36 kvmppc_set_host_ipi(cpu); 37 /* Order previous accesses vs. msgsnd, which is treated as a store */ 38 ppc_msgsnd_sync(); 39 ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag); 40 } 41 42 /* 43 * doorbell_core_ipi() sends a dbell to a target CPU in the same core. 44 * Must be used only by architectures that address msgsnd target 45 * by TIR/cpu_thread_in_core. 46 */ 47 void doorbell_core_ipi(int cpu) 48 { 49 u32 tag = cpu_thread_in_core(cpu); 50 51 kvmppc_set_host_ipi(cpu); 52 /* Order previous accesses vs. msgsnd, which is treated as a store */ 53 ppc_msgsnd_sync(); 54 ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag); 55 } 56 57 /* 58 * Attempt to cause a core doorbell if destination is on the same core. 59 * Returns 1 on success, 0 on failure. 60 */ 61 int doorbell_try_core_ipi(int cpu) 62 { 63 int this_cpu = get_cpu(); 64 int ret = 0; 65 66 if (cpumask_test_cpu(cpu, cpu_sibling_mask(this_cpu))) { 67 doorbell_core_ipi(cpu); 68 ret = 1; 69 } 70 71 put_cpu(); 72 73 return ret; 74 } 75 76 void doorbell_exception(struct pt_regs *regs) 77 { 78 struct pt_regs *old_regs = set_irq_regs(regs); 79 80 irq_enter(); 81 trace_doorbell_entry(regs); 82 83 ppc_msgsync(); 84 85 may_hard_irq_enable(); 86 87 kvmppc_clear_host_ipi(smp_processor_id()); 88 __this_cpu_inc(irq_stat.doorbell_irqs); 89 90 smp_ipi_demux_relaxed(); /* already performed the barrier */ 91 92 trace_doorbell_exit(regs); 93 irq_exit(); 94 set_irq_regs(old_regs); 95 } 96 #else /* CONFIG_SMP */ 97 void doorbell_exception(struct pt_regs *regs) 98 { 99 printk(KERN_WARNING "Received doorbell on non-smp system\n"); 100 } 101 #endif /* CONFIG_SMP */ 102 103