]>
Commit | Line | Data |
---|---|---|
2f34ce81 TG |
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 | * | |
c0718df4 | 10 | * Copyright (C) 2008, 2011 Nokia Corporation |
2f34ce81 | 11 | * Kalle Jokiniemi |
c0718df4 | 12 | * Paul Walmsley |
2f34ce81 TG |
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> | |
2f34ce81 | 24 | #include <linux/err.h> |
dc28094b | 25 | #include <linux/export.h> |
2f34ce81 TG |
26 | #include <linux/debugfs.h> |
27 | #include <linux/slab.h> | |
0e2f3d9c | 28 | #include <linux/clk.h> |
2f34ce81 | 29 | |
4e65331c | 30 | #include "common.h" |
2f34ce81 TG |
31 | |
32 | #include "prm-regbits-34xx.h" | |
bd38107b TG |
33 | #include "prm-regbits-44xx.h" |
34 | #include "prm44xx.h" | |
35 | #include "prcm44xx.h" | |
36 | #include "prminst44xx.h" | |
2f34ce81 TG |
37 | #include "control.h" |
38 | ||
e1d6f472 | 39 | #include "voltage.h" |
e69c22b1 | 40 | #include "powerdomain.h" |
e1d6f472 | 41 | |
c0718df4 PW |
42 | #include "vc.h" |
43 | #include "vp.h" | |
44 | ||
81a60482 | 45 | static LIST_HEAD(voltdm_list); |
2f34ce81 | 46 | |
2f34ce81 TG |
47 | /* Public functions */ |
48 | /** | |
d5c12828 KH |
49 | * voltdm_get_voltage() - Gets the current non-auto-compensated voltage |
50 | * @voltdm: pointer to the voltdm for which current voltage info is needed | |
2f34ce81 | 51 | * |
d5c12828 KH |
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. | |
2f34ce81 | 54 | */ |
d5c12828 | 55 | unsigned long voltdm_get_voltage(struct voltagedomain *voltdm) |
2f34ce81 | 56 | { |
2f34ce81 | 57 | if (!voltdm || IS_ERR(voltdm)) { |
3d0cb73e | 58 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
59 | return 0; |
60 | } | |
61 | ||
7590f608 | 62 | return voltdm->nominal_volt; |
2f34ce81 TG |
63 | } |
64 | ||
2f34ce81 | 65 | /** |
5e5651be KH |
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 | |
2f34ce81 TG |
69 | * |
70 | * This API should be called by the kernel to do the voltage scaling | |
5e5651be | 71 | * for a particular voltage domain during DVFS. |
2f34ce81 | 72 | */ |
5e5651be KH |
73 | int voltdm_scale(struct voltagedomain *voltdm, |
74 | unsigned long target_volt) | |
2f34ce81 | 75 | { |
c15f1d84 KH |
76 | int ret, i; |
77 | unsigned long volt = 0; | |
d5c12828 | 78 | |
2f34ce81 | 79 | if (!voltdm || IS_ERR(voltdm)) { |
3d0cb73e | 80 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
81 | return -EINVAL; |
82 | } | |
83 | ||
0f01565a | 84 | if (!voltdm->scale) { |
2f34ce81 TG |
85 | pr_err("%s: No voltage scale API registered for vdd_%s\n", |
86 | __func__, voltdm->name); | |
87 | return -ENODATA; | |
88 | } | |
89 | ||
c15f1d84 KH |
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) { | |
3d0cb73e JP |
99 | pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n", |
100 | __func__, target_volt); | |
c15f1d84 KH |
101 | return -EINVAL; |
102 | } | |
103 | ||
104 | ret = voltdm->scale(voltdm, volt); | |
6a62b78d | 105 | if (!ret) |
c15f1d84 | 106 | voltdm->nominal_volt = volt; |
6a62b78d KH |
107 | |
108 | return ret; | |
2f34ce81 TG |
109 | } |
110 | ||
111 | /** | |
5e5651be KH |
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. | |
2f34ce81 TG |
115 | * |
116 | * This API finds out the correct voltage the voltage domain is supposed | |
25985edc | 117 | * to be at and resets the voltage to that level. Should be used especially |
2f34ce81 TG |
118 | * while disabling any voltage compensation modules. |
119 | */ | |
5e5651be | 120 | void voltdm_reset(struct voltagedomain *voltdm) |
2f34ce81 | 121 | { |
5e5651be | 122 | unsigned long target_volt; |
2f34ce81 TG |
123 | |
124 | if (!voltdm || IS_ERR(voltdm)) { | |
3d0cb73e | 125 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
126 | return; |
127 | } | |
128 | ||
d5c12828 | 129 | target_volt = voltdm_get_voltage(voltdm); |
5e5651be | 130 | if (!target_volt) { |
2f34ce81 TG |
131 | pr_err("%s: unable to find current voltage for vdd_%s\n", |
132 | __func__, voltdm->name); | |
133 | return; | |
134 | } | |
135 | ||
5e5651be | 136 | voltdm_scale(voltdm, target_volt); |
2f34ce81 TG |
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, | |
e3277880 | 152 | struct omap_volt_data **volt_data) |
2f34ce81 | 153 | { |
2f34ce81 | 154 | if (!voltdm || IS_ERR(voltdm)) { |
3d0cb73e | 155 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
156 | return; |
157 | } | |
158 | ||
e3277880 | 159 | *volt_data = voltdm->volt_data; |
2f34ce81 TG |
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 | |
25985edc | 174 | * success. Returns -ENODATA if no voltage table exisits for the passed voltage |
2f34ce81 TG |
175 | * domain or if there is no matching entry. |
176 | */ | |
177 | struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, | |
e3277880 | 178 | unsigned long volt) |
2f34ce81 | 179 | { |
2f34ce81 TG |
180 | int i; |
181 | ||
182 | if (!voltdm || IS_ERR(voltdm)) { | |
3d0cb73e | 183 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
184 | return ERR_PTR(-EINVAL); |
185 | } | |
186 | ||
e3277880 | 187 | if (!voltdm->volt_data) { |
3d0cb73e | 188 | pr_warn("%s: voltage table does not exist for vdd_%s\n", |
2f34ce81 TG |
189 | __func__, voltdm->name); |
190 | return ERR_PTR(-ENODATA); | |
191 | } | |
192 | ||
e3277880 KH |
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]; | |
2f34ce81 TG |
196 | } |
197 | ||
7852ec05 PW |
198 | pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n", |
199 | __func__, voltdm->name); | |
2f34ce81 TG |
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 | |
ce8ebe0d | 208 | * @pmic: the structure containing pmic info |
2f34ce81 TG |
209 | * |
210 | * This API is to be called by the SOC/PMIC file to specify the | |
ce8ebe0d | 211 | * pmic specific info as present in omap_voltdm_pmic structure. |
2f34ce81 TG |
212 | */ |
213 | int omap_voltage_register_pmic(struct voltagedomain *voltdm, | |
ce8ebe0d | 214 | struct omap_voltdm_pmic *pmic) |
2f34ce81 | 215 | { |
2f34ce81 | 216 | if (!voltdm || IS_ERR(voltdm)) { |
3d0cb73e | 217 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
218 | return -EINVAL; |
219 | } | |
220 | ||
ce8ebe0d | 221 | voltdm->pmic = pmic; |
2f34ce81 TG |
222 | |
223 | return 0; | |
224 | } | |
225 | ||
2f34ce81 TG |
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, | |
0f01565a | 237 | int voltscale_method) |
2f34ce81 | 238 | { |
2f34ce81 | 239 | if (!voltdm || IS_ERR(voltdm)) { |
3d0cb73e | 240 | pr_warn("%s: VDD specified does not exist!\n", __func__); |
2f34ce81 TG |
241 | return; |
242 | } | |
243 | ||
2f34ce81 TG |
244 | switch (voltscale_method) { |
245 | case VOLTSCALE_VPFORCEUPDATE: | |
0f01565a | 246 | voltdm->scale = omap_vp_forceupdate_scale; |
2f34ce81 TG |
247 | return; |
248 | case VOLTSCALE_VCBYPASS: | |
0f01565a | 249 | voltdm->scale = omap_vc_bypass_scale; |
2f34ce81 TG |
250 | return; |
251 | default: | |
7852ec05 PW |
252 | pr_warn("%s: Trying to change the method of voltage scaling to an unsupported one!\n", |
253 | __func__); | |
2f34ce81 TG |
254 | } |
255 | } | |
256 | ||
2f34ce81 TG |
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 | { | |
81a60482 | 266 | struct voltagedomain *voltdm; |
2f34ce81 | 267 | |
81a60482 | 268 | if (list_empty(&voltdm_list)) { |
2f34ce81 TG |
269 | pr_err("%s: Voltage driver support not added\n", |
270 | __func__); | |
271 | return -EINVAL; | |
272 | } | |
273 | ||
81a60482 | 274 | list_for_each_entry(voltdm, &voltdm_list, node) { |
0e2f3d9c KH |
275 | struct clk *sys_ck; |
276 | ||
37efca7e KH |
277 | if (!voltdm->scalable) |
278 | continue; | |
279 | ||
0e2f3d9c KH |
280 | sys_ck = clk_get(NULL, voltdm->sys_clk.name); |
281 | if (IS_ERR(sys_ck)) { | |
3d0cb73e | 282 | pr_warn("%s: Could not get sys clk.\n", __func__); |
0e2f3d9c KH |
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 | ||
4d47506a | 289 | if (voltdm->vc) { |
0f01565a | 290 | voltdm->scale = omap_vc_bypass_scale; |
d84adcf4 | 291 | omap_vc_init_channel(voltdm); |
4d47506a | 292 | } |
d84adcf4 | 293 | |
e3277880 KH |
294 | if (voltdm->vp) { |
295 | voltdm->scale = omap_vp_forceupdate_scale; | |
01f48d30 | 296 | omap_vp_init(voltdm); |
81a60482 | 297 | } |
2f34ce81 TG |
298 | } |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
81a60482 | 303 | static struct voltagedomain *_voltdm_lookup(const char *name) |
2f34ce81 | 304 | { |
81a60482 KH |
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 | ||
e69c22b1 KH |
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 | ||
7852ec05 PW |
334 | pr_debug("voltagedomain: %s: associating powerdomain %s\n", |
335 | voltdm->name, pwrdm->name); | |
e69c22b1 KH |
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 | ||
81a60482 KH |
397 | static int _voltdm_register(struct voltagedomain *voltdm) |
398 | { | |
399 | if (!voltdm || !voltdm->name) | |
400 | return -EINVAL; | |
401 | ||
e69c22b1 | 402 | INIT_LIST_HEAD(&voltdm->pwrdm_list); |
81a60482 KH |
403 | list_add(&voltdm->node, &voltdm_list); |
404 | ||
405 | pr_debug("voltagedomain: registered %s\n", voltdm->name); | |
406 | ||
2f34ce81 TG |
407 | return 0; |
408 | } | |
81a60482 KH |
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 | } |