Merge branch 'pm-next' of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux...
[pandora-kernel.git] / arch / arm / mach-omap2 / pm34xx.c
index 910a7ac..24c1966 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/gpio.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 
 #include <plat/sram.h>
 #include <plat/clockdomain.h>
@@ -57,6 +58,7 @@
 u32 enable_off_mode;
 u32 sleep_while_idle;
 u32 wakeup_timer_seconds;
+u32 wakeup_timer_milliseconds;
 
 struct power_state {
        struct powerdomain *pwrdm;
@@ -266,13 +268,16 @@ static int _prcm_int_handle_wakeup(void)
  */
 static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 {
-       u32 irqstatus_mpu;
+       u32 irqenable_mpu, irqstatus_mpu;
        int c = 0;
 
-       do {
-               irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
-                                       OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+       irqenable_mpu = prm_read_mod_reg(OCP_MOD,
+                                        OMAP3_PRM_IRQENABLE_MPU_OFFSET);
+       irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
+                                        OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+       irqstatus_mpu &= irqenable_mpu;
 
+       do {
                if (irqstatus_mpu & (OMAP3430_WKUP_ST | OMAP3430_IO_ST)) {
                        c = _prcm_int_handle_wakeup();
 
@@ -291,7 +296,11 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
                prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
                                        OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
 
-       } while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET));
+               irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
+                                       OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
+               irqstatus_mpu &= irqenable_mpu;
+
+       } while (irqstatus_mpu);
 
        return IRQ_HANDLED;
 }
@@ -370,12 +379,19 @@ void omap_sram_idle(void)
        if (pwrdm_read_pwrst(neon_pwrdm) == PWRDM_POWER_ON)
                pwrdm_set_next_pwrst(neon_pwrdm, mpu_next_state);
 
-       /* PER */
+       /* Enable IO-PAD and IO-CHAIN wakeups */
        per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
        core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
+       if (per_next_state < PWRDM_POWER_ON ||
+                       core_next_state < PWRDM_POWER_ON) {
+               prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
+               omap3_enable_io_chain();
+       }
+
+       /* PER */
        if (per_next_state < PWRDM_POWER_ON) {
                omap_uart_prepare_idle(2);
-               omap2_gpio_prepare_for_retention();
+               omap2_gpio_prepare_for_idle(per_next_state);
                if (per_next_state == PWRDM_POWER_OFF) {
                        if (core_next_state == PWRDM_POWER_ON) {
                                per_next_state = PWRDM_POWER_RET;
@@ -397,10 +413,8 @@ void omap_sram_idle(void)
                        omap3_core_save_context();
                        omap3_prcm_save_context();
                }
-               /* Enable IO-PAD and IO-CHAIN wakeups */
-               prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
-               omap3_enable_io_chain();
        }
+
        omap3_intc_prepare_idle();
 
        /*
@@ -453,16 +467,17 @@ void omap_sram_idle(void)
        /* PER */
        if (per_next_state < PWRDM_POWER_ON) {
                per_prev_state = pwrdm_read_prev_pwrst(per_pwrdm);
+               omap2_gpio_resume_after_idle();
                if (per_prev_state == PWRDM_POWER_OFF)
                        omap3_per_restore_context();
-               omap2_gpio_resume_after_retention();
                omap_uart_resume_idle(2);
                if (per_state_modified)
                        pwrdm_set_next_pwrst(per_pwrdm, PWRDM_POWER_OFF);
        }
 
        /* Disable IO-PAD and IO-CHAIN wakeup */
-       if (core_next_state < PWRDM_POWER_ON) {
+       if (per_next_state < PWRDM_POWER_ON ||
+                       core_next_state < PWRDM_POWER_ON) {
                prm_clear_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
                omap3_disable_io_chain();
        }
@@ -547,20 +562,21 @@ out:
 #ifdef CONFIG_SUSPEND
 static suspend_state_t suspend_state;
 
-static void omap2_pm_wakeup_on_timer(u32 seconds)
+static void omap2_pm_wakeup_on_timer(u32 seconds, u32 milliseconds)
 {
        u32 tick_rate, cycles;
 
-       if (!seconds)
+       if (!seconds && !milliseconds)
                return;
 
        tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer_wakeup));
-       cycles = tick_rate * seconds;
+       cycles = tick_rate * seconds + tick_rate * milliseconds / 1000;
        omap_dm_timer_stop(gptimer_wakeup);
        omap_dm_timer_set_load_start(gptimer_wakeup, 0, 0xffffffff - cycles);
 
-       pr_info("PM: Resume timer in %d secs (%d ticks at %d ticks/sec.)\n",
-               seconds, cycles, tick_rate);
+       pr_info("PM: Resume timer in %u.%03u secs"
+               " (%d ticks at %d ticks/sec.)\n",
+               seconds, milliseconds, cycles, tick_rate);
 }
 
 static int omap3_pm_prepare(void)
