|
@@ -220,35 +220,64 @@ static inline u32 omap_usec_to_32k(u32 usec)
|
|
return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL);
|
|
return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct omap3_vc_timings {
|
|
|
|
+ u32 voltsetup1;
|
|
|
|
+ u32 voltsetup2;
|
|
|
|
+};
|
|
|
|
+
|
|
struct omap3_vc {
|
|
struct omap3_vc {
|
|
struct voltagedomain *vd;
|
|
struct voltagedomain *vd;
|
|
u32 voltctrl;
|
|
u32 voltctrl;
|
|
|
|
+ u32 voltsetup1;
|
|
|
|
+ u32 voltsetup2;
|
|
|
|
+ struct omap3_vc_timings timings[2];
|
|
};
|
|
};
|
|
static struct omap3_vc vc;
|
|
static struct omap3_vc vc;
|
|
|
|
|
|
void omap3_vc_set_pmic_signaling(int core_next_state)
|
|
void omap3_vc_set_pmic_signaling(int core_next_state)
|
|
{
|
|
{
|
|
struct voltagedomain *vd = vc.vd;
|
|
struct voltagedomain *vd = vc.vd;
|
|
- u32 voltctrl;
|
|
|
|
|
|
+ struct omap3_vc_timings *c = vc.timings;
|
|
|
|
+ u32 voltctrl, voltsetup1, voltsetup2;
|
|
|
|
|
|
voltctrl = vc.voltctrl;
|
|
voltctrl = vc.voltctrl;
|
|
|
|
+ voltsetup1 = vc.voltsetup1;
|
|
|
|
+ voltsetup2 = vc.voltsetup2;
|
|
|
|
+
|
|
switch (core_next_state) {
|
|
switch (core_next_state) {
|
|
case PWRDM_POWER_OFF:
|
|
case PWRDM_POWER_OFF:
|
|
voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_RET |
|
|
voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_RET |
|
|
OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
|
|
OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
|
|
voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_OFF;
|
|
voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_OFF;
|
|
|
|
+ if (voltctrl & OMAP3430_PRM_VOLTCTRL_SEL_OFF)
|
|
|
|
+ voltsetup2 = c->voltsetup2;
|
|
|
|
+ else
|
|
|
|
+ voltsetup1 = c->voltsetup1;
|
|
break;
|
|
break;
|
|
case PWRDM_POWER_RET:
|
|
case PWRDM_POWER_RET:
|
|
default:
|
|
default:
|
|
|
|
+ c++;
|
|
voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_OFF |
|
|
voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_OFF |
|
|
OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
|
|
OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
|
|
voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_RET;
|
|
voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_RET;
|
|
|
|
+ voltsetup1 = c->voltsetup1;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+
|
|
if (voltctrl != vc.voltctrl) {
|
|
if (voltctrl != vc.voltctrl) {
|
|
vd->write(voltctrl, OMAP3_PRM_VOLTCTRL_OFFSET);
|
|
vd->write(voltctrl, OMAP3_PRM_VOLTCTRL_OFFSET);
|
|
vc.voltctrl = voltctrl;
|
|
vc.voltctrl = voltctrl;
|
|
}
|
|
}
|
|
|
|
+ if (voltsetup1 != vc.voltsetup1) {
|
|
|
|
+ vd->write(c->voltsetup1,
|
|
|
|
+ OMAP3_PRM_VOLTSETUP1_OFFSET);
|
|
|
|
+ vc.voltsetup1 = voltsetup1;
|
|
|
|
+ }
|
|
|
|
+ if (voltsetup2 != vc.voltsetup2) {
|
|
|
|
+ vd->write(c->voltsetup2,
|
|
|
|
+ OMAP3_PRM_VOLTSETUP2_OFFSET);
|
|
|
|
+ vc.voltsetup2 = voltsetup2;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
#define PRM_POLCTRL_TWL_MASK (OMAP3430_PRM_POLCTRL_CLKREQ_POL | \
|
|
#define PRM_POLCTRL_TWL_MASK (OMAP3430_PRM_POLCTRL_CLKREQ_POL | \
|
|
@@ -301,6 +330,18 @@ static void __init omap3_vc_init_pmic_signaling(struct voltagedomain *voltdm)
|
|
omap3_vc_set_pmic_signaling(PWRDM_POWER_ON);
|
|
omap3_vc_set_pmic_signaling(PWRDM_POWER_ON);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void omap3_init_voltsetup1(struct voltagedomain *voltdm,
|
|
|
|
+ struct omap3_vc_timings *c, u32 idle)
|
|
|
|
+{
|
|
|
|
+ unsigned long val;
|
|
|
|
+
|
|
|
|
+ val = (voltdm->vc_param->on - idle) / voltdm->pmic->slew_rate;
|
|
|
|
+ val *= voltdm->sys_clk.rate / 8 / 1000000 + 1;
|
|
|
|
+ val <<= __ffs(voltdm->vfsm->voltsetup_mask);
|
|
|
|
+ c->voltsetup1 &= ~voltdm->vfsm->voltsetup_mask;
|
|
|
|
+ c->voltsetup1 |= val;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* omap3_set_i2c_timings - sets i2c sleep timings for a channel
|
|
* omap3_set_i2c_timings - sets i2c sleep timings for a channel
|
|
* @voltdm: channel to configure
|
|
* @voltdm: channel to configure
|
|
@@ -311,31 +352,21 @@ static void __init omap3_vc_init_pmic_signaling(struct voltagedomain *voltdm)
|
|
* or retention. Off mode has additionally an option to use sys_off_mode
|
|
* or retention. Off mode has additionally an option to use sys_off_mode
|
|
* pad, which uses a global signal to program the whole power IC to
|
|
* pad, which uses a global signal to program the whole power IC to
|
|
* off-mode.
|
|
* off-mode.
|
|
|
|
+ *
|
|
|
|
+ * Note that pmic is not controlling the voltage scaling during
|
|
|
|
+ * retention signaled over I2C4, so we can keep voltsetup2 as 0.
|
|
|
|
+ * And the oscillator is not shut off over I2C4, so no need to
|
|
|
|
+ * set clksetup.
|
|
*/
|
|
*/
|
|
-static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode)
|
|
|
|
|
|
+static void omap3_set_i2c_timings(struct voltagedomain *voltdm)
|
|
{
|
|
{
|
|
- unsigned long voltsetup1;
|
|
|
|
- u32 tgt_volt;
|
|
|
|
-
|
|
|
|
- if (off_mode)
|
|
|
|
- tgt_volt = voltdm->vc_param->off;
|
|
|
|
- else
|
|
|
|
- tgt_volt = voltdm->vc_param->ret;
|
|
|
|
-
|
|
|
|
- voltsetup1 = (voltdm->vc_param->on - tgt_volt) /
|
|
|
|
- voltdm->pmic->slew_rate;
|
|
|
|
|
|
+ struct omap3_vc_timings *c = vc.timings;
|
|
|
|
|
|
- voltsetup1 = voltsetup1 * voltdm->sys_clk.rate / 8 / 1000000 + 1;
|
|
|
|
-
|
|
|
|
- voltdm->rmw(voltdm->vfsm->voltsetup_mask,
|
|
|
|
- voltsetup1 << __ffs(voltdm->vfsm->voltsetup_mask),
|
|
|
|
- voltdm->vfsm->voltsetup_reg);
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * pmic is not controlling the voltage scaling during retention,
|
|
|
|
- * thus set voltsetup2 to 0
|
|
|
|
- */
|
|
|
|
- voltdm->write(0, OMAP3_PRM_VOLTSETUP2_OFFSET);
|
|
|
|
|
|
+ /* Configure PRWDM_POWER_OFF over I2C4 */
|
|
|
|
+ omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->off);
|
|
|
|
+ c++;
|
|
|
|
+ /* Configure PRWDM_POWER_RET over I2C4 */
|
|
|
|
+ omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->ret);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -344,22 +375,49 @@ static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode)
|
|
*
|
|
*
|
|
* Calculates and sets up off-mode timings for a channel. Off-mode
|
|
* Calculates and sets up off-mode timings for a channel. Off-mode
|
|
* can use either I2C based voltage scaling, or alternatively
|
|
* can use either I2C based voltage scaling, or alternatively
|
|
- * sys_off_mode pad can be used to send a global command to power IC.
|
|
|
|
- * This function first checks which mode is being used, and calls
|
|
|
|
- * omap3_set_i2c_timings() if the system is using I2C control mode.
|
|
|
|
|
|
+ * sys_off_mode pad can be used to send a global command to power IC.n,
|
|
* sys_off_mode has the additional benefit that voltages can be
|
|
* sys_off_mode has the additional benefit that voltages can be
|
|
* scaled to zero volt level with TWL4030 / TWL5030, I2C can only
|
|
* scaled to zero volt level with TWL4030 / TWL5030, I2C can only
|
|
* scale to 600mV.
|
|
* scale to 600mV.
|
|
|
|
+ *
|
|
|
|
+ * Note that omap is not controlling the voltage scaling during
|
|
|
|
+ * off idle signaled by sys_off_mode, so we can keep voltsetup1
|
|
|
|
+ * as 0.
|
|
*/
|
|
*/
|
|
static void omap3_set_off_timings(struct voltagedomain *voltdm)
|
|
static void omap3_set_off_timings(struct voltagedomain *voltdm)
|
|
{
|
|
{
|
|
|
|
+ struct omap3_vc_timings *c = vc.timings;
|
|
|
|
+ u32 tstart, tshut, clksetup, voltoffset;
|
|
|
|
+
|
|
|
|
+ if (c->voltsetup2)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ omap_pm_get_oscillator(&tstart, &tshut);
|
|
|
|
+ if (tstart == ULONG_MAX) {
|
|
|
|
+ pr_debug("PM: oscillator start-up time not initialized, using 10ms\n");
|
|
|
|
+ clksetup = omap_usec_to_32k(10000);
|
|
|
|
+ } else {
|
|
|
|
+ clksetup = omap_usec_to_32k(tstart);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For twl4030 errata 27, we need to allow minimum ~488.32 us wait to
|
|
|
|
+ * switch from HFCLKIN to internal oscillator. That means timings
|
|
|
|
+ * have voltoffset fixed to 0xa in rounded up 32 KiHz cycles. And
|
|
|
|
+ * that means we can calculate the value based on the oscillator
|
|
|
|
+ * start-up time since voltoffset2 = clksetup - voltoffset.
|
|
|
|
+ */
|
|
|
|
+ voltoffset = omap_usec_to_32k(488);
|
|
|
|
+ c->voltsetup2 = clksetup - voltoffset;
|
|
|
|
+ voltdm->write(clksetup, OMAP3_PRM_CLKSETUP_OFFSET);
|
|
|
|
+ voltdm->write(voltoffset, OMAP3_PRM_VOLTOFFSET_OFFSET);
|
|
}
|
|
}
|
|
|
|
|
|
static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
|
|
static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
|
|
{
|
|
{
|
|
omap3_vc_init_pmic_signaling(voltdm);
|
|
omap3_vc_init_pmic_signaling(voltdm);
|
|
omap3_set_off_timings(voltdm);
|
|
omap3_set_off_timings(voltdm);
|
|
- omap3_set_i2c_timings(voltdm, true);
|
|
|
|
|
|
+ omap3_set_i2c_timings(voltdm);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|