Files
mckernel/arch/arm64/kernel/psci.c
Dominique Martinet 516ab87ab9 Copyrights: fujitsu 2018 bump
Separate copyright bumps in a different commit.
A lot of files only had the copyright change at this point; these
were probably changes I added separatly in other patches but just
split these in a different commit instead to simplify git stats

Change-Id: I93cf3fc1c0fa04ee743a79c3fe9768933e6bd0d2
2019-02-01 13:18:52 +09:00

154 lines
3.7 KiB
C

/* psci.c COPYRIGHT FUJITSU LIMITED 2015-2018 */
/* @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 <will.deacon@arm.com>
*/
#include <psci.h>
#include <errno.h>
#include <ihk/types.h>
#include <ihk/debug.h>
#include <compiler.h>
#include <lwk/compiler.h>
#include <debug.h>
//#define DEBUG_PRINT_PSCI
#ifdef DEBUG_PRINT_PSCI
#undef DDEBUG_DEFAULT
#define DDEBUG_DEFAULT DDEBUG_PRINT
#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);
}