/* psci.c COPYRIGHT FUJITSU LIMITED 2015-2016 */ /* @ref.impl arch/arm64/kernel/psci.c */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2013 ARM Limited * * Author: Will Deacon */ #include #include #include #include #include #include //#define DEBUG_PRINT_PSCI #ifdef DEBUG_PRINT_PSCI #define dkprintf(...) kprintf(__VA_ARGS__) #define ekprintf(...) kprintf(__VA_ARGS__) #else #define dkprintf(...) do { if (0) kprintf(__VA_ARGS__); } while (0) #define ekprintf(...) kprintf(__VA_ARGS__) #endif #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 extern uint64_t ihk_param_cpu_logical_map; static uint64_t *__cpu_logical_map = &ihk_param_cpu_logical_map; #define cpu_logical_map(cpu) __cpu_logical_map[cpu] struct psci_power_state { uint16_t id; uint8_t type; uint8_t affinity_level; }; static int psci_to_linux_errno(int errno) { switch (errno) { case PSCI_RET_SUCCESS: return 0; case PSCI_RET_NOT_SUPPORTED: return -EOPNOTSUPP; case PSCI_RET_INVALID_PARAMS: return -EINVAL; case PSCI_RET_DENIED: return -EPERM; }; return -EINVAL; } static uint32_t psci_power_state_pack(struct psci_power_state state) { return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT) & PSCI_0_2_POWER_STATE_ID_MASK) | ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT) & PSCI_0_2_POWER_STATE_TYPE_MASK) | ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT) & PSCI_0_2_POWER_STATE_AFFL_MASK); } static noinline int __invoke_psci_fn_hvc(uint64_t function_id, uint64_t arg0, uint64_t arg1, uint64_t arg2) { asm volatile( __asmeq("%0", "x0") __asmeq("%1", "x1") __asmeq("%2", "x2") __asmeq("%3", "x3") "hvc #0\n" : "+r" (function_id) : "r" (arg0), "r" (arg1), "r" (arg2)); return function_id; } static noinline int __invoke_psci_fn_smc(uint64_t function_id, uint64_t arg0, uint64_t arg1, uint64_t arg2) { asm volatile( __asmeq("%0", "x0") __asmeq("%1", "x1") __asmeq("%2", "x2") __asmeq("%3", "x3") "smc #0\n" : "+r" (function_id) : "r" (arg0), "r" (arg1), "r" (arg2)); return function_id; } static int (*invoke_psci_fn)(uint64_t, uint64_t, uint64_t, uint64_t) = NULL; #define PSCI_METHOD_INVALID -1 #define PSCI_METHOD_HVC 0 #define PSCI_METHOD_SMC 1 int psci_init(void) { extern unsigned long ihk_param_psci_method; int ret = 0; if (ihk_param_psci_method == PSCI_METHOD_SMC) { dkprintf("psci_init(): set invoke_psci_fn = __invoke_psci_fn_smc\n"); invoke_psci_fn = __invoke_psci_fn_smc; } else if (ihk_param_psci_method == PSCI_METHOD_HVC) { dkprintf("psci_init(): set invoke_psci_fn = __invoke_psci_fn_hvc\n"); invoke_psci_fn = __invoke_psci_fn_hvc; } else { ekprintf("psci_init(): ihk_param_psci_method invalid value. %ld\n", ihk_param_psci_method); ret = -1; } return ret; } int psci_cpu_off(void) { int err; uint32_t fn, power_state; struct psci_power_state state = { .type = PSCI_POWER_STATE_TYPE_POWER_DOWN, }; fn = PSCI_0_2_FN_CPU_OFF; power_state = psci_power_state_pack(state); err = invoke_psci_fn(fn, power_state, 0, 0); return psci_to_linux_errno(err); } static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point) { int err; uint32_t fn; fn = PSCI_0_2_FN64_CPU_ON; err = invoke_psci_fn(fn, cpuid, entry_point, 0); return psci_to_linux_errno(err); } int cpu_psci_cpu_boot(unsigned int cpu, unsigned long pc) { return psci_cpu_on(cpu_logical_map(cpu), pc); }