1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * OMAP3/OMAP4 Voltage Management Routines 4 * 5 * Author: Thara Gopinath <thara@ti.com> 6 * 7 * Copyright (C) 2007 Texas Instruments, Inc. 8 * Rajendra Nayak <rnayak@ti.com> 9 * Lesly A M <x0080970@ti.com> 10 * 11 * Copyright (C) 2008, 2011 Nokia Corporation 12 * Kalle Jokiniemi 13 * Paul Walmsley 14 * 15 * Copyright (C) 2010 Texas Instruments, Inc. 16 * Thara Gopinath <thara@ti.com> 17 */ 18 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 #include <linux/err.h> 22 #include <linux/export.h> 23 #include <linux/debugfs.h> 24 #include <linux/slab.h> 25 #include <linux/clk.h> 26 27 #include "common.h" 28 29 #include "prm-regbits-34xx.h" 30 #include "prm-regbits-44xx.h" 31 #include "prm44xx.h" 32 #include "prcm44xx.h" 33 #include "prminst44xx.h" 34 #include "control.h" 35 36 #include "voltage.h" 37 #include "powerdomain.h" 38 39 #include "vc.h" 40 #include "vp.h" 41 42 static LIST_HEAD(voltdm_list); 43 44 /* Public functions */ 45 /** 46 * voltdm_get_voltage() - Gets the current non-auto-compensated voltage 47 * @voltdm: pointer to the voltdm for which current voltage info is needed 48 * 49 * API to get the current non-auto-compensated voltage for a voltage domain. 50 * Returns 0 in case of error else returns the current voltage. 51 */ 52 unsigned long voltdm_get_voltage(struct voltagedomain *voltdm) 53 { 54 if (!voltdm || IS_ERR(voltdm)) { 55 pr_warn("%s: VDD specified does not exist!\n", __func__); 56 return 0; 57 } 58 59 return voltdm->nominal_volt; 60 } 61 62 /** 63 * voltdm_scale() - API to scale voltage of a particular voltage domain. 64 * @voltdm: pointer to the voltage domain which is to be scaled. 65 * @target_volt: The target voltage of the voltage domain 66 * 67 * This API should be called by the kernel to do the voltage scaling 68 * for a particular voltage domain during DVFS. 69 */ 70 int voltdm_scale(struct voltagedomain *voltdm, 71 unsigned long target_volt) 72 { 73 int ret, i; 74 unsigned long volt = 0; 75 76 if (!voltdm || IS_ERR(voltdm)) { 77 pr_warn("%s: VDD specified does not exist!\n", __func__); 78 return -EINVAL; 79 } 80 81 if (!voltdm->scale) { 82 pr_err("%s: No voltage scale API registered for vdd_%s\n", 83 __func__, voltdm->name); 84 return -ENODATA; 85 } 86 87 if (!voltdm->volt_data) { 88 pr_err("%s: No voltage data defined for vdd_%s\n", 89 __func__, voltdm->name); 90 return -ENODATA; 91 } 92 93 /* Adjust voltage to the exact voltage from the OPP table */ 94 for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { 95 if (voltdm->volt_data[i].volt_nominal >= target_volt) { 96 volt = voltdm->volt_data[i].volt_nominal; 97 break; 98 } 99 } 100 101 if (!volt) { 102 pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n", 103 __func__, target_volt); 104 return -EINVAL; 105 } 106 107 ret = voltdm->scale(voltdm, volt); 108 if (!ret) 109 voltdm->nominal_volt = volt; 110 111 return ret; 112 } 113 114 /** 115 * voltdm_reset() - Resets the voltage of a particular voltage domain 116 * to that of the current OPP. 117 * @voltdm: pointer to the voltage domain whose voltage is to be reset. 118 * 119 * This API finds out the correct voltage the voltage domain is supposed 120 * to be at and resets the voltage to that level. Should be used especially 121 * while disabling any voltage compensation modules. 122 */ 123 void voltdm_reset(struct voltagedomain *voltdm) 124 { 125 unsigned long target_volt; 126 127 if (!voltdm || IS_ERR(voltdm)) { 128 pr_warn("%s: VDD specified does not exist!\n", __func__); 129 return; 130 } 131 132 target_volt = voltdm_get_voltage(voltdm); 133 if (!target_volt) { 134 pr_err("%s: unable to find current voltage for vdd_%s\n", 135 __func__, voltdm->name); 136 return; 137 } 138 139 voltdm_scale(voltdm, target_volt); 140 } 141 142 /** 143 * omap_voltage_get_volttable() - API to get the voltage table associated with a 144 * particular voltage domain. 145 * @voltdm: pointer to the VDD for which the voltage table is required 146 * @volt_data: the voltage table for the particular vdd which is to be 147 * populated by this API 148 * 149 * This API populates the voltage table associated with a VDD into the 150 * passed parameter pointer. Returns the count of distinct voltages 151 * supported by this vdd. 152 * 153 */ 154 void omap_voltage_get_volttable(struct voltagedomain *voltdm, 155 struct omap_volt_data **volt_data) 156 { 157 if (!voltdm || IS_ERR(voltdm)) { 158 pr_warn("%s: VDD specified does not exist!\n", __func__); 159 return; 160 } 161 162 *volt_data = voltdm->volt_data; 163 } 164 165 /** 166 * omap_voltage_get_voltdata() - API to get the voltage table entry for a 167 * particular voltage 168 * @voltdm: pointer to the VDD whose voltage table has to be searched 169 * @volt: the voltage to be searched in the voltage table 170 * 171 * This API searches through the voltage table for the required voltage 172 * domain and tries to find a matching entry for the passed voltage volt. 173 * If a matching entry is found volt_data is populated with that entry. 174 * This API searches only through the non-compensated voltages int the 175 * voltage table. 176 * Returns pointer to the voltage table entry corresponding to volt on 177 * success. Returns -ENODATA if no voltage table exisits for the passed voltage 178 * domain or if there is no matching entry. 179 */ 180 struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, 181 unsigned long volt) 182 { 183 int i; 184 185 if (!voltdm || IS_ERR(voltdm)) { 186 pr_warn("%s: VDD specified does not exist!\n", __func__); 187 return ERR_PTR(-EINVAL); 188 } 189 190 if (!voltdm->volt_data) { 191 pr_warn("%s: voltage table does not exist for vdd_%s\n", 192 __func__, voltdm->name); 193 return ERR_PTR(-ENODATA); 194 } 195 196 for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { 197 if (voltdm->volt_data[i].volt_nominal == volt) 198 return &voltdm->volt_data[i]; 199 } 200 201 pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n", 202 __func__, voltdm->name); 203 204 return ERR_PTR(-ENODATA); 205 } 206 207 /** 208 * omap_voltage_register_pmic() - API to register PMIC specific data 209 * @voltdm: pointer to the VDD for which the PMIC specific data is 210 * to be registered 211 * @pmic: the structure containing pmic info 212 * 213 * This API is to be called by the SOC/PMIC file to specify the 214 * pmic specific info as present in omap_voltdm_pmic structure. 215 */ 216 int omap_voltage_register_pmic(struct voltagedomain *voltdm, 217 struct omap_voltdm_pmic *pmic) 218 { 219 if (!voltdm || IS_ERR(voltdm)) { 220 pr_warn("%s: VDD specified does not exist!\n", __func__); 221 return -EINVAL; 222 } 223 224 voltdm->pmic = pmic; 225 226 return 0; 227 } 228 229 /** 230 * omap_voltage_late_init() - Init the various voltage parameters 231 * 232 * This API is to be called in the later stages of the 233 * system boot to init the voltage controller and 234 * voltage processors. 235 */ 236 int __init omap_voltage_late_init(void) 237 { 238 struct voltagedomain *voltdm; 239 240 if (list_empty(&voltdm_list)) { 241 pr_err("%s: Voltage driver support not added\n", 242 __func__); 243 return -EINVAL; 244 } 245 246 list_for_each_entry(voltdm, &voltdm_list, node) { 247 struct clk *sys_ck; 248 249 if (!voltdm->scalable) 250 continue; 251 252 sys_ck = clk_get(NULL, voltdm->sys_clk.name); 253 if (IS_ERR(sys_ck)) { 254 pr_warn("%s: Could not get sys clk.\n", __func__); 255 return -EINVAL; 256 } 257 voltdm->sys_clk.rate = clk_get_rate(sys_ck); 258 WARN_ON(!voltdm->sys_clk.rate); 259 clk_put(sys_ck); 260 261 if (voltdm->vc) { 262 voltdm->scale = omap_vc_bypass_scale; 263 omap_vc_init_channel(voltdm); 264 } 265 266 if (voltdm->vp) { 267 voltdm->scale = omap_vp_forceupdate_scale; 268 omap_vp_init(voltdm); 269 } 270 } 271 272 return 0; 273 } 274 275 static struct voltagedomain *_voltdm_lookup(const char *name) 276 { 277 struct voltagedomain *voltdm, *temp_voltdm; 278 279 voltdm = NULL; 280 281 list_for_each_entry(temp_voltdm, &voltdm_list, node) { 282 if (!strcmp(name, temp_voltdm->name)) { 283 voltdm = temp_voltdm; 284 break; 285 } 286 } 287 288 return voltdm; 289 } 290 291 static int _voltdm_register(struct voltagedomain *voltdm) 292 { 293 if (!voltdm || !voltdm->name) 294 return -EINVAL; 295 296 list_add(&voltdm->node, &voltdm_list); 297 298 pr_debug("voltagedomain: registered %s\n", voltdm->name); 299 300 return 0; 301 } 302 303 /** 304 * voltdm_lookup - look up a voltagedomain by name, return a pointer 305 * @name: name of voltagedomain 306 * 307 * Find a registered voltagedomain by its name @name. Returns a pointer 308 * to the struct voltagedomain if found, or NULL otherwise. 309 */ 310 struct voltagedomain *voltdm_lookup(const char *name) 311 { 312 struct voltagedomain *voltdm ; 313 314 if (!name) 315 return NULL; 316 317 voltdm = _voltdm_lookup(name); 318 319 return voltdm; 320 } 321 322 /** 323 * voltdm_init - set up the voltagedomain layer 324 * @voltdm_list: array of struct voltagedomain pointers to register 325 * 326 * Loop through the array of voltagedomains @voltdm_list, registering all 327 * that are available on the current CPU. If voltdm_list is supplied 328 * and not null, all of the referenced voltagedomains will be 329 * registered. No return value. 330 */ 331 void voltdm_init(struct voltagedomain **voltdms) 332 { 333 struct voltagedomain **v; 334 335 if (voltdms) { 336 for (v = voltdms; *v; v++) 337 _voltdm_register(*v); 338 } 339 } 340