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_voltage_late_init() - Init the various voltage parameters | |
228 | * | |
229 | * This API is to be called in the later stages of the | |
230 | * system boot to init the voltage controller and | |
231 | * voltage processors. | |
232 | */ | |
233 | int __init omap_voltage_late_init(void) | |
234 | { | |
81a60482 | 235 | struct voltagedomain *voltdm; |
2f34ce81 | 236 | |
81a60482 | 237 | if (list_empty(&voltdm_list)) { |
2f34ce81 TG |
238 | pr_err("%s: Voltage driver support not added\n", |
239 | __func__); | |
240 | return -EINVAL; | |
241 | } | |
242 | ||
81a60482 | 243 | list_for_each_entry(voltdm, &voltdm_list, node) { |
0e2f3d9c KH |
244 | struct clk *sys_ck; |
245 | ||
37efca7e KH |
246 | if (!voltdm->scalable) |
247 | continue; | |
248 | ||
0e2f3d9c KH |
249 | sys_ck = clk_get(NULL, voltdm->sys_clk.name); |
250 | if (IS_ERR(sys_ck)) { | |
3d0cb73e | 251 | pr_warn("%s: Could not get sys clk.\n", __func__); |
0e2f3d9c KH |
252 | return -EINVAL; |
253 | } | |
254 | voltdm->sys_clk.rate = clk_get_rate(sys_ck); | |
255 | WARN_ON(!voltdm->sys_clk.rate); | |
256 | clk_put(sys_ck); | |
257 | ||
4d47506a | 258 | if (voltdm->vc) { |
0f01565a | 259 | voltdm->scale = omap_vc_bypass_scale; |
d84adcf4 | 260 | omap_vc_init_channel(voltdm); |
4d47506a | 261 | } |
d84adcf4 | 262 | |
e3277880 KH |
263 | if (voltdm->vp) { |
264 | voltdm->scale = omap_vp_forceupdate_scale; | |
01f48d30 | 265 | omap_vp_init(voltdm); |
81a60482 | 266 | } |
2f34ce81 TG |
267 | } |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
81a60482 | 272 | static struct voltagedomain *_voltdm_lookup(const char *name) |
2f34ce81 | 273 | { |
81a60482 KH |
274 | struct voltagedomain *voltdm, *temp_voltdm; |
275 | ||
276 | voltdm = NULL; | |
277 | ||
278 | list_for_each_entry(temp_voltdm, &voltdm_list, node) { | |
279 | if (!strcmp(name, temp_voltdm->name)) { | |
280 | voltdm = temp_voltdm; | |
281 | break; | |
282 | } | |
283 | } | |
284 | ||
285 | return voltdm; | |
286 | } | |
287 | ||
288 | static int _voltdm_register(struct voltagedomain *voltdm) | |
289 | { | |
290 | if (!voltdm || !voltdm->name) | |
291 | return -EINVAL; | |
292 | ||
293 | list_add(&voltdm->node, &voltdm_list); | |
294 | ||
295 | pr_debug("voltagedomain: registered %s\n", voltdm->name); | |
296 | ||
2f34ce81 TG |
297 | return 0; |
298 | } | |
81a60482 KH |
299 | |
300 | /** | |
301 | * voltdm_lookup - look up a voltagedomain by name, return a pointer | |
302 | * @name: name of voltagedomain | |
303 | * | |
304 | * Find a registered voltagedomain by its name @name. Returns a pointer | |
305 | * to the struct voltagedomain if found, or NULL otherwise. | |
306 | */ | |
307 | struct voltagedomain *voltdm_lookup(const char *name) | |
308 | { | |
309 | struct voltagedomain *voltdm ; | |
310 | ||
311 | if (!name) | |
312 | return NULL; | |
313 | ||
314 | voltdm = _voltdm_lookup(name); | |
315 | ||
316 | return voltdm; | |
317 | } | |
318 | ||
319 | /** | |
320 | * voltdm_init - set up the voltagedomain layer | |
321 | * @voltdm_list: array of struct voltagedomain pointers to register | |
322 | * | |
323 | * Loop through the array of voltagedomains @voltdm_list, registering all | |
324 | * that are available on the current CPU. If voltdm_list is supplied | |
325 | * and not null, all of the referenced voltagedomains will be | |
326 | * registered. No return value. | |
327 | */ | |
328 | void voltdm_init(struct voltagedomain **voltdms) | |
329 | { | |
330 | struct voltagedomain **v; | |
331 | ||
332 | if (voltdms) { | |
333 | for (v = voltdms; *v; v++) | |
334 | _voltdm_register(*v); | |
335 | } | |
336 | } |