#include <linux/list.h>
#include <linux/err.h>
-#include <asm/arch/gpio.h>
-#include <asm/arch/sram.h>
-#include <asm/arch/pm.h>
-#include <asm/arch/clockdomain.h>
-#include <asm/arch/powerdomain.h>
+#include <mach/gpio.h>
+#include <mach/sram.h>
+#include <mach/pm.h>
+#include <mach/clockdomain.h>
+#include <mach/powerdomain.h>
+#include <mach/serial.h>
+#include <mach/control.h>
#include "cm.h"
#include "cm-regbits-34xx.h"
cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
}
- if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
+ if (omap_rev() > OMAP3430_REV_ES1_0) {
/* USBHOST */
wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
if (wkst) {
printk(KERN_ERR "Invalid mpu state in sram_idle\n");
return;
}
+ /* Disable smartreflex before entering WFI */
+ disable_smartreflex(SR1);
+ disable_smartreflex(SR2);
omap2_gpio_prepare_for_retention();
+ omap_uart_prepare_idle(0);
+ omap_uart_prepare_idle(1);
+ omap_uart_prepare_idle(2);
_omap_sram_idle(NULL, save_state);
+ omap_uart_resume_idle(2);
+ omap_uart_resume_idle(1);
+ omap_uart_resume_idle(0);
omap2_gpio_resume_after_retention();
+
+ /* Enable smartreflex after WFI */
+ enable_smartreflex(SR1);
+ enable_smartreflex(SR2);
+}
+
+/*
+ * Check if functional clocks are enabled before entering
+ * sleep. This function could be behind CONFIG_PM_DEBUG
+ * when all drivers are configuring their sysconfig registers
+ * properly and using their clocks properly.
+ */
+static int omap3_fclks_active(void)
+{
+ u32 fck_core1 = 0, fck_core3 = 0, fck_sgx = 0, fck_dss = 0,
+ fck_cam = 0, fck_per = 0, fck_usbhost = 0;
+
+ fck_core1 = cm_read_mod_reg(CORE_MOD,
+ CM_FCLKEN1);
+ if (omap_rev() > OMAP3430_REV_ES1_0) {
+ fck_core3 = cm_read_mod_reg(CORE_MOD,
+ OMAP3430ES2_CM_FCLKEN3);
+ fck_sgx = cm_read_mod_reg(OMAP3430ES2_SGX_MOD,
+ CM_FCLKEN);
+ fck_usbhost = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
+ CM_FCLKEN);
+ } else
+ fck_sgx = cm_read_mod_reg(GFX_MOD,
+ OMAP3430ES2_CM_FCLKEN3);
+ fck_dss = cm_read_mod_reg(OMAP3430_DSS_MOD,
+ CM_FCLKEN);
+ fck_cam = cm_read_mod_reg(OMAP3430_CAM_MOD,
+ CM_FCLKEN);
+ fck_per = cm_read_mod_reg(OMAP3430_PER_MOD,
+ CM_FCLKEN);
+
+ /* Ignore UART clocks. These are handled by UART core (serial.c) */
+ fck_core1 &= ~(OMAP3430_EN_UART1 | OMAP3430_EN_UART2);
+ fck_per &= ~OMAP3430_EN_UART3;
+
+ if (fck_core1 | fck_core3 | fck_sgx | fck_dss |
+ fck_cam | fck_per | fck_usbhost)
+ return 1;
+ return 0;
}
static int omap3_can_sleep(void)
{
if (!enable_dyn_sleep)
return 0;
+ if (!omap_uart_can_sleep())
+ return 0;
+ if (omap3_fclks_active())
+ return 0;
if (atomic_read(&sleep_block) > 0)
return 0;
return 1;
}
-/* _clkdm_deny_idle - private callback function used by set_pwrdm_state() */
-static int _clkdm_deny_idle(struct powerdomain *pwrdm,
- struct clockdomain *clkdm)
-{
- omap2_clkdm_deny_idle(clkdm);
- return 0;
-}
-
-/* _clkdm_allow_idle - private callback function used by set_pwrdm_state() */
-static int _clkdm_allow_idle(struct powerdomain *pwrdm,
- struct clockdomain *clkdm)
-{
- omap2_clkdm_allow_idle(clkdm);
- return 0;
-}
-
/* This sets pwrdm state (other than mpu & core. Currently only ON &
* RET are supported. Function is assuming that clkdm doesn't have
* hw_sup mode enabled. */
static int set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
{
u32 cur_state;
+ int sleep_switch = 0;
int ret = 0;
if (pwrdm == NULL || IS_ERR(pwrdm))
return -EINVAL;
- cur_state = pwrdm_read_next_pwrst(pwrdm);
+ while (!(pwrdm->pwrsts & (1 << state))) {
+ if (state == PWRDM_POWER_OFF)
+ return ret;
+ state--;
+ }
+ cur_state = pwrdm_read_next_pwrst(pwrdm);
if (cur_state == state)
return ret;
- pwrdm_for_each_clkdm(pwrdm, _clkdm_deny_idle);
+ if (pwrdm_read_pwrst(pwrdm) < PWRDM_POWER_ON) {
+ omap2_clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
+ sleep_switch = 1;
+ pwrdm_wait_transition(pwrdm);
+ }
ret = pwrdm_set_next_pwrst(pwrdm, state);
if (ret) {
goto err;
}
- pwrdm_for_each_clkdm(pwrdm, _clkdm_allow_idle);
+ if (sleep_switch) {
+ omap2_clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
+ pwrdm_wait_transition(pwrdm);
+ }
err:
return ret;
struct power_state *pwrst;
int state, ret = 0;
- /* XXX Disable smartreflex before entering suspend */
- disable_smartreflex(SR1);
- disable_smartreflex(SR2);
-
/* Read current next_pwrsts */
list_for_each_entry(pwrst, &pwrst_list, node)
pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
goto restore;
}
+ omap_uart_prepare_suspend();
omap_sram_idle();
restore:
list_for_each_entry(pwrst, &pwrst_list, node) {
set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
- if (state != pwrst->next_state) {
+ if (state > pwrst->next_state) {
printk(KERN_INFO "Powerdomain (%s) didn't enter "
"target state %d\n",
pwrst->pwrdm->name, pwrst->next_state);
printk(KERN_INFO "Successfully put all powerdomains "
"to target state\n");
- /* XXX Enable smartreflex after suspend */
- enable_smartreflex(SR1);
- enable_smartreflex(SR2);
-
return ret;
}
.valid = suspend_valid_only_mem,
};
+
+/**
+ * omap3_iva_idle(): ensure IVA is in idle so it can be put into
+ * retention
+ *
+ * In cases where IVA2 is activated by bootcode, it may prevent
+ * full-chip retention or off-mode because it is not idle. This
+ * function forces the IVA2 into idle state so it can go
+ * into retention/off and thus allow full-chip retention/off.
+ *
+ **/
+static void __init omap3_iva_idle(void)
+{
+ /* ensure IVA2 clock is disabled */
+ cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN);
+
+ /* Reset IVA2 */
+ prm_write_mod_reg(OMAP3430_RST1_IVA2 |
+ OMAP3430_RST2_IVA2 |
+ OMAP3430_RST3_IVA2,
+ OMAP3430_IVA2_MOD, RM_RSTCTRL);
+
+ /* Enable IVA2 clock */
+ cm_write_mod_reg(OMAP3430_CM_FCLKEN_IVA2_EN_IVA2,
+ OMAP3430_IVA2_MOD, CM_FCLKEN);
+
+ /* Set IVA2 boot mode to 'idle' */
+ omap_ctrl_writel(OMAP3_IVA2_BOOTMOD_IDLE,
+ OMAP343X_CONTROL_IVA2_BOOTMOD);
+
+ /* Un-reset IVA2 */
+ prm_write_mod_reg(0, OMAP3430_IVA2_MOD, RM_RSTCTRL);
+
+ /* Disable IVA2 clock */
+ cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN);
+
+ /* Reset IVA2 */
+ prm_write_mod_reg(OMAP3430_RST1_IVA2 |
+ OMAP3430_RST2_IVA2 |
+ OMAP3430_RST3_IVA2,
+ OMAP3430_IVA2_MOD, RM_RSTCTRL);
+}
+
static void __init prcm_setup_regs(void)
{
/* XXX Reset all wkdeps. This should be done when initializing
prm_write_mod_reg(0, OMAP3430_NEON_MOD, PM_WKDEP);
prm_write_mod_reg(0, OMAP3430_CAM_MOD, PM_WKDEP);
prm_write_mod_reg(0, OMAP3430_PER_MOD, PM_WKDEP);
- if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
+ if (omap_rev() > OMAP3430_REV_ES1_0) {
prm_write_mod_reg(0, OMAP3430ES2_SGX_MOD, PM_WKDEP);
prm_write_mod_reg(0, OMAP3430ES2_USBHOST_MOD, PM_WKDEP);
} else
prm_write_mod_reg(0, GFX_MOD, PM_WKDEP);
+ /*
+ * Enable interface clock autoidle for all modules.
+ * Note that in the long run this should be done by clockfw
+ */
+ cm_write_mod_reg(
+ OMAP3430ES2_AUTO_MMC3 |
+ OMAP3430ES2_AUTO_ICR |
+ OMAP3430_AUTO_AES2 |
+ OMAP3430_AUTO_SHA12 |
+ OMAP3430_AUTO_DES2 |
+ OMAP3430_AUTO_MMC2 |
+ OMAP3430_AUTO_MMC1 |
+ OMAP3430_AUTO_MSPRO |
+ OMAP3430_AUTO_HDQ |
+ OMAP3430_AUTO_MCSPI4 |
+ OMAP3430_AUTO_MCSPI3 |
+ OMAP3430_AUTO_MCSPI2 |
+ OMAP3430_AUTO_MCSPI1 |
+ OMAP3430_AUTO_I2C3 |
+ OMAP3430_AUTO_I2C2 |
+ OMAP3430_AUTO_I2C1 |
+ OMAP3430_AUTO_UART2 |
+ OMAP3430_AUTO_UART1 |
+ OMAP3430_AUTO_GPT11 |
+ OMAP3430_AUTO_GPT10 |
+ OMAP3430_AUTO_MCBSP5 |
+ OMAP3430_AUTO_MCBSP1 |
+ OMAP3430ES1_AUTO_FAC | /* This is es1 only */
+ OMAP3430_AUTO_MAILBOXES |
+ OMAP3430_AUTO_OMAPCTRL |
+ OMAP3430ES1_AUTO_FSHOSTUSB |
+ OMAP3430_AUTO_HSOTGUSB |
+ OMAP3430ES1_AUTO_D2D | /* This is es1 only */
+ OMAP3430_AUTO_SSI,
+ CORE_MOD, CM_AUTOIDLE1);
+
+ cm_write_mod_reg(
+ OMAP3430_AUTO_PKA |
+ OMAP3430_AUTO_AES1 |
+ OMAP3430_AUTO_RNG |
+ OMAP3430_AUTO_SHA11 |
+ OMAP3430_AUTO_DES1,
+ CORE_MOD, CM_AUTOIDLE2);
+
+ if (omap_rev() > OMAP3430_REV_ES1_0) {
+ cm_write_mod_reg(
+ OMAP3430ES2_AUTO_USBTLL,
+ CORE_MOD, CM_AUTOIDLE3);
+ }
+
+ cm_write_mod_reg(
+ OMAP3430_AUTO_WDT2 |
+ OMAP3430_AUTO_WDT1 |
+ OMAP3430_AUTO_GPIO1 |
+ OMAP3430_AUTO_32KSYNC |
+ OMAP3430_AUTO_GPT12 |
+ OMAP3430_AUTO_GPT1 ,
+ WKUP_MOD, CM_AUTOIDLE);
+
+ cm_write_mod_reg(
+ OMAP3430_AUTO_DSS,
+ OMAP3430_DSS_MOD,
+ CM_AUTOIDLE);
+
+ cm_write_mod_reg(
+ OMAP3430_AUTO_CAM,
+ OMAP3430_CAM_MOD,
+ CM_AUTOIDLE);
+
+ cm_write_mod_reg(
+ OMAP3430_AUTO_GPIO6 |
+ OMAP3430_AUTO_GPIO5 |
+ OMAP3430_AUTO_GPIO4 |
+ OMAP3430_AUTO_GPIO3 |
+ OMAP3430_AUTO_GPIO2 |
+ OMAP3430_AUTO_WDT3 |
+ OMAP3430_AUTO_UART3 |
+ OMAP3430_AUTO_GPT9 |
+ OMAP3430_AUTO_GPT8 |
+ OMAP3430_AUTO_GPT7 |
+ OMAP3430_AUTO_GPT6 |
+ OMAP3430_AUTO_GPT5 |
+ OMAP3430_AUTO_GPT4 |
+ OMAP3430_AUTO_GPT3 |
+ OMAP3430_AUTO_GPT2 |
+ OMAP3430_AUTO_MCBSP4 |
+ OMAP3430_AUTO_MCBSP3 |
+ OMAP3430_AUTO_MCBSP2,
+ OMAP3430_PER_MOD,
+ CM_AUTOIDLE);
+
+ if (omap_rev() > OMAP3430_REV_ES1_0) {
+ cm_write_mod_reg(
+ OMAP3430ES2_AUTO_USBHOST,
+ OMAP3430ES2_USBHOST_MOD,
+ CM_AUTOIDLE);
+ }
+
+ /*
+ * Set all plls to autoidle. This is needed until autoidle is
+ * enabled by clockfw
+ */
+ cm_write_mod_reg(1 << OMAP3430_AUTO_IVA2_DPLL_SHIFT,
+ OMAP3430_IVA2_MOD, CM_AUTOIDLE2);
+ cm_write_mod_reg(1 << OMAP3430_AUTO_MPU_DPLL_SHIFT,
+ MPU_MOD,
+ CM_AUTOIDLE2);
+ cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT) |
+ (1 << OMAP3430_AUTO_CORE_DPLL_SHIFT),
+ PLL_MOD,
+ CM_AUTOIDLE);
+ cm_write_mod_reg(1 << OMAP3430ES2_AUTO_PERIPH2_DPLL_SHIFT,
+ PLL_MOD,
+ CM_AUTOIDLE2);
+
+ /*
+ * Enable control of expternal oscillator through
+ * sys_clkreq. In the long run clock framework should
+ * take care of this.
+ */
+ prm_rmw_mod_reg_bits(OMAP_AUTOEXTCLKMODE_MASK,
+ 1 << OMAP_AUTOEXTCLKMODE_SHIFT,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_CLKSRC_CTRL_OFFSET);
+
/* setup wakup source */
- prm_write_mod_reg(OMAP3430_EN_IO | OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1,
+ prm_write_mod_reg(OMAP3430_EN_IO | OMAP3430_EN_GPIO1 |
+ OMAP3430_EN_GPT1 | OMAP3430_EN_GPT12,
WKUP_MOD, PM_WKEN);
/* No need to write EN_IO, that is always enabled */
- prm_write_mod_reg(OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1,
+ prm_write_mod_reg(OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1 |
+ OMAP3430_EN_GPT12,
WKUP_MOD, OMAP3430_PM_MPUGRPSEL);
/* For some reason IO doesn't generate wakeup event even if
* it is selected to mpu wakeup goup */
prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
OCP_MOD, OMAP2_PRM_IRQENABLE_MPU_OFFSET);
+
+ omap3_iva_idle();
}
static int __init pwrdms_setup(struct powerdomain *pwrdm)
return set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
}
+static int __init clkdms_setup(struct clockdomain *clkdm)
+{
+ omap2_clkdm_allow_idle(clkdm);
+ return 0;
+}
+
int __init omap3_pm_init(void)
{
- struct power_state *pwrst;
+ struct power_state *pwrst, *tmp;
int ret;
printk(KERN_ERR "Power Management for TI OMAP3.\n");
goto err2;
}
+ (void) clkdm_for_each(clkdms_setup);
+
mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
if (mpu_pwrdm == NULL) {
printk(KERN_ERR "Failed to get mpu_pwrdm\n");
return ret;
err2:
free_irq(INT_34XX_PRCM_MPU_IRQ, NULL);
- list_for_each_entry(pwrst, &pwrst_list, node) {
+ list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) {
list_del(&pwrst->node);
kfree(pwrst);
}
OMAP3_PRM_VC_I2C_CFG_OFFSET);
/* Setup voltctrl and other setup times */
-
-#ifdef CONFIG_OMAP_SYSOFFMODE
- prm_write_mod_reg(OMAP3430_AUTO_OFF | OMAP3430_AUTO_RET |
- OMAP3430_SEL_OFF, OMAP3430_GR_MOD,
+ prm_write_mod_reg(OMAP3430_AUTO_RET, OMAP3430_GR_MOD,
OMAP3_PRM_VOLTCTRL_OFFSET);
prm_write_mod_reg(OMAP3430_CLKSETUP_DURATION, OMAP3430_GR_MOD,
OMAP3_PRM_VOLTOFFSET_OFFSET);
prm_write_mod_reg(OMAP3430_VOLTSETUP2_DURATION, OMAP3430_GR_MOD,
OMAP3_PRM_VOLTSETUP2_OFFSET);
-#else
- prm_set_mod_reg_bits(OMAP3430_AUTO_RET, OMAP3430_GR_MOD,
- OMAP3_PRM_VOLTCTRL_OFFSET);
-#endif
+}
+
+static int __init omap3_pm_early_init(void)
+{
+ prm_clear_mod_reg_bits(OMAP3430_OFFMODE_POL, OMAP3430_GR_MOD,
+ OMAP3_PRM_POLCTRL_OFFSET);
+ configure_vc();
+
+ return 0;
}
+
+arch_initcall(omap3_pm_early_init);