@@ -574,8 +590,9 @@ static int omap3_pm_suspend(void)
        struct power_state *pwrst;
        int state, ret = 0;
 
-       if (wakeup_timer_seconds)
-               omap2_pm_wakeup_on_timer(wakeup_timer_seconds);
+       if (wakeup_timer_seconds || wakeup_timer_milliseconds)
+               omap2_pm_wakeup_on_timer(wakeup_timer_seconds,
+                                        wakeup_timer_milliseconds);
 
        /* Read current next_pwrsts */
        list_for_each_entry(pwrst, &pwrst_list, node)
@@ -685,10 +702,10 @@ static void __init omap3_iva_idle(void)
        prm_write_mod_reg(OMAP3430_RST1_IVA2 |
                          OMAP3430_RST2_IVA2 |
                          OMAP3430_RST3_IVA2,
-                         OMAP3430_IVA2_MOD, RM_RSTCTRL);
+                         OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL);
 
        /* Enable IVA2 clock */
-       cm_write_mod_reg(OMAP3430_CM_FCLKEN_IVA2_EN_IVA2,
+       cm_write_mod_reg(OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_MASK,
                         OMAP3430_IVA2_MOD, CM_FCLKEN);
 
        /* Set IVA2 boot mode to 'idle' */
@@ -696,7 +713,7 @@ static void __init omap3_iva_idle(void)
                         OMAP343X_CONTROL_IVA2_BOOTMOD);
 
        /* Un-reset IVA2 */
-       prm_write_mod_reg(0, OMAP3430_IVA2_MOD, RM_RSTCTRL);
+       prm_write_mod_reg(0, OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL);
 
        /* Disable IVA2 clock */
        cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN);
@@ -705,7 +722,7 @@ static void __init omap3_iva_idle(void)
        prm_write_mod_reg(OMAP3430_RST1_IVA2 |
                          OMAP3430_RST2_IVA2 |
                          OMAP3430_RST3_IVA2,
-                         OMAP3430_IVA2_MOD, RM_RSTCTRL);
+                         OMAP3430_IVA2_MOD, OMAP2_RM_RSTCTRL);
 }
 
 static void __init omap3_d2d_idle(void)
@@ -728,8 +745,8 @@ static void __init omap3_d2d_idle(void)
        /* reset modem */
        prm_write_mod_reg(OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RSTPWRON |
                          OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RST,
-                         CORE_MOD, RM_RSTCTRL);
-       prm_write_mod_reg(0, CORE_MOD, RM_RSTCTRL);
+                         CORE_MOD, OMAP2_RM_RSTCTRL);
+       prm_write_mod_reg(0, CORE_MOD, OMAP2_RM_RSTCTRL);
 }
 
 static void __init prcm_setup_regs(void)
@@ -916,13 +933,13 @@ static void __init prcm_setup_regs(void)
        prm_write_mod_reg(0, OMAP3430_PER_MOD, OMAP3430_PM_IVAGRPSEL);
 
        /* Clear any pending 'reset' flags */
