18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2009 Freescale Semiconductor, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * provides masks and opcode images for use by code generation, emulation
68c2ecf20Sopenharmony_ci * and for instructions that older assemblers might not know about
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#ifndef _ASM_POWERPC_DBELL_H
98c2ecf20Sopenharmony_ci#define _ASM_POWERPC_DBELL_H
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/smp.h>
128c2ecf20Sopenharmony_ci#include <linux/threads.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/cputhreads.h>
158c2ecf20Sopenharmony_ci#include <asm/ppc-opcode.h>
168c2ecf20Sopenharmony_ci#include <asm/feature-fixups.h>
178c2ecf20Sopenharmony_ci#include <asm/kvm_ppc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define PPC_DBELL_MSG_BRDCAST	(0x04000000)
208c2ecf20Sopenharmony_ci#define PPC_DBELL_TYPE(x)	(((x) & 0xf) << (63-36))
218c2ecf20Sopenharmony_ci#define PPC_DBELL_TYPE_MASK	PPC_DBELL_TYPE(0xf)
228c2ecf20Sopenharmony_ci#define PPC_DBELL_LPID(x)	((x) << (63 - 49))
238c2ecf20Sopenharmony_ci#define PPC_DBELL_PIR_MASK	0x3fff
248c2ecf20Sopenharmony_cienum ppc_dbell {
258c2ecf20Sopenharmony_ci	PPC_DBELL = 0,		/* doorbell */
268c2ecf20Sopenharmony_ci	PPC_DBELL_CRIT = 1,	/* critical doorbell */
278c2ecf20Sopenharmony_ci	PPC_G_DBELL = 2,	/* guest doorbell */
288c2ecf20Sopenharmony_ci	PPC_G_DBELL_CRIT = 3,	/* guest critical doorbell */
298c2ecf20Sopenharmony_ci	PPC_G_DBELL_MC = 4,	/* guest mcheck doorbell */
308c2ecf20Sopenharmony_ci	PPC_DBELL_SERVER = 5,	/* doorbell on server */
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define PPC_DBELL_MSGTYPE		PPC_DBELL_SERVER
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic inline void _ppc_msgsnd(u32 msg)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	__asm__ __volatile__ (ASM_FTR_IFSET(PPC_MSGSND(%1), PPC_MSGSNDP(%1), %0)
408c2ecf20Sopenharmony_ci				: : "i" (CPU_FTR_HVMODE), "r" (msg));
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* sync before sending message */
448c2ecf20Sopenharmony_cistatic inline void ppc_msgsnd_sync(void)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	__asm__ __volatile__ ("sync" : : : "memory");
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* sync after taking message interrupt */
508c2ecf20Sopenharmony_cistatic inline void ppc_msgsync(void)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	/* sync is not required when taking messages from the same core */
538c2ecf20Sopenharmony_ci	__asm__ __volatile__ (ASM_FTR_IFSET(PPC_MSGSYNC " ; lwsync", "", %0)
548c2ecf20Sopenharmony_ci				: : "i" (CPU_FTR_HVMODE|CPU_FTR_ARCH_300));
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic inline void _ppc_msgclr(u32 msg)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	__asm__ __volatile__ (ASM_FTR_IFSET(PPC_MSGCLR(%1), PPC_MSGCLRP(%1), %0)
608c2ecf20Sopenharmony_ci				: : "i" (CPU_FTR_HVMODE), "r" (msg));
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic inline void ppc_msgclr(enum ppc_dbell type)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	u32 msg = PPC_DBELL_TYPE(type);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	_ppc_msgclr(msg);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#else /* CONFIG_PPC_BOOK3S */
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define PPC_DBELL_MSGTYPE		PPC_DBELL
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic inline void _ppc_msgsnd(u32 msg)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	__asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg));
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* sync before sending message */
808c2ecf20Sopenharmony_cistatic inline void ppc_msgsnd_sync(void)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	__asm__ __volatile__ ("sync" : : : "memory");
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* sync after taking message interrupt */
868c2ecf20Sopenharmony_cistatic inline void ppc_msgsync(void)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_BOOK3S */
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ciextern void doorbell_exception(struct pt_regs *regs);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	u32 msg = PPC_DBELL_TYPE(type) | (flags & PPC_DBELL_MSG_BRDCAST) |
978c2ecf20Sopenharmony_ci			(tag & 0x07ffffff);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	_ppc_msgsnd(msg);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * Doorbells must only be used if CPU_FTR_DBELL is available.
1068c2ecf20Sopenharmony_ci * msgsnd is used in HV, and msgsndp is used in !HV.
1078c2ecf20Sopenharmony_ci *
1088c2ecf20Sopenharmony_ci * These should be used by platform code that is aware of restrictions.
1098c2ecf20Sopenharmony_ci * Other arch code should use ->cause_ipi.
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * doorbell_global_ipi() sends a dbell to any target CPU.
1128c2ecf20Sopenharmony_ci * Must be used only by architectures that address msgsnd target
1138c2ecf20Sopenharmony_ci * by PIR/get_hard_smp_processor_id.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic inline void doorbell_global_ipi(int cpu)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	u32 tag = get_hard_smp_processor_id(cpu);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	kvmppc_set_host_ipi(cpu);
1208c2ecf20Sopenharmony_ci	/* Order previous accesses vs. msgsnd, which is treated as a store */
1218c2ecf20Sopenharmony_ci	ppc_msgsnd_sync();
1228c2ecf20Sopenharmony_ci	ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * doorbell_core_ipi() sends a dbell to a target CPU in the same core.
1278c2ecf20Sopenharmony_ci * Must be used only by architectures that address msgsnd target
1288c2ecf20Sopenharmony_ci * by TIR/cpu_thread_in_core.
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_cistatic inline void doorbell_core_ipi(int cpu)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	u32 tag = cpu_thread_in_core(cpu);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	kvmppc_set_host_ipi(cpu);
1358c2ecf20Sopenharmony_ci	/* Order previous accesses vs. msgsnd, which is treated as a store */
1368c2ecf20Sopenharmony_ci	ppc_msgsnd_sync();
1378c2ecf20Sopenharmony_ci	ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, tag);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * Attempt to cause a core doorbell if destination is on the same core.
1428c2ecf20Sopenharmony_ci * Returns 1 on success, 0 on failure.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistatic inline int doorbell_try_core_ipi(int cpu)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int this_cpu = get_cpu();
1478c2ecf20Sopenharmony_ci	int ret = 0;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (cpumask_test_cpu(cpu, cpu_sibling_mask(this_cpu))) {
1508c2ecf20Sopenharmony_ci		doorbell_core_ipi(cpu);
1518c2ecf20Sopenharmony_ci		ret = 1;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	put_cpu();
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return ret;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci#endif /* CONFIG_SMP */
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#endif /* _ASM_POWERPC_DBELL_H */
162