diff options
| author | Jonas Aaberg <jonas.aberg@stericsson.com> | 2011-09-20 08:04:18 +0200 |
|---|---|---|
| committer | Jonas ABERG <jonas.aberg@stericsson.com> | 2011-09-29 08:47:34 +0200 |
| commit | 0d440d790c4545209e6bf7e0c0d2e447633276c0 (patch) | |
| tree | afaf8d2d0a24a32dc29c7a9b4c091799fa655b92 | |
| parent | b702d82198e3572281c6da5382b0e5677a2b9ba2 (diff) | |
ARM: ux500: Add suspend support
Change-Id: I039557aed52693dfad9b76ea0f2f746193aa4316
Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32074
| -rwxr-xr-x | arch/arm/configs/u8500_defconfig | 1 | ||||
| -rw-r--r-- | arch/arm/mach-ux500/hotplug.c | 30 | ||||
| -rw-r--r-- | arch/arm/mach-ux500/pm/Kconfig | 7 | ||||
| -rw-r--r-- | arch/arm/mach-ux500/pm/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/mach-ux500/pm/suspend.c | 122 |
5 files changed, 157 insertions, 4 deletions
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index 3d534f7d33f..0dfc7040436 100755 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -37,6 +37,7 @@ CONFIG_DISPLAY_AV8100_TRIPPLE_BUFFER=y CONFIG_DBX500_PRCMU_QOS_POWER=y CONFIG_DBX500_PRCMU_DEBUG=y # CONFIG_UX500_DEBUG_NO_LAUTERBACH is not set +CONFIG_UX500_SUSPEND=y CONFIG_UX500_CONTEXT=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT_DETAILS=y diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c index 5a05b15cc87..0cf4798aa07 100644 --- a/arch/arm/mach-ux500/hotplug.c +++ b/arch/arm/mach-ux500/hotplug.c @@ -15,20 +15,29 @@ #include <asm/cacheflush.h> +#include <mach/context.h> + extern volatile int pen_release; +static DECLARE_COMPLETION(cpu_killed); + static inline void platform_do_lowpower(unsigned int cpu) { flush_cache_all(); for (;;) { - __asm__ __volatile__("dsb\n\t" "wfi\n\t" - : : : "memory"); + context_varm_save_core(); + context_save_cpu_registers(); + + context_save_to_sram_and_wfi(false); + + context_restore_cpu_registers(); + context_varm_restore_core(); if (pen_release == cpu) { /* - * OK, proper wakeup, we're done + * OK, proper wakeup, we're done */ break; } @@ -37,7 +46,7 @@ static inline void platform_do_lowpower(unsigned int cpu) int platform_cpu_kill(unsigned int cpu) { - return 1; + return wait_for_completion_timeout(&cpu_killed, 5000); } /* @@ -47,6 +56,19 @@ int platform_cpu_kill(unsigned int cpu) */ void platform_cpu_die(unsigned int cpu) { +#ifdef DEBUG + unsigned int this_cpu = hard_smp_processor_id(); + + if (cpu != this_cpu) { + printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n", + this_cpu, cpu); + BUG(); + } +#endif + + printk(KERN_NOTICE "CPU%u: shutdown\n", cpu); + complete(&cpu_killed); + /* directly enter low power state, skipping secure registers */ platform_do_lowpower(cpu); } diff --git a/arch/arm/mach-ux500/pm/Kconfig b/arch/arm/mach-ux500/pm/Kconfig index cfa12977bc0..0a449421eca 100644 --- a/arch/arm/mach-ux500/pm/Kconfig +++ b/arch/arm/mach-ux500/pm/Kconfig @@ -18,3 +18,10 @@ config UX500_PM_PERFORMANCE help Enable supervision of events which may require a boost of platform performance. + +config UX500_SUSPEND + bool "Suspend to mem and standby support" + depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && PM && SUSPEND + select UX500_CONTEXT + help + Add support for suspend. diff --git a/arch/arm/mach-ux500/pm/Makefile b/arch/arm/mach-ux500/pm/Makefile index 9f2aa590c6b..68112b371a4 100644 --- a/arch/arm/mach-ux500/pm/Makefile +++ b/arch/arm/mach-ux500/pm/Makefile @@ -5,4 +5,5 @@ obj-y := pm.o runtime.o no_suspend.o obj-$(CONFIG_DBX500_PRCMU_QOS_POWER) += prcmu-qos-power.o obj-$(CONFIG_UX500_CONTEXT) += context.o context_arm.o context-db8500.o context-db5500.o +obj-$(CONFIG_UX500_SUSPEND) += suspend.o obj-$(CONFIG_UX500_PM_PERFORMANCE) += performance.o diff --git a/arch/arm/mach-ux500/pm/suspend.c b/arch/arm/mach-ux500/pm/suspend.c new file mode 100644 index 00000000000..a3ad87c6d84 --- /dev/null +++ b/arch/arm/mach-ux500/pm/suspend.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010-2011 + * + * License Terms: GNU General Public License v2 + * + * Authors: Rickard Andersson <rickard.andersson@stericsson.com>, + * Jonas Aaberg <jonas.aberg@stericsson.com>, + * Sundar Iyer for ST-Ericsson. + */ + +#include <linux/suspend.h> +#include <linux/mfd/dbx500-prcmu.h> + +#include <mach/context.h> +#include <mach/pm.h> + +static int suspend(bool do_deepsleep) +{ + int ret = 0; + + /* configure the prcm for a sleep wakeup */ + prcmu_enable_wakeups(PRCMU_WAKEUP(ABB)); + + context_vape_save(); + + ux500_pm_gic_decouple(); + + if (ux500_pm_gic_pending_interrupt()) { + pr_info("suspend/resume: pending interrupt\n"); + + /* Recouple GIC with the interrupt bus */ + ux500_pm_gic_recouple(); + ret = -EBUSY; + + goto exit; + } + ux500_pm_prcmu_set_ioforce(true); + + if (do_deepsleep) { + context_varm_save_common(); + context_varm_save_core(); + context_gic_dist_disable_unneeded_irqs(); + context_save_cpu_registers(); + + /* + * Due to we have only 100us between requesting a powerstate + * and wfi, we clean the cache before as well to assure the + * final cache clean before wfi has as little as possible to + * do. + */ + context_clean_l1_cache_all(); + + (void) prcmu_set_power_state(PRCMU_AP_DEEP_SLEEP, + false, false); + context_save_to_sram_and_wfi(true); + + context_restore_cpu_registers(); + context_varm_restore_core(); + context_varm_restore_common(); + + } else { + + context_clean_l1_cache_all(); + (void) prcmu_set_power_state(APEXECUTE_TO_APSLEEP, + false, false); + dsb(); + __asm__ __volatile__("wfi\n\t" : : : "memory"); + } + + context_vape_restore(); + + /* APE was turned off, restore IO ring */ + ux500_pm_prcmu_set_ioforce(false); + +exit: + /* This is what cpuidle wants */ + prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) | + PRCMU_WAKEUP(ABB)); + return ret; +} + +static int ux500_suspend_enter(suspend_state_t state) +{ + if (state == PM_SUSPEND_MEM) + return suspend(true); + else if (state == PM_SUSPEND_STANDBY) + return suspend(false); + else + return -EINVAL; +} + +static int ux500_suspend_valid(suspend_state_t state) +{ + return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY; +} + +static int ux500_suspend_prepare_late(void) +{ + /* ESRAM to retention instead of OFF until ROM is fixed */ + (void) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); + return 0; +} + +static void ux500_suspend_wake(void) +{ + (void) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET); +} + +static struct platform_suspend_ops ux500_suspend_ops = { + .enter = ux500_suspend_enter, + .valid = ux500_suspend_valid, + .prepare_late = ux500_suspend_prepare_late, + .wake = ux500_suspend_wake, +}; + +static __init int ux500_suspend_init(void) +{ + suspend_set_ops(&ux500_suspend_ops); + return 0; +} +device_initcall(ux500_suspend_init); |
