This patch adds OSWR support for MPU/CORE domains in CPUidle.
Two new C states are being added to the existing C states
which makes the new states look like below.

        C1 - MPU WFI + Core active
        C2 - MPU WFI + Core inactive
        C3 - MPU CSWR + Core inactive
        C4 - MPU OFF + Core inactive
        C5 - MPU CSWR + Core CSWR
        C6 - MPU OFF + Core CSWR
        C7 - MPU OSWR + CORE OSWR (New State)
        C8 - MPU OFF + CORE OSWR  (New State)
        C9 - MPU OFF + CORE OFF

To enable this feature echo 1 > /sys/powwer/enable_oswr_ret
To disable this feature echo 0 > /sys/poweer/enable_oswr_ret

This patch depends on http://patchwork.kernel.org/patch/44290/ and
is validated on latest PM branch on OMAP3430 SDP.

Signed-off-by: Thara Gopinath <th...@ti.com>
---
 arch/arm/mach-omap2/control.c                 |   20 +++
 arch/arm/mach-omap2/cpuidle34xx.c             |  154 +++++++++++++++++++++++--
 arch/arm/mach-omap2/pm-debug.c                |    3 +
 arch/arm/mach-omap2/pm.c                      |   16 +++-
 arch/arm/mach-omap2/pm.h                      |    1 +
 arch/arm/mach-omap2/pm34xx.c                  |  119 +++++++++++++++-----
 arch/arm/mach-omap2/powerdomain.c             |   89 ++++++++++++++-
 arch/arm/mach-omap2/powerdomains34xx.h        |    2 +
 arch/arm/mach-omap2/serial.c                  |   17 +--
 arch/arm/mach-omap2/sleep34xx.S               |    7 +-
 arch/arm/plat-omap/include/mach/control.h     |    1 +
 arch/arm/plat-omap/include/mach/powerdomain.h |    4 +
 arch/arm/plat-omap/include/mach/serial.h      |    2 +-
 include/linux/cpuidle.h                       |    2 +-
 14 files changed, 379 insertions(+), 58 deletions(-)

diff --git a/arch/arm/mach-omap2/control.c b/arch/arm/mach-omap2/control.c
index c9407c0..dc90207 100644
--- a/arch/arm/mach-omap2/control.c
+++ b/arch/arm/mach-omap2/control.c
@@ -331,6 +331,26 @@ void omap3_save_scratchpad_contents(void)
                sizeof(sdrc_block_contents), &arm_context_addr, 4);
 }
 
