OMAP3: PM: Prevent PER from going OFF when CORE is going INA
[pandora-kernel.git] / arch / arm / mach-omap2 / pm34xx.c
index ebb88f3..55567bf 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/clk.h>
 
 #include <plat/sram.h>
 #include <plat/clockdomain.h>
@@ -35,6 +36,7 @@
 #include <plat/prcm.h>
 #include <plat/gpmc.h>
 #include <plat/dma.h>
+#include <plat/dmtimer.h>
 
 #include <asm/tlbflush.h>
 
 
 #include "prm.h"
 #include "pm.h"
+#include "sdrc.h"
+
+#define SDRC_POWER_AUTOCOUNT_SHIFT 8
+#define SDRC_POWER_AUTOCOUNT_MASK (0xffff << SDRC_POWER_AUTOCOUNT_SHIFT)
+#define SDRC_POWER_CLKCTRL_SHIFT 4
+#define SDRC_POWER_CLKCTRL_MASK (0x3 << SDRC_POWER_CLKCTRL_SHIFT)
+#define SDRC_SELF_REFRESH_ON_AUTOCOUNT (0x2 << SDRC_POWER_CLKCTRL_SHIFT)
 
 /* Scratchpad offsets */
 #define OMAP343X_TABLE_ADDRESS_OFFSET     0x31
 #define OMAP343X_TABLE_VALUE_OFFSET       0x30
 #define OMAP343X_CONTROL_REG_VALUE_OFFSET  0x32
 
+u32 enable_off_mode;
+u32 sleep_while_idle;
+u32 wakeup_timer_seconds;
+
 struct power_state {
        struct powerdomain *pwrdm;
        u32 next_state;
@@ -112,14 +125,17 @@ static void omap3_core_restore_context(void)
        omap_dma_global_context_restore();
 }
 
