/* * OMAP4 MPUSS Low Power Entry Function * * The purpose of this function is to manage low power programming * of OMAP4 MPUSS subsystem * Paramenters: * cpu : CPU ID * power_state: Targetted Low power state. * * MPUSS Low power states * The basic rule is that the MPUSS power domain must be at the higher or * equal power state (state that consume more power) than the higher of the * two CPUs. For example, it is illegal for system power to be OFF, while * the power of one or both of the CPU is DORMANT. When an illegal state is * entered, then the hardware behavior is unpredictable. * * MPUSS state for the context save * save_state = * 0 - Nothing lost and no need to save: MPUSS INACTIVE * 1 - CPUx L1 and logic lost: MPUSS CSWR * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR * 3 - CPUx L1 and logic lost + GIC + L2 lost: MPUSS OFF */ void omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) { unsigned int save_state, wakeup_cpu; if (cpu > NR_CPUS) return; /* * Low power state not supported on ES1.0 silicon */ if (omap_rev() == OMAP4430_REV_ES1_0) { wmb(); do_wfi(); return; } switch (power_state) { case PWRDM_POWER_ON: case PWRDM_POWER_INACTIVE: save_state = 0; break; case PWRDM_POWER_OFF: save_state = 1; setup_wakeup_routine(cpu); save_local_timers(cpu); break; case PWRDM_POWER_RET: /* * CPUx CSWR is invalid hardware state. Additionally * CPUx OSWR doesn't give any gain vs CPUxOFF and * hence not supported */ default: /* Invalid state */ pr_debug("Invalid CPU low power state\n"); return; } /* * MPUSS book keeping should be executed by master * CPU only which is the last CPU to go down */ if (cpu) goto cpu_prepare; /* * Check MPUSS next state and save GIC if needed * GIC lost during MPU OFF and OSWR */ pwrdm_clear_all_prev_pwrst(mpuss_pd); if (omap4_device_off_read_next_state() && (omap_type() != OMAP2_DEVICE_TYPE_GP)) { /* FIXME: Check if this can be optimised */ save_secure_all(); save_ivahd_tesla_regs(); save_l3instr_regs(); save_state = 3; goto cpu_prepare; } switch (pwrdm_read_next_pwrst(mpuss_pd)) { case PWRDM_POWER_ON: case PWRDM_POWER_INACTIVE: /* No need to save MPUSS context */ break; case PWRDM_POWER_RET: /* MPUSS OSWR, logic lost */ if (pwrdm_read_logic_retst(mpuss_pd) == PWRDM_POWER_OFF) { if (omap_type() != OMAP2_DEVICE_TYPE_GP) { save_gic_wakeupgen_secure(); save_l3instr_regs(); } else { save_gic(); omap4_wakeupgen_save(); } save_state = 2; } break; case PWRDM_POWER_OFF: /* MPUSS OFF */ if (omap_type() != OMAP2_DEVICE_TYPE_GP) { save_secure_ram(); save_gic_wakeupgen_secure(); save_ivahd_tesla_regs(); save_l3instr_regs(); } else { save_gic(); omap4_wakeupgen_save(); } save_state = 3; break; default: /* Fall through */ ; } /* * Program the CPU targeted state */ cpu_prepare: clear_cpu_prev_pwrst(cpu); if (cpu) pwrdm_set_next_pwrst(cpu1_pwrdm, power_state); else pwrdm_set_next_pwrst(cpu0_pwrdm, power_state); scu_pwrst_prepare(cpu, power_state); /* * Call low level routine to enter to * targeted power state */ __omap4_cpu_suspend(cpu, save_state); wakeup_cpu = hard_smp_processor_id(); /* * Restore the CPUx and mpuss power state to ON otherwise * CPUx power domain can transitions to programmed low power * state while doing WFI outside the low powe code. On HS devices, * CPUx can do WFI outside idle thread which can result in * power domain domain transition if the previous state was * programmed to OFF/RET. */ if (wakeup_cpu) { pwrdm_set_next_pwrst(cpu1_pwrdm, PWRDM_POWER_ON); } else { pwrdm_set_next_pwrst(cpu0_pwrdm, PWRDM_POWER_ON); pwrdm_set_next_pwrst(mpuss_pd, PWRDM_POWER_ON); } /* * Check the CPUx previous power state */ if (read_cpu_prev_pwrst(wakeup_cpu) == PWRDM_POWER_OFF) { cpu_init(); restore_mmu_table_entry(); restore_local_timers(wakeup_cpu); } /* * Check MPUSS previous power state and enable * GIC if needed. */ switch (pwrdm_read_prev_pwrst(mpuss_pd)) { case PWRDM_POWER_ON: /* No need to restore */ break; case PWRDM_POWER_RET: /* FIXME: * if (pwrdm_read_prev_logic_pwrst(mpuss_pd) == PWRDM_POWER_OFF) */ if (omap_readl(0x4a306324) == PWRDM_POWER_OFF) break; case PWRDM_POWER_OFF: /* * Enable GIC distributor */ if (!wakeup_cpu) { if ((omap_type() == OMAP2_DEVICE_TYPE_GP) && omap4_device_off_read_prev_state()) { restore_gic(); omap4_wakeupgen_restore(); } enable_gic_distributor(); if (omap_type() != OMAP2_DEVICE_TYPE_GP) { restore_ivahd_tesla_regs(); restore_l3instr_regs(); } } /* * Enable GIC cpu inrterface */ enable_gic_cpu_interface(); break; default: ; } }
/** * omap4_enter_lowpower: OMAP4 MPUSS Low Power Entry Function * The purpose of this function is to manage low power programming * of OMAP4 MPUSS subsystem * @cpu : CPU ID * @power_state: Low power state. * * MPUSS states for the context save: * save_state = * 0 - Nothing lost and no need to save: MPUSS INACTIVE * 1 - CPUx L1 and logic lost: MPUSS CSWR * 2 - CPUx L1 and logic lost + GIC lost: MPUSS OSWR * 3 - CPUx L1 and logic lost + GIC + L2 lost: DEVICE OFF */ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) { 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; }