|
@@ -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 @@ error:
|
|
|
*/
|
|
|
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 platform_device *pdev = NULL;
|
|
|
|
|
|
+ if ((pm_data.standby_mode != AT91_PM_BACKUP) &&
|
|
|
+ (pm_data.suspend_mode != AT91_PM_BACKUP))
|
|
|
+ return;
|
|
|
+
|
|
|
pm_bu = NULL;
|
|
|
|
|
|
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();
|
|
|
|
|
|
- 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)
|
|
@@ -613,3 +630,28 @@ void __init sama5d2_pm_init(void)
|
|
|
at91_pm_backup_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);
|