diff options
-rw-r--r-- | arch/arm/plat-s3c/include/plat/pm.h | 27 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm.c | 211 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/include/plat/pm-core.h | 59 | ||||
-rw-r--r-- | arch/arm/plat-s3c24xx/pm.c | 224 |
4 files changed, 311 insertions, 210 deletions
diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index 95c2612d497..42c75e6d2ba 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 122e9b91a7f..fea58bea973 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; +} diff --git a/arch/arm/plat-s3c24xx/include/plat/pm-core.h b/arch/arm/plat-s3c24xx/include/plat/pm-core.h new file mode 100644 index 00000000000..c75882113e0 --- /dev/null +++ b/arch/arm/plat-s3c24xx/include/plat/pm-core.h @@ -0,0 +1,59 @@ +/* linux/arch/arm/plat-s3c24xx/include/plat/pll.h + * + * Copyright 2008 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * http://armlinux.simtec.co.uk/ + * + * S3C24xx - PM core support for arch/arm/plat-s3c/pm.c + * + * 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. + */ + +static inline void s3c_pm_debug_init_uart(void) +{ + unsigned long tmp = __raw_readl(S3C2410_CLKCON); + + /* re-start uart clocks */ + tmp |= S3C2410_CLKCON_UART0; + tmp |= S3C2410_CLKCON_UART1; + tmp |= S3C2410_CLKCON_UART2; + + __raw_writel(tmp, S3C2410_CLKCON); + udelay(10); +} + +static inline void s3c_pm_arch_prepare_irqs(void) +{ + __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); + __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK); + + /* ack any outstanding external interrupts before we go to sleep */ + + __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); + __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); + __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND); + +} + +static inline void s3c_pm_arch_stop_clocks(void) +{ + __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ +} + +static void s3c_pm_show_resume_irqs(int start, unsigned long which, + unsigned long mask); + +static inline void s3c_pm_arch_show_resume_irqs(void) +{ + S3C_PMDBG("post sleep: IRQs 0x%08x, 0x%08x\n", + __raw_readl(S3C2410_SRCPND), + __raw_readl(S3C2410_EINTPEND)); + + s3c_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), + s3c_irqwake_intmask); + + s3c_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), + s3c_irqwake_eintmask); +} diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c index e2ae12da16e..062a29339a9 100644 --- a/arch/arm/plat-s3c24xx/pm.c +++ b/arch/arm/plat-s3c24xx/pm.c @@ -34,9 +34,6 @@ #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> @@ -47,7 +44,6 @@ #include <plat/pm.h> - #define PFX "s3c24xx-pm: " static struct sleep_save core_save[] = { @@ -115,66 +111,6 @@ static struct sleep_save misc_save[] = { SAVE_ITEM(S3C2410_DCLKCON), }; -#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(S3C24XX_VA_UART0), - SAVE_UART(S3C24XX_VA_UART1), -#ifndef CONFIG_CPU_S3C2400 - SAVE_UART(S3C24XX_VA_UART2), -#endif -}; - -/* debug - * - * we send the debug to printascii() to allow it to be seen if the - * system never wakes up from the sleep -*/ - -static void s3c2410_pm_debug_init(void) -{ - unsigned long tmp = __raw_readl(S3C2410_CLKCON); - - /* re-start uart clocks */ - tmp |= S3C2410_CLKCON_UART0; - tmp |= S3C2410_CLKCON_UART1; - tmp |= S3C2410_CLKCON_UART2; - - __raw_writel(tmp, S3C2410_CLKCON); - udelay(10); -} - -#else -#define s3c2410_pm_debug_init() do { } while(0) - -static struct sleep_save uart_save[] = {}; -#endif - -/* s3c2410_pm_show_resume_irqs - * - * print any IRQs asserted at resume time (ie, we woke from) -*/ - -static void s3c2410_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); - } - } -} /* s3c_pm_check_resume_pin * @@ -206,12 +142,12 @@ static void s3c_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs) } } -/* s3c2410_pm_configure_extint +/* s3c_pm_configure_extint * * configure all external interrupt pins */ -static void s3c2410_pm_configure_extint(void) +void s3c_pm_configure_extint(void) { int pin; @@ -235,12 +171,12 @@ static void s3c2410_pm_configure_extint(void) #define OFFS_DAT (S3C2410_GPADAT - S3C2410_GPACON) #define OFFS_UP (S3C2410_GPBUP - S3C2410_GPBCON) -/* s3c2410_pm_save_gpios() +/* s3c_pm_save_gpios() * * Save the state of the GPIOs */ -static void s3c2410_pm_save_gpios(void) +void s3c_pm_save_gpios(void) { struct gpio_sleep *gps = gpio_save; unsigned int gpio; @@ -279,7 +215,10 @@ static inline int is_out(unsigned long con) return con == 1; } -/* s3c2410_pm_restore_gpio() +/** + * s3c2410_pm_restore_gpio() - restore the given GPIO bank + * @index: The number of the GPIO bank being resumed. + * @gps: The sleep confgiuration for the bank. * * Restore one of the GPIO banks that was saved during suspend. This is * not as simple as once thought, due to the possibility of glitches @@ -397,7 +336,7 @@ static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps) * Restore the state of the GPIOs */ -static void s3c2410_pm_restore_gpios(void) +void s3c_pm_restore_gpios(void) { struct gpio_sleep *gps = gpio_save; int gpio; @@ -407,150 +346,15 @@ static void s3c2410_pm_restore_gpios(void) } } -void (*pm_cpu_prep)(void); -void (*pm_cpu_sleep)(void); - -#define any_allowed(mask, allow) (((mask) & (allow)) != (allow)) - -/* s3c2410_pm_enter - * - * central control for sleep/resume process -*/ - -static int s3c2410_pm_enter(suspend_state_t state) +void s3c_pm_restore_core(void) { - unsigned long regs_save[16]; - - /* ensure the debug is initialised (if enabled) */ - - s3c2410_pm_debug_init(); - - S3C_PMDBG("s3c2410_pm_enter(%d)\n", state); - - if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) { - printk(KERN_ERR PFX "error: no cpu sleep functions set\n"); - 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 PFX "No sources enabled for wake-up!\n"); - printk(KERN_ERR PFX "Aborting sleep\n"); - 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 */ - - s3c2410_pm_save_gpios(); - s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); - s3c_pm_do_save(core_save, ARRAY_SIZE(core_save)); - s3c_pm_do_save(uart_save, ARRAY_SIZE(uart_save)); - - /* set the irq configuration for wake */ - - s3c2410_pm_configure_extint(); - - S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n", - s3c_irqwake_intmask, s3c_irqwake_eintmask); - - __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK); - __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK); - - /* ack any outstanding external interrupts before we go to sleep */ - - __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND); - __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND); - __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND); - - /* call cpu specific preparation */ - - pm_cpu_prep(); - - /* flush cache back to ram */ - - flush_cache_all(); - - s3c_pm_check_store(); - - /* send the cpu to sleep... */ - - __raw_writel(0x00, S3C2410_CLKCON); /* turn off clocks over sleep */ - - /* 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 */ - - cpu_init(); - - /* restore the system state */ - s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save)); - s3c_pm_do_restore(uart_save, ARRAY_SIZE(uart_save)); - s3c2410_pm_restore_gpios(); - - s3c2410_pm_debug_init(); - - /* check what irq (if any) restored the system */ - - S3C_PMDBG("post sleep: IRQs 0x%08x, 0x%08x\n", - __raw_readl(S3C2410_SRCPND), - __raw_readl(S3C2410_EINTPEND)); - - s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND), - s3c_irqwake_intmask); - - s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND), - s3c_irqwake_eintmask); - - S3C_PMDBG("post sleep, preparing to return\n"); - - s3c_pm_check_restore(); - - /* ok, let's return from sleep */ - - S3C_PMDBG("S3C2410 PM Resume (post-restore)\n"); - return 0; } -static struct platform_suspend_ops s3c2410_pm_ops = { - .enter = s3c2410_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) +void s3c_pm_save_core(void) { - printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n"); - - suspend_set_ops(&s3c2410_pm_ops); - return 0; + s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); + s3c_pm_do_save(core_save, ARRAY_SIZE(core_save)); } + |