summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/cpu/armv7/smccc-call.S2
-rw-r--r--arch/arm/cpu/armv8/Kconfig1
-rw-r--r--arch/arm/cpu/armv8/fwcall.c52
-rw-r--r--arch/arm/cpu/armv8/smccc-call.S2
-rw-r--r--drivers/firmware/psci.c100
-rw-r--r--include/linux/psci.h6
6 files changed, 96 insertions, 67 deletions
diff --git a/arch/arm/cpu/armv7/smccc-call.S b/arch/arm/cpu/armv7/smccc-call.S
index 0d8b59eb6b..eae69e36c3 100644
--- a/arch/arm/cpu/armv7/smccc-call.S
+++ b/arch/arm/cpu/armv7/smccc-call.S
@@ -7,6 +7,8 @@
#include <asm/opcodes-sec.h>
#include <asm/opcodes-virt.h>
+ .section .text.efi_runtime
+
#define UNWIND(x...)
/*
* Wrap c macros in asm macros to delay expansion until after the
diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig
index ff42791fb4..1c12bbde75 100644
--- a/arch/arm/cpu/armv8/Kconfig
+++ b/arch/arm/cpu/armv8/Kconfig
@@ -96,6 +96,7 @@ endmenu
config PSCI_RESET
bool "Use PSCI for reset and shutdown"
default y
+ select ARM_SMCCC if OF_CONTROL
depends on !ARCH_EXYNOS7 && !ARCH_BCM283X && \
!TARGET_LS2080A_SIMU && !TARGET_LS2080AQDS && \
!TARGET_LS2080ARDB && !TARGET_LS2080A_EMU && \
diff --git a/arch/arm/cpu/armv8/fwcall.c b/arch/arm/cpu/armv8/fwcall.c
index 0ba3dad8cc..9957c2974b 100644
--- a/arch/arm/cpu/armv8/fwcall.c
+++ b/arch/arm/cpu/armv8/fwcall.c
@@ -7,7 +7,6 @@
#include <asm-offsets.h>
#include <config.h>
-#include <efi_loader.h>
#include <version.h>
#include <asm/macro.h>
#include <asm/psci.h>
@@ -19,7 +18,7 @@
* x0~x7: input arguments
* x0~x3: output arguments
*/
-static void __efi_runtime hvc_call(struct pt_regs *args)
+static void hvc_call(struct pt_regs *args)
{
asm volatile(
"ldr x0, %0\n"
@@ -53,7 +52,7 @@ static void __efi_runtime hvc_call(struct pt_regs *args)
* x0~x3: output arguments
*/
-void __efi_runtime smc_call(struct pt_regs *args)
+void smc_call(struct pt_regs *args)
{
asm volatile(
"ldr x0, %0\n"
@@ -83,9 +82,9 @@ void __efi_runtime smc_call(struct pt_regs *args)
* use PSCI on U-Boot running below a hypervisor, please detect
* this and set the flag accordingly.
*/
-static const __efi_runtime_data bool use_smc_for_psci = true;
+static const bool use_smc_for_psci = true;
-void __noreturn __efi_runtime psci_system_reset(void)
+void __noreturn psci_system_reset(void)
{
struct pt_regs regs;
@@ -100,7 +99,7 @@ void __noreturn __efi_runtime psci_system_reset(void)
;
}
-void __noreturn __efi_runtime psci_system_off(void)
+void __noreturn psci_system_off(void)
{
struct pt_regs regs;
@@ -114,44 +113,3 @@ void __noreturn __efi_runtime psci_system_off(void)
while (1)
;
}
-
-#ifdef CONFIG_CMD_POWEROFF
-int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
-{
- puts("poweroff ...\n");
-
- udelay(50000); /* wait 50 ms */
-
- disable_interrupts();
-
- psci_system_off();
-
- /*NOTREACHED*/
- return 0;
-}
-#endif
-
-#ifdef CONFIG_PSCI_RESET
-void reset_misc(void)
-{
- psci_system_reset();
-}
-
-#ifdef CONFIG_EFI_LOADER
-void __efi_runtime EFIAPI efi_reset_system(
- enum efi_reset_type reset_type,
- efi_status_t reset_status,
- unsigned long data_size, void *reset_data)
-{
- if (reset_type == EFI_RESET_COLD ||
- reset_type == EFI_RESET_WARM ||
- reset_type == EFI_RESET_PLATFORM_SPECIFIC) {
- psci_system_reset();
- } else if (reset_type == EFI_RESET_SHUTDOWN) {
- psci_system_off();
- }
-
- while (1) { }
-}
-#endif /* CONFIG_EFI_LOADER */
-#endif /* CONFIG_PSCI_RESET */
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S
index 16c9e298b4..86de4b4089 100644
--- a/arch/arm/cpu/armv8/smccc-call.S
+++ b/arch/arm/cpu/armv8/smccc-call.S
@@ -6,6 +6,8 @@
#include <linux/arm-smccc.h>
#include <generated/asm-offsets.h>
+ .section .text.efi_runtime
+
.macro SMCCC instr
.cfi_startproc
\instr #0
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 2cb35f356f..c8c47acfd3 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -9,31 +9,38 @@
#include <common.h>
#include <dm.h>
#include <dm/lists.h>
+#include <efi_loader.h>
#include <linux/libfdt.h>
#include <linux/arm-smccc.h>
#include <linux/errno.h>
#include <linux/printk.h>
#include <linux/psci.h>
-psci_fn *invoke_psci_fn;
+#define DRIVER_NAME "psci"
-static unsigned long __invoke_psci_fn_hvc(unsigned long function_id,
- unsigned long arg0, unsigned long arg1,
- unsigned long arg2)
-{
- struct arm_smccc_res res;
+#define PSCI_METHOD_HVC 1
+#define PSCI_METHOD_SMC 2
- arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
- return res.a0;
-}
+int __efi_runtime_data psci_method;
-static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
- unsigned long arg0, unsigned long arg1,
- unsigned long arg2)
+unsigned long __efi_runtime invoke_psci_fn
+ (unsigned long function_id, unsigned long arg0,
+ unsigned long arg1, unsigned long arg2)
{
struct arm_smccc_res res;
- arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ /*
+ * In the __efi_runtime we need to avoid the switch statement. In some
+ * cases the compiler creates lookup tables to implement switch. These
+ * tables are not correctly relocated when SetVirtualAddressMap is
+ * called.
+ */
+ if (psci_method == PSCI_METHOD_SMC)
+ arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ else if (psci_method == PSCI_METHOD_HVC)
+ arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
+ else
+ res.a0 = PSCI_RET_DISABLED;
return res.a0;
}
@@ -67,9 +74,9 @@ static int psci_probe(struct udevice *dev)
}
if (!strcmp("hvc", method)) {
- invoke_psci_fn = __invoke_psci_fn_hvc;
+ psci_method = PSCI_METHOD_HVC;
} else if (!strcmp("smc", method)) {
- invoke_psci_fn = __invoke_psci_fn_smc;
+ psci_method = PSCI_METHOD_SMC;
} else {
pr_warn("invalid \"method\" property: %s\n", method);
return -EINVAL;
@@ -78,6 +85,67 @@ static int psci_probe(struct udevice *dev)
return 0;
}
+/**
+ * void do_psci_probe() - probe PSCI firmware driver
+ *
+ * Ensure that psci_method is initialized.
+ */
+static void __maybe_unused do_psci_probe(void)
+{
+ struct udevice *dev;
+
+ uclass_get_device_by_name(UCLASS_FIRMWARE, DRIVER_NAME, &dev);
+}
+
+#if IS_ENABLED(CONFIG_EFI_LOADER) && IS_ENABLED(CONFIG_PSCI_RESET)
+efi_status_t efi_reset_system_init(void)
+{
+ do_psci_probe();
+ return EFI_SUCCESS;
+}
+
+void __efi_runtime EFIAPI efi_reset_system(enum efi_reset_type reset_type,
+ efi_status_t reset_status,
+ unsigned long data_size,
+ void *reset_data)
+{
+ if (reset_type == EFI_RESET_COLD ||
+ reset_type == EFI_RESET_WARM ||
+ reset_type == EFI_RESET_PLATFORM_SPECIFIC) {
+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+ } else if (reset_type == EFI_RESET_SHUTDOWN) {
+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+ }
+ while (1)
+ ;
+}
+#endif /* IS_ENABLED(CONFIG_EFI_LOADER) && IS_ENABLED(CONFIG_PSCI_RESET) */
+
+#ifdef CONFIG_PSCI_RESET
+void reset_misc(void)
+{
+ do_psci_probe();
+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+}
+#endif /* CONFIG_PSCI_RESET */
+
+#ifdef CONFIG_CMD_POWEROFF
+int do_poweroff(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ do_psci_probe();
+
+ puts("poweroff ...\n");
+ udelay(50000); /* wait 50 ms */
+
+ disable_interrupts();
+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+ enable_interrupts();
+
+ log_err("Power off not supported on this platform\n");
+ return CMD_RET_FAILURE;
+}
+#endif
+
static const struct udevice_id psci_of_match[] = {
{ .compatible = "arm,psci" },
{ .compatible = "arm,psci-0.2" },
@@ -86,7 +154,7 @@ static const struct udevice_id psci_of_match[] = {
};
U_BOOT_DRIVER(psci) = {
- .name = "psci",
+ .name = DRIVER_NAME,
.id = UCLASS_FIRMWARE,
.of_match = psci_of_match,
.bind = psci_bind,
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 8d13bd2702..9433df836b 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -88,10 +88,8 @@
#define PSCI_RET_DISABLED -8
#ifdef CONFIG_ARM_PSCI_FW
-typedef unsigned long (psci_fn)(unsigned long, unsigned long,
- unsigned long, unsigned long);
-
-extern psci_fn *invoke_psci_fn;
+unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3);
#else
unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3)