]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/arm/mach-at91/pm.c
Merge tag 'at91-ab-4.13-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/abellon...
[mirror_ubuntu-artful-kernel.git] / arch / arm / mach-at91 / pm.c
index 283e79ab587de91405e0b579fb16d202ca5072b7..667fddac38561eff3f25788ccbf9e05ac8214d84 100644 (file)
@@ -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>
@@ -22,6 +23,7 @@
 #include <asm/cacheflush.h>
 #include <asm/fncpy.h>
 #include <asm/system_misc.h>
+#include <asm/suspend.h>
 
 #include "generic.h"
 #include "pm.h"
@@ -37,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)
@@ -58,15 +70,33 @@ static int at91_pm_valid_state(suspend_state_t state)
        }
 }
 
+static int canary = 0xA5A5A5A5;
 
-static suspend_state_t target_state;
+static struct at91_pm_bu {
+       int suspended;
+       unsigned long reserved;
+       phys_addr_t canary;
+       phys_addr_t resume;
+} *pm_bu;
 
 /*
  * 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;
 }
 
@@ -115,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);
 
@@ -123,50 +153,65 @@ static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
 extern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data);
 extern u32 at91_pm_suspend_in_sram_sz;
 
-static void at91_pm_suspend(suspend_state_t state)
+static int at91_suspend_finish(unsigned long val)
 {
-       pm_data.mode = (state == PM_SUSPEND_MEM) ? AT91_PM_SLOW_CLOCK : 0;
-
        flush_cache_all();
        outer_disable();
 
        at91_suspend_sram_fn(&pm_data);
 
+       return 0;
+}
+
+static void at91_pm_suspend(suspend_state_t state)
+{
+       if (pm_data.mode == AT91_PM_BACKUP) {
+               pm_bu->suspended = 1;
+
+               cpu_suspend(0, at91_suspend_finish);
+
+               /* The SRAM is lost between suspend cycles */
+               at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
+                                            &at91_pm_suspend_in_sram,
+                                            at91_pm_suspend_in_sram_sz);
+       } else {
+               at91_suspend_finish(0);
+       }
+
        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;
@@ -177,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
@@ -190,7 +233,6 @@ error:
  */
 static void at91_pm_end(void)
 {
-       target_state = PM_SUSPEND_ON;
 }
 
 
@@ -436,6 +478,79 @@ static void __init at91_pm_sram_init(void)
                        &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
 }
 
+static void __init at91_pm_backup_init(void)
+{
+       struct gen_pool *sram_pool;
+       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");
+       if (!np) {
+               pr_warn("%s: failed to find shdwc!\n", __func__);
+               return;
+       }
+
+       pm_data.shdwc = of_iomap(np, 0);
+       of_node_put(np);
+
+       np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
+       if (!np) {
+               pr_warn("%s: failed to find sfrbu!\n", __func__);
+               goto sfrbu_fail;
+       }
+
+       pm_data.sfrbu = of_iomap(np, 0);
+       of_node_put(np);
+       pm_bu = NULL;
+
+       np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
+       if (!np)
+               goto securam_fail;
+
+       pdev = of_find_device_by_node(np);
+       of_node_put(np);
+       if (!pdev) {
+               pr_warn("%s: failed to find securam device!\n", __func__);
+               goto securam_fail;
+       }
+
+       sram_pool = gen_pool_get(&pdev->dev, NULL);
+       if (!sram_pool) {
+               pr_warn("%s: securam pool unavailable!\n", __func__);
+               goto securam_fail;
+       }
+
+       pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
+       if (!pm_bu) {
+               pr_warn("%s: unable to alloc securam!\n", __func__);
+               goto securam_fail;
+       }
+
+       pm_bu->suspended = 0;
+       pm_bu->canary = virt_to_phys(&canary);
+       pm_bu->resume = virt_to_phys(cpu_resume);
+
+       return;
+
+sfrbu_fail:
+       iounmap(pm_data.shdwc);
+       pm_data.shdwc = NULL;
+securam_fail:
+       iounmap(pm_data.sfrbu);
+       pm_data.sfrbu = NULL;
+
+       if (pm_data.standby_mode == AT91_PM_BACKUP)
+               pm_data.standby_mode = AT91_PM_SLOW_CLOCK;
+       if (pm_data.suspend_mode == AT91_PM_BACKUP)
+               pm_data.suspend_mode = AT91_PM_SLOW_CLOCK;
+}
+
 struct pmc_info {
        unsigned long uhp_udp_mask;
 };
@@ -481,10 +596,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)
@@ -510,3 +629,34 @@ void __init sama5_pm_init(void)
        at91_dt_ramc();
        at91_pm_init(NULL);
 }
+
+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);