Commit 825294cd authored by Olof Johansson's avatar Olof Johansson

Merge tag 'firmware/psci-1.0' of...

Merge tag 'firmware/psci-1.0' of git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux into next/drivers

This pull request contains patches that enable PSCI 1.0 firmware
features for arm/arm64 platforms:

- Lorenzo Pieralisi adds support for the PSCI_FEATURES call, manages
  various 1.0 specifications updates (power state id and functions return
  values) and provides PSCI v1.0 DT bindings
- Sudeep Holla implements PSCI v1.0 system suspend support to enable PSCI
  based suspend-to-RAM

* tag 'firmware/psci-1.0' of git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux:
  drivers: firmware: psci: add system suspend support
  drivers: firmware: psci: define more generic PSCI_FN_NATIVE macro
  drivers: firmware: psci: add PSCI v1.0 DT bindings
  drivers: firmware: psci: add extended stateid power_state support
  drivers: firmware: psci: add PSCI_FEATURES call
  drivers: firmware: psci: move power_state handling to generic code
  drivers: firmware: psci: add INVALID_ADDRESS return value
Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 73ebb854 faf7ec4a
...@@ -31,6 +31,10 @@ Main node required properties: ...@@ -31,6 +31,10 @@ Main node required properties:
support, but are permitted to be present for compatibility with support, but are permitted to be present for compatibility with
existing software when "arm,psci" is later in the compatible list. existing software when "arm,psci" is later in the compatible list.
* "arm,psci-1.0" : for implementations complying to PSCI 1.0. PSCI 1.0 is
backward compatible with PSCI 0.2 with minor specification updates,
as defined in the PSCI specification[2].
- method : The method of calling the PSCI firmware. Permitted - method : The method of calling the PSCI firmware. Permitted
values are: values are:
...@@ -100,3 +104,5 @@ Case 3: PSCI v0.2 and PSCI v0.1. ...@@ -100,3 +104,5 @@ Case 3: PSCI v0.2 and PSCI v0.1.
[1] Kernel documentation - ARM idle states bindings [1] Kernel documentation - ARM idle states bindings
Documentation/devicetree/bindings/arm/idle-states.txt Documentation/devicetree/bindings/arm/idle-states.txt
[2] Power State Coordination Interface (PSCI) specification
http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf
...@@ -30,20 +30,6 @@ ...@@ -30,20 +30,6 @@
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include <asm/suspend.h> #include <asm/suspend.h>
static bool psci_power_state_loses_context(u32 state)
{
return state & PSCI_0_2_POWER_STATE_TYPE_MASK;
}
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);
}
static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu) static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
......
...@@ -20,23 +20,25 @@ ...@@ -20,23 +20,25 @@
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/psci.h> #include <linux/psci.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/suspend.h>
#include <uapi/linux/psci.h> #include <uapi/linux/psci.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/system_misc.h> #include <asm/system_misc.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include <asm/suspend.h>
/* /*
* While a 64-bit OS can make calls with SMC32 calling conventions, for some * While a 64-bit OS can make calls with SMC32 calling conventions, for some
* calls it is necessary to use SMC64 to pass or return 64-bit values. For such * calls it is necessary to use SMC64 to pass or return 64-bit values.
* calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate (native-width) * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate
* function ID. * (native-width) function ID.
*/ */
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN64_##name #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name
#else #else
#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN_##name #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name
#endif #endif
/* /*
...@@ -70,6 +72,41 @@ enum psci_function { ...@@ -70,6 +72,41 @@ enum psci_function {
static u32 psci_function_id[PSCI_FN_MAX]; static u32 psci_function_id[PSCI_FN_MAX];
#define PSCI_0_2_POWER_STATE_MASK \
(PSCI_0_2_POWER_STATE_ID_MASK | \
PSCI_0_2_POWER_STATE_TYPE_MASK | \
PSCI_0_2_POWER_STATE_AFFL_MASK)
#define PSCI_1_0_EXT_POWER_STATE_MASK \
(PSCI_1_0_EXT_POWER_STATE_ID_MASK | \
PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
static u32 psci_cpu_suspend_feature;
static inline bool psci_has_ext_power_state(void)
{
return psci_cpu_suspend_feature &
PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
}
bool psci_power_state_loses_context(u32 state)
{
const u32 mask = psci_has_ext_power_state() ?
PSCI_1_0_EXT_POWER_STATE_TYPE_MASK :
PSCI_0_2_POWER_STATE_TYPE_MASK;
return state & mask;
}
bool psci_power_state_is_valid(u32 state)
{
const u32 valid_mask = psci_has_ext_power_state() ?
PSCI_1_0_EXT_POWER_STATE_MASK :
PSCI_0_2_POWER_STATE_MASK;
return !(state & ~valid_mask);
}
static int psci_to_linux_errno(int errno) static int psci_to_linux_errno(int errno)
{ {
switch (errno) { switch (errno) {
...@@ -78,6 +115,7 @@ static int psci_to_linux_errno(int errno) ...@@ -78,6 +115,7 @@ static int psci_to_linux_errno(int errno)
case PSCI_RET_NOT_SUPPORTED: case PSCI_RET_NOT_SUPPORTED:
return -EOPNOTSUPP; return -EOPNOTSUPP;
case PSCI_RET_INVALID_PARAMS: case PSCI_RET_INVALID_PARAMS:
case PSCI_RET_INVALID_ADDRESS:
return -EINVAL; return -EINVAL;
case PSCI_RET_DENIED: case PSCI_RET_DENIED:
return -EPERM; return -EPERM;
...@@ -134,7 +172,7 @@ static int psci_migrate(unsigned long cpuid) ...@@ -134,7 +172,7 @@ static int psci_migrate(unsigned long cpuid)
static int psci_affinity_info(unsigned long target_affinity, static int psci_affinity_info(unsigned long target_affinity,
unsigned long lowest_affinity_level) unsigned long lowest_affinity_level)
{ {
return invoke_psci_fn(PSCI_0_2_FN_NATIVE(AFFINITY_INFO), return invoke_psci_fn(PSCI_FN_NATIVE(0_2, AFFINITY_INFO),
target_affinity, lowest_affinity_level, 0); target_affinity, lowest_affinity_level, 0);
} }
...@@ -145,7 +183,7 @@ static int psci_migrate_info_type(void) ...@@ -145,7 +183,7 @@ static int psci_migrate_info_type(void)
static unsigned long psci_migrate_info_up_cpu(void) static unsigned long psci_migrate_info_up_cpu(void)
{ {
return invoke_psci_fn(PSCI_0_2_FN_NATIVE(MIGRATE_INFO_UP_CPU), return invoke_psci_fn(PSCI_FN_NATIVE(0_2, MIGRATE_INFO_UP_CPU),
0, 0, 0); 0, 0, 0);
} }
...@@ -181,6 +219,49 @@ static void psci_sys_poweroff(void) ...@@ -181,6 +219,49 @@ static void psci_sys_poweroff(void)
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
} }
static int __init psci_features(u32 psci_func_id)
{
return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
psci_func_id, 0, 0);
}
static int psci_system_suspend(unsigned long unused)
{
return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
virt_to_phys(cpu_resume), 0, 0);
}
static int psci_system_suspend_enter(suspend_state_t state)
{
return cpu_suspend(0, psci_system_suspend);
}
static const struct platform_suspend_ops psci_suspend_ops = {
.valid = suspend_valid_only_mem,
.enter = psci_system_suspend_enter,
};
static void __init psci_init_system_suspend(void)
{
int ret;
if (!IS_ENABLED(CONFIG_SUSPEND))
return;
ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
if (ret != PSCI_RET_NOT_SUPPORTED)
suspend_set_ops(&psci_suspend_ops);
}
static void __init psci_init_cpu_suspend(void)
{
int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]);
if (feature != PSCI_RET_NOT_SUPPORTED)
psci_cpu_suspend_feature = feature;
}
/* /*
* Detect the presence of a resident Trusted OS which may cause CPU_OFF to * Detect the presence of a resident Trusted OS which may cause CPU_OFF to
* return DENIED (which would be fatal). * return DENIED (which would be fatal).
...@@ -224,16 +305,17 @@ static void __init psci_init_migrate(void) ...@@ -224,16 +305,17 @@ static void __init psci_init_migrate(void)
static void __init psci_0_2_set_functions(void) static void __init psci_0_2_set_functions(void)
{ {
pr_info("Using standard PSCI v0.2 function IDs\n"); pr_info("Using standard PSCI v0.2 function IDs\n");
psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_NATIVE(CPU_SUSPEND); psci_function_id[PSCI_FN_CPU_SUSPEND] =
PSCI_FN_NATIVE(0_2, CPU_SUSPEND);
psci_ops.cpu_suspend = psci_cpu_suspend; psci_ops.cpu_suspend = psci_cpu_suspend;
psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
psci_ops.cpu_off = psci_cpu_off; psci_ops.cpu_off = psci_cpu_off;
psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_NATIVE(CPU_ON); psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON);
psci_ops.cpu_on = psci_cpu_on; psci_ops.cpu_on = psci_cpu_on;
psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_NATIVE(MIGRATE); psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE);
psci_ops.migrate = psci_migrate; psci_ops.migrate = psci_migrate;
psci_ops.affinity_info = psci_affinity_info; psci_ops.affinity_info = psci_affinity_info;
...@@ -265,6 +347,10 @@ static int __init psci_probe(void) ...@@ -265,6 +347,10 @@ static int __init psci_probe(void)
psci_init_migrate(); psci_init_migrate();
psci_init_cpu_suspend();
psci_init_system_suspend();
return 0; return 0;
} }
...@@ -340,6 +426,7 @@ static int __init psci_0_1_init(struct device_node *np) ...@@ -340,6 +426,7 @@ static int __init psci_0_1_init(struct device_node *np)
static const struct of_device_id const psci_of_match[] __initconst = { static const struct of_device_id const psci_of_match[] __initconst = {
{ .compatible = "arm,psci", .data = psci_0_1_init}, { .compatible = "arm,psci", .data = psci_0_1_init},
{ .compatible = "arm,psci-0.2", .data = psci_0_2_init}, { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
{ .compatible = "arm,psci-1.0", .data = psci_0_2_init},
{}, {},
}; };
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1
bool psci_tos_resident_on(int cpu); bool psci_tos_resident_on(int cpu);
bool psci_power_state_loses_context(u32 state);
bool psci_power_state_is_valid(u32 state);
struct psci_operations { struct psci_operations {
int (*cpu_suspend)(u32 state, unsigned long entry_point); int (*cpu_suspend)(u32 state, unsigned long entry_point);
......
...@@ -46,6 +46,11 @@ ...@@ -46,6 +46,11 @@
#define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) #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) #define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7)
#define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10)
#define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14)
#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14)
/* PSCI v0.2 power state encoding for CPU_SUSPEND function */ /* PSCI v0.2 power state encoding for CPU_SUSPEND function */
#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff
#define PSCI_0_2_POWER_STATE_ID_SHIFT 0 #define PSCI_0_2_POWER_STATE_ID_SHIFT 0
...@@ -56,6 +61,13 @@ ...@@ -56,6 +61,13 @@
#define PSCI_0_2_POWER_STATE_AFFL_MASK \ #define PSCI_0_2_POWER_STATE_AFFL_MASK \
(0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
/* PSCI extended power state encoding for CPU_SUSPEND function */
#define PSCI_1_0_EXT_POWER_STATE_ID_MASK 0xfffffff
#define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT 0
#define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30
#define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK \
(0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT)
/* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ /* PSCI v0.2 affinity level state returned by AFFINITY_INFO */
#define PSCI_0_2_AFFINITY_LEVEL_ON 0 #define PSCI_0_2_AFFINITY_LEVEL_ON 0
#define PSCI_0_2_AFFINITY_LEVEL_OFF 1 #define PSCI_0_2_AFFINITY_LEVEL_OFF 1
...@@ -76,6 +88,11 @@ ...@@ -76,6 +88,11 @@
#define PSCI_VERSION_MINOR(ver) \ #define PSCI_VERSION_MINOR(ver) \
((ver) & PSCI_VERSION_MINOR_MASK) ((ver) & PSCI_VERSION_MINOR_MASK)
/* PSCI features decoding (>=1.0) */
#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1
#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \
(0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT)
/* PSCI return values (inclusive of all PSCI versions) */ /* PSCI return values (inclusive of all PSCI versions) */
#define PSCI_RET_SUCCESS 0 #define PSCI_RET_SUCCESS 0
#define PSCI_RET_NOT_SUPPORTED -1 #define PSCI_RET_NOT_SUPPORTED -1
...@@ -86,5 +103,6 @@ ...@@ -86,5 +103,6 @@
#define PSCI_RET_INTERNAL_FAILURE -6 #define PSCI_RET_INTERNAL_FAILURE -6
#define PSCI_RET_NOT_PRESENT -7 #define PSCI_RET_NOT_PRESENT -7
#define PSCI_RET_DISABLED -8 #define PSCI_RET_DISABLED -8
#define PSCI_RET_INVALID_ADDRESS -9
#endif /* _UAPI_LINUX_PSCI_H */ #endif /* _UAPI_LINUX_PSCI_H */
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