+void omap3_scratchpad_dpll4autoidle(int enable)
+{
+       void * __iomem scratchpad_address;
+       struct omap3_scratchpad_prcm_block prcm_block_contents;
+
+       scratchpad_address = OMAP2_IO_ADDRESS(OMAP343X_SCRATCHPAD);
+
+       memcpy_fromio(&prcm_block_contents, scratchpad_address + 0x2C,
+               sizeof(prcm_block_contents));
+       if (enable)
+               prcm_block_contents.cm_autoidle_pll |=
+                       (1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT);
+       else
+               prcm_block_contents.cm_autoidle_pll &=
+                       ~OMAP3430_AUTO_PERIPH_DPLL_MASK;
+
+       memcpy_toio(scratchpad_address + 0x2C, &prcm_block_contents,
+               sizeof(prcm_block_contents));
+}
+
 void omap3_control_save_context(void)
 {
        control_context.sysconfig = omap_ctrl_readl(OMAP2_CONTROL_SYSCONFIG);
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c 
b/arch/arm/mach-omap2/cpuidle34xx.c
index 7bbec90..a5b811b 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -36,14 +36,17 @@
 
 #ifdef CONFIG_CPU_IDLE
 
-#define OMAP3_MAX_STATES 7
+#define OMAP3_MAX_STATES 9
 #define OMAP3_STATE_C1 0 /* C1 - MPU WFI + Core active */
 #define OMAP3_STATE_C2 1 /* C2 - MPU WFI + Core inactive */
 #define OMAP3_STATE_C3 2 /* C3 - MPU CSWR + Core inactive */
-#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core iactive */
-#define OMAP3_STATE_C5 4 /* C5 - MPU RET + Core RET */
-#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core RET */
-#define OMAP3_STATE_C7 6 /* C7 - MPU OFF + Core OFF */
+#define OMAP3_STATE_C4 3 /* C4 - MPU OFF + Core inactive */
+#define OMAP3_STATE_C5 4 /* C5 - MPU CSWR + Core CSWR */
+#define OMAP3_STATE_C6 5 /* C6 - MPU OFF + Core CSWR */
+#define OMAP3_STATE_C7 6 /* C7 - MPU OSWR + CORE OSWR */
+#define OMAP3_STATE_C8 7 /* C8 - MPU OFF + CORE OSWR */
+#define OMAP3_STATE_C9 8 /* C9 - MPU OFF + CORE OFF */
+
 
 struct omap3_processor_cx {
        u8 valid;
@@ -52,6 +55,11 @@ struct omap3_processor_cx {
        u32 wakeup_latency;
        u32 mpu_state;
        u32 core_state;
+       u32 mpu_logicl1_ret_state;
+       u32 mpu_l2cache_ret_state;
+       u32 core_logic_state;
+       u32 core_mem1_ret_state;
+       u32 core_mem2_ret_state;
        u32 threshold;
        u32 flags;
 };
@@ -95,6 +103,11 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
        struct omap3_processor_cx *cx = cpuidle_get_statedata(state);
        struct timespec ts_preidle, ts_postidle, ts_idle;
        u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
+       u32 mpu_logicl1_ret_state = cx->mpu_logicl1_ret_state;
+       u32 mpu_l2cache_ret_state = cx->mpu_l2cache_ret_state;
+       u32 core_logic_state = cx->core_logic_state;
+       u32 core_mem1_ret_state = cx->core_mem1_ret_state;
+       u32 core_mem2_ret_state = cx->core_mem2_ret_state;
 
        current_cx_state = *cx;
 
@@ -111,6 +124,34 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
                        core_state = PWRDM_POWER_RET;
        }
 
+       if (!enable_oswr_ret) {
+               if (mpu_logicl1_ret_state == PWRDM_POWER_OFF)
+                       mpu_logicl1_ret_state = PWRDM_POWER_RET;
+               if (mpu_l2cache_ret_state == PWRDM_POWER_OFF)
+                       mpu_l2cache_ret_state = PWRDM_POWER_RET;
+               if (core_logic_state == PWRDM_POWER_OFF)
+                       core_logic_state = PWRDM_POWER_RET;
+               if (core_mem1_ret_state == PWRDM_POWER_OFF)
+                       core_mem1_ret_state = PWRDM_POWER_RET;
+               if (core_mem2_ret_state == PWRDM_POWER_OFF)
+                       core_mem2_ret_state = PWRDM_POWER_RET;
+       }
+
+       if (mpu_logicl1_ret_state != 0xFF)
+               pwrdm_set_logic_retst(mpu_pd, mpu_logicl1_ret_state);
+
+       if (mpu_l2cache_ret_state != 0xFF)
+               pwrdm_set_mem_retst(mpu_pd, 0, mpu_l2cache_ret_state);
+
+       if (core_logic_state != 0xFF)
+               pwrdm_set_logic_retst(core_pd, core_logic_state);
+
+       if (core_mem1_ret_state != 0xFF)
+               pwrdm_set_mem_retst(core_pd, 0, core_mem1_ret_state);
+
+       if (core_mem2_ret_state != 0xFF)
+               pwrdm_set_mem_retst(core_pd, 1, core_mem2_ret_state);
+
        pwrdm_set_next_pwrst(mpu_pd, mpu_state);
        pwrdm_set_next_pwrst(core_pd, core_state);
 
@@ -174,10 +215,24 @@ DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
  *     C4 . MPU OFF + Core inactive
  *     C5 . MPU CSWR + Core CSWR
  *     C6 . MPU OFF + Core CSWR
- *     C7 . MPU OFF + Core OFF
+ *     C7 . MPU OSWR + Core OSWR
+ *     C8 . MPU OFF + Core OSWR
+ *     C9 . MPU OFF + Core OFF
  */
 void omap_init_power_states(void)
 {
+       int i;
+       struct omap3_processor_cx *cx;
+
+       for (i = OMAP3_STATE_C1; i < OMAP3_MAX_STATES; i++) {
+               cx = &omap3_power_states[i];
+               cx->mpu_logicl1_ret_state = 0xFF;
+               cx->mpu_l2cache_ret_state = 0xFF;
+               cx->core_logic_state = 0xFF;
+               cx->core_mem1_ret_state = 0xFF;
+               cx->core_mem2_ret_state = 0xFF;
+       }
+
        /* C1 . MPU WFI + Core active */
        omap3_power_states[OMAP3_STATE_C1].valid = 1;
        omap3_power_states[OMAP3_STATE_C1].type = OMAP3_STATE_C1;
@@ -206,6 +261,10 @@ void omap_init_power_states(void)
        omap3_power_states[OMAP3_STATE_C3].threshold = 300;
        omap3_power_states[OMAP3_STATE_C3].mpu_state = PWRDM_POWER_RET;
        omap3_power_states[OMAP3_STATE_C3].core_state = PWRDM_POWER_ON;
+       omap3_power_states[OMAP3_STATE_C3].mpu_logicl1_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C3].mpu_l2cache_ret_state =
+                               PWRDM_POWER_RET;
        omap3_power_states[OMAP3_STATE_C3].flags = CPUIDLE_FLAG_TIME_VALID |
                                CPUIDLE_FLAG_CHECK_BM;
 
@@ -217,6 +276,10 @@ void omap_init_power_states(void)
        omap3_power_states[OMAP3_STATE_C4].threshold = 4000;
        omap3_power_states[OMAP3_STATE_C4].mpu_state = PWRDM_POWER_OFF;
        omap3_power_states[OMAP3_STATE_C4].core_state = PWRDM_POWER_ON;
+       omap3_power_states[OMAP3_STATE_C4].mpu_logicl1_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C4].mpu_l2cache_ret_state =
+                               PWRDM_POWER_RET;
        omap3_power_states[OMAP3_STATE_C4].flags = CPUIDLE_FLAG_TIME_VALID |
                                CPUIDLE_FLAG_CHECK_BM;
 
