#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: smccc.c,v 1.4 2023/11/07 13:31:26 rin Exp $");
#include <sys/param.h>
#include <sys/kernel.h>
#include <arm/arm/psci.h>
#include <arm/arm/smccc.h>
#if defined(__arm__)
# if defined(__clang__)
#define SMCCC_ARCH_ATTRIBUTE __attribute__ ((target("armv7ve")))
# else
#define SMCCC_ARCH_ATTRIBUTE __attribute__ ((target("arch=armv7ve")))
# endif
#else
#define SMCCC_ARCH_ATTRIBUTE
#endif
#define PSCI_VERSION_1_0 0x10000
#define SMCCC_VERSION 0x80000000
static bool smccc_present;
static enum psci_conduit smccc_conduit = PSCI_CONDUIT_NONE;
bool
smccc_probe(void)
{
if (cold && !smccc_present) {
if (!psci_available() || psci_version() < PSCI_VERSION_1_0) {
return false;
}
smccc_present = psci_features(SMCCC_VERSION) == PSCI_SUCCESS;
if (smccc_present) {
smccc_conduit = psci_conduit();
aprint_debug("SMCCC: Version %#x (%s)\n",
smccc_version(),
smccc_conduit == PSCI_CONDUIT_SMC ? "SMC" : "HVC");
}
}
return smccc_present;
}
int
smccc_version(void)
{
return smccc_call(SMCCC_VERSION, 0, 0, 0, 0,
NULL, NULL, NULL, NULL);
}
SMCCC_ARCH_ATTRIBUTE int
smccc_call(uint32_t fid,
register_t arg1, register_t arg2, register_t arg3, register_t arg4,
register_t *res0, register_t *res1, register_t *res2, register_t *res3)
{
register_t args[5] = { fid, arg1, arg2, arg3, arg4 };
register register_t r0 asm ("r0");
register register_t r1 asm ("r1");
register register_t r2 asm ("r2");
register register_t r3 asm ("r3");
register register_t r4 asm ("r4");
if (!smccc_present) {
return SMCCC_NOT_SUPPORTED;
}
KASSERT(smccc_conduit != PSCI_CONDUIT_NONE);
r0 = args[0];
r1 = args[1];
r2 = args[2];
r3 = args[3];
r4 = args[4];
if (smccc_conduit == PSCI_CONDUIT_SMC) {
asm volatile ("smc #0" :
"=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) :
"r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4) :
"memory");
} else {
asm volatile ("hvc #0" :
"=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) :
"r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4) :
"memory");
}
if (res0) {
*res0 = r0;
}
if (res1) {
*res1 = r1;
}
if (res2) {
*res2 = r2;
}
if (res3) {
*res3 = r3;
}
return r0;
}