/* * PSCI API * From arch/arm[64]/kernel/psci.c * * Copyright (C) 2015, Red Hat Inc, Andrew Jones * * This work is licensed under the terms of the GNU LGPL, version 2. */ #include #include #include #include #include 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 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(); }