Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -31,6 +31,10 @@ Main node required properties:
					support, but are permitted to be present for compatibility with
					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
                   values are:

@@ -100,3 +104,5 @@ Case 3: PSCI v0.2 and PSCI v0.1.

[1] Kernel documentation - ARM idle states bindings
    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
+0 −14
Original line number Diff line number Diff line
@@ -30,20 +30,6 @@
#include <asm/smp_plat.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 int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
+97 −10
Original line number Diff line number Diff line
@@ -20,23 +20,25 @@
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
#include <linux/suspend.h>

#include <uapi/linux/psci.h>

#include <asm/cputype.h>
#include <asm/system_misc.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>

/*
 * 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 PSCI_0_2_FN_NATIVE(x) will choose the appropriate (native-width)
 * function ID.
 * calls it is necessary to use SMC64 to pass or return 64-bit values.
 * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate
 * (native-width) function ID.
 */
#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
#define PSCI_0_2_FN_NATIVE(name)	PSCI_0_2_FN_##name
#define PSCI_FN_NATIVE(version, name)	PSCI_##version##_FN_##name
#endif

/*
@@ -70,6 +72,41 @@ enum psci_function {

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)
{
	switch (errno) {
@@ -78,6 +115,7 @@ static int psci_to_linux_errno(int errno)
	case PSCI_RET_NOT_SUPPORTED:
		return -EOPNOTSUPP;
	case PSCI_RET_INVALID_PARAMS:
	case PSCI_RET_INVALID_ADDRESS:
		return -EINVAL;
	case PSCI_RET_DENIED:
		return -EPERM;
@@ -134,7 +172,7 @@ static int psci_migrate(unsigned long cpuid)
static int psci_affinity_info(unsigned long target_affinity,
		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);
}

@@ -145,7 +183,7 @@ static int psci_migrate_info_type(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);
}

@@ -181,6 +219,49 @@ static void psci_sys_poweroff(void)
	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
 * return DENIED (which would be fatal).
@@ -224,16 +305,17 @@ static void __init psci_init_migrate(void)
static void __init psci_0_2_set_functions(void)
{
	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_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_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_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.affinity_info = psci_affinity_info;
@@ -265,6 +347,10 @@ static int __init psci_probe(void)

	psci_init_migrate();

	psci_init_cpu_suspend();

	psci_init_system_suspend();

	return 0;
}

@@ -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 = {
	{ .compatible = "arm,psci",	.data = psci_0_1_init},
	{ .compatible = "arm,psci-0.2",	.data = psci_0_2_init},
	{ .compatible = "arm,psci-1.0",	.data = psci_0_2_init},
	{},
};

+2 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#define PSCI_POWER_STATE_TYPE_POWER_DOWN	1

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 {
	int (*cpu_suspend)(u32 state, unsigned long entry_point);
+18 −0
Original line number Diff line number Diff line
@@ -46,6 +46,11 @@
#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_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 */
#define PSCI_0_2_POWER_STATE_ID_MASK		0xffff
#define PSCI_0_2_POWER_STATE_ID_SHIFT		0
@@ -56,6 +61,13 @@
#define PSCI_0_2_POWER_STATE_AFFL_MASK		\
				(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 */
#define PSCI_0_2_AFFINITY_LEVEL_ON		0
#define PSCI_0_2_AFFINITY_LEVEL_OFF		1
@@ -76,6 +88,11 @@
#define PSCI_VERSION_MINOR(ver)			\
		((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) */
#define PSCI_RET_SUCCESS			0
#define PSCI_RET_NOT_SUPPORTED			-1
@@ -86,5 +103,6 @@
#define PSCI_RET_INTERNAL_FAILURE		-6
#define PSCI_RET_NOT_PRESENT			-7
#define PSCI_RET_DISABLED			-8
#define PSCI_RET_INVALID_ADDRESS		-9

#endif /* _UAPI_LINUX_PSCI_H */