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 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 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 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); 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 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 59 void psci_system_reset(void) 60 { 61 psci_invoke(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); 62 } 63 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 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 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 110 static void psci_set_conduit_acpi(void) 111 { 112 assert_msg(false, "ACPI not available"); 113 } 114 115 #endif 116 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