diff options
Diffstat (limited to 'drivers/gpu/pvr/services4/system/omap/sgxfreq.c')
-rw-r--r-- | drivers/gpu/pvr/services4/system/omap/sgxfreq.c | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/drivers/gpu/pvr/services4/system/omap/sgxfreq.c b/drivers/gpu/pvr/services4/system/omap/sgxfreq.c new file mode 100644 index 000000000000..6e5571eb5c98 --- /dev/null +++ b/drivers/gpu/pvr/services4/system/omap/sgxfreq.c | |||
@@ -0,0 +1,846 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Texas Instruments, Inc | ||
3 | * | ||
4 | @License Dual MIT/GPLv2 | ||
5 | |||
6 | The contents of this file are subject to the MIT license as set out below. | ||
7 | |||
8 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
9 | of this software and associated documentation files (the "Software"), to deal | ||
10 | in the Software without restriction, including without limitation the rights | ||
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
12 | copies of the Software, and to permit persons to whom the Software is | ||
13 | furnished to do so, subject to the following conditions: | ||
14 | |||
15 | The above copyright notice and this permission notice shall be included in | ||
16 | all copies or substantial portions of the Software. | ||
17 | |||
18 | Alternatively, the contents of this file may be used under the terms of | ||
19 | the GNU General Public License Version 2 ("GPL") in which case the provisions | ||
20 | of GPL are applicable instead of those above. | ||
21 | |||
22 | If you wish to allow use of your version of this file only under the terms of | ||
23 | GPL, and not to allow others to use your version of this file under the terms | ||
24 | of the MIT license, indicate your decision by deleting the provisions above | ||
25 | and replace them with the notice and other provisions required by GPL as set | ||
26 | out in the file called "GPL-COPYING" included in this distribution. If you do | ||
27 | not delete the provisions above, a recipient may use your version of this file | ||
28 | under the terms of either the MIT license or GPL. | ||
29 | |||
30 | This License is also included in this distribution in the file called | ||
31 | "MIT-COPYING". | ||
32 | |||
33 | EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS | ||
34 | PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | ||
35 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | ||
36 | PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR | ||
37 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
38 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
39 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
40 | */ | ||
41 | |||
42 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) | ||
43 | #include <linux/pm_opp.h> | ||
44 | #include <linux/of.h> | ||
45 | #else | ||
46 | #include <linux/opp.h> | ||
47 | #endif | ||
48 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) | ||
49 | #include <plat/gpu.h> | ||
50 | #endif | ||
51 | |||
52 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) | ||
53 | #include <linux/pm_voltage_domain.h> | ||
54 | #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)) | ||
55 | #include <linux/regulator/consumer.h> | ||
56 | #endif | ||
57 | |||
58 | #include "sgxfreq.h" | ||
59 | |||
60 | static struct sgxfreq_data { | ||
61 | int freq_cnt; | ||
62 | unsigned long *freq_list; | ||
63 | unsigned long freq; | ||
64 | unsigned long freq_request; | ||
65 | unsigned long freq_limit; | ||
66 | unsigned long total_idle_time; | ||
67 | unsigned long total_active_time; | ||
68 | struct mutex freq_mutex; | ||
69 | struct list_head gov_list; | ||
70 | struct sgxfreq_governor *gov; | ||
71 | struct mutex gov_mutex; | ||
72 | struct sgxfreq_sgx_data sgx_data; | ||
73 | struct device *dev; | ||
74 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) | ||
75 | struct gpu_platform_data *pdata; | ||
76 | #else | ||
77 | struct clk *core_clk; | ||
78 | struct clk *gpu_clk; | ||
79 | struct clk *per_clk; | ||
80 | struct clk *gpu_core_clk; | ||
81 | struct clk *gpu_hyd_clk; | ||
82 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
83 | struct regulator *gpu_reg; | ||
84 | #endif | ||
85 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) | ||
86 | struct notifier_block *clk_nb; | ||
87 | #endif | ||
88 | #endif | ||
89 | } sfd; | ||
90 | |||
91 | /* Governor init/deinit functions */ | ||
92 | int onoff_init(void); | ||
93 | int onoff_deinit(void); | ||
94 | int activeidle_init(void); | ||
95 | int activeidle_deinit(void); | ||
96 | int on3demand_init(void); | ||
97 | int on3demand_deinit(void); | ||
98 | int userspace_init(void); | ||
99 | int userspace_deinit(void); | ||
100 | |||
101 | |||
102 | typedef int sgxfreq_gov_init_t(void); | ||
103 | sgxfreq_gov_init_t *sgxfreq_gov_init[] = { | ||
104 | onoff_init, | ||
105 | activeidle_init, | ||
106 | on3demand_init, | ||
107 | userspace_init, | ||
108 | NULL, | ||
109 | }; | ||
110 | |||
111 | typedef int sgxfreq_gov_deinit_t(void); | ||
112 | sgxfreq_gov_deinit_t *sgxfreq_gov_deinit[] = { | ||
113 | onoff_deinit, | ||
114 | activeidle_deinit, | ||
115 | on3demand_deinit, | ||
116 | userspace_deinit, | ||
117 | NULL, | ||
118 | }; | ||
119 | |||
120 | #define SGXFREQ_DEFAULT_GOV_NAME "on3demand" | ||
121 | static unsigned long _idle_curr_time; | ||
122 | static unsigned long _idle_prev_time; | ||
123 | static unsigned long _active_curr_time; | ||
124 | static unsigned long _active_prev_time; | ||
125 | |||
126 | #if (defined(CONFIG_THERMAL) || defined(CONFIG_THERMAL_FRAMEWORK)) | ||
127 | int cool_init(void); | ||
128 | void cool_deinit(void); | ||
129 | #endif | ||
130 | |||
131 | /*********************** begin sysfs interface ***********************/ | ||
132 | |||
133 | struct kobject *sgxfreq_kobj; | ||
134 | |||
135 | static ssize_t show_frequency_list(struct device *dev, | ||
136 | struct device_attribute *attr, | ||
137 | char *buf) | ||
138 | { | ||
139 | int i; | ||
140 | ssize_t count = 0; | ||
141 | |||
142 | for (i = 0; i < sfd.freq_cnt; i++) | ||
143 | count += sprintf(&buf[count], "%lu ", sfd.freq_list[i]); | ||
144 | count += sprintf(&buf[count], "\n"); | ||
145 | |||
146 | return count; | ||
147 | } | ||
148 | |||
149 | static ssize_t show_frequency_request(struct device *dev, | ||
150 | struct device_attribute *attr, | ||
151 | char *buf) | ||
152 | { | ||
153 | return sprintf(buf, "%lu\n", sfd.freq_request); | ||
154 | } | ||
155 | |||
156 | static ssize_t show_frequency_limit(struct device *dev, | ||
157 | struct device_attribute *attr, | ||
158 | char *buf) | ||
159 | { | ||
160 | return sprintf(buf, "%lu\n", sfd.freq_limit); | ||
161 | } | ||
162 | |||
163 | static ssize_t show_frequency(struct device *dev, | ||
164 | struct device_attribute *attr, | ||
165 | char *buf) | ||
166 | { | ||
167 | return sprintf(buf, "%lu\n", sfd.freq); | ||
168 | } | ||
169 | |||
170 | static ssize_t show_stat(struct device *dev, | ||
171 | struct device_attribute *attr, | ||
172 | char *buf) | ||
173 | { | ||
174 | return sprintf(buf, "gpu %lu %lu\n", | ||
175 | sfd.total_active_time, sfd.total_idle_time); | ||
176 | } | ||
177 | |||
178 | static ssize_t show_governor_list(struct device *dev, | ||
179 | struct device_attribute *attr, | ||
180 | char *buf) | ||
181 | { | ||
182 | ssize_t i = 0; | ||
183 | struct sgxfreq_governor *t; | ||
184 | |||
185 | list_for_each_entry(t, &sfd.gov_list, governor_list) { | ||
186 | if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) | ||
187 | - (SGXFREQ_NAME_LEN + 2))) | ||
188 | goto out; | ||
189 | i += scnprintf(&buf[i], SGXFREQ_NAME_LEN, "%s ", t->name); | ||
190 | } | ||
191 | out: | ||
192 | i += sprintf(&buf[i], "\n"); | ||
193 | return i; | ||
194 | } | ||
195 | |||
196 | static ssize_t show_governor(struct device *dev, | ||
197 | struct device_attribute *attr, char *buf) | ||
198 | { | ||
199 | if (sfd.gov) | ||
200 | return scnprintf(buf, SGXFREQ_NAME_LEN, "%s\n", sfd.gov->name); | ||
201 | |||
202 | return sprintf(buf, "\n"); | ||
203 | } | ||
204 | |||
205 | static ssize_t store_governor(struct device *dev, | ||
206 | struct device_attribute *attr, const char *buf, | ||
207 | size_t count) | ||
208 | { | ||
209 | int ret; | ||
210 | char name[16]; | ||
211 | |||
212 | ret = sscanf(buf, "%15s", name); | ||
213 | if (ret != 1) | ||
214 | return -EINVAL; | ||
215 | |||
216 | ret = sgxfreq_set_governor(name); | ||
217 | if (ret) | ||
218 | return ret; | ||
219 | else | ||
220 | return count; | ||
221 | } | ||
222 | |||
223 | static DEVICE_ATTR(frequency_list, 0444, show_frequency_list, NULL); | ||
224 | static DEVICE_ATTR(frequency_request, 0444, show_frequency_request, NULL); | ||
225 | static DEVICE_ATTR(frequency_limit, 0444, show_frequency_limit, NULL); | ||
226 | static DEVICE_ATTR(frequency, 0444, show_frequency, NULL); | ||
227 | static DEVICE_ATTR(governor_list, 0444, show_governor_list, NULL); | ||
228 | static DEVICE_ATTR(governor, 0644, show_governor, store_governor); | ||
229 | static DEVICE_ATTR(stat, 0444, show_stat, NULL); | ||
230 | |||
231 | static const struct attribute *sgxfreq_attributes[] = { | ||
232 | &dev_attr_frequency_list.attr, | ||
233 | &dev_attr_frequency_request.attr, | ||
234 | &dev_attr_frequency_limit.attr, | ||
235 | &dev_attr_frequency.attr, | ||
236 | &dev_attr_governor_list.attr, | ||
237 | &dev_attr_governor.attr, | ||
238 | &dev_attr_stat.attr, | ||
239 | NULL | ||
240 | }; | ||
241 | |||
242 | /************************ end sysfs interface ************************/ | ||
243 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
244 | static int set_volt_for_freq(unsigned long freq) | ||
245 | { | ||
246 | struct opp *opp; | ||
247 | unsigned long volt = 0; | ||
248 | int ret; | ||
249 | |||
250 | if (sfd.gpu_reg) { | ||
251 | opp = opp_find_freq_exact(sfd.dev, freq, true); | ||
252 | if(IS_ERR(opp)) | ||
253 | { | ||
254 | int r = PTR_ERR(opp); | ||
255 | pr_err("sgxfreq: Couldn't find opp matching freq: %lu. Err: %d", | ||
256 | freq, r); | ||
257 | return -1; | ||
258 | } | ||
259 | |||
260 | volt = opp_get_voltage(opp); | ||
261 | if (!volt) | ||
262 | { | ||
263 | pr_err("sgxfreq: Could find volt corresponding to freq: %lu\n", | ||
264 | freq); | ||
265 | return -1; | ||
266 | } | ||
267 | |||
268 | ret = regulator_set_voltage_tol(sfd.gpu_reg, volt , 6000); | ||
269 | if (ret) { | ||
270 | pr_err("sgxfreq: Error(%d) setting volt: %lu for freq:%lu\n", | ||
271 | ret, volt, freq); | ||
272 | return ret; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | return 0; | ||
277 | |||
278 | } | ||
279 | #endif | ||
280 | |||
281 | static int __set_freq(void) | ||
282 | { | ||
283 | unsigned long freq; | ||
284 | int ret = 0; | ||
285 | |||
286 | freq = min(sfd.freq_request, sfd.freq_limit); | ||
287 | if (freq != sfd.freq) { | ||
288 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)) | ||
289 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
290 | if (freq > sfd.freq) { | ||
291 | /* Going up - must scale voltage before clocks */ | ||
292 | if (set_volt_for_freq(freq) != 0) { | ||
293 | pr_err("sgxfreq: Error setting voltage for freq: %lu\n", | ||
294 | freq); | ||
295 | goto err1; | ||
296 | } | ||
297 | } | ||
298 | #endif | ||
299 | |||
300 | ret = clk_set_rate(sfd.gpu_core_clk, freq); | ||
301 | if (ret) { | ||
302 | pr_err("sgxfreq: Error(%d) setting gpu core clock rate: %lu\n", | ||
303 | ret, freq); | ||
304 | goto err2; | ||
305 | } | ||
306 | |||
307 | ret = clk_set_rate(sfd.gpu_hyd_clk, freq); | ||
308 | if (ret) { | ||
309 | pr_err("sgxfreq: Error(%d) setting gpu hyd clock rate: %lu\n", | ||
310 | ret, freq); | ||
311 | goto err3; | ||
312 | } | ||
313 | |||
314 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
315 | if (freq < sfd.freq) { | ||
316 | /* Going down - must scale voltage after clocks */ | ||
317 | if(set_volt_for_freq(freq) != 0) { | ||
318 | pr_err("sgxfreq: Error setting voltage for freq: %lu\n", | ||
319 | freq); | ||
320 | goto err4; | ||
321 | } | ||
322 | } | ||
323 | #endif | ||
324 | #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)) | ||
325 | sfd.pdata->device_scale(sfd.dev, sfd.dev, freq); | ||
326 | #else | ||
327 | sfd.pdata->device_scale(sfd.dev, freq); | ||
328 | #endif | ||
329 | sfd.freq = freq; | ||
330 | |||
331 | goto noerr; | ||
332 | |||
333 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)) | ||
334 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
335 | err4: | ||
336 | #endif | ||
337 | ret |= clk_set_rate(sfd.gpu_hyd_clk, sfd.freq); | ||
338 | |||
339 | err3: | ||
340 | ret |= clk_set_rate(sfd.gpu_core_clk, sfd.freq); | ||
341 | err2: | ||
342 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
343 | if(freq > sfd.freq) | ||
344 | ret |= set_volt_for_freq(sfd.freq); | ||
345 | err1: | ||
346 | #endif | ||
347 | #endif | ||
348 | noerr: | ||
349 | return ret; | ||
350 | } | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | static struct sgxfreq_governor *__find_governor(const char *name) | ||
355 | { | ||
356 | struct sgxfreq_governor *t; | ||
357 | |||
358 | list_for_each_entry(t, &sfd.gov_list, governor_list) | ||
359 | if (!strncasecmp(name, t->name, SGXFREQ_NAME_LEN)) | ||
360 | return t; | ||
361 | |||
362 | return NULL; | ||
363 | } | ||
364 | |||
365 | static void __update_timing_info(bool active) | ||
366 | { | ||
367 | struct timeval tv; | ||
368 | do_gettimeofday(&tv); | ||
369 | if(active) | ||
370 | { | ||
371 | if(sfd.sgx_data.active == true) { | ||
372 | _active_curr_time = __tv2msec(tv); | ||
373 | sfd.total_active_time += __delta32( | ||
374 | _active_curr_time, _active_prev_time); | ||
375 | SGXFREQ_TRACE("A->A TA:= %lums \tdA: %lums \tTI: %lums \tdI: %lums\n", | ||
376 | sfd.total_active_time, | ||
377 | __delta32(_active_curr_time, _active_prev_time), | ||
378 | sfd.total_active_time, | ||
379 | (unsigned long)0); | ||
380 | _active_prev_time = _active_curr_time; | ||
381 | } else { | ||
382 | _idle_curr_time = __tv2msec(tv); | ||
383 | _active_prev_time = _idle_curr_time; | ||
384 | sfd.total_idle_time += | ||
385 | __delta32(_idle_curr_time, _idle_prev_time); | ||
386 | SGXFREQ_TRACE("I->A TA:= %lums \tdA: %lums \tTI: %lums \tdI: %lums\n", | ||
387 | sfd.total_active_time, | ||
388 | (unsigned long)0, | ||
389 | sfd.total_idle_time, | ||
390 | __delta32(_idle_curr_time, _idle_prev_time)); | ||
391 | } | ||
392 | } else { | ||
393 | if(sfd.sgx_data.active == true) | ||
394 | { | ||
395 | _idle_prev_time = _active_curr_time = __tv2msec(tv); | ||
396 | sfd.total_active_time += | ||
397 | __delta32(_active_curr_time, _active_prev_time); | ||
398 | SGXFREQ_TRACE("A->I TA:= %lums \tdA: %lums \tTI: %lums \tdI: %lums\n", | ||
399 | sfd.total_active_time, | ||
400 | __delta32(_active_curr_time, _active_prev_time), | ||
401 | sfd.total_active_time, | ||
402 | (unsigned long)0); | ||
403 | } | ||
404 | else | ||
405 | { | ||
406 | _idle_curr_time = __tv2msec(tv); | ||
407 | sfd.total_idle_time += __delta32( | ||
408 | _idle_curr_time, _idle_prev_time); | ||
409 | SGXFREQ_TRACE("I->I TA:= %lums \tdA: %lums \tTI: %lums \tdI: %lums\n", | ||
410 | sfd.total_active_time, | ||
411 | (unsigned long)0, | ||
412 | sfd.total_idle_time, | ||
413 | __delta32(_idle_curr_time, _idle_prev_time)); | ||
414 | _idle_prev_time = _idle_curr_time; | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | |||
419 | int sgxfreq_init(struct device *dev) | ||
420 | { | ||
421 | int i, ret; | ||
422 | unsigned long freq; | ||
423 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) | ||
424 | struct dev_pm_opp *opp; | ||
425 | struct device_node *np; | ||
426 | unsigned int voltage_latency; | ||
427 | #else | ||
428 | struct opp *opp; | ||
429 | #endif | ||
430 | struct timeval tv; | ||
431 | |||
432 | sfd.dev = dev; | ||
433 | if (!sfd.dev) | ||
434 | return -EINVAL; | ||
435 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) | ||
436 | sfd.pdata = (struct gpu_platform_data *)dev->platform_data; | ||
437 | if (!sfd.pdata || | ||
438 | !sfd.pdata->opp_get_opp_count || | ||
439 | !sfd.pdata->opp_find_freq_ceil || | ||
440 | !sfd.pdata->device_scale) | ||
441 | return -EINVAL; | ||
442 | #endif | ||
443 | |||
444 | rcu_read_lock(); | ||
445 | |||
446 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) | ||
447 | sfd.freq_cnt = sfd.pdata->opp_get_opp_count(dev); | ||
448 | #else | ||
449 | ret = of_init_opp_table(dev); | ||
450 | if (ret) { | ||
451 | pr_err("sgxfreq: failed to init OPP table: %d\n", ret); | ||
452 | return -EINVAL; | ||
453 | } | ||
454 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
455 | sfd.freq_cnt = opp_get_opp_count(dev); | ||
456 | #else | ||
457 | sfd.freq_cnt = dev_pm_opp_get_opp_count(dev); | ||
458 | #endif | ||
459 | #endif | ||
460 | if (sfd.freq_cnt < 1) { | ||
461 | pr_err("sgxfreq: failed to get operating frequencies\n"); | ||
462 | rcu_read_unlock(); | ||
463 | return -ENODEV; | ||
464 | } | ||
465 | |||
466 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) | ||
467 | np = of_node_get(dev->of_node); | ||
468 | sfd.clk_nb = of_pm_voltdm_notifier_register(dev, np, sfd.gpu_core_clk, "gpu", | ||
469 | &voltage_latency); | ||
470 | |||
471 | if (IS_ERR(sfd.clk_nb)) { | ||
472 | ret = PTR_ERR(sfd.clk_nb); | ||
473 | /* defer probe if regulator is not yet registered */ | ||
474 | if (ret == -EPROBE_DEFER) { | ||
475 | dev_err(dev, | ||
476 | "gpu clock notifier not ready, retry\n"); | ||
477 | } else { | ||
478 | dev_err(dev, | ||
479 | "Failed to register gpu clock notifier: %d\n", | ||
480 | ret); | ||
481 | } | ||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | #endif | ||
486 | sfd.freq_list = kmalloc(sfd.freq_cnt * sizeof(unsigned long), GFP_ATOMIC); | ||
487 | if (!sfd.freq_list) { | ||
488 | rcu_read_unlock(); | ||
489 | return -ENOMEM; | ||
490 | } | ||
491 | |||
492 | freq = 0; | ||
493 | for (i = 0; i < sfd.freq_cnt; i++) { | ||
494 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) | ||
495 | opp = sfd.pdata->opp_find_freq_ceil(dev, &freq); | ||
496 | #elif (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
497 | opp = opp_find_freq_ceil(dev, &freq); | ||
498 | #else | ||
499 | /* 3.14 and later kernels */ | ||
500 | opp = dev_pm_opp_find_freq_ceil(dev, &freq); | ||
501 | #endif | ||
502 | if (IS_ERR_OR_NULL(opp)) { | ||
503 | rcu_read_unlock(); | ||
504 | kfree(sfd.freq_list); | ||
505 | return -ENODEV; | ||
506 | } | ||
507 | sfd.freq_list[i] = freq; | ||
508 | freq++; | ||
509 | } | ||
510 | rcu_read_unlock(); | ||
511 | |||
512 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)) | ||
513 | sfd.core_clk = devm_clk_get(dev, "dpll_core_h14x2_ck"); | ||
514 | if (IS_ERR(sfd.core_clk)) { | ||
515 | ret = PTR_ERR(sfd.core_clk); | ||
516 | pr_err("sgxfreq: failed to get core clock: %d\n", ret); | ||
517 | return ret; | ||
518 | } | ||
519 | |||
520 | sfd.gpu_clk = devm_clk_get(dev, "dpll_gpu_m2_ck"); | ||
521 | if (IS_ERR(sfd.gpu_clk)) { | ||
522 | ret = PTR_ERR(sfd.gpu_clk); | ||
523 | pr_err("sgxfreq: failed to get gpu clock: %d\n", ret); | ||
524 | return ret; | ||
525 | } | ||
526 | |||
527 | sfd.per_clk = devm_clk_get(dev, "dpll_per_h14x2_ck"); | ||
528 | if (IS_ERR(sfd.per_clk)) { | ||
529 | ret = PTR_ERR(sfd.per_clk); | ||
530 | pr_err("sgxfreq: failed to get per clock: %d\n", ret); | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | sfd.gpu_core_clk = devm_clk_get(dev, "gpu_core_gclk_mux"); | ||
535 | if (IS_ERR(sfd.gpu_core_clk)) { | ||
536 | ret = PTR_ERR(sfd.gpu_core_clk); | ||
537 | pr_err("sgxfreq: failed to get gpu core clock: %d\n", ret); | ||
538 | return ret; | ||
539 | } | ||
540 | |||
541 | sfd.gpu_hyd_clk = devm_clk_get(dev, "gpu_core_gclk_mux"); | ||
542 | if (IS_ERR(sfd.gpu_hyd_clk)) { | ||
543 | ret = PTR_ERR(sfd.gpu_hyd_clk); | ||
544 | pr_err("sgxfreq: failed to get gpu hyd clock: %d\n", ret); | ||
545 | return ret; | ||
546 | } | ||
547 | |||
548 | |||
549 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)) | ||
550 | sfd.gpu_reg = devm_regulator_get(dev, "gpu"); | ||
551 | if (IS_ERR(sfd.gpu_reg)) { | ||
552 | if (PTR_ERR(sfd.gpu_reg) == -EPROBE_DEFER) { | ||
553 | dev_err(dev, "gpu regulator not ready, retry\n"); | ||
554 | return -EPROBE_DEFER; | ||
555 | } | ||
556 | pr_err("sgxfreq: failed to get gpu regulator: %ld\n", PTR_ERR(sfd.gpu_reg)); | ||
557 | sfd.gpu_reg = NULL; | ||
558 | } | ||
559 | #endif | ||
560 | |||
561 | ret = clk_set_parent(sfd.gpu_hyd_clk, sfd.core_clk); | ||
562 | if (ret != 0) { | ||
563 | pr_err("sgxfreq: failed to set gpu_hyd_clk parent: %d\n", ret); | ||
564 | } | ||
565 | |||
566 | ret = clk_set_parent(sfd.gpu_core_clk, sfd.core_clk); | ||
567 | if (ret != 0) { | ||
568 | pr_err("sgxfreq: failed to set gpu_core_clk parent: %d\n", ret); | ||
569 | } | ||
570 | #endif | ||
571 | |||
572 | mutex_init(&sfd.freq_mutex); | ||
573 | sfd.freq_limit = sfd.freq_list[sfd.freq_cnt - 1]; | ||
574 | sgxfreq_set_freq_request(sfd.freq_list[sfd.freq_cnt - 1]); | ||
575 | sfd.sgx_data.clk_on = false; | ||
576 | sfd.sgx_data.active = false; | ||
577 | |||
578 | mutex_init(&sfd.gov_mutex); | ||
579 | INIT_LIST_HEAD(&sfd.gov_list); | ||
580 | |||
581 | sgxfreq_kobj = kobject_create_and_add("sgxfreq", &sfd.dev->kobj); | ||
582 | ret = sysfs_create_files(sgxfreq_kobj, sgxfreq_attributes); | ||
583 | if (ret) { | ||
584 | kfree(sfd.freq_list); | ||
585 | return ret; | ||
586 | } | ||
587 | |||
588 | #if (defined(CONFIG_THERMAL) || defined(CONFIG_THERMAL_FRAMEWORK)) | ||
589 | cool_init(); | ||
590 | #endif | ||
591 | |||
592 | for (i = 0; sgxfreq_gov_init[i] != NULL; i++) | ||
593 | sgxfreq_gov_init[i](); | ||
594 | |||
595 | if (sgxfreq_set_governor(SGXFREQ_DEFAULT_GOV_NAME)) { | ||
596 | kfree(sfd.freq_list); | ||
597 | return -ENODEV; | ||
598 | } | ||
599 | do_gettimeofday(&tv); | ||
600 | _idle_prev_time = _active_curr_time = _idle_curr_time = | ||
601 | _active_prev_time = __tv2msec(tv); | ||
602 | |||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | int sgxfreq_deinit(void) | ||
607 | { | ||
608 | int i; | ||
609 | |||
610 | sgxfreq_set_governor(NULL); | ||
611 | |||
612 | sgxfreq_set_freq_request(sfd.freq_list[0]); | ||
613 | |||
614 | #if (defined(CONFIG_THERMAL) || defined(CONFIG_THERMAL_FRAMEWORK)) | ||
615 | cool_deinit(); | ||
616 | #endif | ||
617 | |||
618 | for (i = 0; sgxfreq_gov_deinit[i] != NULL; i++) | ||
619 | sgxfreq_gov_deinit[i](); | ||
620 | |||
621 | sysfs_remove_files(sgxfreq_kobj, sgxfreq_attributes); | ||
622 | kobject_put(sgxfreq_kobj); | ||
623 | |||
624 | kfree(sfd.freq_list); | ||
625 | |||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | int sgxfreq_register_governor(struct sgxfreq_governor *governor) | ||
630 | { | ||
631 | if (!governor) | ||
632 | return -EINVAL; | ||
633 | |||
634 | list_add(&governor->governor_list, &sfd.gov_list); | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | void sgxfreq_unregister_governor(struct sgxfreq_governor *governor) | ||
640 | { | ||
641 | if (!governor) | ||
642 | return; | ||
643 | |||
644 | list_del(&governor->governor_list); | ||
645 | } | ||
646 | |||
647 | int sgxfreq_set_governor(const char *name) | ||
648 | { | ||
649 | int ret = 0; | ||
650 | struct sgxfreq_governor *new_gov = 0; | ||
651 | |||
652 | if (name) { | ||
653 | new_gov = __find_governor(name); | ||
654 | if (!new_gov) | ||
655 | return -EINVAL; | ||
656 | } | ||
657 | |||
658 | mutex_lock(&sfd.gov_mutex); | ||
659 | |||
660 | if (sfd.gov && sfd.gov->gov_stop) | ||
661 | sfd.gov->gov_stop(); | ||
662 | |||
663 | if (new_gov && new_gov->gov_start) | ||
664 | ret = new_gov->gov_start(&sfd.sgx_data); | ||
665 | |||
666 | if (ret) { | ||
667 | if (sfd.gov && sfd.gov->gov_start) | ||
668 | sfd.gov->gov_start(&sfd.sgx_data); | ||
669 | return -ENODEV; | ||
670 | } | ||
671 | sfd.gov = new_gov; | ||
672 | |||
673 | mutex_unlock(&sfd.gov_mutex); | ||
674 | |||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | int sgxfreq_get_freq_list(unsigned long **pfreq_list) | ||
679 | { | ||
680 | *pfreq_list = sfd.freq_list; | ||
681 | |||
682 | return sfd.freq_cnt; | ||
683 | } | ||
684 | |||
685 | unsigned long sgxfreq_get_freq_min(void) | ||
686 | { | ||
687 | return sfd.freq_list[0]; | ||
688 | } | ||
689 | |||
690 | unsigned long sgxfreq_get_freq_max(void) | ||
691 | { | ||
692 | return sfd.freq_list[sfd.freq_cnt - 1]; | ||
693 | } | ||
694 | |||
695 | unsigned long sgxfreq_get_freq_floor(unsigned long freq) | ||
696 | { | ||
697 | int i; | ||
698 | unsigned long f = 0; | ||
699 | |||
700 | for (i = sfd.freq_cnt - 1; i >= 0; i--) { | ||
701 | f = sfd.freq_list[i]; | ||
702 | if (f <= freq) | ||
703 | return f; | ||
704 | } | ||
705 | |||
706 | return f; | ||
707 | } | ||
708 | |||
709 | unsigned long sgxfreq_get_freq_ceil(unsigned long freq) | ||
710 | { | ||
711 | int i; | ||
712 | unsigned long f = 0; | ||
713 | |||
714 | for (i = 0; i < sfd.freq_cnt; i++) { | ||
715 | f = sfd.freq_list[i]; | ||
716 | if (f >= freq) | ||
717 | return f; | ||
718 | } | ||
719 | |||
720 | return f; | ||
721 | } | ||
722 | |||
723 | unsigned long sgxfreq_get_freq(void) | ||
724 | { | ||
725 | return sfd.freq; | ||
726 | } | ||
727 | |||
728 | unsigned long sgxfreq_get_freq_request(void) | ||
729 | { | ||
730 | return sfd.freq_request; | ||
731 | } | ||
732 | |||
733 | unsigned long sgxfreq_get_freq_limit(void) | ||
734 | { | ||
735 | return sfd.freq_limit; | ||
736 | } | ||
737 | |||
738 | unsigned long sgxfreq_set_freq_request(unsigned long freq_request) | ||
739 | { | ||
740 | freq_request = sgxfreq_get_freq_ceil(freq_request); | ||
741 | |||
742 | mutex_lock(&sfd.freq_mutex); | ||
743 | |||
744 | sfd.freq_request = freq_request; | ||
745 | __set_freq(); | ||
746 | |||
747 | mutex_unlock(&sfd.freq_mutex); | ||
748 | |||
749 | return freq_request; | ||
750 | } | ||
751 | |||
752 | unsigned long sgxfreq_set_freq_limit(unsigned long freq_limit) | ||
753 | { | ||
754 | freq_limit = sgxfreq_get_freq_ceil(freq_limit); | ||
755 | |||
756 | mutex_lock(&sfd.freq_mutex); | ||
757 | |||
758 | sfd.freq_limit = freq_limit; | ||
759 | __set_freq(); | ||
760 | |||
761 | mutex_unlock(&sfd.freq_mutex); | ||
762 | |||
763 | return freq_limit; | ||
764 | } | ||
765 | |||
766 | unsigned long sgxfreq_get_total_active_time(void) | ||
767 | { | ||
768 | __update_timing_info(sfd.sgx_data.active); | ||
769 | return sfd.total_active_time; | ||
770 | } | ||
771 | |||
772 | unsigned long sgxfreq_get_total_idle_time(void) | ||
773 | { | ||
774 | __update_timing_info(sfd.sgx_data.active); | ||
775 | return sfd.total_idle_time; | ||
776 | } | ||
777 | |||
778 | /* | ||
779 | * sgx_clk_on, sgx_clk_off, sgx_active, and sgx_idle notifications are | ||
780 | * serialized by power lock. governor notif calls need sync with governor | ||
781 | * setting. | ||
782 | */ | ||
783 | void sgxfreq_notif_sgx_clk_on(void) | ||
784 | { | ||
785 | sfd.sgx_data.clk_on = true; | ||
786 | |||
787 | mutex_lock(&sfd.gov_mutex); | ||
788 | |||
789 | if (sfd.gov && sfd.gov->sgx_clk_on) | ||
790 | sfd.gov->sgx_clk_on(); | ||
791 | |||
792 | mutex_unlock(&sfd.gov_mutex); | ||
793 | } | ||
794 | |||
795 | void sgxfreq_notif_sgx_clk_off(void) | ||
796 | { | ||
797 | sfd.sgx_data.clk_on = false; | ||
798 | |||
799 | mutex_lock(&sfd.gov_mutex); | ||
800 | |||
801 | if (sfd.gov && sfd.gov->sgx_clk_off) | ||
802 | sfd.gov->sgx_clk_off(); | ||
803 | |||
804 | mutex_unlock(&sfd.gov_mutex); | ||
805 | } | ||
806 | |||
807 | |||
808 | void sgxfreq_notif_sgx_active(void) | ||
809 | { | ||
810 | __update_timing_info(true); | ||
811 | |||
812 | sfd.sgx_data.active = true; | ||
813 | |||
814 | mutex_lock(&sfd.gov_mutex); | ||
815 | |||
816 | if (sfd.gov && sfd.gov->sgx_active) | ||
817 | sfd.gov->sgx_active(); | ||
818 | |||
819 | mutex_unlock(&sfd.gov_mutex); | ||
820 | |||
821 | } | ||
822 | |||
823 | void sgxfreq_notif_sgx_idle(void) | ||
824 | { | ||
825 | |||
826 | __update_timing_info(false); | ||
827 | |||
828 | sfd.sgx_data.active = false; | ||
829 | |||
830 | mutex_lock(&sfd.gov_mutex); | ||
831 | |||
832 | if (sfd.gov && sfd.gov->sgx_idle) | ||
833 | sfd.gov->sgx_idle(); | ||
834 | |||
835 | mutex_unlock(&sfd.gov_mutex); | ||
836 | } | ||
837 | |||
838 | void sgxfreq_notif_sgx_frame_done(void) | ||
839 | { | ||
840 | mutex_lock(&sfd.gov_mutex); | ||
841 | |||
842 | if (sfd.gov && sfd.gov->sgx_frame_done) | ||
843 | sfd.gov->sgx_frame_done(); | ||
844 | |||
845 | mutex_unlock(&sfd.gov_mutex); | ||
846 | } | ||