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.ho...@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
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to