1f0705d4cSAndrew Jones /*
2f0705d4cSAndrew Jones * PSCI API
3f0705d4cSAndrew Jones * From arch/arm[64]/kernel/psci.c
4f0705d4cSAndrew Jones *
5f0705d4cSAndrew Jones * Copyright (C) 2015, Red Hat Inc, Andrew Jones <drjones@redhat.com>
6f0705d4cSAndrew Jones *
7f0705d4cSAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2.
8f0705d4cSAndrew Jones */
9bd5bd157SAndrew Jones #include <devicetree.h>
10f0705d4cSAndrew Jones #include <asm/psci.h>
1168ea0e0bSAndrew Jones #include <asm/setup.h>
1268ea0e0bSAndrew Jones #include <asm/page.h>
13d556c4a8SAndrew Jones #include <asm/smp.h>
14f0705d4cSAndrew Jones
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)15bd5bd157SAndrew Jones static int psci_invoke_none(unsigned int function_id, unsigned long arg0,
16*cddb18bcSAlexandru Elisei unsigned long arg1, unsigned long arg2,
17*cddb18bcSAlexandru Elisei unsigned long arg3, unsigned long arg4,
18*cddb18bcSAlexandru Elisei unsigned long arg5, unsigned long arg6,
19*cddb18bcSAlexandru Elisei unsigned long arg7, unsigned long arg8,
20*cddb18bcSAlexandru Elisei unsigned long arg9, unsigned long arg10,
21*cddb18bcSAlexandru Elisei struct smccc_result *result)
22f0705d4cSAndrew Jones {
23bd5bd157SAndrew Jones printf("No PSCI method configured! Can't invoke...\n");
24bd5bd157SAndrew Jones return PSCI_RET_NOT_PRESENT;
25f0705d4cSAndrew Jones }
26f0705d4cSAndrew Jones
27*cddb18bcSAlexandru Elisei smccc_invoke_fn psci_invoke_fn = psci_invoke_none;
28*cddb18bcSAlexandru Elisei
psci_invoke(unsigned int function_id,unsigned long arg0,unsigned long arg1,unsigned long arg2)29*cddb18bcSAlexandru Elisei int psci_invoke(unsigned int function_id, unsigned long arg0,
30*cddb18bcSAlexandru Elisei unsigned long arg1, unsigned long arg2)
31*cddb18bcSAlexandru Elisei {
32*cddb18bcSAlexandru Elisei return psci_invoke_fn(function_id, arg0, arg1, arg2, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
33*cddb18bcSAlexandru Elisei }
34bd5bd157SAndrew Jones
psci_cpu_on(unsigned long cpuid,unsigned long entry_point)35f0705d4cSAndrew Jones int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
36f0705d4cSAndrew Jones {
37c535b2f8SAndrew Jones #ifdef __arm__
38c535b2f8SAndrew Jones return psci_invoke(PSCI_0_2_FN_CPU_ON, cpuid, entry_point, 0);
39c535b2f8SAndrew Jones #else
40c535b2f8SAndrew Jones return psci_invoke(PSCI_0_2_FN64_CPU_ON, cpuid, entry_point, 0);
41c535b2f8SAndrew Jones #endif
42f0705d4cSAndrew Jones }
43f0705d4cSAndrew Jones
4468ea0e0bSAndrew Jones extern void secondary_entry(void);
cpu_psci_cpu_boot(unsigned int cpu)4568ea0e0bSAndrew Jones int cpu_psci_cpu_boot(unsigned int cpu)
4668ea0e0bSAndrew Jones {
4768ea0e0bSAndrew Jones int err = psci_cpu_on(cpus[cpu], __pa(secondary_entry));
4868ea0e0bSAndrew Jones if (err)
4968ea0e0bSAndrew Jones printf("failed to boot CPU%d (%d)\n", cpu, err);
5068ea0e0bSAndrew Jones return err;
5168ea0e0bSAndrew Jones }
5268ea0e0bSAndrew Jones
cpu_psci_cpu_die(void)53d556c4a8SAndrew Jones void cpu_psci_cpu_die(void)
5468ea0e0bSAndrew Jones {
55b40a6180SAlexandru Elisei int err = psci_invoke(PSCI_0_2_FN_CPU_OFF, 0, 0, 0);
56d556c4a8SAndrew Jones printf("CPU%d unable to power off (error = %d)\n", smp_processor_id(), err);
5768ea0e0bSAndrew Jones }
5868ea0e0bSAndrew Jones
psci_system_reset(void)597c12e7caSAlexandru Elisei void psci_system_reset(void)
60f0705d4cSAndrew Jones {
61f0705d4cSAndrew Jones psci_invoke(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
62f0705d4cSAndrew Jones }
637c12e7caSAlexandru Elisei
psci_system_off(void)647c12e7caSAlexandru Elisei void psci_system_off(void)
657c12e7caSAlexandru Elisei {
667c12e7caSAlexandru Elisei int err = psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
677c12e7caSAlexandru Elisei printf("CPU%d unable to do system off (error = %d)\n", smp_processor_id(), err);
687c12e7caSAlexandru Elisei }
69bd5bd157SAndrew Jones
psci_set_conduit_fdt(void)70bff097a8SNikos Nikoleris static void psci_set_conduit_fdt(void)
71bd5bd157SAndrew Jones {
72bd5bd157SAndrew Jones const void *fdt = dt_fdt();
73bd5bd157SAndrew Jones const struct fdt_property *method;
74bd5bd157SAndrew Jones int node, len;
75bd5bd157SAndrew Jones
76bd5bd157SAndrew Jones node = fdt_node_offset_by_compatible(fdt, -1, "arm,psci-0.2");
77bd5bd157SAndrew Jones assert_msg(node >= 0, "PSCI v0.2 compatibility required");
78bd5bd157SAndrew Jones
79bd5bd157SAndrew Jones method = fdt_get_property(fdt, node, "method", &len);
80bd5bd157SAndrew Jones assert(method != NULL && len == 4);
81bd5bd157SAndrew Jones
82bd5bd157SAndrew Jones if (strcmp(method->data, "hvc") == 0)
83*cddb18bcSAlexandru Elisei psci_invoke_fn = arm_smccc_hvc;
84bd5bd157SAndrew Jones else if (strcmp(method->data, "smc") == 0)
85*cddb18bcSAlexandru Elisei psci_invoke_fn = arm_smccc_smc;
86bd5bd157SAndrew Jones else
87bd5bd157SAndrew Jones assert_msg(false, "Unknown PSCI conduit: %s", method->data);
88bd5bd157SAndrew Jones }
89bff097a8SNikos Nikoleris
90bff097a8SNikos Nikoleris #ifdef CONFIG_EFI
91bff097a8SNikos Nikoleris
92bff097a8SNikos Nikoleris #include <acpi.h>
93bff097a8SNikos Nikoleris
psci_set_conduit_acpi(void)94bff097a8SNikos Nikoleris static void psci_set_conduit_acpi(void)
95bff097a8SNikos Nikoleris {
96bff097a8SNikos Nikoleris struct acpi_table_fadt *fadt = find_acpi_table_addr(FACP_SIGNATURE);
97bff097a8SNikos Nikoleris
98bff097a8SNikos Nikoleris assert_msg(fadt, "Unable to find ACPI FADT");
99bff097a8SNikos Nikoleris assert_msg(fadt->arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT,
100bff097a8SNikos Nikoleris "PSCI is not supported in this platform");
101bff097a8SNikos Nikoleris
102bff097a8SNikos Nikoleris if (fadt->arm_boot_flags & ACPI_FADT_PSCI_USE_HVC)
103*cddb18bcSAlexandru Elisei psci_invoke_fn = arm_smccc_hvc;
104bff097a8SNikos Nikoleris else
105*cddb18bcSAlexandru Elisei psci_invoke_fn = arm_smccc_smc;
106bff097a8SNikos Nikoleris }
107bff097a8SNikos Nikoleris
108bff097a8SNikos Nikoleris #else
109bff097a8SNikos Nikoleris
psci_set_conduit_acpi(void)110bff097a8SNikos Nikoleris static void psci_set_conduit_acpi(void)
111bff097a8SNikos Nikoleris {
112bff097a8SNikos Nikoleris assert_msg(false, "ACPI not available");
113bff097a8SNikos Nikoleris }
114bff097a8SNikos Nikoleris
115bff097a8SNikos Nikoleris #endif
116bff097a8SNikos Nikoleris
psci_set_conduit(void)117bff097a8SNikos Nikoleris void psci_set_conduit(void)
118bff097a8SNikos Nikoleris {
119bff097a8SNikos Nikoleris if (dt_available())
120bff097a8SNikos Nikoleris psci_set_conduit_fdt();
121bff097a8SNikos Nikoleris else
122bff097a8SNikos Nikoleris psci_set_conduit_acpi();
123bff097a8SNikos Nikoleris }
124