18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/kernel.h> 38c2ecf20Sopenharmony_ci#include <linux/init.h> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include "common.h" 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "voltage.h" 88c2ecf20Sopenharmony_ci#include "vp.h" 98c2ecf20Sopenharmony_ci#include "prm-regbits-34xx.h" 108c2ecf20Sopenharmony_ci#include "prm-regbits-44xx.h" 118c2ecf20Sopenharmony_ci#include "prm44xx.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct omap_vp_instance *vp = voltdm->vp; 168c2ecf20Sopenharmony_ci u32 vpconfig; 178c2ecf20Sopenharmony_ci char vsel; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci vsel = voltdm->pmic->uv_to_vsel(volt); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci vpconfig = voltdm->read(vp->vpconfig); 228c2ecf20Sopenharmony_ci vpconfig &= ~(vp->common->vpconfig_initvoltage_mask | 238c2ecf20Sopenharmony_ci vp->common->vpconfig_forceupdate | 248c2ecf20Sopenharmony_ci vp->common->vpconfig_initvdd); 258c2ecf20Sopenharmony_ci vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask); 268c2ecf20Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* Trigger initVDD value copy to voltage processor */ 298c2ecf20Sopenharmony_ci voltdm->write((vpconfig | vp->common->vpconfig_initvdd), 308c2ecf20Sopenharmony_ci vp->vpconfig); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci /* Clear initVDD copy trigger bit */ 338c2ecf20Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return vpconfig; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Generic voltage init functions */ 398c2ecf20Sopenharmony_civoid __init omap_vp_init(struct voltagedomain *voltdm) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct omap_vp_instance *vp = voltdm->vp; 428c2ecf20Sopenharmony_ci u32 val, sys_clk_rate, timeout, waittime; 438c2ecf20Sopenharmony_ci u32 vddmin, vddmax, vstepmin, vstepmax; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { 468c2ecf20Sopenharmony_ci pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); 478c2ecf20Sopenharmony_ci return; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (!voltdm->read || !voltdm->write) { 518c2ecf20Sopenharmony_ci pr_err("%s: No read/write API for accessing vdd_%s regs\n", 528c2ecf20Sopenharmony_ci __func__, voltdm->name); 538c2ecf20Sopenharmony_ci return; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci vp->enabled = false; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Divide to avoid overflow */ 598c2ecf20Sopenharmony_ci sys_clk_rate = voltdm->sys_clk.rate / 1000; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000; 628c2ecf20Sopenharmony_ci vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin); 638c2ecf20Sopenharmony_ci vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax); 648c2ecf20Sopenharmony_ci vddmin = voltdm->pmic->uv_to_vsel(vddmin); 658c2ecf20Sopenharmony_ci vddmax = voltdm->pmic->uv_to_vsel(vddmax); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate, 688c2ecf20Sopenharmony_ci 1000 * voltdm->pmic->slew_rate); 698c2ecf20Sopenharmony_ci vstepmin = voltdm->pmic->vp_vstepmin; 708c2ecf20Sopenharmony_ci vstepmax = voltdm->pmic->vp_vstepmax; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * VP_CONFIG: error gain is not set here, it will be updated 748c2ecf20Sopenharmony_ci * on each scale, based on OPP. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_ci val = (voltdm->pmic->vp_erroroffset << 778c2ecf20Sopenharmony_ci __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) | 788c2ecf20Sopenharmony_ci vp->common->vpconfig_timeouten; 798c2ecf20Sopenharmony_ci voltdm->write(val, vp->vpconfig); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* VSTEPMIN */ 828c2ecf20Sopenharmony_ci val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) | 838c2ecf20Sopenharmony_ci (vstepmin << vp->common->vstepmin_stepmin_shift); 848c2ecf20Sopenharmony_ci voltdm->write(val, vp->vstepmin); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* VSTEPMAX */ 878c2ecf20Sopenharmony_ci val = (vstepmax << vp->common->vstepmax_stepmax_shift) | 888c2ecf20Sopenharmony_ci (waittime << vp->common->vstepmax_smpswaittimemax_shift); 898c2ecf20Sopenharmony_ci voltdm->write(val, vp->vstepmax); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* VLIMITTO */ 928c2ecf20Sopenharmony_ci val = (vddmax << vp->common->vlimitto_vddmax_shift) | 938c2ecf20Sopenharmony_ci (vddmin << vp->common->vlimitto_vddmin_shift) | 948c2ecf20Sopenharmony_ci (timeout << vp->common->vlimitto_timeout_shift); 958c2ecf20Sopenharmony_ci voltdm->write(val, vp->vlimitto); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ciint omap_vp_update_errorgain(struct voltagedomain *voltdm, 998c2ecf20Sopenharmony_ci unsigned long target_volt) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct omap_volt_data *volt_data; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!voltdm->vp) 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Get volt_data corresponding to target_volt */ 1078c2ecf20Sopenharmony_ci volt_data = omap_voltage_get_voltdata(voltdm, target_volt); 1088c2ecf20Sopenharmony_ci if (IS_ERR(volt_data)) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Setting vp errorgain based on the voltage */ 1128c2ecf20Sopenharmony_ci voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask, 1138c2ecf20Sopenharmony_ci volt_data->vp_errgain << 1148c2ecf20Sopenharmony_ci __ffs(voltdm->vp->common->vpconfig_errorgain_mask), 1158c2ecf20Sopenharmony_ci voltdm->vp->vpconfig); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* VP force update method of voltage scaling */ 1218c2ecf20Sopenharmony_ciint omap_vp_forceupdate_scale(struct voltagedomain *voltdm, 1228c2ecf20Sopenharmony_ci unsigned long target_volt) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct omap_vp_instance *vp = voltdm->vp; 1258c2ecf20Sopenharmony_ci u32 vpconfig; 1268c2ecf20Sopenharmony_ci u8 target_vsel, current_vsel; 1278c2ecf20Sopenharmony_ci int ret, timeout = 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * Clear all pending TransactionDone interrupt/status. Typical latency 1358c2ecf20Sopenharmony_ci * is <3us 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci while (timeout++ < VP_TRANXDONE_TIMEOUT) { 1388c2ecf20Sopenharmony_ci vp->common->ops->clear_txdone(vp->id); 1398c2ecf20Sopenharmony_ci if (!vp->common->ops->check_txdone(vp->id)) 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci udelay(1); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (timeout >= VP_TRANXDONE_TIMEOUT) { 1448c2ecf20Sopenharmony_ci pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n", 1458c2ecf20Sopenharmony_ci __func__, voltdm->name); 1468c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci vpconfig = _vp_set_init_voltage(voltdm, target_volt); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Force update of voltage */ 1528c2ecf20Sopenharmony_ci voltdm->write(vpconfig | vp->common->vpconfig_forceupdate, 1538c2ecf20Sopenharmony_ci voltdm->vp->vpconfig); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* 1568c2ecf20Sopenharmony_ci * Wait for TransactionDone. Typical latency is <200us. 1578c2ecf20Sopenharmony_ci * Depends on SMPSWAITTIMEMIN/MAX and voltage change 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci timeout = 0; 1608c2ecf20Sopenharmony_ci omap_test_timeout(vp->common->ops->check_txdone(vp->id), 1618c2ecf20Sopenharmony_ci VP_TRANXDONE_TIMEOUT, timeout); 1628c2ecf20Sopenharmony_ci if (timeout >= VP_TRANXDONE_TIMEOUT) 1638c2ecf20Sopenharmony_ci pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n", 1648c2ecf20Sopenharmony_ci __func__, voltdm->name); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * Disable TransactionDone interrupt , clear all status, clear 1708c2ecf20Sopenharmony_ci * control registers 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci timeout = 0; 1738c2ecf20Sopenharmony_ci while (timeout++ < VP_TRANXDONE_TIMEOUT) { 1748c2ecf20Sopenharmony_ci vp->common->ops->clear_txdone(vp->id); 1758c2ecf20Sopenharmony_ci if (!vp->common->ops->check_txdone(vp->id)) 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci udelay(1); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (timeout >= VP_TRANXDONE_TIMEOUT) 1818c2ecf20Sopenharmony_ci pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n", 1828c2ecf20Sopenharmony_ci __func__, voltdm->name); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Clear force bit */ 1858c2ecf20Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * omap_vp_enable() - API to enable a particular VP 1928c2ecf20Sopenharmony_ci * @voltdm: pointer to the VDD whose VP is to be enabled. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * This API enables a particular voltage processor. Needed by the smartreflex 1958c2ecf20Sopenharmony_ci * class drivers. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_civoid omap_vp_enable(struct voltagedomain *voltdm) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct omap_vp_instance *vp; 2008c2ecf20Sopenharmony_ci u32 vpconfig, volt; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!voltdm || IS_ERR(voltdm)) { 2038c2ecf20Sopenharmony_ci pr_warn("%s: VDD specified does not exist!\n", __func__); 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci vp = voltdm->vp; 2088c2ecf20Sopenharmony_ci if (!voltdm->read || !voltdm->write) { 2098c2ecf20Sopenharmony_ci pr_err("%s: No read/write API for accessing vdd_%s regs\n", 2108c2ecf20Sopenharmony_ci __func__, voltdm->name); 2118c2ecf20Sopenharmony_ci return; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* If VP is already enabled, do nothing. Return */ 2158c2ecf20Sopenharmony_ci if (vp->enabled) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci volt = voltdm_get_voltage(voltdm); 2198c2ecf20Sopenharmony_ci if (!volt) { 2208c2ecf20Sopenharmony_ci pr_warn("%s: unable to find current voltage for %s\n", 2218c2ecf20Sopenharmony_ci __func__, voltdm->name); 2228c2ecf20Sopenharmony_ci return; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci vpconfig = _vp_set_init_voltage(voltdm, volt); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* Enable VP */ 2288c2ecf20Sopenharmony_ci vpconfig |= vp->common->vpconfig_vpenable; 2298c2ecf20Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci vp->enabled = true; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/** 2358c2ecf20Sopenharmony_ci * omap_vp_disable() - API to disable a particular VP 2368c2ecf20Sopenharmony_ci * @voltdm: pointer to the VDD whose VP is to be disabled. 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * This API disables a particular voltage processor. Needed by the smartreflex 2398c2ecf20Sopenharmony_ci * class drivers. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_civoid omap_vp_disable(struct voltagedomain *voltdm) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct omap_vp_instance *vp; 2448c2ecf20Sopenharmony_ci u32 vpconfig; 2458c2ecf20Sopenharmony_ci int timeout; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!voltdm || IS_ERR(voltdm)) { 2488c2ecf20Sopenharmony_ci pr_warn("%s: VDD specified does not exist!\n", __func__); 2498c2ecf20Sopenharmony_ci return; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci vp = voltdm->vp; 2538c2ecf20Sopenharmony_ci if (!voltdm->read || !voltdm->write) { 2548c2ecf20Sopenharmony_ci pr_err("%s: No read/write API for accessing vdd_%s regs\n", 2558c2ecf20Sopenharmony_ci __func__, voltdm->name); 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* If VP is already disabled, do nothing. Return */ 2608c2ecf20Sopenharmony_ci if (!vp->enabled) { 2618c2ecf20Sopenharmony_ci pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n", 2628c2ecf20Sopenharmony_ci __func__, voltdm->name); 2638c2ecf20Sopenharmony_ci return; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Disable VP */ 2678c2ecf20Sopenharmony_ci vpconfig = voltdm->read(vp->vpconfig); 2688c2ecf20Sopenharmony_ci vpconfig &= ~vp->common->vpconfig_vpenable; 2698c2ecf20Sopenharmony_ci voltdm->write(vpconfig, vp->vpconfig); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* 2728c2ecf20Sopenharmony_ci * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci omap_test_timeout((voltdm->read(vp->vstatus)), 2758c2ecf20Sopenharmony_ci VP_IDLE_TIMEOUT, timeout); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (timeout >= VP_IDLE_TIMEOUT) 2788c2ecf20Sopenharmony_ci pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci vp->enabled = false; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci} 284