diff options
Diffstat (limited to 'arch/arm/mach-omap2/pm34xx.c')
| -rw-r--r-- | arch/arm/mach-omap2/pm34xx.c | 141 |
1 files changed, 124 insertions, 17 deletions
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 5a2d8034c8d..6f67bfa0b03 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -30,15 +30,18 @@ #include <linux/slab.h> #include <linux/omap-dma.h> #include <linux/platform_data/gpio-omap.h> +#include <linux/pm_runtime.h> #include <trace/events/power.h> #include <asm/fncpy.h> +#include <asm/setup.h> #include <asm/suspend.h> #include <asm/system_misc.h> #include "clockdomain.h" #include "powerdomain.h" +#include "omap-pm.h" #include "soc.h" #include "common.h" #include "cm3xxx.h" @@ -50,9 +53,17 @@ #include "sdrc.h" #include "sram.h" #include "control.h" +#include "pm-debug-regs.h" +#include "iomap.h" +#include "omap_device.h" + +#include "pad_wkup.h" /* pm34xx errata defined in pm.h */ u16 pm34xx_errata; +bool suspend_debug; +bool suspend_offmode_ref_saved; +static struct timespec suspend_time_before; struct power_state { struct powerdomain *pwrdm; @@ -70,6 +81,8 @@ void (*omap3_do_wfi_sram)(void); static struct powerdomain *mpu_pwrdm, *neon_pwrdm; static struct powerdomain *core_pwrdm, *per_pwrdm; +static struct powerdomain *dss_pwrdm; +static struct omap_hwmod *wd_hwmod; static void omap3_core_save_context(void) { @@ -144,6 +157,7 @@ static void omap3_save_secure_ram_context(void) */ static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits) { + static struct clk *per_dpll_clk; u32 wkst, fclk, iclk, clken; u16 wkst_off = (regs == 3) ? OMAP3430ES2_PM_WKST3 : PM_WKST1; u16 fclk_off = (regs == 3) ? OMAP3430ES2_CM_FCLKEN3 : CM_FCLKEN1; @@ -156,6 +170,16 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits) wkst &= omap2_prm_read_mod_reg(module, grpsel_off); wkst &= ~ignore_bits; if (wkst) { + if (module != WKUP_MOD) { + if (per_dpll_clk == NULL) { + per_dpll_clk = clk_get(NULL, "dpll4_ck"); + if (per_dpll_clk == NULL) { + pr_emerg("Unable to get dpll4_ck\n"); + BUG(); + } + } + clk_enable(per_dpll_clk); + } iclk = omap2_cm_read_mod_reg(module, iclk_off); fclk = omap2_cm_read_mod_reg(module, fclk_off); while (wkst) { @@ -172,9 +196,20 @@ static int prcm_clear_mod_irqs(s16 module, u8 regs, u32 ignore_bits) wkst = omap2_prm_read_mod_reg(module, wkst_off); wkst &= ~ignore_bits; c++; + + /* Better to panic than to fade away */ + if (c > 1000) { + pr_emerg("FATAL: Unable to clear wkst " + "wkst = 0x%08x module = 0x%04x " + "regs = 0x%02x\n", + wkst, module, regs); + BUG(); + } } omap2_cm_write_mod_reg(iclk, module, iclk_off); omap2_cm_write_mod_reg(fclk, module, fclk_off); + if (module != WKUP_MOD) + clk_disable(per_dpll_clk); } return c; @@ -184,6 +219,7 @@ static irqreturn_t _prcm_int_handle_io(int irq, void *unused) { int c; + _reconfigure_io_chain(); c = prcm_clear_mod_irqs(WKUP_MOD, 1, ~(OMAP3430_ST_IO_MASK | OMAP3430_ST_IO_CHAIN_MASK)); @@ -228,11 +264,21 @@ static void omap34xx_save_context(u32 *save) static int omap34xx_do_sram_idle(unsigned long save_state) { + if (suspend_debug) { + pr_debug("OMAP3_PRM_IRQENABLE_MPU_OFFSET 0x%08x\n", + omap2_prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET)); + omap_prcm_irq_restore(); + pr_debug("OMAP3_PRM_IRQENABLE_MPU_OFFSET 0x%08x\n", + omap2_prm_read_mod_reg(OCP_MOD, + OMAP3_PRM_IRQENABLE_MPU_OFFSET)); + } + omap34xx_cpu_suspend(save_state); return 0; } -void omap_sram_idle(void) +void omap_sram_idle(bool in_suspend) { /* Variable to tell what needs to be saved and restored * in omap_sram_idle*/ @@ -276,20 +322,29 @@ void omap_sram_idle(void) /* PER */ if (per_next_state < PWRDM_POWER_ON) { - per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0; + per_going_off = (per_next_state == PWRDM_POWER_OFF) ? + OFF_MODE : 0; omap2_gpio_prepare_for_idle(per_going_off); } /* CORE */ if (core_next_state < PWRDM_POWER_ON) { + _reconfigure_io_chain(); + if (!in_suspend && wd_hwmod != NULL) + platform_pm_suspend(&wd_hwmod->od->pdev->dev); + if (core_next_state == PWRDM_POWER_OFF) { omap3_core_save_context(); omap3_cm_save_context(); - } + + omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, + WKUP_MOD, PM_WKEN); + omap_prm_configure(true); + } else + omap_prm_configure(false); } omap3_intc_prepare_idle(); - /* * On EMU/HS devices ROM code restores a SRDC value * from scratchpad which has automatic self refresh on timeout @@ -302,6 +357,9 @@ void omap_sram_idle(void) core_next_state == PWRDM_POWER_OFF) sdrc_pwr = sdrc_read_reg(SDRC_POWER); + if (suspend_debug) + pm_dbg_regs_save(1); + /* * omap3_arm_context is the location where some ARM context * get saved. The rest is placed on the stack, and restored @@ -314,6 +372,9 @@ void omap_sram_idle(void) else omap34xx_do_sram_idle(save_state); + if (suspend_debug) + pm_dbg_regs_save(2); + /* Restore normal SDRC POWER settings */ if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 && (omap_type() == OMAP2_DEVICE_TYPE_EMU || @@ -323,17 +384,26 @@ void omap_sram_idle(void) /* CORE */ if (core_next_state < PWRDM_POWER_ON) { + if (!in_suspend && wd_hwmod != NULL) + platform_pm_resume(&wd_hwmod->od->pdev->dev); + core_prev_state = pwrdm_read_prev_pwrst(core_pwrdm); if (core_prev_state == PWRDM_POWER_OFF) { omap3_core_restore_context(); omap3_cm_restore_context(); omap3_sram_restore_context(); omap2_sms_restore_context(); + + if (unlikely(!suspend_offmode_ref_saved)) { + suspend_offmode_ref_saved = true; + pm_dbg_regs_copy(3, 1); + pm_dbg_regs_copy(4, 2); + } + } + if (core_next_state == PWRDM_POWER_OFF) { + omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, + WKUP_MOD, PM_WKEN); } - if (core_next_state == PWRDM_POWER_OFF) - omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK, - OMAP3430_GR_MOD, - OMAP3_PRM_VOLTCTRL_OFFSET); } omap3_intc_resume_idle(); @@ -341,7 +411,8 @@ void omap_sram_idle(void) /* PER */ if (per_next_state < PWRDM_POWER_ON) - omap2_gpio_resume_after_idle(); + omap2_gpio_resume_after_idle(in_suspend); + } static void omap3_pm_idle(void) @@ -351,7 +422,7 @@ static void omap3_pm_idle(void) trace_cpu_idle(1, smp_processor_id()); - omap_sram_idle(); + omap_sram_idle(false); trace_cpu_idle(PWR_EVENT_EXIT, smp_processor_id()); } @@ -361,6 +432,9 @@ static int omap3_pm_suspend(void) { struct power_state *pwrst; int state, ret = 0; + struct timespec after; + + suspend_debug = true; /* Read current next_pwrsts */ list_for_each_entry(pwrst, &pwrst_list, node) @@ -373,11 +447,18 @@ static int omap3_pm_suspend(void) goto restore; } + read_persistent_clock(&suspend_time_before); + omap3_intc_suspend(); - omap_sram_idle(); + omap_sram_idle(true); + + prcm_handle_pad_wkup(); restore: + read_persistent_clock(&after); + after = timespec_sub(after, suspend_time_before); + /* Restore next_pwrsts */ list_for_each_entry(pwrst, &pwrst_list, node) { state = pwrdm_read_prev_pwrst(pwrst->pwrdm); @@ -388,11 +469,26 @@ restore: } omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); } - if (ret) - pr_err("Could not enter target state in pm_suspend\n"); - else - pr_info("Successfully put all powerdomains to target state\n"); + if (ret) { + pr_err("Could not enter target state in pm_suspend " + "for %lu.%03lu seconds\n", after.tv_sec, + after.tv_nsec / NSEC_PER_MSEC); + if (suspend_offmode_ref_saved) { + pm_dbg_regs_dump_delta(1, 3); + pm_dbg_regs_dump_delta(2, 4); + } else { + pm_dbg_regs_dump(1); + pm_dbg_regs_dump(2); + } + } else + pr_info("Successfully put all powerdomains to target state " + "for %lu.%03lu seconds\n", after.tv_sec, + after.tv_nsec / NSEC_PER_MSEC); + + pm_dbg_show_wakeup_source(); + + suspend_debug = false; return ret; } @@ -644,9 +740,15 @@ static void __init pm_errata_configure(void) pm34xx_errata |= PM_RTA_ERRATUM_i608; /* Enable the l2 cache toggling in sleep logic */ enable_omap3630_toggle_l2_on_restore(); + if (omap_rev() < OMAP3630_REV_ES1_2) - pm34xx_errata |= (PM_SDRC_WAKEUP_ERRATUM_i583 | - PM_PER_MEMORIES_ERRATUM_i582); + pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; +#ifndef CONFIG_DISABLE_OMAP_ERRATA_i583 + pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; +#endif + if (meminfo.bank[0].size > 256 * (1024 * 1024)) + pm34xx_errata |= PM_SDRC_WAKEUP_ERRATUM_i583; + } else if (cpu_is_omap34xx()) { pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; } @@ -700,10 +802,12 @@ int __init omap3_pm_init(void) ret = -EINVAL; goto err3; } + wd_hwmod = omap_hwmod_lookup("wd_timer2"); neon_pwrdm = pwrdm_lookup("neon_pwrdm"); per_pwrdm = pwrdm_lookup("per_pwrdm"); core_pwrdm = pwrdm_lookup("core_pwrdm"); + dss_pwrdm = pwrdm_lookup("dss_pwrdm"); neon_clkdm = clkdm_lookup("neon_clkdm"); mpu_clkdm = clkdm_lookup("mpu_clkdm"); @@ -714,6 +818,9 @@ int __init omap3_pm_init(void) omap_pm_suspend = omap3_pm_suspend; #endif + if (omap_pm_get_off_mode()) + omap3_pm_off_mode_enable(true); + arm_pm_idle = omap3_pm_idle; omap3_idle_init(); |