ARM: at91: pm: allow selecting standby and suspend modes
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@free-electrons.com> Acked-by: Wenyou Yang <wenyou.yang@atmel.com>
This commit is contained in:
parent
24a0f5c539
commit
7693e18e8c
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/parser.h>
|
||||||
#include <linux/suspend.h>
|
#include <linux/suspend.h>
|
||||||
|
|
||||||
#include <linux/clk/at91_pmc.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);
|
extern void at91_pinctrl_gpio_resume(void);
|
||||||
#endif
|
#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) \
|
#define at91_ramc_read(id, field) \
|
||||||
__raw_readl(pm_data.ramc[id] + field)
|
__raw_readl(pm_data.ramc[id] + field)
|
||||||
|
@ -68,14 +79,24 @@ static struct at91_pm_bu {
|
||||||
phys_addr_t resume;
|
phys_addr_t resume;
|
||||||
} *pm_bu;
|
} *pm_bu;
|
||||||
|
|
||||||
static suspend_state_t target_state;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Called after processes are frozen, but before we shutdown devices.
|
* Called after processes are frozen, but before we shutdown devices.
|
||||||
*/
|
*/
|
||||||
static int at91_pm_begin(suspend_state_t state)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +145,7 @@ static int at91_pm_verify_clocks(void)
|
||||||
*/
|
*/
|
||||||
int at91_suspend_entering_slow_clock(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);
|
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)
|
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) {
|
if (pm_data.mode == AT91_PM_BACKUP) {
|
||||||
pm_bu->suspended = 1;
|
pm_bu->suspended = 1;
|
||||||
|
|
||||||
|
@ -168,38 +181,37 @@ static void at91_pm_suspend(suspend_state_t state)
|
||||||
outer_resume();
|
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)
|
static int at91_pm_enter(suspend_state_t state)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_PINCTRL_AT91
|
#ifdef CONFIG_PINCTRL_AT91
|
||||||
at91_pinctrl_gpio_suspend();
|
at91_pinctrl_gpio_suspend();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (state) {
|
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_MEM:
|
||||||
|
case PM_SUSPEND_STANDBY:
|
||||||
/*
|
/*
|
||||||
* Ensure that clocks are in a valid state.
|
* 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;
|
goto error;
|
||||||
|
|
||||||
at91_pm_suspend(state);
|
at91_pm_suspend(state);
|
||||||
|
|
||||||
break;
|
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:
|
case PM_SUSPEND_ON:
|
||||||
cpu_do_idle();
|
cpu_do_idle();
|
||||||
break;
|
break;
|
||||||
|
@ -210,8 +222,6 @@ static int at91_pm_enter(suspend_state_t state)
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
target_state = PM_SUSPEND_ON;
|
|
||||||
|
|
||||||
#ifdef CONFIG_PINCTRL_AT91
|
#ifdef CONFIG_PINCTRL_AT91
|
||||||
at91_pinctrl_gpio_resume();
|
at91_pinctrl_gpio_resume();
|
||||||
#endif
|
#endif
|
||||||
|
@ -223,7 +233,6 @@ static int at91_pm_enter(suspend_state_t state)
|
||||||
*/
|
*/
|
||||||
static void at91_pm_end(void)
|
static void at91_pm_end(void)
|
||||||
{
|
{
|
||||||
target_state = PM_SUSPEND_ON;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -475,6 +484,10 @@ static void __init at91_pm_backup_init(void)
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
struct platform_device *pdev = NULL;
|
struct platform_device *pdev = NULL;
|
||||||
|
|
||||||
|
if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
|
||||||
|
(pm_data.suspend_mode != AT91_PM_BACKUP))
|
||||||
|
return;
|
||||||
|
|
||||||
pm_bu = NULL;
|
pm_bu = NULL;
|
||||||
|
|
||||||
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
|
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
|
||||||
|
@ -578,10 +591,14 @@ static void __init at91_pm_init(void (*pm_idle)(void))
|
||||||
|
|
||||||
at91_pm_sram_init();
|
at91_pm_sram_init();
|
||||||
|
|
||||||
if (at91_suspend_sram_fn)
|
if (at91_suspend_sram_fn) {
|
||||||
suspend_set_ops(&at91_pm_ops);
|
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");
|
pr_info("AT91: PM not supported, due to no SRAM allocated\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void __init at91rm9200_pm_init(void)
|
void __init at91rm9200_pm_init(void)
|
||||||
|
@ -613,3 +630,28 @@ void __init sama5d2_pm_init(void)
|
||||||
at91_pm_backup_init();
|
at91_pm_backup_init();
|
||||||
sama5_pm_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);
|
||||||
|
|
|
@ -33,7 +33,8 @@ struct at91_pm_data {
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
void __iomem *shdwc;
|
void __iomem *shdwc;
|
||||||
void __iomem *sfrbu;
|
void __iomem *sfrbu;
|
||||||
unsigned int deepest_state;
|
unsigned int standby_mode;
|
||||||
|
unsigned int suspend_mode;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue