diff options
Diffstat (limited to 'arch/arm/mach-omap2/vc.c')
-rw-r--r-- | arch/arm/mach-omap2/vc.c | 84 |
1 files changed, 83 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 49ac7977e03e..705eb35d7b35 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -220,6 +220,87 @@ static inline u32 omap_usec_to_32k(u32 usec) return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL); } +struct omap3_vc { + struct voltagedomain *vd; + u32 voltctrl; +}; +static struct omap3_vc vc; + +void omap3_vc_set_pmic_signaling(int core_next_state) +{ + struct voltagedomain *vd = vc.vd; + u32 voltctrl; + + voltctrl = vc.voltctrl; + switch (core_next_state) { + case PWRDM_POWER_OFF: + voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_RET | + OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP); + voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_OFF; + break; + case PWRDM_POWER_RET: + default: + voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_OFF | + OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP); + voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_RET; + break; + } + if (voltctrl != vc.voltctrl) { + vd->write(voltctrl, OMAP3_PRM_VOLTCTRL_OFFSET); + vc.voltctrl = voltctrl; + } +} + +#define PRM_POLCTRL_TWL_MASK (OMAP3430_PRM_POLCTRL_CLKREQ_POL | \ + OMAP3430_PRM_POLCTRL_CLKREQ_POL) +#define PRM_POLCTRL_TWL_VAL OMAP3430_PRM_POLCTRL_CLKREQ_POL + +/* + * Configure signal polarity for sys_clkreq and sys_off_mode pins + * as the default values are wrong and can cause the system to hang + * if any twl4030 scripts are loaded. + */ +static void __init omap3_vc_init_pmic_signaling(struct voltagedomain *voltdm) +{ + u32 val; + + if (vc.vd) + return; + + vc.vd = voltdm; + + val = voltdm->read(OMAP3_PRM_POLCTRL_OFFSET); + if (!(val & OMAP3430_PRM_POLCTRL_CLKREQ_POL) || + (val & OMAP3430_PRM_POLCTRL_CLKREQ_POL)) { + val |= OMAP3430_PRM_POLCTRL_CLKREQ_POL; + val &= ~OMAP3430_PRM_POLCTRL_OFFMODE_POL; + pr_debug("PM: fixing sys_clkreq and sys_off_mode polarity to 0x%x\n", + val); + voltdm->write(val, OMAP3_PRM_POLCTRL_OFFSET); + } + + /* + * By default let's use I2C4 signaling for retention idle + * and sys_off_mode pin signaling for off idle. This way we + * have sys_clk_req pin go down for retention and both + * sys_clk_req and sys_off_mode pins will go down for off + * idle. And we can also scale voltages to zero for off-idle. + * Note that no actual voltage scaling during off-idle will + * happen unless the board specific twl4030 PMIC scripts are + * loaded. + */ + val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET); + if (!(val & OMAP3430_PRM_VOLTCTRL_SEL_OFF)) { + val |= OMAP3430_PRM_VOLTCTRL_SEL_OFF; + pr_debug("PM: setting voltctrl sys_off_mode signaling to 0x%x\n", + val); + voltdm->write(val, OMAP3_PRM_VOLTCTRL_OFFSET); + } + vc.voltctrl = val; + + omap3_vc_set_pmic_signaling(PWRDM_POWER_ON); +} + /* Set oscillator setup time for omap3 */ static void omap3_set_clksetup(u32 usec, struct voltagedomain *voltdm) { @@ -292,7 +373,7 @@ static void omap3_set_off_timings(struct voltagedomain *voltdm) /* check if sys_off_mode is used to control off-mode voltages */ val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET); - if (!(val & OMAP3430_SEL_OFF_MASK)) { + if (!(val & OMAP3430_PRM_VOLTCTRL_SEL_OFF)) { /* No, omap is controlling them over I2C */ omap3_set_i2c_timings(voltdm, true); return; @@ -337,6 +418,7 @@ static void omap3_set_off_timings(struct voltagedomain *voltdm) static void __init omap3_vc_init_channel(struct voltagedomain *voltdm) { + omap3_vc_init_pmic_signaling(voltdm); omap3_set_off_timings(voltdm); } |