/* * OMAP4 Power Management Routines * * Copyright (C) 2010-2011 Texas Instruments, Inc. * Rajendra Nayak * Santosh Shilimkar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "powerdomain.h" #include "clockdomain.h" #include "pm.h" #include "prm-regbits-44xx.h" #include "prcm44xx.h" #include "prm44xx.h" #include "prminst44xx.h" #include "clock.h" #include "cm2_44xx.h" #include "cm1_44xx.h" #include "cm44xx.h" #include "cm-regbits-44xx.h" #include "cminst44xx.h" #include "scrm44xx.h" #include "prcm-debug.h" #include "smartreflex.h" #include "dvfs.h" #include "voltage.h" #include "vc.h" #include "control.h" struct power_state { struct powerdomain *pwrdm; u32 next_state; #ifdef CONFIG_SUSPEND u32 saved_state; u32 saved_logic_state; #endif struct list_head node; }; static LIST_HEAD(pwrst_list); static struct powerdomain *mpu_pwrdm, *cpu0_pwrdm; static struct powerdomain *core_pwrdm, *per_pwrdm; static struct voltagedomain *mpu_voltdm, *iva_voltdm, *core_voltdm; static struct clockdomain *tesla_clkdm; static struct powerdomain *tesla_pwrdm; /* Yet un-named erratum which requires AUTORET to be disabled for IVA PD */ #define OMAP4_PM_ERRATUM_IVA_AUTO_RET_iXXX BIT(1) /* * HSI - OMAP4430-2.2BUG00055: * HSI: DSP Swakeup generated is the same than MPU Swakeup. * System can’t enter in off mode due to the DSP. */ #define OMAP4_PM_ERRATUM_HSI_SWAKEUP_iXXX BIT(2) u8 pm44xx_errata; #define is_pm44xx_erratum(erratum) (pm44xx_errata & OMAP4_PM_ERRATUM_##erratum) #define MAX_IOPAD_LATCH_TIME 1000 void omap4_trigger_ioctrl(void) { int i = 0; /* Enable GLOBAL_WUEN */ if (!omap4_cminst_read_inst_reg_bits(OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET, OMAP4430_GLOBAL_WUEN_MASK)) omap4_prminst_rmw_inst_reg_bits(OMAP4430_GLOBAL_WUEN_MASK, OMAP4430_GLOBAL_WUEN_MASK, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET); /* Trigger WUCLKIN enable */ omap4_prminst_rmw_inst_reg_bits(OMAP4430_WUCLK_CTRL_MASK, OMAP4430_WUCLK_CTRL_MASK, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET); omap_test_timeout( ((omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET) >> OMAP4430_WUCLK_STATUS_SHIFT) == 1), MAX_IOPAD_LATCH_TIME, i); /* Trigger WUCLKIN disable */ omap4_prminst_rmw_inst_reg_bits(OMAP4430_WUCLK_CTRL_MASK, 0x0, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET); return; } #ifdef CONFIG_SUSPEND /* This is a common low power function called from suspend and * cpuidle */ void omap4_enter_sleep(unsigned int cpu, unsigned int power_state, bool suspend) { int cpu0_next_state = PWRDM_POWER_ON; int per_next_state = PWRDM_POWER_ON; int core_next_state = PWRDM_POWER_ON; int mpu_next_state = PWRDM_POWER_ON; pwrdm_clear_all_prev_pwrst(cpu0_pwrdm); pwrdm_clear_all_prev_pwrst(mpu_pwrdm); pwrdm_clear_all_prev_pwrst(core_pwrdm); pwrdm_clear_all_prev_pwrst(per_pwrdm); omap4_device_clear_prev_off_state(); cpu0_next_state = pwrdm_read_next_pwrst(cpu0_pwrdm); per_next_state = pwrdm_read_next_pwrst(per_pwrdm); core_next_state = pwrdm_read_next_pwrst(core_pwrdm); mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); if (mpu_next_state < PWRDM_POWER_INACTIVE) { if (omap_dvfs_is_scaling(mpu_voltdm)) { mpu_next_state = PWRDM_POWER_INACTIVE; pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state); } else { omap_sr_disable_reset_volt(mpu_voltdm); omap_vc_set_auto_trans(mpu_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_RETENTION); } } if (core_next_state < PWRDM_POWER_ON) { /* * Note: IVA can hit RET outside of cpuidle and hence this is * not the right optimal place to enable IVA AUTO RET. But since * enabling AUTO RET requires SR to disabled, its done here for * now. Needs a relook to see if this can be optimized. */ if (omap_dvfs_is_scaling(core_voltdm) || omap_dvfs_is_scaling(iva_voltdm)) { core_next_state = PWRDM_POWER_ON; pwrdm_set_next_pwrst(core_pwrdm, core_next_state); } else { omap_sr_disable_reset_volt(iva_voltdm); omap_sr_disable_reset_volt(core_voltdm); omap_vc_set_auto_trans(core_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_RETENTION); if (!is_pm44xx_erratum(IVA_AUTO_RET_iXXX)) { omap_vc_set_auto_trans(iva_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_RETENTION); } omap_temp_sensor_prepare_idle(); } } omap2_gpio_set_edge_wakeup(); if (omap4_device_next_state_off()) { omap2_gpio_prepare_for_idle(true); omap_gpmc_save_context(); omap_dma_global_context_save(); } if (suspend && cpu_is_omap44xx()) omap4_pm_suspend_save_regs(); if (omap4_device_next_state_off()) { /* Save the device context to SAR RAM */ if (omap4_sar_save()) goto abort_device_off; omap4_sar_overwrite(); omap4_cm_prepare_off(); omap4_dpll_prepare_off(); /* Extend Non-EMIF I/O isolation */ omap4_prminst_rmw_inst_reg_bits(OMAP4430_ISOOVR_EXTEND_MASK, OMAP4430_ISOOVR_EXTEND_MASK, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET); } omap4_enter_lowpower(cpu, power_state); if (omap4_device_prev_state_off()) { /* Reconfigure the trim settings as well */ omap4_ldo_trim_configure(); omap4_dpll_resume_off(); omap4_cm_resume_off(); #ifdef CONFIG_PM_DEBUG omap4_device_off_counter++; #endif } abort_device_off: if (core_next_state < PWRDM_POWER_ON) { /* See note above */ omap_vc_set_auto_trans(core_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE); if (!is_pm44xx_erratum(IVA_AUTO_RET_iXXX)) { omap_vc_set_auto_trans(iva_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE); } omap_temp_sensor_resume_idle(); omap_sr_enable(iva_voltdm); omap_sr_enable(core_voltdm); } if (omap4_device_prev_state_off()) { omap_dma_global_context_restore(); omap_gpmc_restore_context(); } if (omap4_device_next_state_off()) { /* * GPIO: since we have put_synced clks, we need to resume * even if OFF was not really achieved */ omap2_gpio_resume_after_idle(); /* Disable the extension of Non-EMIF I/O isolation */ omap4_prminst_rmw_inst_reg_bits(OMAP4430_ISOOVR_EXTEND_MASK, 0, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET); } omap2_gpio_restore_edge_wakeup(); if (mpu_next_state < PWRDM_POWER_INACTIVE) { omap_vc_set_auto_trans(mpu_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE); omap_sr_enable(mpu_voltdm); } return; } #ifdef CONFIG_PM_DEBUG #define GPIO_BANKS 6 #define MODULEMODE_DISABLED 0x0 #define MODULEMODE_AUTO 0x1 static void _print_wakeirq(int irq) { struct irq_desc *desc = irq_to_desc(irq); if (irq == OMAP44XX_IRQ_LOCALTIMER) pr_info("Resume caused by IRQ %d, localtimer\n", irq); else if (!desc || !desc->action || !desc->action->name) pr_info("Resume caused by IRQ %d\n", irq); else pr_info("Resume caused by IRQ %d, %s\n", irq, desc->action->name); } static void _print_gpio_wakeirq(int irq) { int bank = irq - OMAP44XX_IRQ_GPIO1; int bit; int gpioirq; int restoremod = 0; int timeout = 10; u32 wken, irqst, gpio; u32 clkctrl; long unsigned int wkirqs; void *gpio_base[GPIO_BANKS] = { OMAP2_L4_IO_ADDRESS(0x4a310000), OMAP2_L4_IO_ADDRESS(0x48055000), OMAP2_L4_IO_ADDRESS(0x48057000), OMAP2_L4_IO_ADDRESS(0x48059000), OMAP2_L4_IO_ADDRESS(0x4805b000), OMAP2_L4_IO_ADDRESS(0x4805d000), }; void *gpio_clkctrl[GPIO_BANKS] = { OMAP4430_CM_WKUP_GPIO1_CLKCTRL, OMAP4430_CM_L4PER_GPIO2_CLKCTRL, OMAP4430_CM_L4PER_GPIO3_CLKCTRL, OMAP4430_CM_L4PER_GPIO4_CLKCTRL, OMAP4430_CM_L4PER_GPIO5_CLKCTRL, OMAP4430_CM_L4PER_GPIO6_CLKCTRL, }; /* * GPIO1 is in CD_WKUP. * GPIO2-6 are in CD_l4_PER. * * Both of these clock domains are static dependencies of * the MPUSS clock domain (CD_CORTEXA9) and are guaranteed * to be already enabled (_CLKSTCTRL.CLKTRCTRL = HW_AUTO). * * Ensure the GPIO module is enabled (_CLKCTRL.MODULEMODE = * h/w managed). If not, will set it back to disabled when * done. */ clkctrl = __raw_readl(gpio_clkctrl[bank]); if ((clkctrl & OMAP4430_MODULEMODE_MASK) != MODULEMODE_AUTO << OMAP4430_MODULEMODE_SHIFT) { restoremod = 1; __raw_writel((clkctrl & ~(OMAP4430_MODULEMODE_MASK)) | MODULEMODE_AUTO << OMAP4430_MODULEMODE_SHIFT, gpio_clkctrl[bank]); while ((__raw_readl(gpio_clkctrl[bank]) & OMAP4430_MODULEMODE_MASK) != MODULEMODE_AUTO << OMAP4430_MODULEMODE_SHIFT && --timeout) udelay(5); if (!timeout) goto punt; } wken = __raw_readl(gpio_base[bank] + OMAP4_GPIO_IRQWAKEN0); irqst = __raw_readl(gpio_base[bank] + OMAP4_GPIO_IRQSTATUS0); wkirqs = irqst & wken; if (!wkirqs) wkirqs = irqst; if (!wkirqs) goto punt; for_each_set_bit(bit, &wkirqs, 32) { gpio = bit + bank * 32; gpioirq = gpio_to_irq(gpio); if (gpioirq < 0) pr_info("Resume caused by GPIO %d\n", (int)gpio); else _print_wakeirq(gpioirq); } goto out; punt: pr_info("Resume caused by IRQ %d, unknown GPIO%d interrupt\n", irq, bank + 1); out: if (restoremod) __raw_writel(clkctrl, gpio_clkctrl[bank]); } #define CONTROL_PADCONF_WAKEUPEVENT_0 0x4a1001d8 #define CONTROL_WKUP_PADCONF_WAKEUPEVENT_0 0x4a31E07C static void _print_prcm_wakeirq(int irq) { int i, bit; int iopad_wake_found = 0; u32 prcm_irqs1, prcm_irqs2; long unsigned int wkup_pad_event; prcm_irqs1 = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQSTATUS_MPU_OFFSET); prcm_irqs1 &= omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_OFFSET); prcm_irqs2 = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET); prcm_irqs2 &= omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); if (prcm_irqs1 & OMAP4430_IO_ST_MASK) { for (i = 0; i <= 6; i++) { long unsigned int wkevt = omap_readl(CONTROL_PADCONF_WAKEUPEVENT_0 + i*4); for_each_set_bit(bit, &wkevt, 32) { pr_info("Resume caused by I/O pad: CONTROL_PADCONF_WAKEUPEVENT_%d[%d]\n", i, bit); iopad_wake_found = 1; } } wkup_pad_event = omap_readl(CONTROL_WKUP_PADCONF_WAKEUPEVENT_0); for_each_set_bit(bit, &wkup_pad_event, 25) { pr_info("Resume caused by wakeup I/O pad: CONTROL_WKUP_PADCONF_WAKEUPEVENT_0[%d]\n", bit); iopad_wake_found = 1; } } if (prcm_irqs1 & ~OMAP4430_IO_ST_MASK || !iopad_wake_found || prcm_irqs2) pr_info("Resume caused by IRQ %d, prcm: 0x%x 0x%x\n", irq, prcm_irqs1, prcm_irqs2); } static void omap4_print_wakeirq(void) { int irq; irq = gic_cpu_read(GIC_CPU_HIGHPRI) & 0x3ff; if ((irq == 1022) || (irq == 1023)) { pr_info("GIC returns spurious interrupt for resume IRQ\n"); return; } if (irq >= OMAP44XX_IRQ_GPIO1 && irq <= OMAP44XX_IRQ_GPIO1 + GPIO_BANKS - 1) _print_gpio_wakeirq(irq); else if (irq == OMAP44XX_IRQ_PRCM) _print_prcm_wakeirq(irq); else _print_wakeirq(irq); } #else static void omap4_print_wakeirq(void) { } #endif /** * get_achievable_state() - Provide achievable state * @available_states: what states are available * @req_min_state: what state is the minimum we'd like to hit * @is_parent_pd: is this a parent power domain? * * Power domains have varied capabilities. When attempting a low power * state such as OFF/RET, a specific min requested state may not be * supported on the power domain, in which case: * a) if this power domain is a parent power domain, we do not intend * for it to go to a lower power state(because we are not targetting it), * select the next higher power state which is supported is returned. * b) However, for all children power domains, we first try to match * with a lower power domain state before attempting a higher state. * This is because a combination of system power states where the * parent PD's state is not in line with expectation can result in * system instabilities. */ static inline u8 get_achievable_state(u8 available_states, u8 req_min_state, bool is_parent_pd) { u8 max_mask = 0xFF << req_min_state; u8 min_mask = ~max_mask; /* First see if we have an accurate match */ if (available_states & BIT(req_min_state)) return req_min_state; /* See if a lower power state is possible on this child domain */ if (!is_parent_pd && available_states & min_mask) return __ffs(available_states & min_mask); if (available_states & max_mask) return __ffs(available_states & max_mask); return PWRDM_POWER_ON; } /** * omap4_configure_pwdm_suspend() - Program powerdomain on suspend * @is_off_mode: is this an OFF mode transition? * * Program all powerdomain to required power domain state: This logic * Takes the requested mode -OFF/RET translates it to logic and power * states. This then walks down the power domain states to program * each domain to the state requested. if the requested state is not * available, it will check for the higher state. */ static void omap4_configure_pwdm_suspend(bool is_off_mode) { struct power_state *pwrst; u32 state; u32 logic_state, als; #ifdef CONFIG_OMAP_ALLOW_OSWR if (is_off_mode) { state = PWRDM_POWER_OFF; logic_state = PWRDM_POWER_OFF; } else { state = PWRDM_POWER_RET; logic_state = PWRDM_POWER_OFF; } #else state = PWRDM_POWER_RET; logic_state = PWRDM_POWER_RET; #endif list_for_each_entry(pwrst, &pwrst_list, node) { bool parent_power_domain = false; pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm); pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm); if ((!strcmp(pwrst->pwrdm->name, "cpu0_pwrdm")) || (!strcmp(pwrst->pwrdm->name, "cpu1_pwrdm"))) continue; if (!strcmp(pwrst->pwrdm->name, "core_pwrdm") || !strcmp(pwrst->pwrdm->name, "mpu_pwrdm") || !strcmp(pwrst->pwrdm->name, "iva_pwrdm")) parent_power_domain = true; /* * Write only to registers which are writable! Don't touch * read-only/reserved registers. If pwrdm->pwrsts_logic_ret or * pwrdm->pwrsts are 0, consider those power domains containing * readonly/reserved registers which cannot be controlled by * software. */ if (pwrst->pwrdm->pwrsts_logic_ret) { als = get_achievable_state(pwrst->pwrdm->pwrsts_logic_ret, logic_state, parent_power_domain); if (als < pwrst->saved_logic_state) pwrdm_set_logic_retst(pwrst->pwrdm, als); } if (pwrst->pwrdm->pwrsts) { pwrst->next_state = get_achievable_state(pwrst->pwrdm->pwrsts, state, parent_power_domain); if (pwrst->next_state < pwrst->saved_state) omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); else pwrst->next_state = pwrst->saved_state; } } } /** * omap4_restore_pwdms_after_suspend() - Restore powerdomains after suspend * * Re-program all powerdomains to saved power domain states. * * returns 0 if all power domains hit targeted power state, -1 if any domain * failed to hit targeted power state (status related to the actual restore * is not returned). */ static int omap4_restore_pwdms_after_suspend(void) { struct power_state *pwrst; int cstate, pstate, ret = 0; /* Restore next powerdomain state */ list_for_each_entry(pwrst, &pwrst_list, node) { cstate = pwrdm_read_pwrst(pwrst->pwrdm); pstate = pwrdm_read_prev_pwrst(pwrst->pwrdm); if (pstate > pwrst->next_state) { pr_info("Powerdomain (%s) didn't enter " "target state %d Vs achieved state %d. " "current state %d\n", pwrst->pwrdm->name, pwrst->next_state, pstate, cstate); ret = -1; } /* If state already ON due to h/w dep, don't do anything */ if (cstate == PWRDM_POWER_ON) continue; /* If we have already achieved saved state, nothing to do */ if (cstate == pwrst->saved_state) continue; /* mpuss code takes care of this */ if ((!strcmp(pwrst->pwrdm->name, "cpu0_pwrdm")) || (!strcmp(pwrst->pwrdm->name, "cpu1_pwrdm"))) continue; /* * Skip pd program if saved state higher than current state * Since we would have already returned if the state * was ON, if the current state is yet another low power * state, the PRCM specification clearly states that * transition from a lower LP state to a higher LP state * is forbidden. */ if (pwrst->saved_state > cstate) continue; if (pwrst->pwrdm->pwrsts) omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); if (pwrst->pwrdm->pwrsts_logic_ret) pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state); } return ret; } static int omap4_pm_suspend(void) { int ret = 0; /* * If any device was in the middle of a scale operation * then abort, as we cannot predict which part of the scale * operation we interrupted. */ if (omap_dvfs_is_any_dev_scaling()) { pr_err("%s: oops.. middle of scale op.. aborting suspend\n", __func__); return -EBUSY; } /* Wakeup timer from suspend */ if (wakeup_timer_seconds || wakeup_timer_milliseconds) omap2_pm_wakeup_on_timer(wakeup_timer_seconds, wakeup_timer_milliseconds); omap4_configure_pwdm_suspend(off_mode_enabled); /* Enable Device OFF */ if (off_mode_enabled) omap4_device_set_state_off(1); /* * For MPUSS to hit power domain retention(CSWR or OSWR), * CPU0 and CPU1 power domain needs to be in OFF or DORMANT * state. For MPUSS to reach off-mode. CPU0 and CPU1 power domain * should be in off state. * Only master CPU followes suspend path. All other CPUs follow * cpu-hotplug path in system wide suspend. On OMAP4, CPU power * domain CSWR is not supported by hardware. * More details can be found in OMAP4430 TRM section 4.3.4.2. */ omap4_enter_sleep(0, PWRDM_POWER_OFF, true); omap4_print_wakeirq(); prcmdebug_dump(PRCMDEBUG_LASTSLEEP); /* Disable Device OFF state*/ if (off_mode_enabled) omap4_device_set_state_off(0); ret = omap4_restore_pwdms_after_suspend(); if (ret) pr_err("Could not enter target state in pm_suspend\n"); else pr_err("Successfully put all powerdomains to target state\n"); return 0; } static int omap4_pm_enter(suspend_state_t suspend_state) { int ret = 0; switch (suspend_state) { case PM_SUSPEND_STANDBY: case PM_SUSPEND_MEM: ret = omap4_pm_suspend(); break; default: ret = -EINVAL; } return ret; } static int omap4_pm_begin(suspend_state_t state) { disable_hlt(); return 0; } static void omap4_pm_end(void) { enable_hlt(); return; } static const struct platform_suspend_ops omap_pm_ops = { .begin = omap4_pm_begin, .end = omap4_pm_end, .enter = omap4_pm_enter, .valid = suspend_valid_only_mem, }; #else void omap4_enter_sleep(unsigned int cpu, unsigned int power_state){ return; } #endif /* CONFIG_SUSPEND */ /* * Enable hardware supervised mode for all clockdomains if it's * supported. Initiate sleep transition for other clockdomains, if * they are not used */ static int __init clkdms_setup(struct clockdomain *clkdm, void *unused) { if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO) clkdm_allow_idle(clkdm); else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP && atomic_read(&clkdm->usecount) == 0) clkdm_sleep(clkdm); return 0; } static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) { struct power_state *pwrst; if (!pwrdm->pwrsts) return 0; pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); if (!pwrst) return -ENOMEM; pwrst->pwrdm = pwrdm; if ((!strcmp(pwrdm->name, "mpu_pwrdm")) || (!strcmp(pwrdm->name, "core_pwrdm")) || (!strcmp(pwrdm->name, "cpu0_pwrdm")) || (!strcmp(pwrdm->name, "cpu1_pwrdm"))) pwrst->next_state = PWRDM_POWER_ON; else pwrst->next_state = PWRDM_POWER_RET; list_add(&pwrst->node, &pwrst_list); return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); } static u32 __init _usec_to_val_scrm(unsigned long rate, u32 usec, u32 shift, u32 mask) { u32 val; /* limit to max value */ val = ((mask >> shift) * 1000000) / rate; if (usec > val) usec = val; /* convert the time in usec to cycles */ val = DIV_ROUND_UP(rate * usec, 1000000); return (val << shift) & mask; } static void __init syscontrol_setup_regs(void) { u32 v; /* Disable LPDDR VREF manual control */ v = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO1_3); v &= ~(OMAP4_LPDDR21_VREF_EN_CA_MASK | OMAP4_LPDDR21_VREF_EN_DQ_MASK); omap4_ctrl_pad_writel(v, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO1_3); v = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO2_3); v &= ~(OMAP4_LPDDR21_VREF_EN_CA_MASK | OMAP4_LPDDR21_VREF_EN_DQ_MASK); omap4_ctrl_pad_writel(v, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO2_3); /* * Workaround for CK differential IO PADn, PADp values due to bug in * EMIF CMD phy. */ v = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO1_2); v &= ~OMAP4_LPDDR2IO1_GR10_WD_MASK; omap4_ctrl_pad_writel(v, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO1_2); v = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO2_2); v &= ~OMAP4_LPDDR2IO2_GR10_WD_MASK; omap4_ctrl_pad_writel(v, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_LPDDR2IO2_2); } static void __init prcm_setup_regs(void) { struct clk *clk32k = clk_get(NULL, "sys_32k_ck"); unsigned long rate32k = 0; u32 val, tshut, tstart; if (clk32k) { rate32k = clk_get_rate(clk32k); clk_put(clk32k); } else { pr_err("%s: no 32k clk!!!\n", __func__); dump_stack(); } /* Enable IO_ST interrupt */ omap4_prminst_rmw_inst_reg_bits(OMAP4430_IO_ST_MASK, OMAP4430_IO_ST_MASK, OMAP4430_PRM_PARTITION, OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_OFFSET); /* * Errata ID: i608 Impacted OMAP4430 ES 1.0,2.0,2.1,2.2 * On OMAP4, Retention-Till-Access Memory feature is not working * reliably and hardware recommondation is keep it disabled by * default */ omap4_prminst_rmw_inst_reg_bits(OMAP4430_DISABLE_RTA_EXPORT_MASK, 0x1 << OMAP4430_DISABLE_RTA_EXPORT_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_SRAM_WKUP_SETUP_OFFSET); omap4_prminst_rmw_inst_reg_bits(OMAP4430_DISABLE_RTA_EXPORT_MASK, 0x1 << OMAP4430_DISABLE_RTA_EXPORT_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_LDO_SRAM_CORE_SETUP_OFFSET); omap4_prminst_rmw_inst_reg_bits(OMAP4430_DISABLE_RTA_EXPORT_MASK, 0x1 << OMAP4430_DISABLE_RTA_EXPORT_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_LDO_SRAM_MPU_SETUP_OFFSET); omap4_prminst_rmw_inst_reg_bits(OMAP4430_DISABLE_RTA_EXPORT_MASK, 0x1 << OMAP4430_DISABLE_RTA_EXPORT_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_LDO_SRAM_IVA_SETUP_OFFSET); /* Allow SRAM LDO to enter RET during low power state*/ if (cpu_is_omap446x()) { omap4_prminst_rmw_inst_reg_bits(OMAP4430_RETMODE_ENABLE_MASK, 0x1 << OMAP4430_RETMODE_ENABLE_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_LDO_SRAM_CORE_CTRL_OFFSET); omap4_prminst_rmw_inst_reg_bits(OMAP4430_RETMODE_ENABLE_MASK, 0x1 << OMAP4430_RETMODE_ENABLE_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_LDO_SRAM_MPU_CTRL_OFFSET); omap4_prminst_rmw_inst_reg_bits(OMAP4430_RETMODE_ENABLE_MASK, 0x1 << OMAP4430_RETMODE_ENABLE_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_LDO_SRAM_IVA_CTRL_OFFSET); } /* Toggle CLKREQ in RET and OFF states */ omap4_prminst_write_inst_reg(0x2, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_CLKREQCTRL_OFFSET); /* Setup max clksetup time for oscillator */ if (rate32k) { omap_pm_get_osc_lp_time(&tstart, &tshut); val = _usec_to_val_scrm(rate32k, tstart, OMAP4_SETUPTIME_SHIFT, OMAP4_SETUPTIME_MASK); val |= _usec_to_val_scrm(rate32k, tshut, OMAP4_DOWNTIME_SHIFT, OMAP4_DOWNTIME_MASK); omap4_prminst_write_inst_reg(val, OMAP4430_SCRM_PARTITION, 0x0, OMAP4_SCRM_CLKSETUPTIME_OFFSET); } /* * De-assert PWRREQ signal in Device OFF state * 0x3: PWRREQ is de-asserted if all voltage domain are in * OFF state. Conversely, PWRREQ is asserted upon any * voltage domain entering or staying in ON or SLEEP or * RET state. */ omap4_prminst_write_inst_reg(0x3, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_PWRREQCTRL_OFFSET); /* Setup max PMIC startup time */ if (rate32k) { omap_pm_get_pmic_lp_time(&tstart, &tshut); val = _usec_to_val_scrm(rate32k, tstart, OMAP4_WAKEUPTIME_SHIFT, OMAP4_WAKEUPTIME_MASK); val |= _usec_to_val_scrm(rate32k, tshut, OMAP4_SLEEPTIME_SHIFT, OMAP4_SLEEPTIME_MASK); omap4_prminst_write_inst_reg(val, OMAP4430_SCRM_PARTITION, 0x0, OMAP4_SCRM_PMICSETUPTIME_OFFSET); } } /* omap_pm_clear_dsp_wake_up - SW WA for hardcoded wakeup dependency * from HSI to DSP * * Due to HW bug, same SWakeup signal is used for both MPU and DSP. * Thus Swakeup will unexpectedly wakeup the DSP domain even if nothing runs on * DSP. Since MPU is faster to process SWakeup, it acknowledges the Swakeup to * HSI before the DSP has completed its domain transition. This leaves the DSP * Power Domain in INTRANSITION state forever, and prevents the DEVICE-OFF mode. * * Workaround consists in : * when a SWakeup is asserted from HSI to MPU (and DSP) : * - force a DSP SW wakeup * - wait DSP module to be fully ON * - Configure a DSP CLK CTRL to HW_AUTO * - Wait on DSP module to be OFF * * Note : we detect a Swakeup is asserted to MPU by checking when an interrupt * is received while HSI module is ON. * * Bug ref is HSI-C1BUG00106 : dsp swakeup generated by HSI same as mpu swakeup */ void omap_pm_clear_dsp_wake_up(void) { int ret; int timeout = 10; if (!tesla_pwrdm || !tesla_clkdm) { WARN_ONCE(1, "%s: unable to use tesla workaround\n", __func__); return; } ret = pwrdm_read_pwrst(tesla_pwrdm); /* If Tesla power state in RET or OFF, then not hit by errata */ if (ret <= PWRDM_POWER_RET) return; if (clkdm_wakeup(tesla_clkdm)) pr_err("%s: Failed to force wakeup of %s\n", __func__, tesla_clkdm->name); /* This takes less than a few microseconds, hence in context */ pwrdm_wait_transition(tesla_pwrdm); /* * Check current power state of Tesla after transition, to make sure * that Tesla is indeed turned ON. */ ret = pwrdm_read_pwrst(tesla_pwrdm); do { pwrdm_wait_transition(tesla_pwrdm); ret = pwrdm_read_pwrst(tesla_pwrdm); } while ((ret < PWRDM_POWER_INACTIVE) && --timeout); if (!timeout) pr_err("%s: Tesla failed to transition to ON state!\n", __func__); timeout = 10; clkdm_allow_idle(tesla_clkdm); /* Ensure Tesla power state in OFF state */ ret = pwrdm_read_pwrst(tesla_pwrdm); do { pwrdm_wait_transition(tesla_pwrdm); ret = pwrdm_read_pwrst(tesla_pwrdm); } while ((ret >= PWRDM_POWER_INACTIVE) && --timeout); if (!timeout) pr_err("%s: Tesla failed to transition to OFF state\n", __func__); } static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) { u32 irqenable_mpu, irqstatus_mpu; int hsi_port; irqenable_mpu = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQENABLE_MPU_OFFSET); irqstatus_mpu = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQSTATUS_MPU_OFFSET); /* Check if a IO_ST interrupt */ if (irqstatus_mpu & OMAP4430_IO_ST_MASK) { /* Check if HSI caused the IO wakeup */ if (omap_hsi_is_io_wakeup_from_hsi(&hsi_port)) { omap_hsi_wakeup(hsi_port); } omap_uart_resume_idle(); usbhs_wakeup(); omap4_trigger_ioctrl(); } /* Clear the interrupt */ irqstatus_mpu &= irqenable_mpu; omap4_prm_write_inst_reg(irqstatus_mpu, OMAP4430_PRM_OCP_SOCKET_INST, OMAP4_PRM_IRQSTATUS_MPU_OFFSET); return IRQ_HANDLED; } /** * omap_default_idle() - implement a default idle for !CONFIG_CPUIDLE * * Implements OMAP4 memory, IO ordering requirements which can't be addressed * with default arch_idle() hook. Used by all CPUs with !CONFIG_CPUIDLE and * by secondary CPU with CONFIG_CPUIDLE. */ static void omap_default_idle(void) { local_irq_disable(); local_fiq_disable(); omap_do_wfi(); local_fiq_enable(); local_irq_enable(); } /** * omap4_device_set_state_off() - setup device off state * @enable: set to off or not. * * When Device OFF is enabled, Device is allowed to perform * transition to off mode as soon as all power domains in MPU, IVA * and CORE voltage are in OFF or OSWR state (open switch retention) */ void omap4_device_set_state_off(u8 enable) { #ifdef CONFIG_OMAP_ALLOW_OSWR if (enable) omap4_prminst_write_inst_reg(0x1 << OMAP4430_DEVICE_OFF_ENABLE_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_DEVICE_OFF_CTRL_OFFSET); else #endif omap4_prminst_write_inst_reg(0x0 << OMAP4430_DEVICE_OFF_ENABLE_SHIFT, OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_DEVICE_OFF_CTRL_OFFSET); } /** * omap4_device_prev_state_off: * returns true if the device hit OFF mode * This is API to check whether OMAP is waking up from device OFF mode. * There is no other status bit available for SW to read whether last state * entered was device OFF. To work around this, CORE PD, RFF context state * is used which is lost only when we hit device OFF state */ bool omap4_device_prev_state_off(void) { u32 reg; reg = omap4_prminst_read_inst_reg(core_pwrdm->prcm_partition, core_pwrdm->prcm_offs, OMAP4_RM_L3_1_L3_1_CONTEXT_OFFSET) & OMAP4430_LOSTCONTEXT_RFF_MASK; return reg ? true : false; } void omap4_device_clear_prev_off_state(void) { omap4_prminst_write_inst_reg(OMAP4430_LOSTCONTEXT_RFF_MASK | OMAP4430_LOSTCONTEXT_DFF_MASK, core_pwrdm->prcm_partition, core_pwrdm->prcm_offs, OMAP4_RM_L3_1_L3_1_CONTEXT_OFFSET); } /** * omap4_device_next_state_off: * returns true if the device next state is OFF * This is API to check whether OMAP is programmed for device OFF */ bool omap4_device_next_state_off(void) { return omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION, OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_DEVICE_OFF_CTRL_OFFSET) & OMAP4430_DEVICE_OFF_ENABLE_MASK ? true : false; } static void __init omap4_pm_setup_errata(void) { /* * Current understanding is that the following errata impacts * all OMAP4 silica */ if (cpu_is_omap44xx()) pm44xx_errata |= OMAP4_PM_ERRATUM_IVA_AUTO_RET_iXXX | OMAP4_PM_ERRATUM_HSI_SWAKEUP_iXXX; } /** * omap4_pm_init - Init routine for OMAP4 PM * * Initializes all powerdomain and clockdomain target states * and all PRCM settings. */ static int __init omap4_pm_init(void) { int ret; struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm; struct clockdomain *ducati_clkdm, *l3_2_clkdm, *l4_per, *l4_cfg; if (!cpu_is_omap44xx()) return -ENODEV; if (omap_rev() == OMAP4430_REV_ES1_0) { WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); return -ENODEV; } pr_err("Power Management for TI OMAP4.\n"); /* setup the erratas */ omap4_pm_setup_errata(); prcm_setup_regs(); syscontrol_setup_regs(); ret = request_irq(OMAP44XX_IRQ_PRCM, (irq_handler_t)prcm_interrupt_handler, IRQF_DISABLED, "prcm", NULL); if (ret) { printk(KERN_ERR "request_irq failed to register for 0x%x\n", OMAP44XX_IRQ_PRCM); goto err2; } ret = pwrdm_for_each(pwrdms_setup, NULL); if (ret) { pr_err("Failed to setup powerdomains\n"); goto err2; } /* * The dynamic dependency between MPUSS -> MEMIF and * MPUSS -> L3_* and DUCATI -> doesn't work as expected. * The hardware recommendation is to keep above dependencies. * Without this system locks up or randomly crashesh. */ mpuss_clkdm = clkdm_lookup("mpuss_clkdm"); emif_clkdm = clkdm_lookup("l3_emif_clkdm"); l3_1_clkdm = clkdm_lookup("l3_1_clkdm"); l3_2_clkdm = clkdm_lookup("l3_2_clkdm"); ducati_clkdm = clkdm_lookup("ducati_clkdm"); l4_per = clkdm_lookup("l4_per_clkdm"); l4_cfg = clkdm_lookup("l4_cfg_clkdm"); if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) || (!l3_2_clkdm) || (!ducati_clkdm) || (!l4_per) || (!l4_cfg)) goto err2; ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm); ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm); ret |= clkdm_add_wkdep(mpuss_clkdm, l3_2_clkdm); ret |= clkdm_add_wkdep(ducati_clkdm, l3_1_clkdm); ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm); ret |= clkdm_add_wkdep(mpuss_clkdm, l4_per); ret |= clkdm_add_wkdep(mpuss_clkdm, l4_cfg); ret |= clkdm_add_wkdep(ducati_clkdm, l4_per); ret |= clkdm_add_wkdep(ducati_clkdm, l4_cfg); if (ret) { pr_err("Failed to add MPUSS -> L3/EMIF, DUCATI -> L3" " and MPUSS -> L4* wakeup dependency\n"); goto err2; } (void) clkdm_for_each(clkdms_setup, NULL); pr_info("OMAP4 PM: Static dependency added between MPUSS <-> EMIF" " MPUSS <-> L4_PER/CFG and MPUSS <-> L3_MAIN_1.\n"); /* Get handles for VDD's for enabling/disabling SR */ mpu_voltdm = voltdm_lookup("mpu"); if (!mpu_voltdm) { pr_err("%s: Failed to get voltdm for VDD MPU\n", __func__); goto err2; } omap_vc_set_auto_trans(mpu_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE); iva_voltdm = voltdm_lookup("iva"); if (!iva_voltdm) { pr_err("%s: Failed to get voltdm for VDD IVA\n", __func__); goto err2; } omap_vc_set_auto_trans(iva_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE); core_voltdm = voltdm_lookup("core"); if (!core_voltdm) { pr_err("%s: Failed to get voltdm for VDD CORE\n", __func__); goto err2; } omap_vc_set_auto_trans(core_voltdm, OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE); ret = omap4_mpuss_init(); if (ret) { pr_err("Failed to initialise OMAP4 MPUSS\n"); goto err2; } #ifdef CONFIG_SUSPEND suspend_set_ops(&omap_pm_ops); #endif /* CONFIG_SUSPEND */ mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); cpu0_pwrdm = pwrdm_lookup("cpu0_pwrdm"); core_pwrdm = pwrdm_lookup("core_pwrdm"); per_pwrdm = pwrdm_lookup("l4per_pwrdm"); tesla_pwrdm = pwrdm_lookup("tesla_pwrdm"); if (!tesla_pwrdm) pr_err("%s: Failed to lookup tesla_pwrdm\n", __func__); tesla_clkdm = clkdm_lookup("tesla_clkdm"); if (!tesla_clkdm) pr_err("%s: Failed to lookup tesla_clkdm\n", __func__); /* Enable wakeup for PRCM IRQ for system wide suspend */ enable_irq_wake(OMAP44XX_IRQ_PRCM); /* Overwrite the default arch_idle() */ pm_idle = omap_default_idle; omap4_idle_init(); omap_pm_is_ready_status = true; /* let the other CPU know as well */ smp_wmb(); err2: return ret; } late_initcall(omap4_pm_init);