xref: /kvm-unit-tests/lib/arm/psci.c (revision 201b9e8bdc84c6436dd53b45d93a60c681b92719)
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