diff options
Diffstat (limited to 'arch/arm/mach-omap2/pm.c')
-rw-r--r-- | arch/arm/mach-omap2/pm.c | 271 |
1 files changed, 265 insertions, 6 deletions
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 562168fa2b16..d7eee99b7e3f 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/sysfs.h> #include <linux/module.h> +#include <linux/delay.h> #include <asm/io.h> #include <asm/irq.h> @@ -36,11 +37,18 @@ #include <asm/arch/sram.h> #include <asm/arch/pm.h> +#include "prcm-regs.h" + static struct clk *vclk; static void (*omap2_sram_idle)(void); static void (*omap2_sram_suspend)(int dllctrl, int cpu_rev); static void (*saved_idle)(void); +extern void __init pmdomain_init(void); +extern void pmdomain_set_autoidle(void); + +static unsigned int omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_SIZE]; + void omap2_pm_idle(void) { local_irq_disable(); @@ -87,23 +95,272 @@ static int omap2_pm_prepare(suspend_state_t state) return error; } +#define INT0_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK1) | \ + OMAP_IRQ_BIT(INT_24XX_GPIO_BANK2) | \ + OMAP_IRQ_BIT(INT_24XX_GPIO_BANK3)) + +#define INT1_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK4)) + +#define INT2_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_UART1_IRQ) | \ + OMAP_IRQ_BIT(INT_24XX_UART2_IRQ) | \ + OMAP_IRQ_BIT(INT_24XX_UART3_IRQ)) + +#define preg(reg) printk("%s\t(0x%p):\t0x%08x\n", #reg, ®, reg); + +static void omap2_pm_debug(char * desc) +{ + printk("%s:\n", desc); + + preg(CM_CLKSTCTRL_MPU); + preg(CM_CLKSTCTRL_CORE); + preg(CM_CLKSTCTRL_GFX); + preg(CM_CLKSTCTRL_DSP); + preg(CM_CLKSTCTRL_MDM); + + preg(PM_PWSTCTRL_MPU); + preg(PM_PWSTCTRL_CORE); + preg(PM_PWSTCTRL_GFX); + preg(PM_PWSTCTRL_DSP); + preg(PM_PWSTCTRL_MDM); + + preg(PM_PWSTST_MPU); + preg(PM_PWSTST_CORE); + preg(PM_PWSTST_GFX); + preg(PM_PWSTST_DSP); + preg(PM_PWSTST_MDM); + + preg(CM_AUTOIDLE1_CORE); + preg(CM_AUTOIDLE2_CORE); + preg(CM_AUTOIDLE3_CORE); + preg(CM_AUTOIDLE4_CORE); + preg(CM_AUTOIDLE_WKUP); + preg(CM_AUTOIDLE_PLL); + preg(CM_AUTOIDLE_DSP); + preg(CM_AUTOIDLE_MDM); + + preg(CM_ICLKEN1_CORE); + preg(CM_ICLKEN2_CORE); + preg(CM_ICLKEN3_CORE); + preg(CM_ICLKEN4_CORE); + preg(CM_ICLKEN_GFX); + preg(CM_ICLKEN_WKUP); + preg(CM_ICLKEN_DSP); + preg(CM_ICLKEN_MDM); + + preg(CM_IDLEST1_CORE); + preg(CM_IDLEST2_CORE); + preg(CM_IDLEST3_CORE); + preg(CM_IDLEST4_CORE); + preg(CM_IDLEST_GFX); + preg(CM_IDLEST_WKUP); + preg(CM_IDLEST_CKGEN); + preg(CM_IDLEST_DSP); + preg(CM_IDLEST_MDM); + + preg(RM_RSTST_MPU); + preg(RM_RSTST_GFX); + preg(RM_RSTST_WKUP); + preg(RM_RSTST_DSP); + preg(RM_RSTST_MDM); + + preg(PM_WKDEP_MPU); + preg(PM_WKDEP_CORE); + preg(PM_WKDEP_GFX); + preg(PM_WKDEP_DSP); + preg(PM_WKDEP_MDM); + + preg(CM_FCLKEN_WKUP); + preg(CM_ICLKEN_WKUP); + preg(CM_IDLEST_WKUP); + preg(CM_AUTOIDLE_WKUP); + preg(CM_CLKSEL_WKUP); + + preg(PM_WKEN_WKUP); + preg(PM_WKST_WKUP); +} + +static inline void omap2_pm_save_registers(void) +{ + /* Save interrupt registers */ + OMAP24XX_SAVE(INTC_MIR0); + OMAP24XX_SAVE(INTC_MIR1); + OMAP24XX_SAVE(INTC_MIR2); + + /* Save power control registers */ + OMAP24XX_SAVE(CM_CLKSTCTRL_MPU); + OMAP24XX_SAVE(CM_CLKSTCTRL_CORE); + OMAP24XX_SAVE(CM_CLKSTCTRL_GFX); + OMAP24XX_SAVE(CM_CLKSTCTRL_DSP); + OMAP24XX_SAVE(CM_CLKSTCTRL_MDM); + + /* Save power state registers */ + OMAP24XX_SAVE(PM_PWSTCTRL_MPU); + OMAP24XX_SAVE(PM_PWSTCTRL_CORE); + OMAP24XX_SAVE(PM_PWSTCTRL_GFX); + OMAP24XX_SAVE(PM_PWSTCTRL_DSP); + OMAP24XX_SAVE(PM_PWSTCTRL_MDM); + + /* Save autoidle registers */ + OMAP24XX_SAVE(CM_AUTOIDLE1_CORE); + OMAP24XX_SAVE(CM_AUTOIDLE2_CORE); + OMAP24XX_SAVE(CM_AUTOIDLE3_CORE); + OMAP24XX_SAVE(CM_AUTOIDLE4_CORE); + OMAP24XX_SAVE(CM_AUTOIDLE_WKUP); + OMAP24XX_SAVE(CM_AUTOIDLE_PLL); + OMAP24XX_SAVE(CM_AUTOIDLE_DSP); + OMAP24XX_SAVE(CM_AUTOIDLE_MDM); + + /* Save idle state registers */ + OMAP24XX_SAVE(CM_IDLEST1_CORE); + OMAP24XX_SAVE(CM_IDLEST2_CORE); + OMAP24XX_SAVE(CM_IDLEST3_CORE); + OMAP24XX_SAVE(CM_IDLEST4_CORE); + OMAP24XX_SAVE(CM_IDLEST_GFX); + OMAP24XX_SAVE(CM_IDLEST_WKUP); + OMAP24XX_SAVE(CM_IDLEST_CKGEN); + OMAP24XX_SAVE(CM_IDLEST_DSP); + OMAP24XX_SAVE(CM_IDLEST_MDM); + + /* Save clock registers */ + OMAP24XX_SAVE(CM_FCLKEN1_CORE); + OMAP24XX_SAVE(CM_FCLKEN2_CORE); + OMAP24XX_SAVE(CM_ICLKEN1_CORE); + OMAP24XX_SAVE(CM_ICLKEN2_CORE); + OMAP24XX_SAVE(CM_ICLKEN3_CORE); + OMAP24XX_SAVE(CM_ICLKEN4_CORE); +} + +static inline void omap2_pm_restore_registers(void) +{ + /* Restore clock state registers */ + OMAP24XX_RESTORE(CM_CLKSTCTRL_MPU); + OMAP24XX_RESTORE(CM_CLKSTCTRL_CORE); + OMAP24XX_RESTORE(CM_CLKSTCTRL_GFX); + OMAP24XX_RESTORE(CM_CLKSTCTRL_DSP); + OMAP24XX_RESTORE(CM_CLKSTCTRL_MDM); + + /* Restore power state registers */ + OMAP24XX_RESTORE(PM_PWSTCTRL_MPU); + OMAP24XX_RESTORE(PM_PWSTCTRL_CORE); + OMAP24XX_RESTORE(PM_PWSTCTRL_GFX); + OMAP24XX_RESTORE(PM_PWSTCTRL_DSP); + OMAP24XX_RESTORE(PM_PWSTCTRL_MDM); + + /* Restore idle state registers */ + OMAP24XX_RESTORE(CM_IDLEST1_CORE); + OMAP24XX_RESTORE(CM_IDLEST2_CORE); + OMAP24XX_RESTORE(CM_IDLEST3_CORE); + OMAP24XX_RESTORE(CM_IDLEST4_CORE); + OMAP24XX_RESTORE(CM_IDLEST_GFX); + OMAP24XX_RESTORE(CM_IDLEST_WKUP); + OMAP24XX_RESTORE(CM_IDLEST_CKGEN); + OMAP24XX_RESTORE(CM_IDLEST_DSP); + OMAP24XX_RESTORE(CM_IDLEST_MDM); + + /* Restore autoidle registers */ + OMAP24XX_RESTORE(CM_AUTOIDLE1_CORE); + OMAP24XX_RESTORE(CM_AUTOIDLE2_CORE); + OMAP24XX_RESTORE(CM_AUTOIDLE3_CORE); + OMAP24XX_RESTORE(CM_AUTOIDLE4_CORE); + OMAP24XX_RESTORE(CM_AUTOIDLE_WKUP); + OMAP24XX_RESTORE(CM_AUTOIDLE_PLL); + OMAP24XX_RESTORE(CM_AUTOIDLE_DSP); + OMAP24XX_RESTORE(CM_AUTOIDLE_MDM); + + /* Restore clock registers */ + OMAP24XX_RESTORE(CM_FCLKEN1_CORE); + OMAP24XX_RESTORE(CM_FCLKEN2_CORE); + OMAP24XX_RESTORE(CM_ICLKEN1_CORE); + OMAP24XX_RESTORE(CM_ICLKEN2_CORE); + OMAP24XX_RESTORE(CM_ICLKEN3_CORE); + OMAP24XX_RESTORE(CM_ICLKEN4_CORE); + + /* REVISIT: Clear interrupts here */ + + /* Restore interrupt registers */ + OMAP24XX_RESTORE(INTC_MIR0); + OMAP24XX_RESTORE(INTC_MIR1); + OMAP24XX_RESTORE(INTC_MIR2); +} + +static int omap2_pm_suspend(void) +{ + int processor_type = 0; + + /* REVISIT: 0x21 or 0x26? */ + if (cpu_is_omap2420()) + processor_type = 0x21; + + if (!processor_type) + return -ENOTSUPP; + + local_irq_disable(); + local_fiq_disable(); + + omap2_pm_save_registers(); + + /* Disable interrupts except for the wake events */ + INTC_MIR_SET0 = 0xffffffff & ~INT0_WAKE_MASK; + INTC_MIR_SET1 = 0xffffffff & ~INT1_WAKE_MASK; + INTC_MIR_SET2 = 0xffffffff & ~INT2_WAKE_MASK; + + pmdomain_set_autoidle(); + + /* Clear old wake-up events */ + PM_WKST1_CORE = 0; + PM_WKST2_CORE = 0; + PM_WKST_WKUP = 0; + + /* Enable wake-up events */ + PM_WKEN1_CORE = (1 << 22) | (1 << 21); /* UART1 & 2 */ + PM_WKEN2_CORE = (1 << 2); /* UART3 */ + PM_WKEN_WKUP = (1 << 2) | (1 << 0); /* GPIO & GPT1 */ + + /* Disable clocks except for CM_ICLKEN2_CORE. It gets disabled + * in the SRAM suspend code */ + CM_FCLKEN1_CORE = 0; + CM_FCLKEN2_CORE = 0; + CM_ICLKEN1_CORE = 0; + CM_ICLKEN3_CORE = 0; + CM_ICLKEN4_CORE = 0; + + omap2_pm_debug("Status before suspend"); + + /* Must wait for serial buffers to clear */ + mdelay(200); + + /* Jump to SRAM suspend code + * REVISIT: When is this SDRC_DLLB_CTRL? + */ + omap2_sram_suspend(SDRC_DLLA_CTRL, processor_type); + + /* Back from sleep */ + omap2_pm_restore_registers(); + + local_fiq_enable(); + local_irq_enable(); + + return 0; +} + static int omap2_pm_enter(suspend_state_t state) { + int ret = 0; + switch (state) { case PM_SUSPEND_STANDBY: case PM_SUSPEND_MEM: - /* FIXME: Add suspend */ + ret = omap2_pm_suspend(); break; - case PM_SUSPEND_DISK: - return -ENOTSUPP; - + ret = -ENOTSUPP; + break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; + return ret; } static int omap2_pm_finish(suspend_state_t state) @@ -143,6 +400,8 @@ int __init omap2_pm_init(void) pm_set_ops(&omap_pm_ops); pm_idle = omap2_pm_idle; + pmdomain_init(); + return 0; } |