1370ad4fae49250c95e70cb0981f6308b5806c11
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / arm / t6xx / kbase / src / platform / rk / mali_kbase_dvfs.c
1 /* drivers/gpu/t6xx/kbase/src/platform/manta/mali_kbase_dvfs.c
2  *
3  * Copyright 2011 by S.LSI. Samsung Electronics Inc.
4  * San#24, Nongseo-Dong, Giheung-Gu, Yongin, Korea
5  *
6  * Samsung SoC Mali-T604 DVFS driver
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software FoundatIon.
11  */
12
13 /**
14  * @file mali_kbase_dvfs.c
15  * DVFS
16  */
17
18 #include <kbase/src/common/mali_kbase.h>
19 #include <kbase/src/common/mali_kbase_uku.h>
20 #include <kbase/src/common/mali_kbase_mem.h>
21 #include <kbase/src/common/mali_midg_regmap.h>
22 #include <kbase/src/linux/mali_kbase_mem_linux.h>
23
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <linux/poll.h>
27 #include <linux/kernel.h>
28 #include <linux/errno.h>
29 #include <linux/platform_device.h>
30 #include <linux/pci.h>
31 #include <linux/miscdevice.h>
32 #include <linux/list.h>
33 #include <linux/semaphore.h>
34 #include <linux/fs.h>
35 #include <linux/uaccess.h>
36 #include <linux/interrupt.h>
37 #include <linux/io.h>
38
39 #include <linux/fb.h>
40 #include <linux/clk.h>
41 #include <linux/delay.h>
42 #include <linux/regulator/consumer.h>
43 #include <linux/regulator/driver.h>
44 #include <kbase/src/platform/rk/mali_kbase_platform.h>
45 #include <kbase/src/platform/rk/mali_kbase_dvfs.h>
46 #include <kbase/src/common/mali_kbase_gator.h>
47
48 /***********************************************************/
49 /*  This table and variable are using the check time share of GPU Clock  */
50 /***********************************************************/
51
52 typedef struct _mali_dvfs_info {
53         unsigned int voltage;
54         unsigned int clock;
55         int min_threshold;
56         int max_threshold;
57         unsigned long long time;
58 } mali_dvfs_info;
59
60 static mali_dvfs_info mali_dvfs_infotbl[] = {
61         {925000, 100, 0, 70, 0},
62         {925000, 160, 50, 65, 0},
63         {1025000, 266, 60, 78, 0},
64         {1075000, 350, 70, 80, 0},
65         {1125000, 400, 70, 80, 0},
66         {1150000, 450, 76, 99, 0},
67         {1200000, 533, 99, 100, 0},
68 };
69
70 #define MALI_DVFS_STEP  ARRAY_SIZE(mali_dvfs_infotbl)
71
72 #ifdef CONFIG_MALI_T6XX_DVFS
73 typedef struct _mali_dvfs_status_type {
74         kbase_device *kbdev;
75         int step;
76         int utilisation;
77 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
78         int upper_lock;
79         int under_lock;
80 #endif
81
82 } mali_dvfs_status;
83
84 static struct workqueue_struct *mali_dvfs_wq = 0;
85 spinlock_t mali_dvfs_spinlock;
86 struct mutex mali_set_clock_lock;
87 struct mutex mali_enable_clock_lock;
88
89 #ifdef CONFIG_MALI_T6XX_DEBUG_SYS
90 static void update_time_in_state(int level);
91 #endif
92
93 /*dvfs status*/
94 static mali_dvfs_status mali_dvfs_status_current;
95
96 static void mali_dvfs_event_proc(struct work_struct *w)
97 {
98         unsigned long flags;
99         mali_dvfs_status *dvfs_status;
100         struct rk_context *platform;
101
102         mutex_lock(&mali_enable_clock_lock);
103         dvfs_status = &mali_dvfs_status_current;
104
105         if (!kbase_platform_dvfs_get_enable_status()) {
106                 mutex_unlock(&mali_enable_clock_lock);
107                 return;
108         }
109
110         platform = (struct rk_context *)dvfs_status->kbdev->platform_context;
111         
112         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
113         if (dvfs_status->utilisation > mali_dvfs_infotbl[dvfs_status->step].max_threshold) 
114         {
115                 if (dvfs_status->step==kbase_platform_dvfs_get_level(450)) 
116                 {
117                         if (platform->utilisation > mali_dvfs_infotbl[dvfs_status->step].max_threshold)
118                                 dvfs_status->step++;
119                         BUG_ON(dvfs_status->step >= MALI_DVFS_STEP);
120                 } 
121                 else 
122                 {
123                         dvfs_status->step++;
124                         BUG_ON(dvfs_status->step >= MALI_DVFS_STEP);
125                 }
126         } 
127         else if((dvfs_status->step > 0) && (platform->time_tick == MALI_DVFS_TIME_INTERVAL) && (platform->utilisation < mali_dvfs_infotbl[dvfs_status->step].min_threshold)) 
128         {
129                 BUG_ON(dvfs_status->step <= 0);
130                 dvfs_status->step--;
131         }
132 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
133         if ((dvfs_status->upper_lock >= 0) && (dvfs_status->step > dvfs_status->upper_lock)) 
134         {
135                 dvfs_status->step = dvfs_status->upper_lock;
136         }
137
138         if (dvfs_status->under_lock > 0) 
139         {
140                 if (dvfs_status->step < dvfs_status->under_lock)
141                         dvfs_status->step = dvfs_status->under_lock;
142         }
143 #endif
144         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
145         kbase_platform_dvfs_set_level(dvfs_status->kbdev, dvfs_status->step);
146
147         mutex_unlock(&mali_enable_clock_lock);
148 }
149
150 static DECLARE_WORK(mali_dvfs_work, mali_dvfs_event_proc);
151
152 int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation)
153 {
154         unsigned long flags;
155         struct rk_context *platform;
156
157         BUG_ON(!kbdev);
158         platform = (struct rk_context *)kbdev->platform_context;
159
160         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
161         if (platform->time_tick < MALI_DVFS_TIME_INTERVAL) {
162                 platform->time_tick++;
163                 platform->time_busy += kbdev->pm.metrics.time_busy;
164                 platform->time_idle += kbdev->pm.metrics.time_idle;
165         } else {
166                 platform->time_busy = kbdev->pm.metrics.time_busy;
167                 platform->time_idle = kbdev->pm.metrics.time_idle;
168                 platform->time_tick = 0;
169         }
170
171         if ((platform->time_tick == MALI_DVFS_TIME_INTERVAL) && (platform->time_idle + platform->time_busy > 0))
172                 platform->utilisation = (100 * platform->time_busy) / (platform->time_idle + platform->time_busy);
173
174         mali_dvfs_status_current.utilisation = utilisation;
175         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
176
177         queue_work_on(0, mali_dvfs_wq, &mali_dvfs_work);
178         /*add error handle here */
179         return MALI_TRUE;
180 }
181
182 int kbase_platform_dvfs_get_utilisation(void)
183 {
184         unsigned long flags;
185         int utilisation = 0;
186
187         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
188         utilisation = mali_dvfs_status_current.utilisation;
189         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
190
191         return utilisation;
192 }
193
194 int kbase_platform_dvfs_get_enable_status(void)
195 {
196         struct kbase_device *kbdev;
197         unsigned long flags;
198         int enable;
199
200         kbdev = mali_dvfs_status_current.kbdev;
201         spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
202         enable = kbdev->pm.metrics.timer_active;
203         spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
204
205         return enable;
206 }
207
208 int kbase_platform_dvfs_enable(bool enable, int freq)
209 {
210         mali_dvfs_status *dvfs_status;
211         struct kbase_device *kbdev;
212         unsigned long flags;
213         struct rk_context *platform;
214
215         dvfs_status = &mali_dvfs_status_current;
216         kbdev = mali_dvfs_status_current.kbdev;
217
218         BUG_ON(kbdev == NULL);
219         platform = (struct rk_context *)kbdev->platform_context;
220
221         mutex_lock(&mali_enable_clock_lock);
222
223         if (enable != kbdev->pm.metrics.timer_active) {
224                 if (enable) {
225                         spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
226                         kbdev->pm.metrics.timer_active = MALI_TRUE;
227                         spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
228                         hrtimer_start(&kbdev->pm.metrics.timer,
229                                         HR_TIMER_DELAY_MSEC(KBASE_PM_DVFS_FREQUENCY),
230                                         HRTIMER_MODE_REL);
231                 } else {
232                         spin_lock_irqsave(&kbdev->pm.metrics.lock, flags);
233                         kbdev->pm.metrics.timer_active = MALI_FALSE;
234                         spin_unlock_irqrestore(&kbdev->pm.metrics.lock, flags);
235                         hrtimer_cancel(&kbdev->pm.metrics.timer);
236                 }
237         }
238
239         if (freq != MALI_DVFS_CURRENT_FREQ) {
240                 spin_lock_irqsave(&mali_dvfs_spinlock, flags);
241                 platform->time_tick = 0;
242                 platform->time_busy = 0;
243                 platform->time_idle = 0;
244                 platform->utilisation = 0;
245                 dvfs_status->step = kbase_platform_dvfs_get_level(freq);
246                 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
247
248                 kbase_platform_dvfs_set_level(dvfs_status->kbdev, dvfs_status->step);
249         }
250  
251         mutex_unlock(&mali_enable_clock_lock);
252
253         return MALI_TRUE;
254 }
255
256 int kbase_platform_dvfs_init(struct kbase_device *kbdev)
257 {
258         unsigned long flags;
259         /*default status
260            add here with the right function to get initilization value.
261          */
262         if (!mali_dvfs_wq)
263                 mali_dvfs_wq = create_singlethread_workqueue("mali_dvfs");
264
265         spin_lock_init(&mali_dvfs_spinlock);
266         mutex_init(&mali_set_clock_lock);
267         mutex_init(&mali_enable_clock_lock);
268
269         /*add a error handling here */
270         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
271         mali_dvfs_status_current.kbdev = kbdev;
272         mali_dvfs_status_current.utilisation = 100;
273         mali_dvfs_status_current.step = MALI_DVFS_STEP - 1;
274 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
275         mali_dvfs_status_current.upper_lock = -1;
276         mali_dvfs_status_current.under_lock = -1;
277 #endif
278
279         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
280
281         return MALI_TRUE;
282 }
283
284 void kbase_platform_dvfs_term(void)
285 {
286         if (mali_dvfs_wq)
287                 destroy_workqueue(mali_dvfs_wq);
288
289         mali_dvfs_wq = NULL;
290 }
291 #endif /*CONFIG_MALI_T6XX_DVFS*/
292
293 int mali_get_dvfs_upper_locked_freq(void)
294 {
295         unsigned long flags;
296         int locked_level = -1;
297
298 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
299         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
300         if (mali_dvfs_status_current.upper_lock >= 0)
301                 locked_level = mali_dvfs_infotbl[mali_dvfs_status_current.upper_lock].clock;
302         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
303 #endif
304         return locked_level;
305 }
306
307 int mali_get_dvfs_under_locked_freq(void)
308 {
309         unsigned long flags;
310         int locked_level = -1;
311
312 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
313         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
314         if (mali_dvfs_status_current.under_lock >= 0)
315                 locked_level = mali_dvfs_infotbl[mali_dvfs_status_current.under_lock].clock;
316         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
317 #endif
318         return locked_level;
319 }
320
321 int mali_get_dvfs_current_level(void)
322 {
323         unsigned long flags;
324         int current_level = -1;
325
326 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
327         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
328         current_level = mali_dvfs_status_current.step;
329         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
330 #endif
331         return current_level;
332 }
333
334 int mali_dvfs_freq_lock(int level)
335 {
336         unsigned long flags;
337 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
338         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
339         if (mali_dvfs_status_current.under_lock >= 0 && mali_dvfs_status_current.under_lock > level) {
340                 printk(KERN_ERR " Upper lock Error : Attempting to set upper lock to below under lock\n");
341                 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
342                 return -1;
343         }
344         mali_dvfs_status_current.upper_lock = level;
345         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
346
347         printk(KERN_DEBUG " Upper Lock Set : %d\n", level);
348 #endif
349         return 0;
350 }
351
352 void mali_dvfs_freq_unlock(void)
353 {
354         unsigned long flags;
355 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
356         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
357         mali_dvfs_status_current.upper_lock = -1;
358         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
359 #endif
360         printk(KERN_DEBUG "mali Upper Lock Unset\n");
361 }
362
363 int mali_dvfs_freq_under_lock(int level)
364 {
365         unsigned long flags;
366 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
367         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
368         if (mali_dvfs_status_current.upper_lock >= 0 && mali_dvfs_status_current.upper_lock < level) {
369                 printk(KERN_ERR "mali Under lock Error : Attempting to set under lock to above upper lock\n");
370                 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
371                 return -1;
372         }
373         mali_dvfs_status_current.under_lock = level;
374         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
375
376         printk(KERN_DEBUG "mali Under Lock Set : %d\n", level);
377 #endif
378         return 0;
379 }
380
381 void mali_dvfs_freq_under_unlock(void)
382 {
383         unsigned long flags;
384 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
385         spin_lock_irqsave(&mali_dvfs_spinlock, flags);
386         mali_dvfs_status_current.under_lock = -1;
387         spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
388 #endif
389         printk(KERN_DEBUG " mali clock Under Lock Unset\n");
390 }
391
392 void kbase_platform_dvfs_set_clock(kbase_device *kbdev, int freq)
393 {
394         unsigned long aclk_400_rate = 0;
395         struct rk_context *platform;
396
397         if (!kbdev)
398                 panic("oops");
399
400         platform = (struct rk_context *)kbdev->platform_context;
401         if (NULL == platform)
402                 panic("oops");
403
404         if (platform->clk_mali == 0) 
405                 return;
406
407         switch (freq) {
408                 case 533:
409                         aclk_400_rate = 533000000;
410                         break;
411                 case 450:
412                         aclk_400_rate = 450000000;
413                         break;
414                 case 400:
415                         aclk_400_rate = 400000000;
416                         break;
417                 case 350:
418                         aclk_400_rate = 350000000;
419                         break;
420                 case 266:
421                         aclk_400_rate = 267000000;
422                         break;
423                 case 160:
424                         aclk_400_rate = 160000000;
425                         break;
426                 case 100:
427                         aclk_400_rate = 100000000;
428                         break;
429                 default:
430                         return;
431         }
432
433         mali_dvfs_clk_set(platform->mali_clk_node,aclk_400_rate);
434         /* Waiting for clock is stable
435         do {
436                 tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_TOP0);
437         } while (tmp & 0x1000000);
438         */
439
440         return;
441 }
442
443
444 int kbase_platform_dvfs_get_level(int freq)
445 {
446         int i;
447         for (i = 0; i < MALI_DVFS_STEP; i++) {
448                 if (mali_dvfs_infotbl[i].clock == freq)
449                         return i;
450         }
451         return -1;
452 }
453 void kbase_platform_dvfs_set_level(kbase_device *kbdev, int level)
454 {
455         static int prev_level = -1;
456
457         if (level == prev_level)
458                 return;
459
460         if (WARN_ON((level >= MALI_DVFS_STEP) || (level < 0)))
461                 panic("invalid level");
462
463         if (mali_dvfs_status_current.upper_lock >= 0 && level > mali_dvfs_status_current.upper_lock)
464                 level = mali_dvfs_status_current.upper_lock;
465
466         if (mali_dvfs_status_current.under_lock >= 0 && level < mali_dvfs_status_current.under_lock)
467                 level = mali_dvfs_status_current.under_lock;
468
469 #ifdef CONFIG_MALI_T6XX_DVFS
470         mutex_lock(&mali_set_clock_lock);
471 #endif
472
473         kbase_platform_dvfs_set_clock(kbdev, mali_dvfs_infotbl[level].clock);
474
475 #if defined(CONFIG_MALI_T6XX_DEBUG_SYS) && defined(CONFIG_MALI_T6XX_DVFS)
476         update_time_in_state(prev_level);
477 #endif
478         prev_level = level;
479 #ifdef CONFIG_MALI_T6XX_DVFS
480         mutex_unlock(&mali_set_clock_lock);
481 #endif
482 }
483
484 #ifdef CONFIG_MALI_T6XX_DEBUG_SYS
485 #ifdef CONFIG_MALI_T6XX_DVFS
486 static void update_time_in_state(int level)
487 {
488         u64 current_time;
489         static u64 prev_time=0;
490
491         if (level < 0)
492                 return;
493
494         if (!kbase_platform_dvfs_get_enable_status())
495                 return;
496
497         if (prev_time ==0)
498                 prev_time=get_jiffies_64();
499
500         current_time = get_jiffies_64();
501         mali_dvfs_infotbl[level].time += current_time-prev_time;
502
503         prev_time = current_time;
504 }
505 #endif
506
507 ssize_t show_time_in_state(struct device *dev, struct device_attribute *attr, char *buf)
508 {
509         struct kbase_device *kbdev;
510         ssize_t ret = 0;
511         int i;
512
513         kbdev = dev_get_drvdata(dev);
514
515 #ifdef CONFIG_MALI_T6XX_DVFS
516         update_time_in_state(mali_dvfs_status_current.step);
517 #endif
518         if (!kbdev)
519                 return -ENODEV;
520
521         for (i = 0; i < MALI_DVFS_STEP; i++)
522                 ret += snprintf(buf + ret, PAGE_SIZE - ret, "%d %llu\n", mali_dvfs_infotbl[i].clock, mali_dvfs_infotbl[i].time);
523
524         if (ret < PAGE_SIZE - 1)
525                 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
526         else {
527                 buf[PAGE_SIZE - 2] = '\n';
528                 buf[PAGE_SIZE - 1] = '\0';
529                 ret = PAGE_SIZE - 1;
530         }
531
532         return ret;
533 }
534
535 ssize_t set_time_in_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
536 {
537         int i;
538
539         for (i = 0; i < MALI_DVFS_STEP; i++)
540                 mali_dvfs_infotbl[i].time = 0;
541
542         printk(KERN_DEBUG "time_in_state value is reset complete.\n");
543         return count;
544 }
545 #endif