162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/kernel.h> 362306a36Sopenharmony_ci#include <linux/init.h> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "common.h" 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "voltage.h" 862306a36Sopenharmony_ci#include "vp.h" 962306a36Sopenharmony_ci#include "prm-regbits-34xx.h" 1062306a36Sopenharmony_ci#include "prm-regbits-44xx.h" 1162306a36Sopenharmony_ci#include "prm44xx.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct omap_vp_instance *vp = voltdm->vp; 1662306a36Sopenharmony_ci u32 vpconfig; 1762306a36Sopenharmony_ci char vsel; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci vsel = voltdm->pmic->uv_to_vsel(volt); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci vpconfig = voltdm->read(vp->vpconfig); 2262306a36Sopenharmony_ci vpconfig &= ~(vp->common->vpconfig_initvoltage_mask | 2362306a36Sopenharmony_ci vp->common->vpconfig_forceupdate | 2462306a36Sopenharmony_ci vp->common->vpconfig_initvdd); 2562306a36Sopenharmony_ci vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask); 2662306a36Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* Trigger initVDD value copy to voltage processor */ 2962306a36Sopenharmony_ci voltdm->write((vpconfig | vp->common->vpconfig_initvdd), 3062306a36Sopenharmony_ci vp->vpconfig); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Clear initVDD copy trigger bit */ 3362306a36Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return vpconfig; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Generic voltage init functions */ 3962306a36Sopenharmony_civoid __init omap_vp_init(struct voltagedomain *voltdm) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct omap_vp_instance *vp = voltdm->vp; 4262306a36Sopenharmony_ci u32 val, sys_clk_rate, timeout, waittime; 4362306a36Sopenharmony_ci u32 vddmin, vddmax, vstepmin, vstepmax; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { 4662306a36Sopenharmony_ci pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); 4762306a36Sopenharmony_ci return; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (!voltdm->read || !voltdm->write) { 5162306a36Sopenharmony_ci pr_err("%s: No read/write API for accessing vdd_%s regs\n", 5262306a36Sopenharmony_ci __func__, voltdm->name); 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci vp->enabled = false; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Divide to avoid overflow */ 5962306a36Sopenharmony_ci sys_clk_rate = voltdm->sys_clk.rate / 1000; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000; 6262306a36Sopenharmony_ci vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin); 6362306a36Sopenharmony_ci vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax); 6462306a36Sopenharmony_ci vddmin = voltdm->pmic->uv_to_vsel(vddmin); 6562306a36Sopenharmony_ci vddmax = voltdm->pmic->uv_to_vsel(vddmax); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate, 6862306a36Sopenharmony_ci 1000 * voltdm->pmic->slew_rate); 6962306a36Sopenharmony_ci vstepmin = voltdm->pmic->vp_vstepmin; 7062306a36Sopenharmony_ci vstepmax = voltdm->pmic->vp_vstepmax; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* 7362306a36Sopenharmony_ci * VP_CONFIG: error gain is not set here, it will be updated 7462306a36Sopenharmony_ci * on each scale, based on OPP. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci val = (voltdm->pmic->vp_erroroffset << 7762306a36Sopenharmony_ci __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) | 7862306a36Sopenharmony_ci vp->common->vpconfig_timeouten; 7962306a36Sopenharmony_ci voltdm->write(val, vp->vpconfig); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* VSTEPMIN */ 8262306a36Sopenharmony_ci val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) | 8362306a36Sopenharmony_ci (vstepmin << vp->common->vstepmin_stepmin_shift); 8462306a36Sopenharmony_ci voltdm->write(val, vp->vstepmin); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* VSTEPMAX */ 8762306a36Sopenharmony_ci val = (vstepmax << vp->common->vstepmax_stepmax_shift) | 8862306a36Sopenharmony_ci (waittime << vp->common->vstepmax_smpswaittimemax_shift); 8962306a36Sopenharmony_ci voltdm->write(val, vp->vstepmax); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* VLIMITTO */ 9262306a36Sopenharmony_ci val = (vddmax << vp->common->vlimitto_vddmax_shift) | 9362306a36Sopenharmony_ci (vddmin << vp->common->vlimitto_vddmin_shift) | 9462306a36Sopenharmony_ci (timeout << vp->common->vlimitto_timeout_shift); 9562306a36Sopenharmony_ci voltdm->write(val, vp->vlimitto); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint omap_vp_update_errorgain(struct voltagedomain *voltdm, 9962306a36Sopenharmony_ci unsigned long target_volt) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct omap_volt_data *volt_data; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!voltdm->vp) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Get volt_data corresponding to target_volt */ 10762306a36Sopenharmony_ci volt_data = omap_voltage_get_voltdata(voltdm, target_volt); 10862306a36Sopenharmony_ci if (IS_ERR(volt_data)) 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Setting vp errorgain based on the voltage */ 11262306a36Sopenharmony_ci voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask, 11362306a36Sopenharmony_ci volt_data->vp_errgain << 11462306a36Sopenharmony_ci __ffs(voltdm->vp->common->vpconfig_errorgain_mask), 11562306a36Sopenharmony_ci voltdm->vp->vpconfig); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* VP force update method of voltage scaling */ 12162306a36Sopenharmony_ciint omap_vp_forceupdate_scale(struct voltagedomain *voltdm, 12262306a36Sopenharmony_ci unsigned long target_volt) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct omap_vp_instance *vp = voltdm->vp; 12562306a36Sopenharmony_ci u32 vpconfig; 12662306a36Sopenharmony_ci u8 target_vsel, current_vsel; 12762306a36Sopenharmony_ci int ret, timeout = 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); 13062306a36Sopenharmony_ci if (ret) 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * Clear all pending TransactionDone interrupt/status. Typical latency 13562306a36Sopenharmony_ci * is <3us 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci while (timeout++ < VP_TRANXDONE_TIMEOUT) { 13862306a36Sopenharmony_ci vp->common->ops->clear_txdone(vp->id); 13962306a36Sopenharmony_ci if (!vp->common->ops->check_txdone(vp->id)) 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci udelay(1); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci if (timeout >= VP_TRANXDONE_TIMEOUT) { 14462306a36Sopenharmony_ci pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n", 14562306a36Sopenharmony_ci __func__, voltdm->name); 14662306a36Sopenharmony_ci return -ETIMEDOUT; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci vpconfig = _vp_set_init_voltage(voltdm, target_volt); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Force update of voltage */ 15262306a36Sopenharmony_ci voltdm->write(vpconfig | vp->common->vpconfig_forceupdate, 15362306a36Sopenharmony_ci voltdm->vp->vpconfig); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Wait for TransactionDone. Typical latency is <200us. 15762306a36Sopenharmony_ci * Depends on SMPSWAITTIMEMIN/MAX and voltage change 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci timeout = 0; 16062306a36Sopenharmony_ci omap_test_timeout(vp->common->ops->check_txdone(vp->id), 16162306a36Sopenharmony_ci VP_TRANXDONE_TIMEOUT, timeout); 16262306a36Sopenharmony_ci if (timeout >= VP_TRANXDONE_TIMEOUT) 16362306a36Sopenharmony_ci pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n", 16462306a36Sopenharmony_ci __func__, voltdm->name); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Disable TransactionDone interrupt , clear all status, clear 17062306a36Sopenharmony_ci * control registers 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci timeout = 0; 17362306a36Sopenharmony_ci while (timeout++ < VP_TRANXDONE_TIMEOUT) { 17462306a36Sopenharmony_ci vp->common->ops->clear_txdone(vp->id); 17562306a36Sopenharmony_ci if (!vp->common->ops->check_txdone(vp->id)) 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci udelay(1); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (timeout >= VP_TRANXDONE_TIMEOUT) 18162306a36Sopenharmony_ci pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n", 18262306a36Sopenharmony_ci __func__, voltdm->name); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Clear force bit */ 18562306a36Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * omap_vp_enable() - API to enable a particular VP 19262306a36Sopenharmony_ci * @voltdm: pointer to the VDD whose VP is to be enabled. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * This API enables a particular voltage processor. Needed by the smartreflex 19562306a36Sopenharmony_ci * class drivers. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_civoid omap_vp_enable(struct voltagedomain *voltdm) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct omap_vp_instance *vp; 20062306a36Sopenharmony_ci u32 vpconfig, volt; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!voltdm || IS_ERR(voltdm)) { 20362306a36Sopenharmony_ci pr_warn("%s: VDD specified does not exist!\n", __func__); 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci vp = voltdm->vp; 20862306a36Sopenharmony_ci if (!voltdm->read || !voltdm->write) { 20962306a36Sopenharmony_ci pr_err("%s: No read/write API for accessing vdd_%s regs\n", 21062306a36Sopenharmony_ci __func__, voltdm->name); 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* If VP is already enabled, do nothing. Return */ 21562306a36Sopenharmony_ci if (vp->enabled) 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci volt = voltdm_get_voltage(voltdm); 21962306a36Sopenharmony_ci if (!volt) { 22062306a36Sopenharmony_ci pr_warn("%s: unable to find current voltage for %s\n", 22162306a36Sopenharmony_ci __func__, voltdm->name); 22262306a36Sopenharmony_ci return; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci vpconfig = _vp_set_init_voltage(voltdm, volt); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Enable VP */ 22862306a36Sopenharmony_ci vpconfig |= vp->common->vpconfig_vpenable; 22962306a36Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci vp->enabled = true; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/** 23562306a36Sopenharmony_ci * omap_vp_disable() - API to disable a particular VP 23662306a36Sopenharmony_ci * @voltdm: pointer to the VDD whose VP is to be disabled. 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * This API disables a particular voltage processor. Needed by the smartreflex 23962306a36Sopenharmony_ci * class drivers. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_civoid omap_vp_disable(struct voltagedomain *voltdm) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct omap_vp_instance *vp; 24462306a36Sopenharmony_ci u32 vpconfig; 24562306a36Sopenharmony_ci int timeout; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (!voltdm || IS_ERR(voltdm)) { 24862306a36Sopenharmony_ci pr_warn("%s: VDD specified does not exist!\n", __func__); 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci vp = voltdm->vp; 25362306a36Sopenharmony_ci if (!voltdm->read || !voltdm->write) { 25462306a36Sopenharmony_ci pr_err("%s: No read/write API for accessing vdd_%s regs\n", 25562306a36Sopenharmony_ci __func__, voltdm->name); 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* If VP is already disabled, do nothing. Return */ 26062306a36Sopenharmony_ci if (!vp->enabled) { 26162306a36Sopenharmony_ci pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n", 26262306a36Sopenharmony_ci __func__, voltdm->name); 26362306a36Sopenharmony_ci return; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Disable VP */ 26762306a36Sopenharmony_ci vpconfig = voltdm->read(vp->vpconfig); 26862306a36Sopenharmony_ci vpconfig &= ~vp->common->vpconfig_vpenable; 26962306a36Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* 27262306a36Sopenharmony_ci * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci omap_test_timeout((voltdm->read(vp->vstatus)), 27562306a36Sopenharmony_ci VP_IDLE_TIMEOUT, timeout); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (timeout >= VP_IDLE_TIMEOUT) 27862306a36Sopenharmony_ci pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci vp->enabled = false; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci} 284