blob: e0614e09450e8a297ce80ca76eeae37fbd2bab23 [file] [log] [blame]
/*
* PSCI API
* From arch/arm[64]/kernel/psci.c
*
* Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.
*/
#include <devicetree.h>
#include <asm/psci.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/smp.h>
static int psci_invoke_none(unsigned int function_id, unsigned long arg0,
unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5, unsigned long arg6,
unsigned long arg7, unsigned long arg8,
unsigned long arg9, unsigned long arg10,
struct smccc_result *result)
{
printf("No PSCI method configured! Can't invoke...\n");
return PSCI_RET_NOT_PRESENT;
}
smccc_invoke_fn psci_invoke_fn = psci_invoke_none;
int psci_invoke(unsigned int function_id, unsigned long arg0,
unsigned long arg1, unsigned long arg2)
{
return psci_invoke_fn(function_id, arg0, arg1, arg2, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
}
int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
{
#ifdef __arm__
return psci_invoke(PSCI_0_2_FN_CPU_ON, cpuid, entry_point, 0);
#else
return psci_invoke(PSCI_0_2_FN64_CPU_ON, cpuid, entry_point, 0);
#endif
}
extern void secondary_entry(void);
int cpu_psci_cpu_boot(unsigned int cpu)
{
int err = psci_cpu_on(cpus[cpu], __pa(secondary_entry));
if (err)
printf("failed to boot CPU%d (%d)\n", cpu, err);
return err;
}
void cpu_psci_cpu_die(void)
{
int err = psci_invoke(PSCI_0_2_FN_CPU_OFF, 0, 0, 0);
printf("CPU%d unable to power off (error = %d)\n", smp_processor_id(), err);
}
void psci_system_reset(void)
{
psci_invoke(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
}
void psci_system_off(void)
{
int err = psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
printf("CPU%d unable to do system off (error = %d)\n", smp_processor_id(), err);
}
static void psci_set_conduit_fdt(void)
{
const void *fdt = dt_fdt();
const struct fdt_property *method;
int node, len;
node = fdt_node_offset_by_compatible(fdt, -1, "arm,psci-0.2");
assert_msg(node >= 0, "PSCI v0.2 compatibility required");
method = fdt_get_property(fdt, node, "method", &len);
assert(method != NULL && len == 4);
if (strcmp(method->data, "hvc") == 0)
psci_invoke_fn = arm_smccc_hvc;
else if (strcmp(method->data, "smc") == 0)
psci_invoke_fn = arm_smccc_smc;
else
assert_msg(false, "Unknown PSCI conduit: %s", method->data);
}
#ifdef CONFIG_EFI
#include <acpi.h>
static void psci_set_conduit_acpi(void)
{
struct acpi_table_fadt *fadt = find_acpi_table_addr(FACP_SIGNATURE);
assert_msg(fadt, "Unable to find ACPI FADT");
assert_msg(fadt->arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT,
"PSCI is not supported in this platform");
if (fadt->arm_boot_flags & ACPI_FADT_PSCI_USE_HVC)
psci_invoke_fn = arm_smccc_hvc;
else
psci_invoke_fn = arm_smccc_smc;
}
#else
static void psci_set_conduit_acpi(void)
{
assert_msg(false, "ACPI not available");
}
#endif
void psci_set_conduit(void)
{
if (dt_available())
psci_set_conduit_fdt();
else
psci_set_conduit_acpi();
}