diff options
author | Ben Dooks <ben-linux@fluff.org> | 2008-12-12 03:24:08 +0300 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-03-08 15:33:51 +0300 |
commit | 2261e0e6e3991d4c5f33e9fedadfc465eedc05a7 (patch) | |
tree | aa0125f362a24ecaf0cc69ff3715fecf098b4791 /arch/arm/plat-s3c | |
parent | 549c7e33aeb9bfe441ecf68639d2227bb90978e7 (diff) | |
download | linux-2261e0e6e3991d4c5f33e9fedadfc465eedc05a7.tar.xz |
[ARM] S3C: Move plat-s3c24xx pm.c support into plat-s3c
Move parts of the core and debug suspend code into the
plat-s3c for use with the new s3c64xx code.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c')
-rw-r--r-- | arch/arm/plat-s3c/include/plat/pm.h | 27 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm.c | 211 |
2 files changed, 238 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index 95c2612d4976..42c75e6d2ba5 100644 --- a/arch/arm/plat-s3c/include/plat/pm.h +++ b/arch/arm/plat-s3c/include/plat/pm.h @@ -113,3 +113,30 @@ extern void s3c_pm_check_store(void); #define s3c_pm_check_restore() do { } while(0) #define s3c_pm_check_store() do { } while(0) #endif + +/** + * s3c_pm_configure_extint() - ensure pins are correctly set for IRQ + * + * Setup all the necessary GPIO pins for waking the system on external + * interrupt. + */ +extern void s3c_pm_configure_extint(void); + +/** + * s3c_pm_restore_gpios() - restore the state of the gpios after sleep. + * + * Restore the state of the GPIO pins after sleep, which may involve ensuring + * that we do not glitch the state of the pins from that the bootloader's + * resume code has done. +*/ +extern void s3c_pm_restore_gpios(void); + +/** + * s3c_pm_save_gpios() - save the state of the GPIOs for restoring after sleep. + * + * Save the GPIO states for resotration on resume. See s3c_pm_restore_gpios(). + */ +extern void s3c_pm_save_gpios(void); + +extern void s3c_pm_save_core(void); +extern void s3c_pm_restore_core(void); diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c index 122e9b91a7f4..fea58bea973d 100644 --- a/arch/arm/plat-s3c/pm.c +++ b/arch/arm/plat-s3c/pm.c @@ -15,14 +15,32 @@ #include <linux/init.h> #include <linux/suspend.h> #include <linux/errno.h> +#include <linux/delay.h> +#include <linux/serial_core.h> #include <linux/io.h> +#include <asm/cacheflush.h> +#include <mach/hardware.h> + +#include <plat/regs-serial.h> +#include <mach/regs-clock.h> +#include <mach/regs-gpio.h> +#include <mach/regs-mem.h> +#include <mach/regs-irq.h> + #include <plat/pm.h> +#include <plat/pm-core.h> /* for external use */ unsigned long s3c_pm_flags; +/* Debug code: + * + * This code supports debug output to the low level UARTs for use on + * resume before the console layer is available. +*/ + #ifdef CONFIG_S3C2410_PM_DEBUG extern void printascii(const char *); @@ -37,8 +55,51 @@ void s3c_pm_dbg(const char *fmt, ...) printascii(buff); } + +static inline void s3c_pm_debug_init(void) +{ + /* restart uart clocks so we can use them to output */ + s3c_pm_debug_init_uart(); +} + +#else +#define s3c_pm_debug_init() do { } while(0) + #endif /* CONFIG_S3C2410_PM_DEBUG */ +/* Save the UART configurations if we are configured for debug. */ + +#ifdef CONFIG_S3C2410_PM_DEBUG + +#define SAVE_UART(va) \ + SAVE_ITEM((va) + S3C2410_ULCON), \ + SAVE_ITEM((va) + S3C2410_UCON), \ + SAVE_ITEM((va) + S3C2410_UFCON), \ + SAVE_ITEM((va) + S3C2410_UMCON), \ + SAVE_ITEM((va) + S3C2410_UBRDIV) + +static struct sleep_save uart_save[] = { + SAVE_UART(S3C_VA_UART0), + SAVE_UART(S3C_VA_UART1), +#ifndef CONFIG_CPU_S3C2400 + SAVE_UART(S3C_VA_UART2), +#endif +}; + +static void s3c_pm_save_uart(void) +{ + s3c_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); +} + +static void s3c_pm_restore_uart(void) +{ + s3c_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); +} +#else +static void s3c_pm_save_uart(void) { } +static void s3c_pm_restore_uart(void) { } +#endif + /* helper functions to save and restore register state */ @@ -95,3 +156,153 @@ void s3c_pm_do_restore_core(struct sleep_save *ptr, int count) for (; count > 0; count--, ptr++) __raw_writel(ptr->val, ptr->reg); } + +/* s3c2410_pm_show_resume_irqs + * + * print any IRQs asserted at resume time (ie, we woke from) +*/ +static void 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); +void (*pm_cpu_sleep)(void); + +#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) +{ + unsigned long regs_save[16]; + + /* 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 (!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; + } + + /* prepare check area if configured */ + + s3c_pm_check_prepare(); + + /* store the physical address of the register recovery block */ + + s3c_sleep_save_phys = virt_to_phys(regs_save); + + S3C_PMDBG("s3c_sleep_save_phys=0x%08lx\n", s3c_sleep_save_phys); + + /* save all necessary core registers not covered by the drivers */ + + s3c_pm_save_gpios(); + s3c_pm_save_uart(); + 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(); + + /* s3c2410_cpu_save will also act as our return point from when + * we resume as it saves its own register state, so use the return + * code to differentiate return from save and return from sleep */ + + if (s3c2410_cpu_save(regs_save) == 0) { + flush_cache_all(); + pm_cpu_sleep(); + } + + /* restore the cpu state using the kernel's cpu init code. */ + + cpu_init(); + + /* restore the system state */ + + s3c_pm_restore_core(); + s3c_pm_restore_uart(); + s3c_pm_restore_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__); + + s3c_pm_check_restore(); + + /* ok, let's return from sleep */ + + S3C_PMDBG("S3C PM Resume (post-restore)\n"); + return 0; +} + +static struct platform_suspend_ops s3c_pm_ops = { + .enter = s3c_pm_enter, + .valid = suspend_valid_only_mem, +}; + +/* s3c2410_pm_init + * + * Attach the power management functions. This should be called + * from the board specific initialisation if the board supports + * it. +*/ + +int __init s3c2410_pm_init(void) +{ + printk("S3C Power Management, Copyright 2004 Simtec Electronics\n"); + + suspend_set_ops(&s3c_pm_ops); + return 0; +} |