+/*
+ * FIXME: This function should be called before entering off-mode after
+ * OMAP3 secure services have been accessed. Currently it is only called
+ * once during boot sequence, but this works as we are not using secure
+ * services.
+ */
 static void omap3_save_secure_ram_context(u32 target_mpu_state)
 {
        u32 ret;
 
        if (omap_type() != OMAP2_DEVICE_TYPE_GP) {
-               /* Disable dma irq before calling secure rom code API */
-               omap_dma_disable_irq(0);
-               omap_dma_disable_irq(1);
                /*
                 * MPU next state must be set to POWER_ON temporarily,
                 * otherwise the WFI executed inside the ROM code
@@ -291,6 +307,8 @@ static void omap_sram_idle(void)
        int per_next_state = PWRDM_POWER_ON;
        int core_next_state = PWRDM_POWER_ON;
        int core_prev_state, per_prev_state;
+       u32 sdrc_pwr = 0;
+       int per_state_modified = 0;
 
        if (!_omap_sram_idle)
                return;
@@ -321,28 +339,49 @@ static void omap_sram_idle(void)
        if (pwrdm_read_pwrst(neon_pwrdm) == PWRDM_POWER_ON)
                set_pwrdm_state(neon_pwrdm, mpu_next_state);
 
-       /* CORE & PER */
+       /* PER */
+       per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
        core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
-       if (core_next_state < PWRDM_POWER_ON) {
+       if (per_next_state < PWRDM_POWER_ON) {
+               omap_uart_prepare_idle(2);
                omap2_gpio_prepare_for_retention();
-               omap_uart_prepare_idle(0);
-               omap_uart_prepare_idle(1);
-               /* PER changes only with core */
-               per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
-               if (per_next_state < PWRDM_POWER_ON) {
-                       omap_uart_prepare_idle(2);
-                       if (per_next_state == PWRDM_POWER_OFF)
+               if (per_next_state == PWRDM_POWER_OFF) {
+                       if (core_next_state == PWRDM_POWER_ON) {
+                               per_next_state = PWRDM_POWER_RET;
+                               pwrdm_set_next_pwrst(per_pwrdm, per_next_state);
+                               per_state_modified = 1;
+                       } else
                                omap3_per_save_context();
                }
+       }
+
+       /* CORE */
+       if (core_next_state < PWRDM_POWER_ON) {
+               omap_uart_prepare_idle(0);
+               omap_uart_prepare_idle(1);
                if (core_next_state == PWRDM_POWER_OFF) {
                        omap3_core_save_context();
                        omap3_prcm_save_context();
-                       omap3_save_secure_ram_context(mpu_next_state);
                }
                /* Enable IO-PAD wakeup */
                prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
        }
 
+       /*
+        * Force SDRAM controller to self-refresh mode after timeout on
+        * autocount. This is needed on ES3.0 to avoid SDRAM controller
+        * hang-ups.
+        */
+       if (omap_rev() >= OMAP3430_REV_ES3_0 &&
+           omap_type() != OMAP2_DEVICE_TYPE_GP &&
+           core_next_state == PWRDM_POWER_OFF) {
+               sdrc_pwr = sdrc_read_reg(SDRC_POWER);
+               sdrc_write_reg((sdrc_pwr &
+                       ~(SDRC_POWER_AUTOCOUNT_MASK|SDRC_POWER_CLKCTRL_MASK)) |
+                       (1 << SDRC_POWER_AUTOCOUNT_SHIFT) |
+                       SDRC_SELF_REFRESH_ON_AUTOCOUNT, SDRC_POWER);
+       }
+
        /*
         * omap3_arm_context is the location where ARM registers
         * get saved. The restore path then reads from this
@@ -351,33 +390,48 @@ static void omap_sram_idle(void)
        _omap_sram_idle(omap3_arm_context, save_state);
        cpu_init();
 
+       /* Restore normal SDRAM settings */
+       if (omap_rev() >= OMAP3430_REV_ES3_0 &&
+           omap_type() != OMAP2_DEVICE_TYPE_GP &&
+           core_next_state == PWRDM_POWER_OFF)
+               sdrc_write_reg(sdrc_pwr, SDRC_POWER);
+
        /* Restore table entry modified during MMU restoration */
        if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF)
                restore_table_entry();
 
+       /* CORE */
        if (core_next_state < PWRDM_POWER_ON) {
-               if (per_next_state < PWRDM_POWER_ON)
-                       omap_uart_resume_idle(2);
-               omap_uart_resume_idle(1);
-               omap_uart_resume_idle(0);
-
-               /* Disable IO-PAD wakeup */
-               prm_clear_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
                core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);
                if (core_prev_state == PWRDM_POWER_OFF) {
                        omap3_core_restore_context();
                        omap3_prcm_restore_context();
                        omap3_sram_restore_context();
+                       omap2_sms_restore_context();
                }
-               if (per_next_state < PWRDM_POWER_ON) {
-                       per_prev_state =
-                               pwrdm_read_prev_pwrst(per_pwrdm);
-                       if (per_prev_state == PWRDM_POWER_OFF)
-                               omap3_per_restore_context();
-               }
+               omap_uart_resume_idle(0);
+               omap_uart_resume_idle(1);
+               if (core_next_state == PWRDM_POWER_OFF)
+                       prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF,
+                                              OMAP3430_GR_MOD,
+                                              OMAP3_PRM_VOLTCTRL_OFFSET);
+       }
+
+       /* PER */
+       if (per_next_state < PWRDM_POWER_ON) {
+               per_prev_state = pwrdm_read_prev_pwrst(per_pwrdm);
+               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 wakeup */
+       if (core_next_state < PWRDM_POWER_ON)
+               prm_clear_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
+
        pwrdm_post_transition();
 
 }
@@ -424,6 +478,8 @@ static int omap3_fclks_active(void)
 
 static int omap3_can_sleep(void)
 {
+       if (!sleep_while_idle)
+               return 0;
        if (!omap_uart_can_sleep())
                return 0;
        if (omap3_fclks_active())
@@ -497,6 +553,22 @@ out:
 #ifdef CONFIG_SUSPEND
 static suspend_state_t suspend_state;
 
+static void omap2_pm_wakeup_on_timer(u32 seconds)
+{
+       u32 tick_rate, cycles;
+
+       if (!seconds)
+               return;
+
+       tick_rate = clk_get_rate(omap_dm_timer_get_fclk(gptimer_wakeup));
+       cycles = tick_rate * seconds;
+       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);
+}
+
 static int omap3_pm_prepare(void)
 {
        disable_hlt();
@@ -508,6 +580,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);
+
        /* Read current next_pwrsts */
        list_for_each_entry(pwrst, &pwrst_list, node)
                pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
@@ -868,6 +943,22 @@ static void __init prcm_setup_regs(void)
        omap3_d2d_idle();
 }
 
+void omap3_pm_off_mode_enable(int enable)
+{
+       struct power_state *pwrst;
+       u32 state;
+
+       if (enable)
+               state = PWRDM_POWER_OFF;
+       else
+               state = PWRDM_POWER_RET;
+
+       list_for_each_entry(pwrst, &pwrst_list, node) {
+               pwrst->next_state = state;
+               set_pwrdm_state(pwrst->pwrdm, state);
+       }
+}
+
 int omap3_pm_get_suspend_state(struct powerdomain *pwrdm)
 {
        struct power_state *pwrst;
@@ -999,9 +1090,19 @@ static int __init omap3_pm_init(void)
                if (!omap3_secure_ram_storage)
                        printk(KERN_ERR "Memory allocation failed when"
                                        "allocating for secure sram context\n");
+
+               local_irq_disable();
+               local_fiq_disable();
+
+               omap_dma_global_context_save();
+               omap3_save_secure_ram_context(PWRDM_POWER_ON);
+               omap_dma_global_context_restore();
+
+               local_irq_enable();
+               local_fiq_enable();
        }
-       omap3_save_scratchpad_contents();
 
+       omap3_save_scratchpad_contents();
 err1:
        return ret;
 err2: