diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ce3044c4ae7..e3384b26ad8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -645,11 +645,17 @@ config ARCH_CHIP_TLSR82 config ARCH_CHIP_QEMU_ARM bool "QEMU virt platform (ARMv7a)" + select ARCH_HAVE_PSCI + select ARCH_HAVE_POWEROFF + select ARCH_HAVE_RESET ---help--- QEMU virt platform (ARMv7a) config ARCH_CHIP_GOLDFISH_ARM bool "GOLDFISH virt platform (ARMv7a)" + select ARCH_HAVE_PSCI + select ARCH_HAVE_POWEROFF + select ARCH_HAVE_RESET ---help--- GOLDFISH virt platform (ARMv7a) @@ -1064,6 +1070,16 @@ config ARM_THUMB bool "Thumb Mode" default n +config ARCH_HAVE_PSCI + bool "ARM PCSI (Power State Coordination Interface) Support" + default n + ---help--- + This Power State Coordination Interface (PSCI) defines + a standard interface for power management. the PCSI need + to implement handling firmware at EL2 or EL3. The option + maybe not applicable for arm core without PCSI firmware + interface implement + config ARM_HAVE_WFE_SEV bool default n diff --git a/arch/arm/include/syscall.h b/arch/arm/include/syscall.h index 3b424bbb08a..ca5fd9dbf48 100644 --- a/arch/arm/include/syscall.h +++ b/arch/arm/include/syscall.h @@ -126,6 +126,31 @@ * Public Types ****************************************************************************/ +/* Result from SMC/HVC call + * a0-a7 result values from registers 0 to 7 + */ + +struct arm_smccc_res +{ + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; +}; + +typedef struct arm_smccc_res arm_smccc_res_t; + +enum arm_smccc_conduit +{ + SMCCC_CONDUIT_NONE, + SMCCC_CONDUIT_SMC, + SMCCC_CONDUIT_HVC, +}; + /**************************************************************************** * Inline functions ****************************************************************************/ @@ -316,6 +341,32 @@ static inline long smh_call(unsigned int nbr, void *parm) * Public Function Prototypes ****************************************************************************/ +/* Make HVC calls + * + * param a0 function identifier + * param a1-a7 parameters registers + * param res results + */ + +void arm_smccc_hvc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +/* Make SMC calls + * + * param a0 function identifier + * param a1-a7 parameters registers + * param res results + */ + +void arm_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + #ifdef __cplusplus #define EXTERN extern "C" extern "C" diff --git a/arch/arm/src/common/CMakeLists.txt b/arch/arm/src/common/CMakeLists.txt index ac630ec46b9..c1dcef481db 100644 --- a/arch/arm/src/common/CMakeLists.txt +++ b/arch/arm/src/common/CMakeLists.txt @@ -55,6 +55,11 @@ if(NOT CONFIG_ARCH_IDLE_CUSTOM) list(APPEND SRCS arm_idle.c) endif() +if(CONFIG_ARCH_HAVE_PSCI) + list(APPEND SRCS arm_smccc.S) + list(APPEND SRCS arm_cpu_psci.c) +endif() + if(CONFIG_BUILD_PROTECTED OR CONFIG_BUILD_KERNEL) list(APPEND SRCS arm_task_start.c arm_pthread_start.c arm_signal_dispatch.c) diff --git a/arch/arm/src/common/Make.defs b/arch/arm/src/common/Make.defs index 7a8073db1c5..850411428c5 100644 --- a/arch/arm/src/common/Make.defs +++ b/arch/arm/src/common/Make.defs @@ -41,6 +41,11 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CMN_CSRCS += arm_idle.c endif +ifeq ($(CONFIG_ARCH_HAVE_PSCI),y) + CMN_ASRCS += arm_smccc.S + CMN_CSRCS += arm_cpu_psci.c +endif + ifeq ($(CONFIG_BUILD_PROTECTED)$(CONFIG_BUILD_KERNEL),y) CMN_CSRCS += arm_task_start.c arm_pthread_start.c CMN_CSRCS += arm_signal_dispatch.c diff --git a/arch/arm/src/common/arm_cpu_psci.c b/arch/arm/src/common/arm_cpu_psci.c new file mode 100644 index 00000000000..8e7dd201e81 --- /dev/null +++ b/arch/arm/src/common/arm_cpu_psci.c @@ -0,0 +1,232 @@ +/*************************************************************************** + * arch/arm/src/common/arm_cpu_psci.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ***************************************************************************/ + +/*************************************************************************** + * Included Files + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "arch/syscall.h" + +#include "arm_cpu_psci.h" + +/*************************************************************************** + * Private Data + ***************************************************************************/ + +static struct psci_interface psci_data; + +/*************************************************************************** + * Private Functions + ***************************************************************************/ + +static int psci_to_dev_err(int ret) +{ + switch (ret) + { + case PSCI_RET_SUCCESS: + { + return 0; + } + + case PSCI_RET_NOT_SUPPORTED: + { + return -ENOTSUP; + } + + case PSCI_RET_INVALID_PARAMS: + case PSCI_RET_INVALID_ADDRESS: + { + return -EINVAL; + } + + case PSCI_RET_DENIED: + { + return -EPERM; + } + } + + return -EINVAL; +} + +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; + + arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); + return res.a0; +} + +static unsigned long __invoke_psci_fn_smc(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); + return res.a0; +} + +static uint32_t psci_get_version(void) +{ + return psci_data.invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); +} + +static int set_conduit_method(const char *method) +{ + if (!strcmp("hvc", method)) + { + psci_data.conduit = SMCCC_CONDUIT_HVC; + psci_data.invoke_psci_fn = __invoke_psci_fn_hvc; + } + else if (!strcmp("smc", method)) + { + psci_data.conduit = SMCCC_CONDUIT_SMC; + psci_data.invoke_psci_fn = __invoke_psci_fn_smc; + } + else + { + serr("Invalid conduit method"); + return -EINVAL; + } + + return 0; +} + +static int psci_detect(void) +{ + uint32_t ver = psci_get_version(); + + sinfo("Detected PSCI v%ld.%ld\n", + PSCI_VERSION_MAJOR(ver), PSCI_VERSION_MINOR(ver)); + + if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) + { + serr("PSCI unsupported version"); + return -ENOTSUP; + } + + psci_data.version = ver; + + return 0; +} + +/*************************************************************************** + * Public Functions + ***************************************************************************/ + +uint32_t psci_version(void) +{ + return psci_data.version; +} + +int psci_cpu_off(void) +{ + int ret; + + if (psci_data.conduit == SMCCC_CONDUIT_NONE) + { + return -EINVAL; + } + + ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_CPU_OFF, 0, 0, 0); + + return psci_to_dev_err(ret); +} + +int psci_cpu_reset(void) +{ + int ret; + + if (psci_data.conduit == SMCCC_CONDUIT_NONE) + { + return -EINVAL; + } + + ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + + return psci_to_dev_err(ret); +} + +int psci_cpu_on(unsigned long cpuid, uintptr_t entry_point) +{ + int ret; + + if (psci_data.conduit == SMCCC_CONDUIT_NONE) + { + return -EINVAL; + } + + ret = psci_data.invoke_psci_fn(PSCI_FN_NATIVE(0_2, CPU_ON), + cpuid, (unsigned long)entry_point, 0); + + return psci_to_dev_err(ret); +} + +int psci_sys_reset(void) +{ + int ret; + + if (psci_data.conduit == SMCCC_CONDUIT_NONE) + { + return -EINVAL; + } + + ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + + return psci_to_dev_err(ret); +} + +int psci_sys_poweroff(void) +{ + int ret; + + if (psci_data.conduit == SMCCC_CONDUIT_NONE) + { + return -EINVAL; + } + + ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); + + return psci_to_dev_err(ret); +} + +int arm_psci_init(const char * method) +{ + psci_data.conduit = SMCCC_CONDUIT_NONE; + + if (set_conduit_method(method)) + { + return -ENOTSUP; + } + + return psci_detect(); +} diff --git a/arch/arm/src/common/arm_cpu_psci.h b/arch/arm/src/common/arm_cpu_psci.h new file mode 100644 index 00000000000..26559a599fa --- /dev/null +++ b/arch/arm/src/common/arm_cpu_psci.h @@ -0,0 +1,107 @@ +/**************************************************************************** + * arch/arm/src/common/arm_cpu_psci.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_COMMON_ARM_CPU_PSCI_H +#define __ARCH_ARM_SRC_COMMON_ARM_CPU_PSCI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name + +/* PSCI v0.2 interface */ +#define PSCI_0_2_FN_BASE 0x84000000 +#define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n)) +#define PSCI_0_2_64BIT 0x40000000 +#define PSCI_0_2_FN64_BASE \ + (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT) +#define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n)) + +#define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0) +#define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1) +#define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2) +#define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3) +#define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4) +#define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5) +#define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6) +#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) +#define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) +#define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) + +#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) +#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) +#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4) +#define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) +#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) + +/* PSCI return values (inclusive of all PSCI versions) */ +#define PSCI_RET_SUCCESS 0 +#define PSCI_RET_NOT_SUPPORTED -1 +#define PSCI_RET_INVALID_PARAMS -2 +#define PSCI_RET_DENIED -3 +#define PSCI_RET_ALREADY_ON -4 +#define PSCI_RET_ON_PENDING -5 +#define PSCI_RET_INTERNAL_FAILURE -6 +#define PSCI_RET_NOT_PRESENT -7 +#define PSCI_RET_DISABLED -8 +#define PSCI_RET_INVALID_ADDRESS -9 + +/* PSCI version decoding (independent of PSCI version) */ +#define PSCI_VERSION_MAJOR_SHIFT 16 +#define PSCI_VERSION_MINOR_MASK \ + ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1) +#define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK + +#define PSCI_VERSION_MAJOR(ver) \ + (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) +#define PSCI_VERSION_MINOR(ver) \ + ((ver) & PSCI_VERSION_MINOR_MASK) + +typedef unsigned long (*psci_fn)(unsigned long, unsigned long, unsigned long, + unsigned long); + +struct psci_interface +{ + enum arm_smccc_conduit conduit; + psci_fn invoke_psci_fn; + uint32_t version; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +uint32_t psci_version(void); +int psci_cpu_off(void); +int psci_cpu_reset(void); +int psci_cpu_on(unsigned long cpuid, uintptr_t entry_point); +int psci_sys_poweroff(void); +int psci_sys_reset(void); + +#endif /* __ARCH_ARM_SRC_COMMON_ARM_CPU_PSCI_H */ diff --git a/arch/arm/src/common/arm_internal.h b/arch/arm/src/common/arm_internal.h index ca9517f2cea..a1857578d32 100644 --- a/arch/arm/src/common/arm_internal.h +++ b/arch/arm/src/common/arm_internal.h @@ -323,6 +323,8 @@ void modifyreg32(unsigned int addr, uint32_t clearbits, uint32_t setbits); void arm_boot(void); +int arm_psci_init(const char *method); + /* Context switching */ uint32_t *arm_decodeirq(uint32_t *regs); diff --git a/arch/arm/src/common/arm_smccc.S b/arch/arm/src/common/arm_smccc.S new file mode 100644 index 00000000000..d9874b0e953 --- /dev/null +++ b/arch/arm/src/common/arm_smccc.S @@ -0,0 +1,76 @@ +/**************************************************************************** + * arch/arm/src/common/arm_smccc.S + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Public Symbols + ****************************************************************************/ + + .file "arm_smccc.S" + +/**************************************************************************** + * Assembly Macros + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* The SMC instruction is used to generate a synchronous exception that is + * handled by Secure Monitor code running in EL3. + */ + + .text + .syntax unified + + .global arm_smccc_smc + .type arm_smccc_smc, function +arm_smccc_smc: + mov r12, sp + push {r4-r7} + ldm r12, {r4-r7} + smc #0 + pop {r4-r7} + ldr r12, [sp, #(4 * 4)] + stm r12, {r0-r3} + bx lr + +/* The HVC instruction is used to generate a synchronous exception that is + * handled by a hypervisor running in EL2. + */ + + .global arm_smccc_hvc + .type arm_smccc_hvc, function +arm_smccc_hvc: + mov r12, sp + push {r4-r7} + ldm r12, {r4-r7} + hvc #0 + pop {r4-r7} + ldr r12, [sp, #(4 * 4)] + stm r12, {r0-r3} + bx lr diff --git a/arch/arm/src/goldfish/Make.defs b/arch/arm/src/goldfish/Make.defs index 041de91a52e..9a2c66b1724 100644 --- a/arch/arm/src/goldfish/Make.defs +++ b/arch/arm/src/goldfish/Make.defs @@ -22,3 +22,4 @@ include armv7-a/Make.defs # goldfish-specific C source files CHIP_CSRCS = goldfish_boot.c goldfish_serial.c goldfish_irq.c goldfish_timer.c goldfish_memorymap.c + diff --git a/arch/arm/src/goldfish/goldfish_boot.c b/arch/arm/src/goldfish/goldfish_boot.c index 311743c69bc..77b58e3278c 100644 --- a/arch/arm/src/goldfish/goldfish_boot.c +++ b/arch/arm/src/goldfish/goldfish_boot.c @@ -55,6 +55,10 @@ void arm_boot(void) fdt_register((FAR const char *)0x40000000); #endif +#if defined(CONFIG_ARCH_HAVE_PSCI) + arm_psci_init("smc"); +#endif + #ifdef USE_EARLYSERIALINIT /* Perform early serial initialization if we are going to use the serial * driver. diff --git a/arch/arm/src/qemu/qemu_boot.c b/arch/arm/src/qemu/qemu_boot.c index f3992170e14..8022d4a1428 100644 --- a/arch/arm/src/qemu/qemu_boot.c +++ b/arch/arm/src/qemu/qemu_boot.c @@ -51,6 +51,10 @@ void arm_boot(void) qemu_setupmappings(); +#if defined(CONFIG_ARCH_HAVE_PSCI) + arm_psci_init("hvc"); +#endif + #ifdef CONFIG_DEVICE_TREE fdt_register((FAR const char *)0x40000000); #endif