-       prm_write_mod_reg(0xffffffff, MPU_MOD, RM_RSTST);
-       prm_write_mod_reg(0xffffffff, CORE_MOD, RM_RSTST);
-       prm_write_mod_reg(0xffffffff, OMAP3430_PER_MOD, RM_RSTST);
-       prm_write_mod_reg(0xffffffff, OMAP3430_EMU_MOD, RM_RSTST);
-       prm_write_mod_reg(0xffffffff, OMAP3430_NEON_MOD, RM_RSTST);
-       prm_write_mod_reg(0xffffffff, OMAP3430_DSS_MOD, RM_RSTST);
-       prm_write_mod_reg(0xffffffff, OMAP3430ES2_USBHOST_MOD, RM_RSTST);
+       prm_write_mod_reg(0xffffffff, MPU_MOD, OMAP2_RM_RSTST);
+       prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP2_RM_RSTST);
+       prm_write_mod_reg(0xffffffff, OMAP3430_PER_MOD, OMAP2_RM_RSTST);
+       prm_write_mod_reg(0xffffffff, OMAP3430_EMU_MOD, OMAP2_RM_RSTST);
+       prm_write_mod_reg(0xffffffff, OMAP3430_NEON_MOD, OMAP2_RM_RSTST);
+       prm_write_mod_reg(0xffffffff, OMAP3430_DSS_MOD, OMAP2_RM_RSTST);
+       prm_write_mod_reg(0xffffffff, OMAP3430ES2_USBHOST_MOD, OMAP2_RM_RSTST);
 
        /* Clear any pending PRCM interrupts */
        prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET);
@@ -941,6 +958,10 @@ void omap3_pm_off_mode_enable(int enable)
        else
                state = PWRDM_POWER_RET;
 
+#ifdef CONFIG_CPU_IDLE
+       omap3_cpuidle_update_states();
+#endif
+
        list_for_each_entry(pwrst, &pwrst_list, node) {
                pwrst->next_state = state;
                set_pwrdm_state(pwrst->pwrdm, state);
@@ -998,6 +1019,9 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
  */
 static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
 {
+       clkdm_clear_all_wkdeps(clkdm);
+       clkdm_clear_all_sleepdeps(clkdm);
+
        if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO)
                omap2_clkdm_allow_idle(clkdm);
        else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
@@ -1018,6 +1042,7 @@ void omap_push_sram_idle(void)
 static int __init omap3_pm_init(void)
 {
        struct power_state *pwrst, *tmp;
+       struct clockdomain *neon_clkdm, *per_clkdm, *mpu_clkdm, *core_clkdm;
        int ret;
 
        if (!cpu_is_omap34xx())
@@ -1057,6 +1082,11 @@ static int __init omap3_pm_init(void)
        core_pwrdm = pwrdm_lookup("core_pwrdm");
        cam_pwrdm = pwrdm_lookup("cam_pwrdm");
 
+       neon_clkdm = clkdm_lookup("neon_clkdm");
+       mpu_clkdm = clkdm_lookup("mpu_clkdm");
+       per_clkdm = clkdm_lookup("per_clkdm");
+       core_clkdm = clkdm_lookup("core_clkdm");
+
        omap_push_sram_idle();
 #ifdef CONFIG_SUSPEND
        suspend_set_ops(&omap_pm_ops);
@@ -1065,15 +1095,7 @@ static int __init omap3_pm_init(void)
        pm_idle = omap3_pm_idle;
        omap3_idle_init();
 
-       pwrdm_add_wkdep(neon_pwrdm, mpu_pwrdm);
-       /*
-        * REVISIT: This wkdep is only necessary when GPIO2-6 are enabled for
-        * IO-pad wakeup.  Otherwise it will unnecessarily waste power
-        * waking up PER with every CORE wakeup - see
-        * http://marc.info/?l=linux-omap&m=121852150710062&w=2
-       */
-       pwrdm_add_wkdep(per_pwrdm, core_pwrdm);
-
+       clkdm_add_wkdep(neon_clkdm, mpu_clkdm);
        if (omap_type() != OMAP2_DEVICE_TYPE_GP) {
                omap3_secure_ram_storage =
                        kmalloc(0x803F, GFP_KERNEL);