@@ -228,6 +291,15 @@ void omap_init_power_states(void)
        omap3_power_states[OMAP3_STATE_C5].threshold = 12000;
        omap3_power_states[OMAP3_STATE_C5].mpu_state = PWRDM_POWER_RET;
        omap3_power_states[OMAP3_STATE_C5].core_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C5].mpu_logicl1_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C5].mpu_l2cache_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C5].core_logic_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C5].core_mem1_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C5].core_mem2_ret_state =
+                               PWRDM_POWER_RET;
        omap3_power_states[OMAP3_STATE_C5].flags = CPUIDLE_FLAG_TIME_VALID |
                                CPUIDLE_FLAG_CHECK_BM;
 
@@ -239,19 +311,77 @@ void omap_init_power_states(void)
        omap3_power_states[OMAP3_STATE_C6].threshold = 15000;
        omap3_power_states[OMAP3_STATE_C6].mpu_state = PWRDM_POWER_OFF;
        omap3_power_states[OMAP3_STATE_C6].core_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C6].mpu_logicl1_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C6].mpu_l2cache_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C6].core_logic_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C6].core_mem1_ret_state =
+                               PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C6].core_mem2_ret_state =
+                               PWRDM_POWER_RET;
        omap3_power_states[OMAP3_STATE_C6].flags = CPUIDLE_FLAG_TIME_VALID |
                                CPUIDLE_FLAG_CHECK_BM;
 
-       /* C7 . MPU OFF + Core OFF */
+       /* C7 . MPU OSWR + Core OSWR */
        omap3_power_states[OMAP3_STATE_C7].valid = 1;
        omap3_power_states[OMAP3_STATE_C7].type = OMAP3_STATE_C7;
-       omap3_power_states[OMAP3_STATE_C7].sleep_latency = 10000;
-       omap3_power_states[OMAP3_STATE_C7].wakeup_latency = 30000;
-       omap3_power_states[OMAP3_STATE_C7].threshold = 300000;
-       omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_OFF;
-       omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C7].sleep_latency = 4000;
+       omap3_power_states[OMAP3_STATE_C7].wakeup_latency = 9000;
+       omap3_power_states[OMAP3_STATE_C7].threshold = 18000;
+       omap3_power_states[OMAP3_STATE_C7].mpu_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C7].core_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C7].mpu_logicl1_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C7].mpu_l2cache_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C7].core_logic_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C7].core_mem1_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C7].core_mem2_ret_state =
+                               PWRDM_POWER_OFF;
        omap3_power_states[OMAP3_STATE_C7].flags = CPUIDLE_FLAG_TIME_VALID |
                                CPUIDLE_FLAG_CHECK_BM;
