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