xref: /openbmc/linux/arch/powerpc/kernel/dbell.c (revision 5b394b2d)
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 
22 #ifdef CONFIG_SMP
23 
24 /*
25  * Doorbells must only be used if CPU_FTR_DBELL is available.
26  * msgsnd is used in HV, and msgsndp is used in !HV.
27  *
28  * These should be used by platform code that is aware of restrictions.
29  * Other arch code should use ->cause_ipi.
30  *
31  * doorbell_global_ipi() sends a dbell to any target CPU.
32  * Must be used only by architectures that address msgsnd target
33  * by PIR/get_hard_smp_processor_id.
34  */
35 void doorbell_global_ipi(int cpu)
36 {
37 	u32 tag = get_hard_smp_processor_id(cpu);
38 
39 	kvmppc_set_host_ipi(cpu, 1);
40 	/* Order previous accesses vs. msgsnd, which is treated as a store */
41 	ppc_msgsnd_sync();
42 	ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
43 }
44 
45 /*
46  * doorbell_core_ipi() sends a dbell to a target CPU in the same core.
47  * Must be used only by architectures that address msgsnd target
48  * by TIR/cpu_thread_in_core.
49  */
50 void doorbell_core_ipi(int cpu)
51 {
52 	u32 tag = cpu_thread_in_core(cpu);
53 
54 	kvmppc_set_host_ipi(cpu, 1);
55 	/* Order previous accesses vs. msgsnd, which is treated as a store */
56 	ppc_msgsnd_sync();
57 	ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
58 }
59 
60 /*
61  * Attempt to cause a core doorbell if destination is on the same core.
62  * Returns 1 on success, 0 on failure.
63  */
64 int doorbell_try_core_ipi(int cpu)
65 {
66 	int this_cpu = get_cpu();
67 	int ret = 0;
68 
69 	if (cpumask_test_cpu(cpu, cpu_sibling_mask(this_cpu))) {
70 		doorbell_core_ipi(cpu);
71 		ret = 1;
72 	}
73 
74 	put_cpu();
75 
76 	return ret;
77 }
78 
79 void doorbell_exception(struct pt_regs *regs)
80 {
81 	struct pt_regs *old_regs = set_irq_regs(regs);
82 
83 	irq_enter();
84 
85 	ppc_msgsync();
86 
87 	may_hard_irq_enable();
88 
89 	kvmppc_set_host_ipi(smp_processor_id(), 0);
90 	__this_cpu_inc(irq_stat.doorbell_irqs);
91 
92 	smp_ipi_demux_relaxed(); /* already performed the barrier */
93 
94 	irq_exit();
95 	set_irq_regs(old_regs);
96 }
97 #else /* CONFIG_SMP */
98 void doorbell_exception(struct pt_regs *regs)
99 {
100 	printk(KERN_WARNING "Received doorbell on non-smp system\n");
101 }
102 #endif /* CONFIG_SMP */
103 
104