|
@@ -231,3 +231,130 @@ static void save_l2x0_context(void)
|
|
int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
|
|
int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
|
|
{
|
|
{
|
|
unsigned int save_state = 0;
|
|
unsigned int save_state = 0;
|
|
|
|
+ unsigned int wakeup_cpu;
|
|
|
|
+
|
|
|
|
+ if (omap_rev() == OMAP4430_REV_ES1_0)
|
|
|
|
+ return -ENXIO;
|
|
|
|
+
|
|
|
|
+ switch (power_state) {
|
|
|
|
+ case PWRDM_POWER_ON:
|
|
|
|
+ case PWRDM_POWER_INACTIVE:
|
|
|
|
+ save_state = 0;
|
|
|
|
+ break;
|
|
|
|
+ case PWRDM_POWER_OFF:
|
|
|
|
+ save_state = 1;
|
|
|
|
+ break;
|
|
|
|
+ case PWRDM_POWER_RET:
|
|
|
|
+ default:
|
|
|
|
+ /*
|
|
|
|
+ * CPUx CSWR is invalid hardware state. Also CPUx OSWR
|
|
|
|
+ * doesn't make much scense, since logic is lost and $L1
|
|
|
|
+ * needs to be cleaned because of coherency. This makes
|
|
|
|
+ * CPUx OSWR equivalent to CPUX OFF and hence not supported
|
|
|
|
+ */
|
|
|
|
+ WARN_ON(1);
|
|
|
|
+ return -ENXIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pwrdm_pre_transition(NULL);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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();
|
|
|
|
+ if ((pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_RET) &&
|
|
|
|
+ (pwrdm_read_logic_retst(mpuss_pd) == PWRDM_POWER_OFF))
|
|
|
|
+ save_state = 2;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ l2x0_pwrst_prepare(cpu, save_state);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Call low level function with targeted low power state.
|
|
|
|
+ */
|
|
|
|
+ cpu_suspend(save_state, omap4_finish_suspend);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Restore the CPUx power state to ON otherwise CPUx
|
|
|
|
+ * power domain can transitions to programmed low power
|
|
|
|
+ * state while doing WFI outside the low powe code. On
|
|
|
|
+ * secure devices, CPUx does WFI which can result in
|
|
|
|
+ * domain transition
|
|
|
|
+ */
|
|
|
|
+ wakeup_cpu = smp_processor_id();
|
|
|
|
+ set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
|
|
|
|
+
|
|
|
|
+ pwrdm_post_transition(NULL);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * omap4_hotplug_cpu: OMAP4 CPU hotplug entry
|
|
|
|
+ * @cpu : CPU ID
|
|
|
|
+ * @power_state: CPU low power state.
|
|
|
|
+ */
|
|
|
|
+int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
|
|
|
|
+{
|
|
|
|
+ unsigned int cpu_state = 0;
|
|
|
|
+ struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu);
|
|
|
|
+
|
|
|
|
+ if (omap_rev() == OMAP4430_REV_ES1_0)
|
|
|
|
+ return -ENXIO;
|
|
|
|
+
|
|
|
|
+ if (power_state == PWRDM_POWER_OFF)
|
|
|
|
+ cpu_state = 1;
|
|
|
|
+
|
|
|
|
+ clear_cpu_prev_pwrst(cpu);
|
|
|
|
+ set_cpu_next_pwrst(cpu, power_state);
|
|
|
|
+ set_cpu_wakeup_addr(cpu, virt_to_phys(pm_info->secondary_startup));
|
|
|
|
+ scu_pwrst_prepare(cpu, power_state);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * CPU never retuns back if targeted power state is OFF mode.
|
|
|
|
+ * CPU ONLINE follows normal CPU ONLINE ptah via
|
|
|
|
+ * omap_secondary_startup().
|
|
|
|
+ */
|
|
|
|
+ omap4_finish_suspend(cpu_state);
|
|
|
|
+
|
|
|
|
+ set_cpu_next_pwrst(cpu, PWRDM_POWER_ON);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Initialise OMAP4 MPUSS
|
|
|
|
+ */
|
|
|
|
+int __init omap4_mpuss_init(void)
|
|
|
|
+{
|
|
|
|
+ struct omap4_cpu_pm_info *pm_info;
|
|
|
|
+
|
|
|
|
+ if (omap_rev() == OMAP4430_REV_ES1_0) {
|
|
|
|
+ WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ sar_base = omap4_get_sar_ram_base();
|
|
|
|
+
|
|
|
|
+ /* Initilaise per CPU PM information */
|
|
|
|
+ pm_info = &per_cpu(omap4_pm_info, 0x0);
|
|
|
|
+ pm_info->scu_sar_addr = sar_base + SCU_OFFSET0;
|
|
|
|
+ pm_info->wkup_sar_addr = sar_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
|
|
|
|
+ pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET0;
|
|
|
|
+ pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
|
|
|
|
+ if (!pm_info->pwrdm) {
|
|
|
|
+ pr_err("Lookup failed for CPU0 pwrdm\n");
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 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);
|
|
|
|
+
|