[PATCH RFC 2/2] ARM64: psci: implement system suspend using PSCI v0.2 CPU SUSPEND

Leo Yan leo.yan at linaro.org
Wed Jan 21 22:18:12 PST 2015


On Wed, Jan 21, 2015 at 11:35:55AM +0000, Sudeep Holla wrote:
> PSCI specifications upto v0.2 and the related kernel back-end
> implementation lack a method to enter system wide suspend state.
> 
> This patch implements suspend to RAM support for all ARM64 systems
> with PSCIv0.2 support using CPU SUSPEND and the new system state DT
> bindings.
> 
> Signed-off-by: Sudeep Holla <sudeep.holla at arm.com>
> ---
>  arch/arm64/kernel/psci.c | 83 ++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 74 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
> index f1dbca7d5c96..8be464747dd6 100644
> --- a/arch/arm64/kernel/psci.c
> +++ b/arch/arm64/kernel/psci.c
> @@ -22,6 +22,7 @@
>  #include <linux/pm.h>
>  #include <linux/delay.h>
>  #include <linux/slab.h>
> +#include <linux/suspend.h>
>  #include <uapi/linux/psci.h>
>  
>  #include <asm/compiler.h>
> @@ -304,6 +305,75 @@ static void psci_sys_poweroff(void)
>  	invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
>  }
>  
> +static int psci_suspend_finisher(unsigned long arg)
> +{
> +	return psci_ops.cpu_suspend(*(struct psci_power_state *)arg,
> +				    virt_to_phys(cpu_resume));
> +}
> +
> +#ifdef CONFIG_SUSPEND
> +static struct psci_power_state psci_system_suspend_state;
> +
> +static int psci_system_suspend_enter(suspend_state_t state)
> +{
> +	if (state != PM_SUSPEND_MEM)
> +		return 0;
> +
> +	/*
> +	 * TODO remove pack/unpacking of power_state to do away with
> +	 * these ugly type conversions
> +	 */
> +	return __cpu_suspend((unsigned long)&psci_system_suspend_state,
> +			     psci_suspend_finisher);
> +}
> +
> +static const struct platform_suspend_ops psci_suspend_ops = {
> +	.valid		= suspend_valid_only_mem,
> +	.enter		= psci_system_suspend_enter,
> +};
> +
> +static void __init psci_0_2_system_suspend_init(void)
> +{
> +	int ret;
> +	u32 psci_power_state;
> +	const char *entry_method;
> +	struct device_node *node;
> +
> +	if (!psci_ops.cpu_suspend)
> +		return; /* -EOPNOTSUPP */
> +
> +	node = of_find_compatible_node(NULL, NULL, "arm,system-suspend");
> +	if (!node || !of_device_is_available(node))
> +		return; /* -EOPNOTSUPP */
> +
> +	if (of_property_read_string(node, "entry-method", &entry_method)) {
> +		pr_warn(" * %s missing entry-method property\n", node->full_name);
> +		goto exit;
> +	}
> +
> +	if (strcmp(entry_method, "arm,psci"))
> +		goto exit; /* out of PSCI scope ignore */
> +
> +	ret = of_property_read_u32(node, "arm,psci-suspend-param",
> +				   &psci_power_state);
> +	if (ret) {
> +		pr_warn(" * %s missing arm,psci-suspend-param property\n",
> +			node->full_name);
> +		goto exit;
> +	}
> +
> +	pr_debug("psci-power-state for system suspend %#x\n", psci_power_state);
> +
> +	psci_power_state_unpack(psci_power_state, &psci_system_suspend_state);
> +

How about unify the power states passing for cpu idle and suspend?

below is a example for dts which place all power state into psci
entry, so that idle-states and system suspend both can reference
the power state.

psci {
	compatible = "arm,psci-0.2";
	method = "smc";

        power_state {
                CPU_POWER_OFF: cpu_power_off {
                        state = <0x00010000>;
                };

                CLUSTER_POWER_OFF: cluster_power_off {
                        state = <0x01010000>;
                };

                SOC_SUSPEND: soc_suspend {
                        state = <0x01010001>;
                };
        };
};

> +	suspend_set_ops(&psci_suspend_ops);
> +exit:
> +	of_node_put(node);
> +}
> +#else
> +static void __init psci_0_2_system_suspend_init(void) { }
> +#endif
> +
>  /*
>   * PSCI Function IDs for v0.2+ are well defined so use
>   * standard values.
> @@ -361,6 +431,8 @@ static int __init psci_0_2_init(struct device_node *np)
>  
>  	pm_power_off = psci_sys_poweroff;
>  
> +	psci_0_2_system_suspend_init();
> +
>  out_put_node:
>  	of_node_put(np);
>  	return err;
> @@ -509,14 +581,6 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
>  #endif
>  #endif
>  
> -static int psci_suspend_finisher(unsigned long index)
> -{
> -	struct psci_power_state *state = __this_cpu_read(psci_power_state);
> -
> -	return psci_ops.cpu_suspend(state[index - 1],
> -				    virt_to_phys(cpu_resume));
> -}
> -
>  static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
>  {
>  	int ret;
> @@ -531,7 +595,8 @@ static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
>  	if (state[index - 1].type == PSCI_POWER_STATE_TYPE_STANDBY)
>  		ret = psci_ops.cpu_suspend(state[index - 1], 0);
>  	else
> -		ret = __cpu_suspend(index, psci_suspend_finisher);
> +		ret = __cpu_suspend((unsigned long)&state[index - 1],
> +				    psci_suspend_finisher);
>  
>  	return ret;
>  }
> -- 
> 1.9.1
> 



More information about the linux-arm-kernel mailing list