+
+       /* C8 . MPU OFF + Core OSWR */
+       omap3_power_states[OMAP3_STATE_C8].valid = 1;
+       omap3_power_states[OMAP3_STATE_C8].type = OMAP3_STATE_C7;
+       omap3_power_states[OMAP3_STATE_C8].sleep_latency = 8000;
+       omap3_power_states[OMAP3_STATE_C8].wakeup_latency = 25000;
+       omap3_power_states[OMAP3_STATE_C8].threshold = 250000;
+       omap3_power_states[OMAP3_STATE_C8].mpu_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C8].core_state = PWRDM_POWER_RET;
+       omap3_power_states[OMAP3_STATE_C8].mpu_logicl1_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C8].mpu_l2cache_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C8].core_logic_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C8].core_mem1_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C8].core_mem2_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C8].flags = CPUIDLE_FLAG_TIME_VALID |
+                               CPUIDLE_FLAG_CHECK_BM;
+
+       /* C9 . MPU OFF + Core OFF */
+       omap3_power_states[OMAP3_STATE_C9].valid = 1;
+       omap3_power_states[OMAP3_STATE_C9].type = OMAP3_STATE_C7;
+       omap3_power_states[OMAP3_STATE_C9].sleep_latency = 10000;
+       omap3_power_states[OMAP3_STATE_C9].wakeup_latency = 30000;
+       omap3_power_states[OMAP3_STATE_C9].threshold = 300000;
+       omap3_power_states[OMAP3_STATE_C9].mpu_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].core_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].mpu_logicl1_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].mpu_l2cache_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].core_logic_state = PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].core_mem1_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].core_mem2_ret_state =
+                               PWRDM_POWER_OFF;
+       omap3_power_states[OMAP3_STATE_C9].flags = CPUIDLE_FLAG_TIME_VALID |
+                               CPUIDLE_FLAG_CHECK_BM;
 }
 
 struct cpuidle_driver omap3_idle_driver = {
diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c
index 1b4c160..ed7eb44 100644
--- a/arch/arm/mach-omap2/pm-debug.c
+++ b/arch/arm/mach-omap2/pm-debug.c
@@ -383,6 +383,9 @@ static int pwrdm_dbg_show_counter(struct powerdomain 
*pwrdm, void *user)
        for (i = 0; i < 4; i++)
                seq_printf(s, ",%s:%d", pwrdm_state_names[i],
                        pwrdm->state_counter[i]);
+       seq_printf(s, ",RET-LOGIC-OFF:%d,RET-MEM-OFF:%d",
+                       pwrdm->ret_logic_off_counter,
+                       pwrdm->ret_mem_off_counter);
 
        seq_printf(s, "\n");
 
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index fec7d00..5e73613 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -40,6 +40,7 @@
 unsigned short enable_dyn_sleep;
 unsigned short clocks_off_while_idle;
 unsigned short enable_off_mode;
+unsigned short enable_oswr_ret;
 unsigned short voltage_off_while_idle;
 unsigned short wakeup_timer_seconds;
 atomic_t sleep_block = ATOMIC_INIT(0);
@@ -57,6 +58,9 @@ static struct kobj_attribute clocks_off_while_idle_attr =
 static struct kobj_attribute enable_off_mode_attr =
        __ATTR(enable_off_mode, 0644, idle_show, idle_store);
 
+static struct kobj_attribute enable_oswr_ret_attr =
+       __ATTR(enable_oswr_ret, 0644, idle_show, idle_store);
+
 static struct kobj_attribute voltage_off_while_idle_attr =
        __ATTR(voltage_off_while_idle, 0644, idle_show, idle_store);
 
@@ -88,6 +92,8 @@ static ssize_t idle_show(struct kobject *kobj, struct 
kobj_attribute *attr,
                return sprintf(buf, "%hu\n", clocks_off_while_idle);
        else if (attr == &enable_off_mode_attr)
                return sprintf(buf, "%hu\n", enable_off_mode);
+       else if (attr == &enable_oswr_ret_attr)
+               return sprintf(buf, "%hu\n", enable_oswr_ret);
        else if (attr == &voltage_off_while_idle_attr)
                return sprintf(buf, "%hu\n", voltage_off_while_idle);
        else if (attr == &wakeup_timer_seconds_attr)
@@ -113,7 +119,9 @@ static ssize_t idle_store(struct kobject *kobj, struct 
kobj_attribute *attr,
        } else if (attr == &enable_off_mode_attr) {
                enable_off_mode = value;
                omap3_pm_off_mode_enable(enable_off_mode);
-       } else if (attr == &wakeup_timer_seconds_attr) {
+       } else if (attr == &enable_oswr_ret_attr)
+               enable_oswr_ret = value;
+       else if (attr == &wakeup_timer_seconds_attr) {
                wakeup_timer_seconds = value;
        } else if (attr == &voltage_off_while_idle_attr) {
                voltage_off_while_idle = value;
@@ -240,6 +248,12 @@ static int __init omap_pm_init(void)
                return error;
        }
        error = sysfs_create_file(power_kobj,
+                               &enable_oswr_ret_attr.attr);
+       if (error) {
+               printk(KERN_ERR "sysfs_create_file failed: %d\n", error);
+               return error;
+       }
+       error = sysfs_create_file(power_kobj,
                                  &wakeup_timer_seconds_attr.attr);
        if (error)
                printk(KERN_ERR "sysfs_create_file failed: %d\n", error);
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 052c601..1e5eb74 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -16,6 +16,7 @@
 extern void *omap3_secure_ram_storage;
 extern unsigned short enable_dyn_sleep;
 extern unsigned short enable_off_mode;
+extern unsigned short enable_oswr_ret;
 extern unsigned short voltage_off_while_idle;
 
 struct prm_setup_vc {
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 0150f29..99605e7 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -132,31 +132,41 @@ static void omap3_disable_io_chain(void)
                prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN, WKUP_MOD, PM_WKEN);
 }
 
-static void omap3_core_save_context(void)
+static void omap3_core_save_context(int core_state)
 {
-       u32 control_padconf_off;
-       /* Save the padconf registers */
-       control_padconf_off =
-       omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF);
-       control_padconf_off |= START_PADCONF_SAVE;
-       omap_ctrl_writel(control_padconf_off, OMAP343X_CONTROL_PADCONF_OFF);
-       /* wait for the save to complete */
-       while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS)
-                       & PADCONF_SAVE_DONE)
-               ;
+       if (core_state == PWRDM_POWER_OFF) {
+               u32 control_padconf_off;
+               /* Save the padconf registers */
+               control_padconf_off =
+               omap_ctrl_readl(OMAP343X_CONTROL_PADCONF_OFF);
+               control_padconf_off |= START_PADCONF_SAVE;
+               omap_ctrl_writel(control_padconf_off,
+                               OMAP343X_CONTROL_PADCONF_OFF);
+               /* wait for the save to complete */
+               while (!omap_ctrl_readl(OMAP343X_CONTROL_GENERAL_PURPOSE_STATUS)
+                               & PADCONF_SAVE_DONE)
+                       ;
+               /* Save the system control module context,
+                * padconf already save above
+                */
+               omap3_control_save_context();
+
+       }
        /* Save the Interrupt controller context */
        omap3_intc_save_context();
        /* Save the GPMC context */
        omap3_gpmc_save_context();
