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