add arm64 support
- add arm64 dependent codes with GICv3 and SVE support - fix bugs based on architecture separation requests
This commit is contained in:
155
arch/arm64/kernel/psci.c
Normal file
155
arch/arm64/kernel/psci.c
Normal file
@ -0,0 +1,155 @@
|
||||
/* 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 <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>
|
||||
|
||||
//#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);
|
||||
}
|
||||
Reference in New Issue
Block a user