xref: /openbmc/linux/arch/powerpc/kernel/dbell.c (revision 8f762fe5)
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