|
@@ -279,3 +279,106 @@ static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode)
|
|
|
* 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 has the additional benefit that voltages can be
|
|
|
+ * scaled to zero volt level with TWL4030 / TWL5030, I2C can only
|
|
|
+ * scale to 600mV.
|
|
|
+ */
|
|
|
+static void omap3_set_off_timings(struct voltagedomain *voltdm)
|
|
|
+{
|
|
|
+ unsigned long clksetup;
|
|
|
+ unsigned long voltsetup2;
|
|
|
+ unsigned long voltsetup2_old;
|
|
|
+ u32 val;
|
|
|
+ u32 tstart, tshut;
|
|
|
+
|
|
|
+ /* 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)) {
|
|
|
+ /* No, omap is controlling them over I2C */
|
|
|
+ omap3_set_i2c_timings(voltdm, true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ omap_pm_get_oscillator(&tstart, &tshut);
|
|
|
+ omap3_set_clksetup(tstart, voltdm);
|
|
|
+
|
|
|
+ clksetup = voltdm->read(OMAP3_PRM_CLKSETUP_OFFSET);
|
|
|
+
|
|
|
+ /* voltsetup 2 in us */
|
|
|
+ voltsetup2 = voltdm->vc_param->on / voltdm->pmic->slew_rate;
|
|
|
+
|
|
|
+ /* convert to 32k clk cycles */
|
|
|
+ voltsetup2 = DIV_ROUND_UP(voltsetup2 * 32768, 1000000);
|
|
|
+
|
|
|
+ voltsetup2_old = voltdm->read(OMAP3_PRM_VOLTSETUP2_OFFSET);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Update voltsetup2 if higher than current value (needed because
|
|
|
+ * we have multiple channels with different ramp times), also
|
|
|
+ * update voltoffset always to value recommended by TRM
|
|
|
+ */
|
|
|
+ if (voltsetup2 > voltsetup2_old) {
|
|
|
+ voltdm->write(voltsetup2, OMAP3_PRM_VOLTSETUP2_OFFSET);
|
|
|
+ voltdm->write(clksetup - voltsetup2,
|
|
|
+ OMAP3_PRM_VOLTOFFSET_OFFSET);
|
|
|
+ } else
|
|
|
+ voltdm->write(clksetup - voltsetup2_old,
|
|
|
+ OMAP3_PRM_VOLTOFFSET_OFFSET);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * omap is not controlling voltage scaling during off-mode,
|
|
|
+ * thus set voltsetup1 to 0
|
|
|
+ */
|
|
|
+ voltdm->rmw(voltdm->vfsm->voltsetup_mask, 0,
|
|
|
+ voltdm->vfsm->voltsetup_reg);
|
|
|
+
|
|
|
+ /* voltoffset must be clksetup minus voltsetup2 according to TRM */
|
|
|
+ voltdm->write(clksetup - voltsetup2, OMAP3_PRM_VOLTOFFSET_OFFSET);
|
|
|
+}
|
|
|
+
|
|
|
+static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
|
|
|
+{
|
|
|
+ omap3_set_off_timings(voltdm);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * omap4_calc_volt_ramp - calculates voltage ramping delays on omap4
|
|
|
+ * @voltdm: channel to calculate values for
|
|
|
+ * @voltage_diff: voltage difference in microvolts
|
|
|
+ *
|
|
|
+ * Calculates voltage ramp prescaler + counter values for a voltage
|
|
|
+ * difference on omap4. Returns a field value suitable for writing to
|
|
|
+ * VOLTSETUP register for a channel in following format:
|
|
|
+ * bits[8:9] prescaler ... bits[0:5] counter. See OMAP4 TRM for reference.
|
|
|
+ */
|
|
|
+static u32 omap4_calc_volt_ramp(struct voltagedomain *voltdm, u32 voltage_diff)
|
|
|
+{
|
|
|
+ u32 prescaler;
|
|
|
+ u32 cycles;
|
|
|
+ u32 time;
|
|
|
+
|
|
|
+ time = voltage_diff / voltdm->pmic->slew_rate;
|
|
|
+
|
|
|
+ cycles = voltdm->sys_clk.rate / 1000 * time / 1000;
|
|
|
+
|
|
|
+ cycles /= 64;
|
|
|
+ prescaler = 0;
|
|
|
+
|
|
|
+ /* shift to next prescaler until no overflow */
|
|
|
+
|
|
|
+ /* scale for div 256 = 64 * 4 */
|
|
|
+ if (cycles > 63) {
|
|
|
+ cycles /= 4;
|
|
|
+ prescaler++;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* scale for div 512 = 256 * 2 */
|
|
|
+ if (cycles > 63) {
|
|
|
+ cycles /= 2;
|
|
|
+ prescaler++;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* scale for div 2048 = 512 * 4 */
|
|
|
+ if (cycles > 63) {
|
|
|
+ cycles /= 4;
|
|
|
+ prescaler++;
|
|
|
+ }
|