forked from Minki/linux
2c793fa349
From Tomasz Figa <t.figa@samsung.com>: Current Samsung PM code is heavily unprepared for multiplatform systems. The design implies accessing functions and global variables defined in particular mach- subdirectory from common code in plat-, which is not allowed when building ARCH_MULTIPLATFORM. In addition there is a lot of forced code unification, which makes common function handle any possible quirks of all supported SoCs. In the end this design turned out to not work too well, ending with a lot of empty functions exported from mach-, just because code in common pm.c calls them. Moreover, recent trend of moving lower level suspend/resume code to proper drivers, like pinctrl or clk, made a lot of code there redundant, especially on DT-only platforms like Exynos. Note that this branch is based on previous tags/samsung-pm-1 and merge tags/samsung-cleanup-2 because of fix build error from recent changes of <linux/serial_s3c.h> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTK2j/AAoJEA0Cl+kVi2xqsMEP/0ziBUK0zV40zlQsfoMd+qFO q/96p/Aw1hxFef8JLsYWmvd2b+2MXpFCPkSw9ga2zhLyyk04CjM6YoLJYZY+h1W7 /NeLQvIMAvzhizO0mw861BKxsSHC2g2EdWPi/2sIe1+39CzbG5bU9BuC6fY9Y75G mlIPtYWBYz1aey1m8xYv3uNYEyunKCtthISpwIrBXbQrANsWZ59W7CAjL+juQh+J NJkAdM7t/S5oRdTyrtd0i90ewr5KsDIFnD1Z59YCboCzoFB1tKW10iFgLBs6aUtB pE7xc5IQu0/F/xxyTzi0uooGSPChxM5g8gLLEFZeN3Qnw06s7dm2HAnrPFZ2WyAQ xLhD++sP3ZhoomRh8ZljMfMhsLd51BaqK3u9oh4kvmVEGzJtytbftXezMIVF12DR Epe4i5vbGm9fhjHjBDpuybsICLDRp6tlICRURDrvbfoCRazQuZC4xZR3IJZ9vHHW 40EU09MBF/P+OoRkXnaYAwpD2F5AG0/QdsyXH9RJkoIZHE9fbjmDviqLiJRC2OH/ LpnEYThZ057mQYiUdcBjqzWFuV4UC8K5fZdlIRBUsRWGyLxBretj1gpMFGvS0ca/ abGZ2E/+s5uDlYAOfYvbwgMSc10lzo2YFkGMS+JpBgemkme8skVZ67SQkzuODqKz TqYfstyw6LreW84Ws4gZ =jtBA -----END PGP SIGNATURE----- Merge tag 'samsung-pm-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/cleanup3 Merge "Samsung PM related 2nd updates for v3.15" from Kukjin Kim: From Tomasz Figa <t.figa@samsung.com>: Current Samsung PM code is heavily unprepared for multiplatform systems. The design implies accessing functions and global variables defined in particular mach- subdirectory from common code in plat-, which is not allowed when building ARCH_MULTIPLATFORM. In addition there is a lot of forced code unification, which makes common function handle any possible quirks of all supported SoCs. In the end this design turned out to not work too well, ending with a lot of empty functions exported from mach-, just because code in common pm.c calls them. Moreover, recent trend of moving lower level suspend/resume code to proper drivers, like pinctrl or clk, made a lot of code there redundant, especially on DT-only platforms like Exynos. Note that this branch is based on previous tags/samsung-pm-1 and merge tags/samsung-cleanup-2 because of fix build error from recent changes of <linux/serial_s3c.h> * tag 'samsung-pm-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung: ARM: EXYNOS: Fix compilation error in cpuidle.c ARM: S5P64X0: Explicitly include linux/serial_s3c.h in mach/pm-core.h ARM: S3C64XX: Fix build for implicit serial_s3c.h inclusion serial: s3c: Fix build of header without serial_core.h preinclusion ARM: EXYNOS: Allow wake-up using GIC interrupts ARM: EXYNOS: Stop using legacy Samsung PM code ARM: EXYNOS: Remove PM initcalls and useless indirection ARM: EXYNOS: Fix abuse of CONFIG_PM ARM: SAMSUNG: Move s3c_pm_check_* prototypes to plat/pm-common.h ARM: SAMSUNG: Move common save/restore helpers to separate file ARM: SAMSUNG: Move Samsung PM debug code into separate file ARM: SAMSUNG: Consolidate PM debug functions ARM: SAMSUNG: Use debug_ll_addr() to get UART base address ARM: SAMSUNG: Save UART DIVSLOT register based on SoC type ARM: SAMSUNG: Add soc_is_s3c2410() helper ARM: EXYNOS: Do not resume l2x0 if not enabled before suspend Signed-off-by: Arnd Bergmann <arnd@arndb.de>
229 lines
4.7 KiB
C
229 lines
4.7 KiB
C
/* linux/arch/arm/plat-s3c/pm.c
|
|
*
|
|
* Copyright 2008 Openmoko, Inc.
|
|
* Copyright 2004-2008 Simtec Electronics
|
|
* Ben Dooks <ben@simtec.co.uk>
|
|
* http://armlinux.simtec.co.uk/
|
|
*
|
|
* S3C common power management (suspend to ram) support.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of.h>
|
|
#include <linux/serial_s3c.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/suspend.h>
|
|
|
|
#ifdef CONFIG_SAMSUNG_ATAGS
|
|
#include <mach/map.h>
|
|
#ifndef CONFIG_ARCH_EXYNOS
|
|
#include <mach/regs-clock.h>
|
|
#include <mach/regs-irq.h>
|
|
#endif
|
|
#include <mach/irqs.h>
|
|
#endif
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <plat/pm.h>
|
|
#include <mach/pm-core.h>
|
|
|
|
/* for external use */
|
|
|
|
unsigned long s3c_pm_flags;
|
|
|
|
/* The IRQ ext-int code goes here, it is too small to currently bother
|
|
* with its own file. */
|
|
|
|
unsigned long s3c_irqwake_intmask = 0xffffffffL;
|
|
unsigned long s3c_irqwake_eintmask = 0xffffffffL;
|
|
|
|
int s3c_irqext_wake(struct irq_data *data, unsigned int state)
|
|
{
|
|
unsigned long bit = 1L << IRQ_EINT_BIT(data->irq);
|
|
|
|
if (!(s3c_irqwake_eintallow & bit))
|
|
return -ENOENT;
|
|
|
|
printk(KERN_INFO "wake %s for irq %d\n",
|
|
state ? "enabled" : "disabled", data->irq);
|
|
|
|
if (!state)
|
|
s3c_irqwake_eintmask |= bit;
|
|
else
|
|
s3c_irqwake_eintmask &= ~bit;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* s3c2410_pm_show_resume_irqs
|
|
*
|
|
* print any IRQs asserted at resume time (ie, we woke from)
|
|
*/
|
|
static void __maybe_unused s3c_pm_show_resume_irqs(int start,
|
|
unsigned long which,
|
|
unsigned long mask)
|
|
{
|
|
int i;
|
|
|
|
which &= ~mask;
|
|
|
|
for (i = 0; i <= 31; i++) {
|
|
if (which & (1L<<i)) {
|
|
S3C_PMDBG("IRQ %d asserted at resume\n", start+i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void (*pm_cpu_prep)(void);
|
|
int (*pm_cpu_sleep)(unsigned long);
|
|
|
|
#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
|
|
|
|
/* s3c_pm_enter
|
|
*
|
|
* central control for sleep/resume process
|
|
*/
|
|
|
|
static int s3c_pm_enter(suspend_state_t state)
|
|
{
|
|
int ret;
|
|
/* ensure the debug is initialised (if enabled) */
|
|
|
|
s3c_pm_debug_init();
|
|
|
|
S3C_PMDBG("%s(%d)\n", __func__, state);
|
|
|
|
if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
|
|
printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check if we have anything to wake-up with... bad things seem
|
|
* to happen if you suspend with no wakeup (system will often
|
|
* require a full power-cycle)
|
|
*/
|
|
|
|
if (!of_have_populated_dt() &&
|
|
!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
|
|
!any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
|
|
printk(KERN_ERR "%s: No wake-up sources!\n", __func__);
|
|
printk(KERN_ERR "%s: Aborting sleep\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* save all necessary core registers not covered by the drivers */
|
|
|
|
if (!of_have_populated_dt()) {
|
|
samsung_pm_save_gpios();
|
|
samsung_pm_saved_gpios();
|
|
}
|
|
|
|
s3c_pm_save_uarts();
|
|
s3c_pm_save_core();
|
|
|
|
/* set the irq configuration for wake */
|
|
|
|
s3c_pm_configure_extint();
|
|
|
|
S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",
|
|
s3c_irqwake_intmask, s3c_irqwake_eintmask);
|
|
|
|
s3c_pm_arch_prepare_irqs();
|
|
|
|
/* call cpu specific preparation */
|
|
|
|
pm_cpu_prep();
|
|
|
|
/* flush cache back to ram */
|
|
|
|
flush_cache_all();
|
|
|
|
s3c_pm_check_store();
|
|
|
|
/* send the cpu to sleep... */
|
|
|
|
s3c_pm_arch_stop_clocks();
|
|
|
|
/* this will also act as our return point from when
|
|
* we resume as it saves its own register state and restores it
|
|
* during the resume. */
|
|
|
|
ret = cpu_suspend(0, pm_cpu_sleep);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* restore the system state */
|
|
|
|
s3c_pm_restore_core();
|
|
s3c_pm_restore_uarts();
|
|
|
|
if (!of_have_populated_dt()) {
|
|
samsung_pm_restore_gpios();
|
|
s3c_pm_restored_gpios();
|
|
}
|
|
|
|
s3c_pm_debug_init();
|
|
|
|
/* check what irq (if any) restored the system */
|
|
|
|
s3c_pm_arch_show_resume_irqs();
|
|
|
|
S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
|
|
|
|
/* LEDs should now be 1110 */
|
|
s3c_pm_debug_smdkled(1 << 1, 0);
|
|
|
|
s3c_pm_check_restore();
|
|
|
|
/* ok, let's return from sleep */
|
|
|
|
S3C_PMDBG("S3C PM Resume (post-restore)\n");
|
|
return 0;
|
|
}
|
|
|
|
static int s3c_pm_prepare(void)
|
|
{
|
|
/* prepare check area if configured */
|
|
|
|
s3c_pm_check_prepare();
|
|
return 0;
|
|
}
|
|
|
|
static void s3c_pm_finish(void)
|
|
{
|
|
s3c_pm_check_cleanup();
|
|
}
|
|
|
|
static const struct platform_suspend_ops s3c_pm_ops = {
|
|
.enter = s3c_pm_enter,
|
|
.prepare = s3c_pm_prepare,
|
|
.finish = s3c_pm_finish,
|
|
.valid = suspend_valid_only_mem,
|
|
};
|
|
|
|
/* s3c_pm_init
|
|
*
|
|
* Attach the power management functions. This should be called
|
|
* from the board specific initialisation if the board supports
|
|
* it.
|
|
*/
|
|
|
|
int __init s3c_pm_init(void)
|
|
{
|
|
printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");
|
|
|
|
suspend_set_ops(&s3c_pm_ops);
|
|
return 0;
|
|
}
|