1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-or-later */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2009 Freescale Semiconductor, Inc.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * provides masks and opcode images for use by code generation, emulation
6*4882a593Smuzhiyun * and for instructions that older assemblers might not know about
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun #ifndef _ASM_POWERPC_DBELL_H
9*4882a593Smuzhiyun #define _ASM_POWERPC_DBELL_H
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/smp.h>
12*4882a593Smuzhiyun #include <linux/threads.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <asm/cputhreads.h>
15*4882a593Smuzhiyun #include <asm/ppc-opcode.h>
16*4882a593Smuzhiyun #include <asm/feature-fixups.h>
17*4882a593Smuzhiyun #include <asm/kvm_ppc.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #define PPC_DBELL_MSG_BRDCAST (0x04000000)
20*4882a593Smuzhiyun #define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36))
21*4882a593Smuzhiyun #define PPC_DBELL_TYPE_MASK PPC_DBELL_TYPE(0xf)
22*4882a593Smuzhiyun #define PPC_DBELL_LPID(x) ((x) << (63 - 49))
23*4882a593Smuzhiyun #define PPC_DBELL_PIR_MASK 0x3fff
24*4882a593Smuzhiyun enum ppc_dbell {
25*4882a593Smuzhiyun PPC_DBELL = 0, /* doorbell */
26*4882a593Smuzhiyun PPC_DBELL_CRIT = 1, /* critical doorbell */
27*4882a593Smuzhiyun PPC_G_DBELL = 2, /* guest doorbell */
28*4882a593Smuzhiyun PPC_G_DBELL_CRIT = 3, /* guest critical doorbell */
29*4882a593Smuzhiyun PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */
30*4882a593Smuzhiyun PPC_DBELL_SERVER = 5, /* doorbell on server */
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #ifdef CONFIG_PPC_BOOK3S
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #define PPC_DBELL_MSGTYPE PPC_DBELL_SERVER
36*4882a593Smuzhiyun
_ppc_msgsnd(u32 msg)37*4882a593Smuzhiyun static inline void _ppc_msgsnd(u32 msg)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun __asm__ __volatile__ (ASM_FTR_IFSET(PPC_MSGSND(%1), PPC_MSGSNDP(%1), %0)
40*4882a593Smuzhiyun : : "i" (CPU_FTR_HVMODE), "r" (msg));
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* sync before sending message */
ppc_msgsnd_sync(void)44*4882a593Smuzhiyun static inline void ppc_msgsnd_sync(void)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun __asm__ __volatile__ ("sync" : : : "memory");
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /* sync after taking message interrupt */
ppc_msgsync(void)50*4882a593Smuzhiyun static inline void ppc_msgsync(void)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun /* sync is not required when taking messages from the same core */
53*4882a593Smuzhiyun __asm__ __volatile__ (ASM_FTR_IFSET(PPC_MSGSYNC " ; lwsync", "", %0)
54*4882a593Smuzhiyun : : "i" (CPU_FTR_HVMODE|CPU_FTR_ARCH_300));
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
_ppc_msgclr(u32 msg)57*4882a593Smuzhiyun static inline void _ppc_msgclr(u32 msg)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun __asm__ __volatile__ (ASM_FTR_IFSET(PPC_MSGCLR(%1), PPC_MSGCLRP(%1), %0)
60*4882a593Smuzhiyun : : "i" (CPU_FTR_HVMODE), "r" (msg));
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
ppc_msgclr(enum ppc_dbell type)63*4882a593Smuzhiyun static inline void ppc_msgclr(enum ppc_dbell type)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun u32 msg = PPC_DBELL_TYPE(type);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun _ppc_msgclr(msg);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun #else /* CONFIG_PPC_BOOK3S */
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun #define PPC_DBELL_MSGTYPE PPC_DBELL
73*4882a593Smuzhiyun
_ppc_msgsnd(u32 msg)74*4882a593Smuzhiyun static inline void _ppc_msgsnd(u32 msg)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg));
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun /* sync before sending message */
ppc_msgsnd_sync(void)80*4882a593Smuzhiyun static inline void ppc_msgsnd_sync(void)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun __asm__ __volatile__ ("sync" : : : "memory");
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /* sync after taking message interrupt */
ppc_msgsync(void)86*4882a593Smuzhiyun static inline void ppc_msgsync(void)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun #endif /* CONFIG_PPC_BOOK3S */
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun extern void doorbell_exception(struct pt_regs *regs);
93*4882a593Smuzhiyun
ppc_msgsnd(enum ppc_dbell type,u32 flags,u32 tag)94*4882a593Smuzhiyun static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun u32 msg = PPC_DBELL_TYPE(type) | (flags & PPC_DBELL_MSG_BRDCAST) |
97*4882a593Smuzhiyun (tag & 0x07ffffff);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun _ppc_msgsnd(msg);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun #ifdef CONFIG_SMP
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /*
105*4882a593Smuzhiyun * Doorbells must only be used if CPU_FTR_DBELL is available.
106*4882a593Smuzhiyun * msgsnd is used in HV, and msgsndp is used in !HV.
107*4882a593Smuzhiyun *
108*4882a593Smuzhiyun * These should be used by platform code that is aware of restrictions.
109*4882a593Smuzhiyun * Other arch code should use ->cause_ipi.
110*4882a593Smuzhiyun *
111*4882a593Smuzhiyun * doorbell_global_ipi() sends a dbell to any target CPU.
112*4882a593Smuzhiyun * Must be used only by architectures that address msgsnd target
113*4882a593Smuzhiyun * by PIR/get_hard_smp_processor_id.
114*4882a593Smuzhiyun */
doorbell_global_ipi(int cpu)115*4882a593Smuzhiyun static inline void doorbell_global_ipi(int cpu)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun u32 tag = get_hard_smp_processor_id(cpu);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun kvmppc_set_host_ipi(cpu);
120*4882a593Smuzhiyun /* Order previous accesses vs. msgsnd, which is treated as a store */
121*4882a593Smuzhiyun ppc_msgsnd_sync();
122*4882a593Smuzhiyun ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /*
126*4882a593Smuzhiyun * doorbell_core_ipi() sends a dbell to a target CPU in the same core.
127*4882a593Smuzhiyun * Must be used only by architectures that address msgsnd target
128*4882a593Smuzhiyun * by TIR/cpu_thread_in_core.
129*4882a593Smuzhiyun */
doorbell_core_ipi(int cpu)130*4882a593Smuzhiyun static inline void doorbell_core_ipi(int cpu)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun u32 tag = cpu_thread_in_core(cpu);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun kvmppc_set_host_ipi(cpu);
135*4882a593Smuzhiyun /* Order previous accesses vs. msgsnd, which is treated as a store */
136*4882a593Smuzhiyun ppc_msgsnd_sync();
137*4882a593Smuzhiyun ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun /*
141*4882a593Smuzhiyun * Attempt to cause a core doorbell if destination is on the same core.
142*4882a593Smuzhiyun * Returns 1 on success, 0 on failure.
143*4882a593Smuzhiyun */
doorbell_try_core_ipi(int cpu)144*4882a593Smuzhiyun static inline int doorbell_try_core_ipi(int cpu)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun int this_cpu = get_cpu();
147*4882a593Smuzhiyun int ret = 0;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun if (cpumask_test_cpu(cpu, cpu_sibling_mask(this_cpu))) {
150*4882a593Smuzhiyun doorbell_core_ipi(cpu);
151*4882a593Smuzhiyun ret = 1;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun put_cpu();
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return ret;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun #endif /* CONFIG_SMP */
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun #endif /* _ASM_POWERPC_DBELL_H */
162