diff options
Diffstat (limited to 'arch/arm/mach-exynos/pm.c')
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 172 |
1 files changed, 131 insertions, 41 deletions
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index ba18214c9aca..15af0ceb0a66 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -17,24 +17,35 @@ #include <linux/suspend.h> #include <linux/syscore_ops.h> #include <linux/io.h> +#include <linux/irqchip/arm-gic.h> #include <linux/err.h> #include <linux/clk.h> #include <asm/cacheflush.h> #include <asm/hardware/cache-l2x0.h> #include <asm/smp_scu.h> +#include <asm/suspend.h> #include <plat/cpu.h> -#include <plat/pm.h> +#include <plat/pm-common.h> #include <plat/pll.h> #include <plat/regs-srom.h> #include <mach/map.h> -#include <mach/pm-core.h> #include "common.h" #include "regs-pmu.h" +/** + * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping + * @hwirq: Hardware IRQ signal of the GIC + * @mask: Mask in PMU wake-up mask register + */ +struct exynos_wkup_irq { + unsigned int hwirq; + u32 mask; +}; + static struct sleep_save exynos5_sys_save[] = { SAVE_ITEM(EXYNOS5_SYS_I2C_CFG), }; @@ -48,6 +59,46 @@ static struct sleep_save exynos_core_save[] = { SAVE_ITEM(S5P_SROM_BC3), }; +/* + * GIC wake-up support + */ + +static u32 exynos_irqwake_intmask = 0xffffffff; + +static const struct exynos_wkup_irq exynos4_wkup_irq[] = { + { 76, BIT(1) }, /* RTC alarm */ + { 77, BIT(2) }, /* RTC tick */ + { /* sentinel */ }, +}; + +static const struct exynos_wkup_irq exynos5250_wkup_irq[] = { + { 75, BIT(1) }, /* RTC alarm */ + { 76, BIT(2) }, /* RTC tick */ + { /* sentinel */ }, +}; + +static int exynos_irq_set_wake(struct irq_data *data, unsigned int state) +{ + const struct exynos_wkup_irq *wkup_irq; + + if (soc_is_exynos5250()) + wkup_irq = exynos5250_wkup_irq; + else + wkup_irq = exynos4_wkup_irq; + + while (wkup_irq->mask) { + if (wkup_irq->hwirq == data->hwirq) { + if (!state) + exynos_irqwake_intmask |= wkup_irq->mask; + else + exynos_irqwake_intmask &= ~wkup_irq->mask; + return 0; + } + ++wkup_irq; + } + + return -ENOENT; +} /* For Cortex-A9 Diagnostic and Power control register */ static unsigned int save_arm_register[2]; @@ -72,6 +123,10 @@ static void exynos_pm_prepare(void) { unsigned int tmp; + /* Set wake-up mask registers */ + __raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK); + __raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK); + s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save)); if (soc_is_exynos5250()) { @@ -89,41 +144,8 @@ static void exynos_pm_prepare(void) /* ensure at least INFORM0 has the resume address */ - __raw_writel(virt_to_phys(s3c_cpu_resume), S5P_INFORM0); -} - -static int exynos_pm_add(struct device *dev, struct subsys_interface *sif) -{ - pm_cpu_prep = exynos_pm_prepare; - pm_cpu_sleep = exynos_cpu_suspend; - - return 0; -} - -static struct subsys_interface exynos_pm_interface = { - .name = "exynos_pm", - .subsys = &exynos_subsys, - .add_dev = exynos_pm_add, -}; - -static __init int exynos_pm_drvinit(void) -{ - unsigned int tmp; - - if (soc_is_exynos5440()) - return 0; - - s3c_pm_init(); - - /* All wakeup disable */ - - tmp = __raw_readl(S5P_WAKEUP_MASK); - tmp |= ((0xFF << 8) | (0x1F << 1)); - __raw_writel(tmp, S5P_WAKEUP_MASK); - - return subsys_interface_register(&exynos_pm_interface); + __raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0); } -arch_initcall(exynos_pm_drvinit); static int exynos_pm_suspend(void) { @@ -220,12 +242,80 @@ static struct syscore_ops exynos_pm_syscore_ops = { .resume = exynos_pm_resume, }; -static __init int exynos_pm_syscore_init(void) +/* + * Suspend Ops + */ + +static int exynos_suspend_enter(suspend_state_t state) { - if (soc_is_exynos5440()) - return 0; + int ret; + + s3c_pm_debug_init(); + + S3C_PMDBG("%s: suspending the system...\n", __func__); + + S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__, + exynos_irqwake_intmask, exynos_get_eint_wake_mask()); + + if (exynos_irqwake_intmask == -1U + && exynos_get_eint_wake_mask() == -1U) { + pr_err("%s: No wake-up sources!\n", __func__); + pr_err("%s: Aborting sleep\n", __func__); + return -EINVAL; + } + + s3c_pm_save_uarts(); + exynos_pm_prepare(); + flush_cache_all(); + s3c_pm_check_store(); + + ret = cpu_suspend(0, exynos_cpu_suspend); + if (ret) + return ret; + + s3c_pm_restore_uarts(); + + S3C_PMDBG("%s: wakeup stat: %08x\n", __func__, + __raw_readl(S5P_WAKEUP_STAT)); + + s3c_pm_check_restore(); + + S3C_PMDBG("%s: resuming the system...\n", __func__); - register_syscore_ops(&exynos_pm_syscore_ops); return 0; } -arch_initcall(exynos_pm_syscore_init); + +static int exynos_suspend_prepare(void) +{ + s3c_pm_check_prepare(); + + return 0; +} + +static void exynos_suspend_finish(void) +{ + s3c_pm_check_cleanup(); +} + +static const struct platform_suspend_ops exynos_suspend_ops = { + .enter = exynos_suspend_enter, + .prepare = exynos_suspend_prepare, + .finish = exynos_suspend_finish, + .valid = suspend_valid_only_mem, +}; + +void __init exynos_pm_init(void) +{ + u32 tmp; + + /* Platform-specific GIC callback */ + gic_arch_extn.irq_set_wake = exynos_irq_set_wake; + + /* All wakeup disable */ + tmp = __raw_readl(S5P_WAKEUP_MASK); + tmp |= ((0xFF << 8) | (0x1F << 1)); + __raw_writel(tmp, S5P_WAKEUP_MASK); + + register_syscore_ops(&exynos_pm_syscore_ops); + suspend_set_ops(&exynos_suspend_ops); +} |