Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-at91 / pm.c
index aa4116e9452f725e0f63241cfc083576c665be19..ac947cdd506cb819ba044e9579b8721cb9aabc07 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/atomic.h>
 #include <asm/mach/time.h>
 #include <asm/mach/irq.h>
+#include <asm/fncpy.h>
+#include <asm/cacheflush.h>
 
 #include <mach/cpu.h>
 #include <mach/hardware.h>
@@ -41,7 +43,6 @@ static struct {
        int memctrl;
 } at91_pm_data;
 
-static void (*at91_pm_standby)(void);
 void __iomem *at91_ramc_base[2];
 
 static int at91_pm_valid_state(suspend_state_t state)
@@ -119,76 +120,67 @@ int at91_suspend_entering_slow_clock(void)
 }
 EXPORT_SYMBOL(at91_suspend_entering_slow_clock);
 
-
-static void (*slow_clock)(void __iomem *pmc, void __iomem *ramc0,
+static void (*at91_suspend_sram_fn)(void __iomem *pmc, void __iomem *ramc0,
                          void __iomem *ramc1, int memctrl);
 
-#ifdef CONFIG_AT91_SLOW_CLOCK
-extern void at91_slow_clock(void __iomem *pmc, void __iomem *ramc0,
+extern void at91_pm_suspend_in_sram(void __iomem *pmc, void __iomem *ramc0,
                            void __iomem *ramc1, int memctrl);
-extern u32 at91_slow_clock_sz;
-#endif
+extern u32 at91_pm_suspend_in_sram_sz;
+
+static void at91_pm_suspend(suspend_state_t state)
+{
+       unsigned int pm_data = at91_pm_data.memctrl;
+
+       pm_data |= (state == PM_SUSPEND_MEM) ?
+                               AT91_PM_MODE(AT91_PM_SLOW_CLOCK) : 0;
+
+       flush_cache_all();
+       outer_disable();
+
+       at91_suspend_sram_fn(at91_pmc_base, at91_ramc_base[0],
+                               at91_ramc_base[1], pm_data);
+
+       outer_resume();
+}
 
 static int at91_pm_enter(suspend_state_t state)
 {
        at91_pinctrl_gpio_suspend();
 
        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:
                /*
-                * Suspend-to-RAM is like STANDBY plus slow clock mode, so
-                * drivers must suspend more deeply:  only the master clock
-                * controller may be using the main oscillator.
+                * Ensure that clocks are in a valid state.
                 */
-               case PM_SUSPEND_MEM:
-                       /*
-                        * Ensure that clocks are in a valid state.
-                        */
-                       if (!at91_pm_verify_clocks())
-                               goto error;
-
-                       /*
-                        * Enter slow clock mode by switching over to clk32k and
-                        * turning off the main oscillator; reverse on wakeup.
-                        */
-                       if (slow_clock) {
-#ifdef CONFIG_AT91_SLOW_CLOCK
-                               /* copy slow_clock handler to SRAM, and call it */
-                               memcpy(slow_clock, at91_slow_clock, at91_slow_clock_sz);
-#endif
-                               slow_clock(at91_pmc_base, at91_ramc_base[0],
-                                          at91_ramc_base[1],
-                                          at91_pm_data.memctrl);
-                               break;
-                       } else {
-                               pr_info("AT91: PM - no slow clock mode enabled ...\n");
-                               /* FALLTHROUGH leaving master clock alone */
-                       }
+               if (!at91_pm_verify_clocks())
+                       goto error;
 
-               /*
-                * 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:
-                       /*
-                        * NOTE: the Wait-for-Interrupt instruction needs to be
-                        * in icache so no SDRAM accesses are needed until the
-                        * wakeup IRQ occurs and self-refresh is terminated.
-                        * For ARM 926 based chips, this requirement is weaker
-                        * as at91sam9 can access a RAM in self-refresh mode.
-                        */
-                       if (at91_pm_standby)
-                               at91_pm_standby();
-                       break;
+               at91_pm_suspend(state);
 
-               case PM_SUSPEND_ON:
-                       cpu_do_idle();
-                       break;
+               break;
 
-               default:
-                       pr_debug("AT91: PM - bogus suspend state %d\n", state);
-                       goto error;
+       /*
+        * 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;
+
+       default:
+               pr_debug("AT91: PM - bogus suspend state %d\n", state);
+               goto error;
        }
 
 error:
@@ -218,12 +210,10 @@ static struct platform_device at91_cpuidle_device = {
        .name = "cpuidle-at91",
 };
 
-void at91_pm_set_standby(void (*at91_standby)(void))
+static void at91_pm_set_standby(void (*at91_standby)(void))
 {
-       if (at91_standby) {
+       if (at91_standby)
                at91_cpuidle_device.dev.platform_data = at91_standby;
-               at91_pm_standby = at91_standby;
-       }
 }
 
 static const struct of_device_id ramc_ids[] __initconst = {
@@ -263,7 +253,6 @@ static __init void at91_dt_ramc(void)
        at91_pm_set_standby(standby);
 }
 
-#ifdef CONFIG_AT91_SLOW_CLOCK
 static void __init at91_pm_sram_init(void)
 {
        struct gen_pool *sram_pool;
@@ -291,30 +280,36 @@ static void __init at91_pm_sram_init(void)
                return;
        }
 
-       sram_base = gen_pool_alloc(sram_pool, at91_slow_clock_sz);
+       sram_base = gen_pool_alloc(sram_pool, at91_pm_suspend_in_sram_sz);
        if (!sram_base) {
-               pr_warn("%s: unable to alloc ocram!\n", __func__);
+               pr_warn("%s: unable to alloc sram!\n", __func__);
                return;
        }
 
        sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base);
-       slow_clock = __arm_ioremap_exec(sram_pbase, at91_slow_clock_sz, false);
-}
-#endif
+       at91_suspend_sram_fn = __arm_ioremap_exec(sram_pbase,
+                                       at91_pm_suspend_in_sram_sz, false);
+       if (!at91_suspend_sram_fn) {
+               pr_warn("SRAM: Could not map\n");
+               return;
+       }
 
+       /* Copy the pm suspend handler to SRAM */
+       at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
+                       &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
+}
 
 static void __init at91_pm_init(void)
 {
-#ifdef CONFIG_AT91_SLOW_CLOCK
        at91_pm_sram_init();
-#endif
-
-       pr_info("AT91: Power Management%s\n", (slow_clock ? " (with slow clock mode)" : ""));
 
        if (at91_cpuidle_device.dev.platform_data)
                platform_device_register(&at91_cpuidle_device);
 
-       suspend_set_ops(&at91_pm_ops);
+       if (at91_suspend_sram_fn)
+               suspend_set_ops(&at91_pm_ops);
+       else
+               pr_info("AT91: PM not supported, due to no SRAM allocated\n");
 }
 
 void __init at91rm9200_pm_init(void)