1 /* 2 * OMAP3/OMAP4 Voltage Management Routines 3 * 4 * Author: Thara Gopinath <thara@ti.com> 5 * 6 * Copyright (C) 2007 Texas Instruments, Inc. 7 * Rajendra Nayak <rnayak@ti.com> 8 * Lesly A M <x0080970@ti.com> 9 * 10 * Copyright (C) 2008, 2011 Nokia Corporation 11 * Kalle Jokiniemi 12 * Paul Walmsley 13 * 14 * Copyright (C) 2010 Texas Instruments, Inc. 15 * Thara Gopinath <thara@ti.com> 16 * 17 * This program is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License version 2 as 19 * published by the Free Software Foundation. 20 */ 21 22 #include <linux/delay.h> 23 #include <linux/io.h> 24 #include <linux/err.h> 25 #include <linux/export.h> 26 #include <linux/debugfs.h> 27 #include <linux/slab.h> 28 #include <linux/clk.h> 29 30 #include "common.h" 31 32 #include "prm-regbits-34xx.h" 33 #include "prm-regbits-44xx.h" 34 #include "prm44xx.h" 35 #include "prcm44xx.h" 36 #include "prminst44xx.h" 37 #include "control.h" 38 39 #include "voltage.h" 40 #include "powerdomain.h" 41 42 #include "vc.h" 43 #include "vp.h" 44 45 static LIST_HEAD(voltdm_list); 46 47 /* Public functions */ 48 /** 49 * voltdm_get_voltage() - Gets the current non-auto-compensated voltage 50 * @voltdm: pointer to the voltdm for which current voltage info is needed 51 * 52 * API to get the current non-auto-compensated voltage for a voltage domain. 53 * Returns 0 in case of error else returns the current voltage. 54 */ 55 unsigned long voltdm_get_voltage(struct voltagedomain *voltdm) 56 { 57 if (!voltdm || IS_ERR(voltdm)) { 58 pr_warning("%s: VDD specified does not exist!\n", __func__); 59 return 0; 60 } 61 62 return voltdm->nominal_volt; 63 } 64 65 /** 66 * voltdm_scale() - API to scale voltage of a particular voltage domain. 67 * @voltdm: pointer to the voltage domain which is to be scaled. 68 * @target_volt: The target voltage of the voltage domain 69 * 70 * This API should be called by the kernel to do the voltage scaling 71 * for a particular voltage domain during DVFS. 72 */ 73 int voltdm_scale(struct voltagedomain *voltdm, 74 unsigned long target_volt) 75 { 76 int ret, i; 77 unsigned long volt = 0; 78 79 if (!voltdm || IS_ERR(voltdm)) { 80 pr_warning("%s: VDD specified does not exist!\n", __func__); 81 return -EINVAL; 82 } 83 84 if (!voltdm->scale) { 85 pr_err("%s: No voltage scale API registered for vdd_%s\n", 86 __func__, voltdm->name); 87 return -ENODATA; 88 } 89 90 /* Adjust voltage to the exact voltage from the OPP table */ 91 for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { 92 if (voltdm->volt_data[i].volt_nominal >= target_volt) { 93 volt = voltdm->volt_data[i].volt_nominal; 94 break; 95 } 96 } 97 98 if (!volt) { 99 pr_warning("%s: not scaling. OPP voltage for %lu, not found.\n", 100 __func__, target_volt); 101 return -EINVAL; 102 } 103 104 ret = voltdm->scale(voltdm, volt); 105 if (!ret) 106 voltdm->nominal_volt = volt; 107 108 return ret; 109 } 110 111 /** 112 * voltdm_reset() - Resets the voltage of a particular voltage domain 113 * to that of the current OPP. 114 * @voltdm: pointer to the voltage domain whose voltage is to be reset. 115 * 116 * This API finds out the correct voltage the voltage domain is supposed 117 * to be at and resets the voltage to that level. Should be used especially 118 * while disabling any voltage compensation modules. 119 */ 120 void voltdm_reset(struct voltagedomain *voltdm) 121 { 122 unsigned long target_volt; 123 124 if (!voltdm || IS_ERR(voltdm)) { 125 pr_warning("%s: VDD specified does not exist!\n", __func__); 126 return; 127 } 128 129 target_volt = voltdm_get_voltage(voltdm); 130 if (!target_volt) { 131 pr_err("%s: unable to find current voltage for vdd_%s\n", 132 __func__, voltdm->name); 133 return; 134 } 135 136 voltdm_scale(voltdm, target_volt); 137 } 138 139 /** 140 * omap_voltage_get_volttable() - API to get the voltage table associated with a 141 * particular voltage domain. 142 * @voltdm: pointer to the VDD for which the voltage table is required 143 * @volt_data: the voltage table for the particular vdd which is to be 144 * populated by this API 145 * 146 * This API populates the voltage table associated with a VDD into the 147 * passed parameter pointer. Returns the count of distinct voltages 148 * supported by this vdd. 149 * 150 */ 151 void omap_voltage_get_volttable(struct voltagedomain *voltdm, 152 struct omap_volt_data **volt_data) 153 { 154 if (!voltdm || IS_ERR(voltdm)) { 155 pr_warning("%s: VDD specified does not exist!\n", __func__); 156 return; 157 } 158 159 *volt_data = voltdm->volt_data; 160 } 161 162 /** 163 * omap_voltage_get_voltdata() - API to get the voltage table entry for a 164 * particular voltage 165 * @voltdm: pointer to the VDD whose voltage table has to be searched 166 * @volt: the voltage to be searched in the voltage table 167 * 168 * This API searches through the voltage table for the required voltage 169 * domain and tries to find a matching entry for the passed voltage volt. 170 * If a matching entry is found volt_data is populated with that entry. 171 * This API searches only through the non-compensated voltages int the 172 * voltage table. 173 * Returns pointer to the voltage table entry corresponding to volt on 174 * success. Returns -ENODATA if no voltage table exisits for the passed voltage 175 * domain or if there is no matching entry. 176 */ 177 struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, 178 unsigned long volt) 179 { 180 int i; 181 182 if (!voltdm || IS_ERR(voltdm)) { 183 pr_warning("%s: VDD specified does not exist!\n", __func__); 184 return ERR_PTR(-EINVAL); 185 } 186 187 if (!voltdm->volt_data) { 188 pr_warning("%s: voltage table does not exist for vdd_%s\n", 189 __func__, voltdm->name); 190 return ERR_PTR(-ENODATA); 191 } 192 193 for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { 194 if (voltdm->volt_data[i].volt_nominal == volt) 195 return &voltdm->volt_data[i]; 196 } 197 198 pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n", 199 __func__, voltdm->name); 200 201 return ERR_PTR(-ENODATA); 202 } 203 204 /** 205 * omap_voltage_register_pmic() - API to register PMIC specific data 206 * @voltdm: pointer to the VDD for which the PMIC specific data is 207 * to be registered 208 * @pmic: the structure containing pmic info 209 * 210 * This API is to be called by the SOC/PMIC file to specify the 211 * pmic specific info as present in omap_voltdm_pmic structure. 212 */ 213 int omap_voltage_register_pmic(struct voltagedomain *voltdm, 214 struct omap_voltdm_pmic *pmic) 215 { 216 if (!voltdm || IS_ERR(voltdm)) { 217 pr_warning("%s: VDD specified does not exist!\n", __func__); 218 return -EINVAL; 219 } 220 221 voltdm->pmic = pmic; 222 223 return 0; 224 } 225 226 /** 227 * omap_change_voltscale_method() - API to change the voltage scaling method. 228 * @voltdm: pointer to the VDD whose voltage scaling method 229 * has to be changed. 230 * @voltscale_method: the method to be used for voltage scaling. 231 * 232 * This API can be used by the board files to change the method of voltage 233 * scaling between vpforceupdate and vcbypass. The parameter values are 234 * defined in voltage.h 235 */ 236 void omap_change_voltscale_method(struct voltagedomain *voltdm, 237 int voltscale_method) 238 { 239 if (!voltdm || IS_ERR(voltdm)) { 240 pr_warning("%s: VDD specified does not exist!\n", __func__); 241 return; 242 } 243 244 switch (voltscale_method) { 245 case VOLTSCALE_VPFORCEUPDATE: 246 voltdm->scale = omap_vp_forceupdate_scale; 247 return; 248 case VOLTSCALE_VCBYPASS: 249 voltdm->scale = omap_vc_bypass_scale; 250 return; 251 default: 252 pr_warn("%s: Trying to change the method of voltage scaling to an unsupported one!\n", 253 __func__); 254 } 255 } 256 257 /** 258 * omap_voltage_late_init() - Init the various voltage parameters 259 * 260 * This API is to be called in the later stages of the 261 * system boot to init the voltage controller and 262 * voltage processors. 263 */ 264 int __init omap_voltage_late_init(void) 265 { 266 struct voltagedomain *voltdm; 267 268 if (list_empty(&voltdm_list)) { 269 pr_err("%s: Voltage driver support not added\n", 270 __func__); 271 return -EINVAL; 272 } 273 274 list_for_each_entry(voltdm, &voltdm_list, node) { 275 struct clk *sys_ck; 276 277 if (!voltdm->scalable) 278 continue; 279 280 sys_ck = clk_get(NULL, voltdm->sys_clk.name); 281 if (IS_ERR(sys_ck)) { 282 pr_warning("%s: Could not get sys clk.\n", __func__); 283 return -EINVAL; 284 } 285 voltdm->sys_clk.rate = clk_get_rate(sys_ck); 286 WARN_ON(!voltdm->sys_clk.rate); 287 clk_put(sys_ck); 288 289 if (voltdm->vc) { 290 voltdm->scale = omap_vc_bypass_scale; 291 omap_vc_init_channel(voltdm); 292 } 293 294 if (voltdm->vp) { 295 voltdm->scale = omap_vp_forceupdate_scale; 296 omap_vp_init(voltdm); 297 } 298 } 299 300 return 0; 301 } 302 303 static struct voltagedomain *_voltdm_lookup(const char *name) 304 { 305 struct voltagedomain *voltdm, *temp_voltdm; 306 307 voltdm = NULL; 308 309 list_for_each_entry(temp_voltdm, &voltdm_list, node) { 310 if (!strcmp(name, temp_voltdm->name)) { 311 voltdm = temp_voltdm; 312 break; 313 } 314 } 315 316 return voltdm; 317 } 318 319 /** 320 * voltdm_add_pwrdm - add a powerdomain to a voltagedomain 321 * @voltdm: struct voltagedomain * to add the powerdomain to 322 * @pwrdm: struct powerdomain * to associate with a voltagedomain 323 * 324 * Associate the powerdomain @pwrdm with a voltagedomain @voltdm. This 325 * enables the use of voltdm_for_each_pwrdm(). Returns -EINVAL if 326 * presented with invalid pointers; -ENOMEM if memory could not be allocated; 327 * or 0 upon success. 328 */ 329 int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm) 330 { 331 if (!voltdm || !pwrdm) 332 return -EINVAL; 333 334 pr_debug("voltagedomain: %s: associating powerdomain %s\n", 335 voltdm->name, pwrdm->name); 336 337 list_add(&pwrdm->voltdm_node, &voltdm->pwrdm_list); 338 339 return 0; 340 } 341 342 /** 343 * voltdm_for_each_pwrdm - call function for each pwrdm in a voltdm 344 * @voltdm: struct voltagedomain * to iterate over 345 * @fn: callback function * 346 * 347 * Call the supplied function @fn for each powerdomain in the 348 * voltagedomain @voltdm. Returns -EINVAL if presented with invalid 349 * pointers; or passes along the last return value of the callback 350 * function, which should be 0 for success or anything else to 351 * indicate failure. 352 */ 353 int voltdm_for_each_pwrdm(struct voltagedomain *voltdm, 354 int (*fn)(struct voltagedomain *voltdm, 355 struct powerdomain *pwrdm)) 356 { 357 struct powerdomain *pwrdm; 358 int ret = 0; 359 360 if (!fn) 361 return -EINVAL; 362 363 list_for_each_entry(pwrdm, &voltdm->pwrdm_list, voltdm_node) 364 ret = (*fn)(voltdm, pwrdm); 365 366 return ret; 367 } 368 369 /** 370 * voltdm_for_each - call function on each registered voltagedomain 371 * @fn: callback function * 372 * 373 * Call the supplied function @fn for each registered voltagedomain. 374 * The callback function @fn can return anything but 0 to bail out 375 * early from the iterator. Returns the last return value of the 376 * callback function, which should be 0 for success or anything else 377 * to indicate failure; or -EINVAL if the function pointer is null. 378 */ 379 int voltdm_for_each(int (*fn)(struct voltagedomain *voltdm, void *user), 380 void *user) 381 { 382 struct voltagedomain *temp_voltdm; 383 int ret = 0; 384 385 if (!fn) 386 return -EINVAL; 387 388 list_for_each_entry(temp_voltdm, &voltdm_list, node) { 389 ret = (*fn)(temp_voltdm, user); 390 if (ret) 391 break; 392 } 393 394 return ret; 395 } 396 397 static int _voltdm_register(struct voltagedomain *voltdm) 398 { 399 if (!voltdm || !voltdm->name) 400 return -EINVAL; 401 402 INIT_LIST_HEAD(&voltdm->pwrdm_list); 403 list_add(&voltdm->node, &voltdm_list); 404 405 pr_debug("voltagedomain: registered %s\n", voltdm->name); 406 407 return 0; 408 } 409 410 /** 411 * voltdm_lookup - look up a voltagedomain by name, return a pointer 412 * @name: name of voltagedomain 413 * 414 * Find a registered voltagedomain by its name @name. Returns a pointer 415 * to the struct voltagedomain if found, or NULL otherwise. 416 */ 417 struct voltagedomain *voltdm_lookup(const char *name) 418 { 419 struct voltagedomain *voltdm ; 420 421 if (!name) 422 return NULL; 423 424 voltdm = _voltdm_lookup(name); 425 426 return voltdm; 427 } 428 429 /** 430 * voltdm_init - set up the voltagedomain layer 431 * @voltdm_list: array of struct voltagedomain pointers to register 432 * 433 * Loop through the array of voltagedomains @voltdm_list, registering all 434 * that are available on the current CPU. If voltdm_list is supplied 435 * and not null, all of the referenced voltagedomains will be 436 * registered. No return value. 437 */ 438 void voltdm_init(struct voltagedomain **voltdms) 439 { 440 struct voltagedomain **v; 441 442 if (voltdms) { 443 for (v = voltdms; *v; v++) 444 _voltdm_register(*v); 445 } 446 } 447