[PATCH 2/3] ARM: at91: pm: allow selecting standby and suspend modes
Yang, Wenyou
Wenyou.Yang at Microchip.com
Thu Apr 27 21:33:01 EDT 2017
On 2017/4/27 0:04, Alexandre Belloni wrote:
> While we can only select between "standby" and "mem" states for power
> management, the atmel platforms can actually support more modes.
>
> For both standby and mem, allow selecting which mode will be used using the
> atmel.pm_modes kernel parameter.
> By default, keep the current modes.
>
> Signed-off-by: Alexandre Belloni <alexandre.belloni at free-electrons.com>
Acked-by: Wenyou Yang <wenyou.yang at atmel.com>
> ---
> arch/arm/mach-at91/pm.c | 110 +++++++++++++++++++++++++++++++++---------------
> arch/arm/mach-at91/pm.h | 3 +-
> 2 files changed, 78 insertions(+), 35 deletions(-)
>
> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
> index 1e03f1277f14..d08f032f9d94 100644
> --- a/arch/arm/mach-at91/pm.c
> +++ b/arch/arm/mach-at91/pm.c
> @@ -15,6 +15,7 @@
> #include <linux/of_address.h>
> #include <linux/of.h>
> #include <linux/of_platform.h>
> +#include <linux/parser.h>
> #include <linux/suspend.h>
>
> #include <linux/clk/at91_pmc.h>
> @@ -38,7 +39,17 @@ extern void at91_pinctrl_gpio_suspend(void);
> extern void at91_pinctrl_gpio_resume(void);
> #endif
>
> -static struct at91_pm_data pm_data;
> +static const match_table_t pm_modes __initconst = {
> + { 0, "standby" },
> + { AT91_PM_SLOW_CLOCK, "ulp0" },
> + { AT91_PM_BACKUP, "backup" },
> + { -1, NULL },
> +};
> +
> +static struct at91_pm_data pm_data = {
> + .standby_mode = 0,
> + .suspend_mode = AT91_PM_SLOW_CLOCK,
> +};
>
> #define at91_ramc_read(id, field) \
> __raw_readl(pm_data.ramc[id] + field)
> @@ -68,14 +79,24 @@ static struct at91_pm_bu {
> phys_addr_t resume;
> } *pm_bu;
>
> -static suspend_state_t target_state;
> -
> /*
> * Called after processes are frozen, but before we shutdown devices.
> */
> static int at91_pm_begin(suspend_state_t state)
> {
> - target_state = state;
> + switch (state) {
> + case PM_SUSPEND_MEM:
> + pm_data.mode = pm_data.suspend_mode;
> + break;
> +
> + case PM_SUSPEND_STANDBY:
> + pm_data.mode = pm_data.standby_mode;
> + break;
> +
> + default:
> + pm_data.mode = -1;
> + }
> +
> return 0;
> }
>
> @@ -124,7 +145,7 @@ static int at91_pm_verify_clocks(void)
> */
> int at91_suspend_entering_slow_clock(void)
> {
> - return (target_state == PM_SUSPEND_MEM);
> + return (pm_data.mode >= AT91_PM_SLOW_CLOCK);
> }
> EXPORT_SYMBOL(at91_suspend_entering_slow_clock);
>
> @@ -144,14 +165,6 @@ static int at91_suspend_finish(unsigned long val)
>
> static void at91_pm_suspend(suspend_state_t state)
> {
> - if (pm_data.deepest_state == AT91_PM_BACKUP)
> - if (state == PM_SUSPEND_MEM)
> - pm_data.mode = AT91_PM_BACKUP;
> - else
> - pm_data.mode = AT91_PM_SLOW_CLOCK;
> - else
> - pm_data.mode = (state == PM_SUSPEND_MEM) ? AT91_PM_SLOW_CLOCK : 0;
> -
> if (pm_data.mode == AT91_PM_BACKUP) {
> pm_bu->suspended = 1;
>
> @@ -168,38 +181,37 @@ static void at91_pm_suspend(suspend_state_t state)
> outer_resume();
> }
>
> +/*
> + * STANDBY mode has *all* drivers suspended; ignores irqs not marked as 'wakeup'
> + * event sources; and reduces DRAM power. But otherwise it's identical to
> + * PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks.
> + *
> + * AT91_PM_SLOW_CLOCK is like STANDBY plus slow clock mode, so drivers must
> + * suspend more deeply, the master clock switches to the clk32k and turns off
> + * the main oscillator
> + *
> + * AT91_PM_BACKUP turns off the whole SoC after placing the DDR in self refresh
> + */
> static int at91_pm_enter(suspend_state_t state)
> {
> #ifdef CONFIG_PINCTRL_AT91
> at91_pinctrl_gpio_suspend();
> #endif
> +
> switch (state) {
> - /*
> - * Suspend-to-RAM is like STANDBY plus slow clock mode, so
> - * drivers must suspend more deeply, the master clock switches
> - * to the clk32k and turns off the main oscillator
> - */
> case PM_SUSPEND_MEM:
> + case PM_SUSPEND_STANDBY:
> /*
> * Ensure that clocks are in a valid state.
> */
> - if (!at91_pm_verify_clocks())
> + if ((pm_data.mode >= AT91_PM_SLOW_CLOCK) &&
> + !at91_pm_verify_clocks())
> goto error;
>
> at91_pm_suspend(state);
>
> break;
>
> - /*
> - * STANDBY mode has *all* drivers suspended; ignores irqs not
> - * marked as 'wakeup' event sources; and reduces DRAM power.
> - * But otherwise it's identical to PM_SUSPEND_ON: cpu idle, and
> - * nothing fancy done with main or cpu clocks.
> - */
> - case PM_SUSPEND_STANDBY:
> - at91_pm_suspend(state);
> - break;
> -
> case PM_SUSPEND_ON:
> cpu_do_idle();
> break;
> @@ -210,8 +222,6 @@ static int at91_pm_enter(suspend_state_t state)
> }
>
> error:
> - target_state = PM_SUSPEND_ON;
> -
> #ifdef CONFIG_PINCTRL_AT91
> at91_pinctrl_gpio_resume();
> #endif
> @@ -223,7 +233,6 @@ static int at91_pm_enter(suspend_state_t state)
> */
> static void at91_pm_end(void)
> {
> - target_state = PM_SUSPEND_ON;
> }
>
>
> @@ -494,6 +503,10 @@ static void __init at91_pm_bu_sram_init(void)
> struct device_node *node;
> struct platform_device *pdev = NULL;
>
> + if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
> + (pm_data.suspend_mode != AT91_PM_BACKUP))
> + return;
> +
> pm_bu = NULL;
>
> for_each_compatible_node(node, NULL, "atmel,sama5d2-securam") {
> @@ -571,10 +584,14 @@ static void __init at91_pm_init(void (*pm_idle)(void))
>
> at91_pm_sram_init();
>
> - if (at91_suspend_sram_fn)
> + if (at91_suspend_sram_fn) {
> suspend_set_ops(&at91_pm_ops);
> - else
> + pr_info("AT91: PM: standby: %s, suspend: %s\n",
> + pm_modes[pm_data.standby_mode].pattern,
> + pm_modes[pm_data.suspend_mode].pattern);
> + } else {
> pr_info("AT91: PM not supported, due to no SRAM allocated\n");
> + }
> }
>
> void __init at91rm9200_pm_init(void)
> @@ -607,3 +624,28 @@ void __init sama5d2_pm_init(void)
> at91_pm_bu_sram_init();
> sama5_pm_init();
> }
> +
> +static int __init at91_pm_modes_select(char *str)
> +{
> + char *s;
> + substring_t args[MAX_OPT_ARGS];
> + int standby, suspend;
> +
> + if (!str)
> + return 0;
> +
> + s = strsep(&str, ",");
> + standby = match_token(s, pm_modes, args);
> + if (standby < 0)
> + return 0;
> +
> + suspend = match_token(str, pm_modes, args);
> + if (suspend < 0)
> + return 0;
> +
> + pm_data.standby_mode = standby;
> + pm_data.suspend_mode = suspend;
> +
> + return 0;
> +}
> +early_param("atmel.pm_modes", at91_pm_modes_select);
> diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
> index d9c6612ef62f..f95d31496f08 100644
> --- a/arch/arm/mach-at91/pm.h
> +++ b/arch/arm/mach-at91/pm.h
> @@ -33,7 +33,8 @@ struct at91_pm_data {
> unsigned int mode;
> void __iomem *shdwc;
> void __iomem *sfrbu;
> - unsigned int deepest_state;
> + unsigned int standby_mode;
> + unsigned int suspend_mode;
> };
> #endif
>
Best Regards,
Wenyou Yang
More information about the linux-arm-kernel
mailing list