-       /* Save the system control module context, padconf already save above*/
-       omap3_control_save_context();
        omap_dma_global_context_save();
 }
 
-static void omap3_core_restore_context(void)
+static void omap3_core_restore_context(int core_state)
 {
-       /* Restore the control module context, padconf restored by h/w */
-       omap3_control_restore_context();
+       if (core_state == PWRDM_POWER_OFF)
+               /* Restore the control module context,
+                * padconf restored by h/w
+                */
+               omap3_control_restore_context();
+
        /* Restore the GPMC context */
        omap3_gpmc_restore_context();
        /* Restore the interrupt controller context */
@@ -343,7 +353,8 @@ void omap_sram_idle(void)
        int mpu_next_state = PWRDM_POWER_ON;
        int per_next_state = PWRDM_POWER_ON;
        int core_next_state = PWRDM_POWER_ON;
-       int core_prev_state, per_prev_state;
+       int mpu_prev_state, core_prev_state, per_prev_state;
+       int mpu_logic_state, mpu_mem_state, core_logic_state, core_mem_state;
        u32 sdrc_pwr = 0;
        int per_state_modified = 0;
 
@@ -356,11 +367,24 @@ void omap_sram_idle(void)
        pwrdm_clear_all_prev_pwrst(per_pwrdm);
 
        mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
+       mpu_logic_state = pwrdm_read_next_logic_pwrst(mpu_pwrdm);
+       mpu_mem_state = pwrdm_read_next_mem_pwrst(mpu_pwrdm, 0);
+
        switch (mpu_next_state) {
        case PWRDM_POWER_ON:
+                       /* No need to save context */
+                       save_state = 0;
+                       break;
        case PWRDM_POWER_RET:
-               /* No need to save context */
-               save_state = 0;
+               if (!mpu_logic_state && !mpu_mem_state)
+                       save_state = 3;
+               else if (!mpu_mem_state)
+                       save_state = 2;
+               else if (!mpu_logic_state)
+                       save_state = 1;
+               else
+                       /* No need to save context */
+                       save_state = 0;
                break;
        case PWRDM_POWER_OFF:
                save_state = 3;
@@ -380,8 +404,11 @@ void omap_sram_idle(void)
        /* PER */
        per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
        core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
+       core_logic_state = pwrdm_read_next_logic_pwrst(core_pwrdm);
+       core_mem_state = pwrdm_read_next_mem_pwrst(core_pwrdm, 0) |
+                               pwrdm_read_next_mem_pwrst(core_pwrdm, 1);
        if (per_next_state < PWRDM_POWER_ON) {
-               omap_uart_prepare_idle(2);
+               omap_uart_prepare_idle(2, per_next_state);
                omap2_gpio_prepare_for_idle(per_next_state);
                if (per_next_state == PWRDM_POWER_OFF) {
                        if (core_next_state == PWRDM_POWER_ON) {
@@ -401,24 +428,45 @@ void omap_sram_idle(void)
                /* Disable smartreflex before entering WFI */
                disable_smartreflex(SR1);
                disable_smartreflex(SR2);
-               omap_uart_prepare_idle(0);
-               omap_uart_prepare_idle(1);
+               omap_uart_prepare_idle(0, core_next_state & core_logic_state);
+               omap_uart_prepare_idle(1, core_next_state & core_logic_state);
                if (core_next_state == PWRDM_POWER_OFF) {
                        prm_set_mod_reg_bits(OMAP3430_AUTO_OFF,
                                             OMAP3430_GR_MOD,
                                             OMAP3_PRM_VOLTCTRL_OFFSET);
-                       omap3_core_save_context();
+                       omap3_core_save_context(PWRDM_POWER_OFF);
+                       omap3_prcm_save_context();
+               } else if ((core_next_state == PWRDM_POWER_RET) &&
+                               (core_logic_state == PWRDM_POWER_OFF) &&
+                               (core_mem_state == PWRDM_POWER_OFF)) {
+                       omap3_core_save_context(PWRDM_POWER_RET);
                        omap3_prcm_save_context();
+                       /*
+                        * This is a hack. Currently OSWR does not
+                        * work if rom code restores DPLL4 to non
+                        * auto idle mode.
+                        * ROM restore takes 20mS longer if PER/DPLL4
+                        * idle is enabled before OFF.So it is typically
+                        * not enabled. Since OSWR hangs if it is not enabled
+                        * enable it for OSWR alone. Later in the restore path
+                        * it is disabled again
+                        */
+
+                       omap3_scratchpad_dpll4autoidle(1);
+                       prm_set_mod_reg_bits(OMAP3430_AUTO_RET,
+                                               OMAP3430_GR_MOD,
+                                               OMAP3_PRM_VOLTCTRL_OFFSET);
+
                } else if (core_next_state == PWRDM_POWER_RET) {
                        prm_set_mod_reg_bits(OMAP3430_AUTO_RET,
                                                OMAP3430_GR_MOD,
                                                OMAP3_PRM_VOLTCTRL_OFFSET);
                }
+
                /* Enable IO-PAD and IO-CHAIN wakeups */
                prm_set_mod_reg_bits(OMAP3430_EN_IO, WKUP_MOD, PM_WKEN);
                omap3_enable_io_chain();
        }
-
        /*
        * On EMU/HS devices ROM code restores a SRDC value
        * from scratchpad which has automatic self refresh on timeout
@@ -447,18 +495,33 @@ void omap_sram_idle(void)
            core_next_state == PWRDM_POWER_OFF)
                sdrc_write_reg(sdrc_pwr, SDRC_POWER);
 
+       mpu_prev_state = pwrdm_read_prev_pwrst(mpu_pwrdm);
+
        /* Restore table entry modified during MMU restoration */
-       if (pwrdm_read_prev_pwrst(mpu_pwrdm) == PWRDM_POWER_OFF)
+       if (((mpu_prev_state == PWRDM_POWER_RET) &&
+                       (pwrdm_read_prev_logic_pwrst(mpu_pwrdm) ==
+                        PWRDM_POWER_OFF)) ||
+                       (mpu_prev_state == PWRDM_POWER_OFF))
                restore_table_entry();
-
        /* CORE */
        if (core_next_state < PWRDM_POWER_ON) {
                core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm);
-               if (core_prev_state == PWRDM_POWER_OFF) {
-                       omap3_core_restore_context();
+               if ((core_prev_state == PWRDM_POWER_OFF) ||
+                               (core_prev_state == PWRDM_POWER_RET &&
+                                pwrdm_read_prev_logic_pwrst(core_pwrdm) ==
+                                PWRDM_POWER_OFF)) {
+                       omap3_core_restore_context(core_prev_state);
                        omap3_prcm_restore_context();
                        omap3_sram_restore_context();
                        omap2_sms_restore_context();
+                       /*
+                        * For OSWR to work we put PER DPLL in auto
+                        * idle mode in scratchpad. Clear it so that
+                        * next time if a OFF is attempted the ROM restore
+                        * does nt take long
+                        */
+                       if (core_prev_state == PWRDM_POWER_RET)
+                               omap3_scratchpad_dpll4autoidle(0);
                }
                omap_uart_resume_idle(0);
                omap_uart_resume_idle(1);
diff --git a/arch/arm/mach-omap2/powerdomain.c 
b/arch/arm/mach-omap2/powerdomain.c
index 6c5fee9..ebd8649 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -128,6 +128,16 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, 
int flag)
                prev = pwrdm_read_prev_pwrst(pwrdm);
                if (pwrdm->state != prev)
                        pwrdm->state_counter[prev]++;
+               if (prev == PWRDM_POWER_RET) {
+                       if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) &&
+                                       (pwrdm_read_prev_logic_pwrst(pwrdm) ==
+                                        PWRDM_POWER_OFF))
+                               pwrdm->ret_logic_off_counter++;
+                       if ((pwrdm->pwrsts_mem_ret[0] == PWRSTS_OFF_RET) &&
+                                       (pwrdm_read_prev_mem_pwrst(pwrdm, 0) ==
+                                       PWRDM_POWER_OFF))
+                               pwrdm->ret_mem_off_counter++;
+               }
                break;
        default:
                return -EINVAL;
@@ -162,7 +172,8 @@ static __init void _pwrdm_setup(struct powerdomain *pwrdm)
 
        for (i = 0; i < 4; i++)
                pwrdm->state_counter[i] = 0;
-
+       pwrdm->ret_logic_off_counter = 0;
+       pwrdm->ret_mem_off_counter = 0;
        pwrdm_wait_transition(pwrdm);
        pwrdm->state = pwrdm_read_pwrst(pwrdm);
        pwrdm->state_counter[pwrdm->state] = 1;
@@ -951,6 +962,30 @@ int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm)
 }
 
 /**
+ * pwrdm_read_next_logic_pwrst - get next powerdomain logic power state
+ * @pwrdm: struct powerdomain * to get next logic power state
+ *
+ * Return the powerdomain pwrdm's logic power state.  Returns -EINVAL
+ * if the powerdomain pointer is null or returns the next logic
+ * power state upon success.
+ */
+int pwrdm_read_next_logic_pwrst(struct powerdomain *pwrdm)
+{
+       if (!pwrdm)
+               return -EINVAL;
+
+       /*
+        * The register bit names below may not correspond to the
+        * actual names of the bits in each powerdomain's register,
+        * but the type of value returned is the same for each
+        * powerdomain.
+        */
+       return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL,
+                                       OMAP3430_LOGICSTATEST);
+}
+
+
+/**
  * pwrdm_read_mem_pwrst - get current memory bank power state
  * @pwrdm: struct powerdomain * to get current memory bank power state
  * @bank: memory bank number (0-3)
@@ -976,7 +1011,7 @@ int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 
bank)
         * in PWST  . So the hack. Think of a cleaner
         * way of doing this
         */
-       if (cpu_is_omap34xx)
+       if (cpu_is_omap34xx())
                if (!strcmp("mpu_pwrdm", pwrdm->name))
                        bank = 1;
 
@@ -1033,7 +1068,7 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, 
u8 bank)
         * in PREPWST  . So the hack. Think of a cleaner
         * way of doing this
         */
-       if (cpu_is_omap34xx)
+       if (cpu_is_omap34xx())
                if (!strcmp("mpu_pwrdm", pwrdm->name))
                        bank = 1;
        /*
@@ -1065,6 +1100,54 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, 
u8 bank)
 }
 
 /**
+ * pwrdm_read_next_mem_pwrst - get next memory bank power state
+ * @pwrdm: struct powerdomain * to get mext memory bank power state
+ * @bank: memory bank number (0-3)
+ *
+ * Return the powerdomain pwrdm's next memory power state for bank
+ * x.  Returns -EINVAL if the powerdomain pointer is null, -EEXIST if
+ * the target memory bank does not exist or is not controllable, or
+ * returns the next memory power state upon success.
+ */
+int pwrdm_read_next_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
+{
+       u32 m;
+
+       if (!pwrdm)
+               return -EINVAL;
+
+       if (pwrdm->banks < (bank + 1))
+               return -EEXIST;
+
+       /*
+        * The register bit names below may not correspond to the
+        * actual names of the bits in each powerdomain's register,
+        * but the type of value returned is the same for each
+        * powerdomain.
+        */
+       switch (bank) {
+       case 0:
+               m = OMAP3430_SHAREDL1CACHEFLATRETSTATE;
+               break;
+       case 1:
+               m = OMAP3430_L1FLATMEMRETSTATE;
+               break;
+       case 2:
+               m = OMAP3430_SHAREDL2CACHEFLATRETSTATE;
+               break;
+       case 3:
+               m = OMAP3430_SHAREDL2CACHEFLATRETSTATE;
+               break;
+       default:
+               WARN_ON(1); /* should never happen */
+               return -EEXIST;
+       }
+
+       return prm_read_mod_bits_shift(pwrdm->prcm_offs,
+                                       PM_PWSTCTRL, m);
+}
+
+/**
  * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm
  * @pwrdm: struct powerdomain * to clear
  *
diff --git a/arch/arm/mach-omap2/powerdomains34xx.h 
b/arch/arm/mach-omap2/powerdomains34xx.h
index aa557b2..e3d470d 100644
--- a/arch/arm/mach-omap2/powerdomains34xx.h
+++ b/arch/arm/mach-omap2/powerdomains34xx.h
@@ -207,6 +207,7 @@ static struct powerdomain core_34xx_pre_es3_1_pwrdm = {
                                           CHIP_IS_OMAP3430ES2 |
                                           CHIP_IS_OMAP3430ES3_0),
        .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
        .dep_bit          = OMAP3430_EN_CORE_SHIFT,
        .banks            = 2,
        .pwrsts_mem_ret   = {
@@ -225,6 +226,7 @@ static struct powerdomain core_34xx_es3_1_pwrdm = {
        .prcm_offs        = CORE_MOD,
        .omap_chip        = OMAP_CHIP_INIT(CHIP_GE_OMAP3430ES3_1),
        .pwrsts           = PWRSTS_OFF_RET_ON,
+       .pwrsts_logic_ret = PWRSTS_OFF_RET,
        .dep_bit          = OMAP3430_EN_CORE_SHIFT,
        .flags            = PWRDM_HAS_HDWR_SAR, /* for USBTLL only */
        .banks            = 2,
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index 858447f..4b139b7 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -164,9 +164,6 @@ static void omap_uart_save_context(struct omap_uart_state 
*uart)
        u16 lcr = 0;
        struct plat_serial8250_port *p = uart->p;
 
-       if (!enable_off_mode)
-               return;
-
        lcr = serial_read_reg(p, UART_LCR);
        serial_write_reg(p, UART_LCR, 0xBF);
        uart->dll = serial_read_reg(p, UART_DLL);
@@ -185,9 +182,6 @@ static void omap_uart_restore_context(struct 
omap_uart_state *uart)
        u16 efr = 0;
        struct plat_serial8250_port *p = uart->p;
 
-       if (!enable_off_mode)
-               return;
-
        if (!uart->context_valid)
                return;
 
@@ -231,12 +225,13 @@ static inline void omap_uart_enable_clocks(struct 
omap_uart_state *uart)
 
 #ifdef CONFIG_PM
 
-static inline void omap_uart_disable_clocks(struct omap_uart_state *uart)
+static inline void omap_uart_disable_clocks(struct omap_uart_state *uart,
+                                                       int power_state)
 {
        if (!uart->clocked)
                return;
-
-       omap_uart_save_context(uart);
+       if (power_state == PWRDM_POWER_OFF)
+               omap_uart_save_context(uart);
        uart->clocked = 0;
        clk_disable(uart->ick);
        clk_disable(uart->fck);
@@ -325,13 +320,13 @@ static void omap_uart_idle_timer(unsigned long data)
        omap_uart_allow_sleep(uart);
 }
 
-void omap_uart_prepare_idle(int num)
+void omap_uart_prepare_idle(int num, int power_state)
 {
        struct omap_uart_state *uart;
 
        list_for_each_entry(uart, &uart_list, node) {
                if (num == uart->num && uart->can_sleep) {
-                       omap_uart_disable_clocks(uart);
+                       omap_uart_disable_clocks(uart, power_state);
                        return;
                }
        }
diff --git a/arch/arm/mach-omap2/sleep34xx.S b/arch/arm/mach-omap2/sleep34xx.S
index de31919..aabcd1f 100644
--- a/arch/arm/mach-omap2/sleep34xx.S
+++ b/arch/arm/mach-omap2/sleep34xx.S
@@ -256,8 +256,13 @@ restore:
        and     r2, r2, #0x3
        cmp     r2, #0x0        @ Check if target power state was OFF or RET
         moveq   r9, #0x3        @ MPU OFF => L1 and L2 lost
+       beq     restore_from_off
+       cmp     r2, #0x1
+       moveq   r9, #0x3
        movne   r9, #0x1        @ Only L1 and L2 lost => avoid L2 invalidation
        bne     logic_l1_restore
+restore_from_off:
+/*     b       restore_from_off*/
        ldr     r0, control_stat
        ldr     r1, [r0]
        and     r1, #0x700
@@ -418,7 +423,7 @@ usettbr0:
 
        ldmfd   sp!, {r0-r12, pc}               @ restore regs and return
 save_context_wfi:
-       /*b     save_context_wfi*/      @ enable to debug save code
+       /* b    save_context_wfi*/      @ enable to debug save code
        mov     r8, r0 /* Store SDRAM address in r8 */
         /* Check what that target sleep state is:stored in r1*/
         /* 1 - Only L1 and logic lost */
diff --git a/arch/arm/plat-omap/include/mach/control.h 
b/arch/arm/plat-omap/include/mach/control.h
index debe3f7..0c012a1 100644
--- a/arch/arm/plat-omap/include/mach/control.h
+++ b/arch/arm/plat-omap/include/mach/control.h
@@ -287,6 +287,7 @@ extern u32 *get_es3_restore_pointer(void);
 extern u32 omap3_arm_context[128];
 extern void omap3_control_save_context(void);
 extern void omap3_control_restore_context(void);
+extern void omap3_scratchpad_dpll4autoidle(int enable);
 
 #else
 #define omap_ctrl_base_get()           0
diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h 
b/arch/arm/plat-omap/include/mach/powerdomain.h
index 6271d85..9960dcc 100644
--- a/arch/arm/plat-omap/include/mach/powerdomain.h
+++ b/arch/arm/plat-omap/include/mach/powerdomain.h
@@ -119,6 +119,8 @@ struct powerdomain {
 
        int state;
        unsigned state_counter[4];
+       unsigned ret_logic_off_counter;
+       unsigned ret_mem_off_counter;
 
 #ifdef CONFIG_PM_DEBUG
        s64 timer;
@@ -163,8 +165,10 @@ int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 
bank, u8 pwrst);
 
 int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm);
 int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm);
+int pwrdm_read_next_logic_pwrst(struct powerdomain *pwrdm);
 int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
 int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
+int pwrdm_read_next_mem_pwrst(struct powerdomain *pwrdm, u8 bank);
 
 int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm);
 int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm);
diff --git a/arch/arm/plat-omap/include/mach/serial.h 
b/arch/arm/plat-omap/include/mach/serial.h
index e249186..92c1199 100644
--- a/arch/arm/plat-omap/include/mach/serial.h
+++ b/arch/arm/plat-omap/include/mach/serial.h
@@ -60,7 +60,7 @@ extern void omap_serial_init(void);
 extern int omap_uart_can_sleep(void);
 extern void omap_uart_check_wakeup(void);
 extern void omap_uart_prepare_suspend(void);
-extern void omap_uart_prepare_idle(int num);
+extern void omap_uart_prepare_idle(int num, int power_state);
 extern void omap_uart_resume_idle(int num);
 extern void omap_uart_enable_irqs(int enable);
 #endif
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index dcf77fa..8ec8300 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -17,7 +17,7 @@
 #include <linux/kobject.h>
 #include <linux/completion.h>
 
-#define CPUIDLE_STATE_MAX      8
+#define CPUIDLE_STATE_MAX      16
 #define CPUIDLE_NAME_LEN       16
 #define CPUIDLE_DESC_LEN       32
 
-- 
1.5.4.7

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to