summaryrefslogtreecommitdiff
path: root/arch/arm/mach-at91/pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-at91/pm.c')
-rw-r--r--arch/arm/mach-at91/pm.c139
1 files changed, 67 insertions, 72 deletions
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index aa4116e9452f..ac947cdd506c 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -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)