X-Git-Url: http://www.pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=arch%2Farm%2Fmach-omap2%2Fpm34xx.c;h=43aac5f6031983f6129064caca3cc32f774feecd;hb=0ec95b96fd77036a13398c66901e11cd301190d0;hp=449e7b5aeaa34dc8161b7725d867b06e5002d419;hpb=d3b3ae0fe6c71641da19c8de466ec366d39847e3;p=linux-2.6-omap-h63xx.git diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 449e7b5aeaa..43aac5f6031 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -24,11 +24,13 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include "cm.h" #include "cm-regbits-34xx.h" @@ -47,7 +49,7 @@ struct power_state { static LIST_HEAD(pwrst_list); -void (*_omap_sram_idle)(u32 *addr, int save_state); +static void (*_omap_sram_idle)(u32 *addr, int save_state); static void (*saved_idle)(void); @@ -109,7 +111,7 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) 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) { @@ -166,56 +168,107 @@ static void omap_sram_idle(void) 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) { @@ -224,7 +277,10 @@ static int set_pwrdm_state(struct powerdomain *pwrdm, u32 state) 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; @@ -260,10 +316,6 @@ static int omap3_pm_suspend(void) 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); @@ -275,6 +327,7 @@ static int omap3_pm_suspend(void) goto restore; } + omap_uart_prepare_suspend(); omap_sram_idle(); restore: @@ -282,7 +335,7 @@ 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); @@ -295,10 +348,6 @@ restore: printk(KERN_INFO "Successfully put all powerdomains " "to target state\n"); - /* XXX Enable smartreflex after suspend */ - enable_smartreflex(SR1); - enable_smartreflex(SR2); - return ret; } @@ -330,8 +379,57 @@ static struct platform_suspend_ops omap_pm_ops = { .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) { + /* reset modem */ + prm_write_mod_reg(OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RSTPWRON | + OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RST, + CORE_MOD, RM_RSTCTRL); + prm_write_mod_reg(0, CORE_MOD, RM_RSTCTRL); + /* XXX Reset all wkdeps. This should be done when initializing * powerdomains */ prm_write_mod_reg(0, OMAP3430_IVA2_MOD, PM_WKDEP); @@ -340,22 +438,151 @@ static void __init prcm_setup_regs(void) 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) @@ -378,9 +605,24 @@ static int __init pwrdms_setup(struct powerdomain *pwrdm) return set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); } +/* + * Enable hw 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) +{ + if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO) + omap2_clkdm_allow_idle(clkdm); + else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP && + atomic_read(&clkdm->usecount) == 0) + omap2_clkdm_sleep(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"); @@ -404,6 +646,8 @@ int __init omap3_pm_init(void) 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"); @@ -421,9 +665,70 @@ err1: 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); } return ret; } + +static void __init configure_vc(void) +{ + prm_write_mod_reg((R_SRI2C_SLAVE_ADDR << OMAP3430_SMPS_SA1_SHIFT) | + (R_SRI2C_SLAVE_ADDR << OMAP3430_SMPS_SA0_SHIFT), + OMAP3430_GR_MOD, OMAP3_PRM_VC_SMPS_SA_OFFSET); + prm_write_mod_reg((R_VDD2_SR_CONTROL << OMAP3430_VOLRA1_SHIFT) | + (R_VDD1_SR_CONTROL << OMAP3430_VOLRA0_SHIFT), + OMAP3430_GR_MOD, OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET); + + prm_write_mod_reg((OMAP3430_VC_CMD_VAL0_ON << + OMAP3430_VC_CMD_ON_SHIFT) | + (OMAP3430_VC_CMD_VAL0_ONLP << OMAP3430_VC_CMD_ONLP_SHIFT) | + (OMAP3430_VC_CMD_VAL0_RET << OMAP3430_VC_CMD_RET_SHIFT) | + (OMAP3430_VC_CMD_VAL0_OFF << OMAP3430_VC_CMD_OFF_SHIFT), + OMAP3430_GR_MOD, OMAP3_PRM_VC_CMD_VAL_0_OFFSET); + + prm_write_mod_reg((OMAP3430_VC_CMD_VAL1_ON << + OMAP3430_VC_CMD_ON_SHIFT) | + (OMAP3430_VC_CMD_VAL1_ONLP << OMAP3430_VC_CMD_ONLP_SHIFT) | + (OMAP3430_VC_CMD_VAL1_RET << OMAP3430_VC_CMD_RET_SHIFT) | + (OMAP3430_VC_CMD_VAL1_OFF << OMAP3430_VC_CMD_OFF_SHIFT), + OMAP3430_GR_MOD, OMAP3_PRM_VC_CMD_VAL_1_OFFSET); + + prm_write_mod_reg(OMAP3430_CMD1 | OMAP3430_RAV1, + OMAP3430_GR_MOD, + OMAP3_PRM_VC_CH_CONF_OFFSET); + + prm_write_mod_reg(OMAP3430_MCODE_SHIFT | OMAP3430_HSEN | OMAP3430_SREN, + OMAP3430_GR_MOD, + OMAP3_PRM_VC_I2C_CFG_OFFSET); + + /* Setup voltctrl and other setup times */ + 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_CLKSETUP_OFFSET); + prm_write_mod_reg((OMAP3430_VOLTSETUP_TIME2 << + OMAP3430_SETUP_TIME2_SHIFT) | + (OMAP3430_VOLTSETUP_TIME1 << + OMAP3430_SETUP_TIME1_SHIFT), + OMAP3430_GR_MOD, OMAP3_PRM_VOLTSETUP1_OFFSET); + + prm_write_mod_reg(OMAP3430_VOLTOFFSET_DURATION, OMAP3430_GR_MOD, + OMAP3_PRM_VOLTOFFSET_OFFSET); + prm_write_mod_reg(OMAP3430_VOLTSETUP2_DURATION, OMAP3430_GR_MOD, + OMAP3_PRM_VOLTSETUP2_OFFSET); +} + +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);