1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/kernel.h> 3 #include <linux/init.h> 4 5 #include "common.h" 6 7 #include "voltage.h" 8 #include "vp.h" 9 #include "prm-regbits-34xx.h" 10 #include "prm-regbits-44xx.h" 11 #include "prm44xx.h" 12 13 static u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt) 14 { 15 struct omap_vp_instance *vp = voltdm->vp; 16 u32 vpconfig; 17 char vsel; 18 19 vsel = voltdm->pmic->uv_to_vsel(volt); 20 21 vpconfig = voltdm->read(vp->vpconfig); 22 vpconfig &= ~(vp->common->vpconfig_initvoltage_mask | 23 vp->common->vpconfig_forceupdate | 24 vp->common->vpconfig_initvdd); 25 vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask); 26 voltdm->write(vpconfig, vp->vpconfig); 27 28 /* Trigger initVDD value copy to voltage processor */ 29 voltdm->write((vpconfig | vp->common->vpconfig_initvdd), 30 vp->vpconfig); 31 32 /* Clear initVDD copy trigger bit */ 33 voltdm->write(vpconfig, vp->vpconfig); 34 35 return vpconfig; 36 } 37 38 /* Generic voltage init functions */ 39 void __init omap_vp_init(struct voltagedomain *voltdm) 40 { 41 struct omap_vp_instance *vp = voltdm->vp; 42 u32 val, sys_clk_rate, timeout, waittime; 43 u32 vddmin, vddmax, vstepmin, vstepmax; 44 45 if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) { 46 pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name); 47 return; 48 } 49 50 if (!voltdm->read || !voltdm->write) { 51 pr_err("%s: No read/write API for accessing vdd_%s regs\n", 52 __func__, voltdm->name); 53 return; 54 } 55 56 vp->enabled = false; 57 58 /* Divide to avoid overflow */ 59 sys_clk_rate = voltdm->sys_clk.rate / 1000; 60 61 timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000; 62 vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin); 63 vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax); 64 vddmin = voltdm->pmic->uv_to_vsel(vddmin); 65 vddmax = voltdm->pmic->uv_to_vsel(vddmax); 66 67 waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate, 68 1000 * voltdm->pmic->slew_rate); 69 vstepmin = voltdm->pmic->vp_vstepmin; 70 vstepmax = voltdm->pmic->vp_vstepmax; 71 72 /* 73 * VP_CONFIG: error gain is not set here, it will be updated 74 * on each scale, based on OPP. 75 */ 76 val = (voltdm->pmic->vp_erroroffset << 77 __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) | 78 vp->common->vpconfig_timeouten; 79 voltdm->write(val, vp->vpconfig); 80 81 /* VSTEPMIN */ 82 val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) | 83 (vstepmin << vp->common->vstepmin_stepmin_shift); 84 voltdm->write(val, vp->vstepmin); 85 86 /* VSTEPMAX */ 87 val = (vstepmax << vp->common->vstepmax_stepmax_shift) | 88 (waittime << vp->common->vstepmax_smpswaittimemax_shift); 89 voltdm->write(val, vp->vstepmax); 90 91 /* VLIMITTO */ 92 val = (vddmax << vp->common->vlimitto_vddmax_shift) | 93 (vddmin << vp->common->vlimitto_vddmin_shift) | 94 (timeout << vp->common->vlimitto_timeout_shift); 95 voltdm->write(val, vp->vlimitto); 96 } 97 98 int omap_vp_update_errorgain(struct voltagedomain *voltdm, 99 unsigned long target_volt) 100 { 101 struct omap_volt_data *volt_data; 102 103 if (!voltdm->vp) 104 return -EINVAL; 105 106 /* Get volt_data corresponding to target_volt */ 107 volt_data = omap_voltage_get_voltdata(voltdm, target_volt); 108 if (IS_ERR(volt_data)) 109 return -EINVAL; 110 111 /* Setting vp errorgain based on the voltage */ 112 voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask, 113 volt_data->vp_errgain << 114 __ffs(voltdm->vp->common->vpconfig_errorgain_mask), 115 voltdm->vp->vpconfig); 116 117 return 0; 118 } 119 120 /* VP force update method of voltage scaling */ 121 int omap_vp_forceupdate_scale(struct voltagedomain *voltdm, 122 unsigned long target_volt) 123 { 124 struct omap_vp_instance *vp = voltdm->vp; 125 u32 vpconfig; 126 u8 target_vsel, current_vsel; 127 int ret, timeout = 0; 128 129 ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, ¤t_vsel); 130 if (ret) 131 return ret; 132 133 /* 134 * Clear all pending TransactionDone interrupt/status. Typical latency 135 * is <3us 136 */ 137 while (timeout++ < VP_TRANXDONE_TIMEOUT) { 138 vp->common->ops->clear_txdone(vp->id); 139 if (!vp->common->ops->check_txdone(vp->id)) 140 break; 141 udelay(1); 142 } 143 if (timeout >= VP_TRANXDONE_TIMEOUT) { 144 pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n", 145 __func__, voltdm->name); 146 return -ETIMEDOUT; 147 } 148 149 vpconfig = _vp_set_init_voltage(voltdm, target_volt); 150 151 /* Force update of voltage */ 152 voltdm->write(vpconfig | vp->common->vpconfig_forceupdate, 153 voltdm->vp->vpconfig); 154 155 /* 156 * Wait for TransactionDone. Typical latency is <200us. 157 * Depends on SMPSWAITTIMEMIN/MAX and voltage change 158 */ 159 timeout = 0; 160 omap_test_timeout(vp->common->ops->check_txdone(vp->id), 161 VP_TRANXDONE_TIMEOUT, timeout); 162 if (timeout >= VP_TRANXDONE_TIMEOUT) 163 pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n", 164 __func__, voltdm->name); 165 166 omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel); 167 168 /* 169 * Disable TransactionDone interrupt , clear all status, clear 170 * control registers 171 */ 172 timeout = 0; 173 while (timeout++ < VP_TRANXDONE_TIMEOUT) { 174 vp->common->ops->clear_txdone(vp->id); 175 if (!vp->common->ops->check_txdone(vp->id)) 176 break; 177 udelay(1); 178 } 179 180 if (timeout >= VP_TRANXDONE_TIMEOUT) 181 pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n", 182 __func__, voltdm->name); 183 184 /* Clear force bit */ 185 voltdm->write(vpconfig, vp->vpconfig); 186 187 return 0; 188 } 189 190 /** 191 * omap_vp_enable() - API to enable a particular VP 192 * @voltdm: pointer to the VDD whose VP is to be enabled. 193 * 194 * This API enables a particular voltage processor. Needed by the smartreflex 195 * class drivers. 196 */ 197 void omap_vp_enable(struct voltagedomain *voltdm) 198 { 199 struct omap_vp_instance *vp; 200 u32 vpconfig, volt; 201 202 if (!voltdm || IS_ERR(voltdm)) { 203 pr_warn("%s: VDD specified does not exist!\n", __func__); 204 return; 205 } 206 207 vp = voltdm->vp; 208 if (!voltdm->read || !voltdm->write) { 209 pr_err("%s: No read/write API for accessing vdd_%s regs\n", 210 __func__, voltdm->name); 211 return; 212 } 213 214 /* If VP is already enabled, do nothing. Return */ 215 if (vp->enabled) 216 return; 217 218 volt = voltdm_get_voltage(voltdm); 219 if (!volt) { 220 pr_warn("%s: unable to find current voltage for %s\n", 221 __func__, voltdm->name); 222 return; 223 } 224 225 vpconfig = _vp_set_init_voltage(voltdm, volt); 226 227 /* Enable VP */ 228 vpconfig |= vp->common->vpconfig_vpenable; 229 voltdm->write(vpconfig, vp->vpconfig); 230 231 vp->enabled = true; 232 } 233 234 /** 235 * omap_vp_disable() - API to disable a particular VP 236 * @voltdm: pointer to the VDD whose VP is to be disabled. 237 * 238 * This API disables a particular voltage processor. Needed by the smartreflex 239 * class drivers. 240 */ 241 void omap_vp_disable(struct voltagedomain *voltdm) 242 { 243 struct omap_vp_instance *vp; 244 u32 vpconfig; 245 int timeout; 246 247 if (!voltdm || IS_ERR(voltdm)) { 248 pr_warn("%s: VDD specified does not exist!\n", __func__); 249 return; 250 } 251 252 vp = voltdm->vp; 253 if (!voltdm->read || !voltdm->write) { 254 pr_err("%s: No read/write API for accessing vdd_%s regs\n", 255 __func__, voltdm->name); 256 return; 257 } 258 259 /* If VP is already disabled, do nothing. Return */ 260 if (!vp->enabled) { 261 pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n", 262 __func__, voltdm->name); 263 return; 264 } 265 266 /* Disable VP */ 267 vpconfig = voltdm->read(vp->vpconfig); 268 vpconfig &= ~vp->common->vpconfig_vpenable; 269 voltdm->write(vpconfig, vp->vpconfig); 270 271 /* 272 * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us 273 */ 274 omap_test_timeout((voltdm->read(vp->vstatus)), 275 VP_IDLE_TIMEOUT, timeout); 276 277 if (timeout >= VP_IDLE_TIMEOUT) 278 pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name); 279 280 vp->enabled = false; 281 282 return; 283 } 284
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.