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_warn("%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_warn("%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 if (!voltdm->volt_data) { 91 pr_err("%s: No voltage data defined for vdd_%s\n", 92 __func__, voltdm->name); 93 return -ENODATA; 94 } 95 96 /* Adjust voltage to the exact voltage from the OPP table */ 97 for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { 98 if (voltdm->volt_data[i].volt_nominal >= target_volt) { 99 volt = voltdm->volt_data[i].volt_nominal; 100 break; 101 } 102 } 103 104 if (!volt) { 105 pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n", 106 __func__, target_volt); 107 return -EINVAL; 108 } 109 110 ret = voltdm->scale(voltdm, volt); 111 if (!ret) 112 voltdm->nominal_volt = volt; 113 114 return ret; 115 } 116 117 /** 118 * voltdm_reset() - Resets the voltage of a particular voltage domain 119 * to that of the current OPP. 120 * @voltdm: pointer to the voltage domain whose voltage is to be reset. 121 * 122 * This API finds out the correct voltage the voltage domain is supposed 123 * to be at and resets the voltage to that level. Should be used especially 124 * while disabling any voltage compensation modules. 125 */ 126 void voltdm_reset(struct voltagedomain *voltdm) 127 { 128 unsigned long target_volt; 129 130 if (!voltdm || IS_ERR(voltdm)) { 131 pr_warn("%s: VDD specified does not exist!\n", __func__); 132 return; 133 } 134 135 target_volt = voltdm_get_voltage(voltdm); 136 if (!target_volt) { 137 pr_err("%s: unable to find current voltage for vdd_%s\n", 138 __func__, voltdm->name); 139 return; 140 } 141 142 voltdm_scale(voltdm, target_volt); 143 } 144 145 /** 146 * omap_voltage_get_volttable() - API to get the voltage table associated with a 147 * particular voltage domain. 148 * @voltdm: pointer to the VDD for which the voltage table is required 149 * @volt_data: the voltage table for the particular vdd which is to be 150 * populated by this API 151 * 152 * This API populates the voltage table associated with a VDD into the 153 * passed parameter pointer. Returns the count of distinct voltages 154 * supported by this vdd. 155 * 156 */ 157 void omap_voltage_get_volttable(struct voltagedomain *voltdm, 158 struct omap_volt_data **volt_data) 159 { 160 if (!voltdm || IS_ERR(voltdm)) { 161 pr_warn("%s: VDD specified does not exist!\n", __func__); 162 return; 163 } 164 165 *volt_data = voltdm->volt_data; 166 } 167 168 /** 169 * omap_voltage_get_voltdata() - API to get the voltage table entry for a 170 * particular voltage 171 * @voltdm: pointer to the VDD whose voltage table has to be searched 172 * @volt: the voltage to be searched in the voltage table 173 * 174 * This API searches through the voltage table for the required voltage 175 * domain and tries to find a matching entry for the passed voltage volt. 176 * If a matching entry is found volt_data is populated with that entry. 177 * This API searches only through the non-compensated voltages int the 178 * voltage table. 179 * Returns pointer to the voltage table entry corresponding to volt on 180 * success. Returns -ENODATA if no voltage table exisits for the passed voltage 181 * domain or if there is no matching entry. 182 */ 183 struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, 184 unsigned long volt) 185 { 186 int i; 187 188 if (!voltdm || IS_ERR(voltdm)) { 189 pr_warn("%s: VDD specified does not exist!\n", __func__); 190 return ERR_PTR(-EINVAL); 191 } 192 193 if (!voltdm->volt_data) { 194 pr_warn("%s: voltage table does not exist for vdd_%s\n", 195 __func__, voltdm->name); 196 return ERR_PTR(-ENODATA); 197 } 198 199 for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { 200 if (voltdm->volt_data[i].volt_nominal == volt) 201 return &voltdm->volt_data[i]; 202 } 203 204 pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n", 205 __func__, voltdm->name); 206 207 return ERR_PTR(-ENODATA); 208 } 209 210 /** 211 * omap_voltage_register_pmic() - API to register PMIC specific data 212 * @voltdm: pointer to the VDD for which the PMIC specific data is 213 * to be registered 214 * @pmic: the structure containing pmic info 215 * 216 * This API is to be called by the SOC/PMIC file to specify the 217 * pmic specific info as present in omap_voltdm_pmic structure. 218 */ 219 int omap_voltage_register_pmic(struct voltagedomain *voltdm, 220 struct omap_voltdm_pmic *pmic) 221 { 222 if (!voltdm || IS_ERR(voltdm)) { 223 pr_warn("%s: VDD specified does not exist!\n", __func__); 224 return -EINVAL; 225 } 226 227 voltdm->pmic = pmic; 228 229 return 0; 230 } 231 232 /** 233 * omap_voltage_late_init() - Init the various voltage parameters 234 * 235 * This API is to be called in the later stages of the 236 * system boot to init the voltage controller and 237 * voltage processors. 238 */ 239 int __init omap_voltage_late_init(void) 240 { 241 struct voltagedomain *voltdm; 242 243 if (list_empty(&voltdm_list)) { 244 pr_err("%s: Voltage driver support not added\n", 245 __func__); 246 return -EINVAL; 247 } 248 249 list_for_each_entry(voltdm, &voltdm_list, node) { 250 struct clk *sys_ck; 251 252 if (!voltdm->scalable) 253 continue; 254 255 sys_ck = clk_get(NULL, voltdm->sys_clk.name); 256 if (IS_ERR(sys_ck)) { 257 pr_warn("%s: Could not get sys clk.\n", __func__); 258 return -EINVAL; 259 } 260 voltdm->sys_clk.rate = clk_get_rate(sys_ck); 261 WARN_ON(!voltdm->sys_clk.rate); 262 clk_put(sys_ck); 263 264 if (voltdm->vc) { 265 voltdm->scale = omap_vc_bypass_scale; 266 omap_vc_init_channel(voltdm); 267 } 268 269 if (voltdm->vp) { 270 voltdm->scale = omap_vp_forceupdate_scale; 271 omap_vp_init(voltdm); 272 } 273 } 274 275 return 0; 276 } 277 278 static struct voltagedomain *_voltdm_lookup(const char *name) 279 { 280 struct voltagedomain *voltdm, *temp_voltdm; 281 282 voltdm = NULL; 283 284 list_for_each_entry(temp_voltdm, &voltdm_list, node) { 285 if (!strcmp(name, temp_voltdm->name)) { 286 voltdm = temp_voltdm; 287 break; 288 } 289 } 290 291 return voltdm; 292 } 293 294 static int _voltdm_register(struct voltagedomain *voltdm) 295 { 296 if (!voltdm || !voltdm->name) 297 return -EINVAL; 298 299 list_add(&voltdm->node, &voltdm_list); 300 301 pr_debug("voltagedomain: registered %s\n", voltdm->name); 302 303 return 0; 304 } 305 306 /** 307 * voltdm_lookup - look up a voltagedomain by name, return a pointer 308 * @name: name of voltagedomain 309 * 310 * Find a registered voltagedomain by its name @name. Returns a pointer 311 * to the struct voltagedomain if found, or NULL otherwise. 312 */ 313 struct voltagedomain *voltdm_lookup(const char *name) 314 { 315 struct voltagedomain *voltdm ; 316 317 if (!name) 318 return NULL; 319 320 voltdm = _voltdm_lookup(name); 321 322 return voltdm; 323 } 324 325 /** 326 * voltdm_init - set up the voltagedomain layer 327 * @voltdm_list: array of struct voltagedomain pointers to register 328 * 329 * Loop through the array of voltagedomains @voltdm_list, registering all 330 * that are available on the current CPU. If voltdm_list is supplied 331 * and not null, all of the referenced voltagedomains will be 332 * registered. No return value. 333 */ 334 void voltdm_init(struct voltagedomain **voltdms) 335 { 336 struct voltagedomain **v; 337 338 if (voltdms) { 339 for (v = voltdms; *v; v++) 340 _voltdm_register(*v); 341 } 342 } 343