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