Commit c8cc4273 authored by Mark Rutland's avatar Mark Rutland

arm64: psci: kill psci_power_state

A PSCI 1.0 implementation may choose to use the new extended StateID
format, the presence of which may be queried via the PSCI_FEATURES call.
The layout of this new StateID format is incompatible with the existing
format, and so to handle both we must abstract attempts to parse the
fields.

In preparation for PSCI 1.0 support, this patch introduces
psci_power_state_loses_context and psci_power_state_is_valid functions
to query information from a PSCI power state, which is no longer
decomposed (and hence the pack/unpack functions are removed). As it is
no longer decomposed, it is now passed round as an opaque u32 token.
Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
Acked-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
Acked-by: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Tested-by: default avatarHanjun Guo <hanjun.guo@linaro.org>
Cc: Will Deacon <will.deacon@arm.com>
parent ff3010e6
...@@ -38,11 +38,19 @@ ...@@ -38,11 +38,19 @@
#define PSCI_POWER_STATE_TYPE_STANDBY 0 #define PSCI_POWER_STATE_TYPE_STANDBY 0
#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1
struct psci_power_state { static bool psci_power_state_loses_context(u32 state)
u16 id; {
u8 type; return state & PSCI_0_2_POWER_STATE_TYPE_MASK;
u8 affinity_level; }
};
static bool psci_power_state_is_valid(u32 state)
{
const u32 valid_mask = PSCI_0_2_POWER_STATE_ID_MASK |
PSCI_0_2_POWER_STATE_TYPE_MASK |
PSCI_0_2_POWER_STATE_AFFL_MASK;
return !(state & ~valid_mask);
}
/* /*
* The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF
...@@ -58,9 +66,8 @@ static bool psci_tos_resident_on(int cpu) ...@@ -58,9 +66,8 @@ static bool psci_tos_resident_on(int cpu)
} }
struct psci_operations { struct psci_operations {
int (*cpu_suspend)(struct psci_power_state state, int (*cpu_suspend)(u32 state, unsigned long entry_point);
unsigned long entry_point); int (*cpu_off)(u32 state);
int (*cpu_off)(struct psci_power_state state);
int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);
int (*migrate)(unsigned long cpuid); int (*migrate)(unsigned long cpuid);
int (*affinity_info)(unsigned long target_affinity, int (*affinity_info)(unsigned long target_affinity,
...@@ -84,7 +91,7 @@ enum psci_function { ...@@ -84,7 +91,7 @@ enum psci_function {
PSCI_FN_MAX, PSCI_FN_MAX,
}; };
static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
static u32 psci_function_id[PSCI_FN_MAX]; static u32 psci_function_id[PSCI_FN_MAX];
...@@ -104,53 +111,28 @@ static int psci_to_linux_errno(int errno) ...@@ -104,53 +111,28 @@ static int psci_to_linux_errno(int errno)
return -EINVAL; return -EINVAL;
} }
static u32 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 void psci_power_state_unpack(u32 power_state,
struct psci_power_state *state)
{
state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >>
PSCI_0_2_POWER_STATE_ID_SHIFT;
state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >>
PSCI_0_2_POWER_STATE_TYPE_SHIFT;
state->affinity_level =
(power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >>
PSCI_0_2_POWER_STATE_AFFL_SHIFT;
}
static u32 psci_get_version(void) static u32 psci_get_version(void)
{ {
return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
} }
static int psci_cpu_suspend(struct psci_power_state state, static int psci_cpu_suspend(u32 state, unsigned long entry_point)
unsigned long entry_point)
{ {
int err; int err;
u32 fn, power_state; u32 fn;
fn = psci_function_id[PSCI_FN_CPU_SUSPEND]; fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
power_state = psci_power_state_pack(state); err = invoke_psci_fn(fn, state, entry_point, 0);
err = invoke_psci_fn(fn, power_state, entry_point, 0);
return psci_to_linux_errno(err); return psci_to_linux_errno(err);
} }
static int psci_cpu_off(struct psci_power_state state) static int psci_cpu_off(u32 state)
{ {
int err; int err;
u32 fn, power_state; u32 fn;
fn = psci_function_id[PSCI_FN_CPU_OFF]; fn = psci_function_id[PSCI_FN_CPU_OFF];
power_state = psci_power_state_pack(state); err = invoke_psci_fn(fn, state, 0, 0);
err = invoke_psci_fn(fn, power_state, 0, 0);
return psci_to_linux_errno(err); return psci_to_linux_errno(err);
} }
...@@ -194,7 +176,7 @@ static unsigned long psci_migrate_info_up_cpu(void) ...@@ -194,7 +176,7 @@ static unsigned long psci_migrate_info_up_cpu(void)
static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu) static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
{ {
int i, ret, count = 0; int i, ret, count = 0;
struct psci_power_state *psci_states; u32 *psci_states;
struct device_node *state_node, *cpu_node; struct device_node *state_node, *cpu_node;
cpu_node = of_get_cpu_node(cpu, NULL); cpu_node = of_get_cpu_node(cpu, NULL);
...@@ -223,13 +205,13 @@ static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu) ...@@ -223,13 +205,13 @@ static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
u32 psci_power_state; u32 state;
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
ret = of_property_read_u32(state_node, ret = of_property_read_u32(state_node,
"arm,psci-suspend-param", "arm,psci-suspend-param",
&psci_power_state); &state);
if (ret) { if (ret) {
pr_warn(" * %s missing arm,psci-suspend-param property\n", pr_warn(" * %s missing arm,psci-suspend-param property\n",
state_node->full_name); state_node->full_name);
...@@ -238,9 +220,13 @@ static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu) ...@@ -238,9 +220,13 @@ static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
} }
of_node_put(state_node); of_node_put(state_node);
pr_debug("psci-power-state %#x index %d\n", psci_power_state, pr_debug("psci-power-state %#x index %d\n", state, i);
i); if (!psci_power_state_is_valid(state)) {
psci_power_state_unpack(psci_power_state, &psci_states[i]); pr_warn("Invalid PSCI power state %#x\n", state);
ret = -EINVAL;
goto free_mem;
}
psci_states[i] = state;
} }
/* Idle states parsed correctly, initialize per-cpu pointer */ /* Idle states parsed correctly, initialize per-cpu pointer */
per_cpu(psci_power_state, cpu) = psci_states; per_cpu(psci_power_state, cpu) = psci_states;
...@@ -528,9 +514,8 @@ static void cpu_psci_cpu_die(unsigned int cpu) ...@@ -528,9 +514,8 @@ static void cpu_psci_cpu_die(unsigned int cpu)
* There are no known implementations of PSCI actually using the * There are no known implementations of PSCI actually using the
* power state field, pass a sensible default for now. * power state field, pass a sensible default for now.
*/ */
struct psci_power_state state = { u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<
.type = PSCI_POWER_STATE_TYPE_POWER_DOWN, PSCI_0_2_POWER_STATE_TYPE_SHIFT;
};
ret = psci_ops.cpu_off(state); ret = psci_ops.cpu_off(state);
...@@ -569,7 +554,7 @@ static int cpu_psci_cpu_kill(unsigned int cpu) ...@@ -569,7 +554,7 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
static int psci_suspend_finisher(unsigned long index) static int psci_suspend_finisher(unsigned long index)
{ {
struct psci_power_state *state = __this_cpu_read(psci_power_state); u32 *state = __this_cpu_read(psci_power_state);
return psci_ops.cpu_suspend(state[index - 1], return psci_ops.cpu_suspend(state[index - 1],
virt_to_phys(cpu_resume)); virt_to_phys(cpu_resume));
...@@ -578,7 +563,7 @@ static int psci_suspend_finisher(unsigned long index) ...@@ -578,7 +563,7 @@ static int psci_suspend_finisher(unsigned long index)
static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
{ {
int ret; int ret;
struct psci_power_state *state = __this_cpu_read(psci_power_state); u32 *state = __this_cpu_read(psci_power_state);
/* /*
* idle state index 0 corresponds to wfi, should never be called * idle state index 0 corresponds to wfi, should never be called
* from the cpu_suspend operations * from the cpu_suspend operations
...@@ -586,7 +571,7 @@ static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) ...@@ -586,7 +571,7 @@ static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
if (WARN_ON_ONCE(!index)) if (WARN_ON_ONCE(!index))
return -EINVAL; return -EINVAL;
if (state[index - 1].type == PSCI_POWER_STATE_TYPE_STANDBY) if (!psci_power_state_loses_context(state[index - 1]))
ret = psci_ops.cpu_suspend(state[index - 1], 0); ret = psci_ops.cpu_suspend(state[index - 1], 0);
else else
ret = __cpu_suspend(index, psci_suspend_finisher); ret = __cpu_suspend(index, psci_suspend_finisher);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment