summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/common.h6
-rw-r--r--arch/arm/mach-omap2/omap-mpuss-lowpower.c65
-rw-r--r--arch/arm/mach-omap2/pm44xx.c4
3 files changed, 72 insertions, 3 deletions
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 3312174d64ba..0911e843f079 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -190,6 +190,7 @@ extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
extern int omap4_finish_suspend(unsigned long cpu_state);
extern void omap4_cpu_resume(void);
extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state);
+extern u32 omap4_mpuss_read_prev_context_state(void);
#else
static inline int omap4_enter_lowpower(unsigned int cpu,
unsigned int power_state)
@@ -216,6 +217,11 @@ static inline int omap4_finish_suspend(unsigned long cpu_state)
static inline void omap4_cpu_resume(void)
{}
+
+static inline u32 omap4_mpuss_read_prev_context_state(void)
+{
+ return 0;
+}
#endif
#endif /* __ASSEMBLER__ */
#endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
index 907a048fe5e9..549aff1bfecb 100644
--- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c
+++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c
@@ -24,8 +24,8 @@
* ON ON ON
* ON(Inactive) OFF ON(Inactive)
* OFF OFF CSWR
- * OFF OFF OSWR (*TBD)
- * OFF OFF OFF* (*TBD)
+ * OFF OFF OSWR
+ * OFF OFF OFF(Device OFF *TBD)
* ----------------------------------------------
*
* Note: CPU0 is the master core and it is the last CPU to go down
@@ -56,7 +56,11 @@
#include "common.h"
#include "omap4-sar-layout.h"
#include "pm.h"
-#include "powerdomain.h"
+#include "prcm_mpu44xx.h"
+#include "prminst44xx.h"
+#include "prcm44xx.h"
+#include "prm44xx.h"
+#include "prm-regbits-44xx.h"
#ifdef CONFIG_SMP
@@ -138,6 +142,48 @@ static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
__raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
}
+/* Helper functions for MPUSS OSWR */
+static inline void mpuss_clear_prev_logic_pwrst(void)
+{
+ u32 reg;
+
+ reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
+ OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
+ omap4_prminst_write_inst_reg(reg, OMAP4430_PRM_PARTITION,
+ OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
+}
+
+static inline void cpu_clear_prev_logic_pwrst(unsigned int cpu_id)
+{
+ u32 reg;
+
+ if (cpu_id) {
+ reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU1_INST,
+ OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET);
+ omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU1_INST,
+ OMAP4_RM_CPU1_CPU1_CONTEXT_OFFSET);
+ } else {
+ reg = omap4_prcm_mpu_read_inst_reg(OMAP4430_PRCM_MPU_CPU0_INST,
+ OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET);
+ omap4_prcm_mpu_write_inst_reg(reg, OMAP4430_PRCM_MPU_CPU0_INST,
+ OMAP4_RM_CPU0_CPU0_CONTEXT_OFFSET);
+ }
+}
+
+/**
+ * omap4_mpuss_read_prev_context_state:
+ * Function returns the MPUSS previous context state
+ */
+u32 omap4_mpuss_read_prev_context_state(void)
+{
+ u32 reg;
+
+ reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
+ OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
+ reg &= OMAP4430_LOSTCONTEXT_DFF_MASK;
+ return reg;
+}
+
/*
* Store the CPU cluster state for L2X0 low power operations.
*/
@@ -210,8 +256,18 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
return -ENXIO;
}
+ /*
+ * Check MPUSS next state and save interrupt controller if needed.
+ * In MPUSS OSWR or device OFF, interrupt controller contest is lost.
+ */
+ mpuss_clear_prev_logic_pwrst();
pwrdm_clear_all_prev_pwrst(mpuss_pd);
+ if ((pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_RET) &&
+ (pwrdm_read_logic_retst(mpuss_pd) == PWRDM_POWER_OFF))
+ save_state = 2;
+
clear_cpu_prev_pwrst(cpu);
+ cpu_clear_prev_logic_pwrst(cpu);
set_cpu_next_pwrst(cpu, power_state);
set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume));
scu_pwrst_prepare(cpu, power_state);
@@ -294,6 +350,7 @@ int __init omap4_mpuss_init(void)
/* Clear CPU previous power domain state */
pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+ cpu_clear_prev_logic_pwrst(0);
/* Initialise CPU0 power domain state to ON */
pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
@@ -310,6 +367,7 @@ int __init omap4_mpuss_init(void)
/* Clear CPU previous power domain state */
pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
+ cpu_clear_prev_logic_pwrst(1);
/* Initialise CPU1 power domain state to ON */
pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
@@ -320,6 +378,7 @@ int __init omap4_mpuss_init(void)
return -ENODEV;
}
pwrdm_clear_all_prev_pwrst(mpuss_pd);
+ mpuss_clear_prev_logic_pwrst();
/* Save device type on scratchpad for low level code to use */
if (omap_type() != OMAP2_DEVICE_TYPE_GP)
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 6dc9bbe0a4a8..92daae07d634 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -27,6 +27,7 @@ struct power_state {
u32 next_state;
#ifdef CONFIG_SUSPEND
u32 saved_state;
+ u32 saved_logic_state;
#endif
struct list_head node;
};
@@ -43,11 +44,13 @@ static int omap4_pm_suspend(void)
/* Save current powerdomain state */
list_for_each_entry(pwrst, &pwrst_list, node) {
pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
+ pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm);
}
/* Set targeted power domain states by suspend */
list_for_each_entry(pwrst, &pwrst_list, node) {
omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+ pwrdm_set_logic_retst(pwrst->pwrdm, PWRDM_POWER_OFF);
}
/*
@@ -71,6 +74,7 @@ static int omap4_pm_suspend(void)
ret = -1;
}
omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
+ pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state);
}
if (ret)
pr_crit("Could not enter target state in pm_suspend\n");