diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 87099bb6de69..3c8bb8f0304d 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -50,6 +50,7 @@ #include "sdrc.h" #include "sram.h" #include "control.h" +#include "vc.h" /* pm34xx errata defined in pm.h */ u16 pm34xx_errata; @@ -288,6 +289,9 @@ void omap_sram_idle(void) } } + /* Configure PMIC signaling for I2C4 or sys_off_mode */ + omap3_vc_set_pmic_signaling(core_next_state); + omap3_intc_prepare_idle(); /* diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h b/arch/arm/mach-omap2/prm-regbits-34xx.h index cebad565ed37..106132db532b 100644 --- a/arch/arm/mach-omap2/prm-regbits-34xx.h +++ b/arch/arm/mach-omap2/prm-regbits-34xx.h @@ -123,8 +123,15 @@ #define OMAP3430_GLOBAL_SW_RST_SHIFT 1 #define OMAP3430_GLOBAL_COLD_RST_SHIFT 0 #define OMAP3430_GLOBAL_COLD_RST_MASK (1 << 0) -#define OMAP3430_SEL_OFF_MASK (1 << 3) -#define OMAP3430_AUTO_OFF_MASK (1 << 2) +#define OMAP3430_PRM_VOLTCTRL_SEL_VMODE (1 << 4) +#define OMAP3430_PRM_VOLTCTRL_SEL_OFF (1 << 3) +#define OMAP3430_PRM_VOLTCTRL_AUTO_OFF (1 << 2) +#define OMAP3430_PRM_VOLTCTRL_AUTO_RET (1 << 1) +#define OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP (1 << 0) #define OMAP3430_SETUP_TIME2_MASK (0xffff << 16) #define OMAP3430_SETUP_TIME1_MASK (0xffff << 0) +#define OMAP3430_PRM_POLCTRL_OFFMODE_POL (1 << 3) +#define OMAP3430_PRM_POLCTRL_CLKOUT_POL (1 << 2) +#define OMAP3430_PRM_POLCTRL_CLKREQ_POL (1 << 1) +#define OMAP3430_PRM_POLCTRL_EXTVOL_POL (1 << 0) #endif 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); } diff --git a/arch/arm/mach-omap2/vc.h b/arch/arm/mach-omap2/vc.h index 91c8d75bf2ea..cdbdd78e755e 100644 --- a/arch/arm/mach-omap2/vc.h +++ b/arch/arm/mach-omap2/vc.h @@ -117,6 +117,9 @@ extern struct omap_vc_param omap4_mpu_vc_data; extern struct omap_vc_param omap4_iva_vc_data; extern struct omap_vc_param omap4_core_vc_data; +void omap3_vc_set_pmic_signaling(int core_next_state); + + void omap_vc_init_channel(struct voltagedomain *voltdm); int omap_vc_pre_scale(struct voltagedomain *voltdm, unsigned long target_volt,