@@ -37,6 +37,11 @@
#define VP_IDLE_TIMEOUT 200
#define VP_TRANXDONE_TIMEOUT 300
+#define ABB_MAX_SETTLING_TIME 30
+#define ABB_FAST_OPP 1
+#define ABB_NOMINAL_OPP 2
+#define ABB_SLOW_OPP 3
+
/**
* OMAP3 Voltage controller SR parameters. TODO: Pass this info as part of
* board data or PMIC data
@@ -635,6 +640,118 @@ static int vp_forceupdate_scale_voltage(u32 vdd, unsigned long target_volt,
}
/**
+ * voltscale_adaptive_body_bias - controls ABB ldo during voltage scaling
+ * @target_volt: target voltage determines if ABB ldo is active or bypassed
+ *
+ * Adaptive Body-Bias is a technique in all OMAP silicon that uses the 45nm
+ * process. ABB can boost voltage in high OPPs for silicon with weak
+ * characteristics (forward Body-Bias) as well as lower voltage in low OPPs
+ * for silicon with strong characteristics (Reverse Body-Bias).
+ *
+ * Only Foward Body-Bias for operating at high OPPs is implemented below, per
+ * recommendations from silicon team.
+ * Reverse Body-Bias for saving power in active cases and sleep cases is not
+ * yet implemented.
+ * OMAP4 hardward also supports ABB ldo, but no recommendations have been made
+ * to implement it yet.
+ */
+int voltscale_adaptive_body_bias(unsigned long target_volt)
+{
+ u32 sr2en_enabled;
+ int timeout;
+ int sr2_wtcnt_value;
+
+ /* calculate SR2_WTCNT_VALUE settling time */
+ sr2_wtcnt_value = (ABB_MAX_SETTLING_TIME *
+ (clk_get_rate("sys_ck") / 1000000) / 8);
+
+ /* has SR2EN been enabled previously? */
+ sr2en_enabled = (prm_read_mod_reg(OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_CTRL_OFFSET) &
+ OMAP3630_SR2EN);
+
+ /* select fast, nominal or slow OPP for ABB ldo */
+ /* FIXME: include OMAP4 once recommendations are complete */
+ if (cpu_is_omap3630() && (target_volt >= 1350000)) {
+ /* program for fast opp - enable FBB */
+ prm_rmw_mod_reg_bits(OMAP3630_OPP_SEL_MASK,
+ (ABB_FAST_OPP << OMAP3630_OPP_SEL_SHIFT),
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_SETUP_OFFSET);
+
+ /* enable the ABB ldo if not done already */
+ if (!sr2en_enabled)
+ prm_set_mod_reg_bits(OMAP3630_SR2EN,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_CTRL_OFFSET);
+ } else if (sr2en_enabled) {
+ /* program for nominal opp - bypass ABB ldo */
+ prm_rmw_mod_reg_bits(OMAP3630_OPP_SEL_MASK,
+ (ABB_NOMINAL_OPP << OMAP3630_OPP_SEL_SHIFT),
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_SETUP_OFFSET);
+ } else {
+ /* nothing to do here yet... might enable RBB here someday */
+ return 0;
+ }
+
+ /* set ACTIVE_FBB_SEL for all 45nm silicon */
+ prm_set_mod_reg_bits(OMAP3630_ACTIVE_FBB_SEL,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_CTRL_OFFSET);
+
+ /* program settling time of 30us for ABB ldo transition */
+ prm_rmw_mod_reg_bits(OMAP3630_SR2_WTCNT_VALUE_MASK,
+ (sr2_wtcnt_value << OMAP3630_SR2_WTCNT_VALUE_SHIFT),
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_CTRL_OFFSET);
+
+ /* clear ABB ldo interrupt status */
+ prm_write_mod_reg(OMAP3630_ABB_LDO_TRANXDONE_ST,
+ OCP_MOD,
+ OMAP2_PRCM_IRQSTATUS_MPU_OFFSET);
+
+ /* enable ABB LDO OPP change */
+ prm_set_mod_reg_bits(OMAP3630_OPP_CHANGE,
+ OMAP3430_GR_MOD,
+ OMAP3_PRM_LDO_ABB_SETUP_OFFSET);
+
+ timeout = 0;
+
+ /* wait until OPP change completes */
+ while ((timeout < ABB_MAX_SETTLING_TIME ) &&
+ (!(prm_read_mod_reg(OCP_MOD,
+ OMAP2_PRCM_IRQSTATUS_MPU_OFFSET) &
+ OMAP3630_ABB_LDO_TRANXDONE_ST))) {
+ udelay(1);
+ timeout++;
+ }
+
+ if (timeout == ABB_MAX_SETTLING_TIME)
+ pr_debug("ABB: TRANXDONE timed out waiting for OPP change\n");
+
+ timeout = 0;
+
+ /* Clear all pending TRANXDONE interrupts/status */
+ while (timeout < ABB_MAX_SETTLING_TIME) {
+ prm_write_mod_reg(OMAP3630_ABB_LDO_TRANXDONE_ST,
+ OCP_MOD,
+ OMAP2_PRCM_IRQSTATUS_MPU_OFFSET);
+ if (!(prm_read_mod_reg(OCP_MOD,
+ OMAP2_PRCM_IRQSTATUS_MPU_OFFSET)
+ & OMAP3630_ABB_LDO_TRANXDONE_ST))
+ break;
+
+ udelay(1);
+ timeout++;
+ }
+ if (timeout == ABB_MAX_SETTLING_TIME)
+ pr_debug("ABB: TRANXDONE timed out trying to clear status\n");
+
+ return 0;
+}
+
+/**
* get_curr_vdd1_voltage : Gets the current non-auto-compensated vdd1 voltage
*
* This is a temporary placeholder for this API. This should ideally belong
@@ -758,11 +875,19 @@ void omap_voltageprocessor_disable(int vp_id)
int omap_voltage_scale(int vdd, unsigned long target_volt,
unsigned long current_volt)
{
+ int ret;
+
if (voltscale_vpforceupdate)
- return vp_forceupdate_scale_voltage(vdd, target_volt,
+ ret = vp_forceupdate_scale_voltage(vdd, target_volt,
current_volt);
else
- return vc_bypass_scale_voltage(vdd, target_volt, current_volt);
+ ret = vc_bypass_scale_voltage(vdd, target_volt, current_volt);
+
+ /* FIXME OMAP4 needs ABB too; recommendations not yet complete */
+ if (ret && (cpu_is_omap3630() && vdd == VDD1))
+ ret = voltscale_adaptive_body_bias(target_volt);
+
+ return ret;
}
/**