1 /* drivers/gpu/t6xx/kbase/src/platform/manta/mali_kbase_dvfs.c
3 * Copyright 2011 by S.LSI. Samsung Electronics Inc.
4 * San#24, Nongseo-Dong, Giheung-Gu, Yongin, Korea
6 * Samsung SoC Mali-T604 DVFS driver
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.
14 * @file mali_kbase_dvfs.c
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>
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>
35 #include <linux/uaccess.h>
36 #include <linux/interrupt.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>
48 /***********************************************************/
49 /* This table and variable are using the check time share of GPU Clock */
50 /***********************************************************/
52 typedef struct _mali_dvfs_info {
57 unsigned long long time;
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},
70 #define MALI_DVFS_STEP ARRAY_SIZE(mali_dvfs_infotbl)
72 #ifdef CONFIG_MALI_T6XX_DVFS
73 typedef struct _mali_dvfs_status_type {
77 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
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;
89 #ifdef CONFIG_MALI_T6XX_DEBUG_SYS
90 static void update_time_in_state(int level);
94 static mali_dvfs_status mali_dvfs_status_current;
96 static void mali_dvfs_event_proc(struct work_struct *w)
99 mali_dvfs_status *dvfs_status;
100 struct rk_context *platform;
102 mutex_lock(&mali_enable_clock_lock);
103 dvfs_status = &mali_dvfs_status_current;
105 if (!kbase_platform_dvfs_get_enable_status()) {
106 mutex_unlock(&mali_enable_clock_lock);
110 platform = (struct rk_context *)dvfs_status->kbdev->platform_context;
112 spin_lock_irqsave(&mali_dvfs_spinlock, flags);
113 if (dvfs_status->utilisation > mali_dvfs_infotbl[dvfs_status->step].max_threshold)
115 if (dvfs_status->step==kbase_platform_dvfs_get_level(450))
117 if (platform->utilisation > mali_dvfs_infotbl[dvfs_status->step].max_threshold)
119 BUG_ON(dvfs_status->step >= MALI_DVFS_STEP);
124 BUG_ON(dvfs_status->step >= MALI_DVFS_STEP);
127 else if((dvfs_status->step > 0) && (platform->time_tick == MALI_DVFS_TIME_INTERVAL) && (platform->utilisation < mali_dvfs_infotbl[dvfs_status->step].min_threshold))
129 BUG_ON(dvfs_status->step <= 0);
132 #ifdef CONFIG_MALI_T6XX_FREQ_LOCK
133 if ((dvfs_status->upper_lock >= 0) && (dvfs_status->step > dvfs_status->upper_lock))
135 dvfs_status->step = dvfs_status->upper_lock;
138 if (dvfs_status->under_lock > 0)
140 if (dvfs_status->step < dvfs_status->under_lock)
141 dvfs_status->step = dvfs_status->under_lock;
144 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
145 kbase_platform_dvfs_set_level(dvfs_status->kbdev, dvfs_status->step);
147 mutex_unlock(&mali_enable_clock_lock);
150 static DECLARE_WORK(mali_dvfs_work, mali_dvfs_event_proc);
152 int kbase_platform_dvfs_event(struct kbase_device *kbdev, u32 utilisation)
155 struct rk_context *platform;
158 platform = (struct rk_context *)kbdev->platform_context;
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;
166 platform->time_busy = kbdev->pm.metrics.time_busy;
167 platform->time_idle = kbdev->pm.metrics.time_idle;
168 platform->time_tick = 0;
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);
174 mali_dvfs_status_current.utilisation = utilisation;
175 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
177 queue_work_on(0, mali_dvfs_wq, &mali_dvfs_work);
178 /*add error handle here */
182 int kbase_platform_dvfs_get_utilisation(void)
187 spin_lock_irqsave(&mali_dvfs_spinlock, flags);
188 utilisation = mali_dvfs_status_current.utilisation;
189 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
194 int kbase_platform_dvfs_get_enable_status(void)
196 struct kbase_device *kbdev;
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);
208 int kbase_platform_dvfs_enable(bool enable, int freq)
210 mali_dvfs_status *dvfs_status;
211 struct kbase_device *kbdev;
213 struct rk_context *platform;
215 dvfs_status = &mali_dvfs_status_current;
216 kbdev = mali_dvfs_status_current.kbdev;
218 BUG_ON(kbdev == NULL);
219 platform = (struct rk_context *)kbdev->platform_context;
221 mutex_lock(&mali_enable_clock_lock);
223 if (enable != kbdev->pm.metrics.timer_active) {
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),
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);
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);
248 kbase_platform_dvfs_set_level(dvfs_status->kbdev, dvfs_status->step);
251 mutex_unlock(&mali_enable_clock_lock);
256 int kbase_platform_dvfs_init(struct kbase_device *kbdev)
260 add here with the right function to get initilization value.
263 mali_dvfs_wq = create_singlethread_workqueue("mali_dvfs");
265 spin_lock_init(&mali_dvfs_spinlock);
266 mutex_init(&mali_set_clock_lock);
267 mutex_init(&mali_enable_clock_lock);
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;
279 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
284 void kbase_platform_dvfs_term(void)
287 destroy_workqueue(mali_dvfs_wq);
291 #endif /*CONFIG_MALI_T6XX_DVFS*/
293 int mali_get_dvfs_upper_locked_freq(void)
296 int locked_level = -1;
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);
307 int mali_get_dvfs_under_locked_freq(void)
310 int locked_level = -1;
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);
321 int mali_get_dvfs_current_level(void)
324 int current_level = -1;
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);
331 return current_level;
334 int mali_dvfs_freq_lock(int level)
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);
344 mali_dvfs_status_current.upper_lock = level;
345 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
347 printk(KERN_DEBUG " Upper Lock Set : %d\n", level);
352 void mali_dvfs_freq_unlock(void)
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);
360 printk(KERN_DEBUG "mali Upper Lock Unset\n");
363 int mali_dvfs_freq_under_lock(int level)
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);
373 mali_dvfs_status_current.under_lock = level;
374 spin_unlock_irqrestore(&mali_dvfs_spinlock, flags);
376 printk(KERN_DEBUG "mali Under Lock Set : %d\n", level);
381 void mali_dvfs_freq_under_unlock(void)
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);
389 printk(KERN_DEBUG " mali clock Under Lock Unset\n");
392 void kbase_platform_dvfs_set_clock(kbase_device *kbdev, int freq)
394 unsigned long aclk_400_rate = 0;
395 struct rk_context *platform;
400 platform = (struct rk_context *)kbdev->platform_context;
401 if (NULL == platform)
404 if (platform->clk_mali == 0)
409 aclk_400_rate = 533000000;
412 aclk_400_rate = 450000000;
415 aclk_400_rate = 400000000;
418 aclk_400_rate = 350000000;
421 aclk_400_rate = 267000000;
424 aclk_400_rate = 160000000;
427 aclk_400_rate = 100000000;
433 mali_dvfs_clk_set(platform->mali_clk_node,aclk_400_rate);
434 /* Waiting for clock is stable
436 tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_TOP0);
437 } while (tmp & 0x1000000);
444 int kbase_platform_dvfs_get_level(int freq)
447 for (i = 0; i < MALI_DVFS_STEP; i++) {
448 if (mali_dvfs_infotbl[i].clock == freq)
453 void kbase_platform_dvfs_set_level(kbase_device *kbdev, int level)
455 static int prev_level = -1;
457 if (level == prev_level)
460 if (WARN_ON((level >= MALI_DVFS_STEP) || (level < 0)))
461 panic("invalid level");
463 if (mali_dvfs_status_current.upper_lock >= 0 && level > mali_dvfs_status_current.upper_lock)
464 level = mali_dvfs_status_current.upper_lock;
466 if (mali_dvfs_status_current.under_lock >= 0 && level < mali_dvfs_status_current.under_lock)
467 level = mali_dvfs_status_current.under_lock;
469 #ifdef CONFIG_MALI_T6XX_DVFS
470 mutex_lock(&mali_set_clock_lock);
473 kbase_platform_dvfs_set_clock(kbdev, mali_dvfs_infotbl[level].clock);
475 #if defined(CONFIG_MALI_T6XX_DEBUG_SYS) && defined(CONFIG_MALI_T6XX_DVFS)
476 update_time_in_state(prev_level);
479 #ifdef CONFIG_MALI_T6XX_DVFS
480 mutex_unlock(&mali_set_clock_lock);
484 #ifdef CONFIG_MALI_T6XX_DEBUG_SYS
485 #ifdef CONFIG_MALI_T6XX_DVFS
486 static void update_time_in_state(int level)
489 static u64 prev_time=0;
494 if (!kbase_platform_dvfs_get_enable_status())
498 prev_time=get_jiffies_64();
500 current_time = get_jiffies_64();
501 mali_dvfs_infotbl[level].time += current_time-prev_time;
503 prev_time = current_time;
507 ssize_t show_time_in_state(struct device *dev, struct device_attribute *attr, char *buf)
509 struct kbase_device *kbdev;
513 kbdev = dev_get_drvdata(dev);
515 #ifdef CONFIG_MALI_T6XX_DVFS
516 update_time_in_state(mali_dvfs_status_current.step);
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);
524 if (ret < PAGE_SIZE - 1)
525 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
527 buf[PAGE_SIZE - 2] = '\n';
528 buf[PAGE_SIZE - 1] = '\0';
535 ssize_t set_time_in_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
539 for (i = 0; i < MALI_DVFS_STEP; i++)
540 mali_dvfs_infotbl[i].time = 0;
542 printk(KERN_DEBUG "time_in_state value is reset complete.\n");