/* VP force update method of voltage scaling */ int omap_vp_forceupdate_scale(struct voltagedomain *voltdm, struct omap_volt_data *target_v) { struct omap_vp_instance *vp = voltdm->vp; u32 vpconfig; u8 target_vsel, current_vsel; int ret, timeout = 0; unsigned long target_volt = omap_get_operation_voltage(target_v); /* * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us * This is an additional allowance to ensure we are in proper state * to enter into forceupdate state transition. */ omap_test_timeout((voltdm->read(vp->vstatus) & vp->common->vstatus_vpidle), VP_IDLE_TIMEOUT, timeout); if (timeout >= VP_IDLE_TIMEOUT) _vp_controlled_err(vp, voltdm, "%s:vdd_%s idletimdout forceupdate(v=%ld)\n", __func__, voltdm->name, target_volt); ret = omap_vc_pre_scale(voltdm, target_volt, target_v, &target_vsel, ¤t_vsel); if (ret) return ret; /* * Clear all pending TransactionDone interrupt/status. Typical latency * is <3us */ while (timeout++ < VP_TRANXDONE_TIMEOUT) { vp->common->ops->clear_txdone(vp->id); if (!vp->common->ops->check_txdone(vp->id)) break; udelay(1); } if (timeout >= VP_TRANXDONE_TIMEOUT) { _vp_controlled_err(vp, voltdm, "%s: vdd_%s TRANXDONE timeout exceeded." "Voltage change aborted target volt=%ld," "target vsel=0x%02x, current_vsel=0x%02x\n", __func__, voltdm->name, target_volt, target_vsel, current_vsel); return -ETIMEDOUT; } /* Configure for VP-Force Update */ vpconfig = voltdm->read(vp->vpconfig); vpconfig &= ~(vp->common->vpconfig_initvdd | vp->common->vpconfig_forceupdate | vp->common->vpconfig_initvoltage_mask); vpconfig |= ((target_vsel << __ffs(vp->common->vpconfig_initvoltage_mask))); voltdm->write(vpconfig, vp->vpconfig); /* Trigger initVDD value copy to voltage processor */ vpconfig |= vp->common->vpconfig_initvdd; voltdm->write(vpconfig, vp->vpconfig); /* Force update of voltage */ vpconfig |= vp->common->vpconfig_forceupdate; voltdm->write(vpconfig, vp->vpconfig); /* * Wait for TransactionDone. Typical latency is <200us. * Depends on SMPSWAITTIMEMIN/MAX and voltage change */ timeout = 0; omap_test_timeout(vp->common->ops->check_txdone(vp->id), VP_TRANXDONE_TIMEOUT, timeout); if (timeout >= VP_TRANXDONE_TIMEOUT) _vp_controlled_err(vp, voltdm, "%s: vdd_%s TRANXDONE timeout exceeded. " "TRANXDONE never got set after the voltage update. " "target volt=%ld, target vsel=0x%02x, " "current_vsel=0x%02x\n", __func__, voltdm->name, target_volt, target_vsel, current_vsel); omap_vc_post_scale(voltdm, target_volt, target_v, target_vsel, current_vsel); /* * Disable TransactionDone interrupt , clear all status, clear * control registers */ timeout = 0; while (timeout++ < VP_TRANXDONE_TIMEOUT) { vp->common->ops->clear_txdone(vp->id); if (!vp->common->ops->check_txdone(vp->id)) break; udelay(1); } if (timeout >= VP_TRANXDONE_TIMEOUT) _vp_controlled_err(vp, voltdm, "%s: vdd_%s TRANXDONE timeout exceeded while" "trying to clear the TRANXDONE status. target volt=%ld," "target vsel=0x%02x, current_vsel=0x%02x\n", __func__, voltdm->name, target_volt, target_vsel, current_vsel); vpconfig = voltdm->read(vp->vpconfig); /* Clear initVDD copy trigger bit */ vpconfig &= ~vp->common->vpconfig_initvdd; voltdm->write(vpconfig, vp->vpconfig); /* Clear force bit */ vpconfig &= ~vp->common->vpconfig_forceupdate; voltdm->write(vpconfig, vp->vpconfig); return 0; }
/* VP force update method of voltage scaling */ int omap_vp_forceupdate_scale(struct voltagedomain *voltdm, struct omap_volt_data *target_v) { struct omap_vp_instance *vp; u32 vpconfig; u8 target_vsel, current_vsel; int ret, timeout = 0; unsigned long target_volt; if (IS_ERR_OR_NULL(voltdm)) { pr_err("%s: VDD specified does not exist!\n", __func__); return -EINVAL; } if (IS_ERR_OR_NULL(voltdm->write)) { pr_err("%s: No write API for writing vdd_%s regs\n", __func__, voltdm->name); return -EINVAL; } if (IS_ERR_OR_NULL(target_v)) { pr_err("%s: No target_v info to scale vdd_%s\n", __func__, voltdm->name); return -EINVAL; } vp = voltdm->vp; if (IS_ERR_OR_NULL(vp)) { pr_err("%s: No VP info for vdd_%s\n", __func__, voltdm->name); return -EINVAL; } target_volt = omap_get_operation_voltage(target_v); ret = _vp_wait_for_idle(voltdm, vp); if (ret) { _vp_controlled_err(vp, voltdm, "%s: vdd_%s idle timedout (v=%ld)\n", __func__, voltdm->name, target_volt); return ret; } ret = omap_vc_pre_scale(voltdm, target_volt, target_v, &target_vsel, ¤t_vsel); if (ret) return ret; /* * Clear all pending TransactionDone interrupt/status. Typical latency * is <3us */ while (timeout++ < VP_TRANXDONE_TIMEOUT) { vp->common->ops->clear_txdone(vp->id); if (!vp->common->ops->check_txdone(vp->id)) break; udelay(1); } if (timeout >= VP_TRANXDONE_TIMEOUT) { _vp_controlled_err(vp, voltdm, "%s: vdd_%s TRANXDONE timeout exceeded." "Voltage change aborted target volt=%ld," "target vsel=0x%02x, current_vsel=0x%02x\n", __func__, voltdm->name, target_volt, target_vsel, current_vsel); return -ETIMEDOUT; } vpconfig = _vp_set_init_voltage(voltdm, target_volt); /* Force update of voltage */ voltdm->write(vpconfig | vp->common->vpconfig_forceupdate, voltdm->vp->vpconfig); /* * Wait for TransactionDone. Typical latency is <200us. * Depends on SMPSWAITTIMEMIN/MAX and voltage change */ timeout = 0; omap_test_timeout(vp->common->ops->check_txdone(vp->id), VP_TRANXDONE_TIMEOUT, timeout); if (timeout >= VP_TRANXDONE_TIMEOUT) _vp_controlled_err(vp, voltdm, "%s: vdd_%s TRANXDONE timeout exceeded. " "TRANXDONE never got set after the voltage update. " "target volt=%ld, target vsel=0x%02x, " "current_vsel=0x%02x\n", __func__, voltdm->name, target_volt, target_vsel, current_vsel); omap_vc_post_scale(voltdm, target_volt, target_v, target_vsel, current_vsel); /* * Disable TransactionDone interrupt , clear all status, clear * control registers */ timeout = 0; while (timeout++ < VP_TRANXDONE_TIMEOUT) { vp->common->ops->clear_txdone(vp->id); if (!vp->common->ops->check_txdone(vp->id)) break; udelay(1); } if (timeout >= VP_TRANXDONE_TIMEOUT) _vp_controlled_err(vp, voltdm, "%s: vdd_%s TRANXDONE timeout exceeded while" "trying to clear the TRANXDONE status. target volt=%ld," "target vsel=0x%02x, current_vsel=0x%02x\n", __func__, voltdm->name, target_volt, target_vsel, current_vsel); /* Clear force bit */ voltdm->write(vpconfig, vp->vpconfig); return 0; }