2 * This confidential and proprietary software may be used only as
3 * authorised by a licensing agreement from ARM Limited
4 * (C) COPYRIGHT 2009-2012 ARM Limited
6 * The entire notice above must be reproduced on all authorised
7 * copies and copies may only be made to the extent permitted
8 * by a licensing agreement from ARM Limited.
12 * @file mali_platform.c
13 * Platform specific Mali driver functions for a default platform
15 #include "mali_kernel_common.h"
17 #include "mali_platform.h"
19 #include <linux/workqueue.h>
20 #include <linux/kernel.h>
21 #include <linux/slab.h>
22 #include <linux/gfp.h>
24 #include <linux/clk.h>
25 #include <linux/device.h>
26 #ifdef CONFIG_HAS_EARLYSUSPEND
27 #include <linux/earlysuspend.h>
28 #endif /* CONFIG_HAS_EARLYSUSPEND */
29 #include <linux/miscdevice.h>
30 #include <asm/uaccess.h>
31 #include <linux/module.h>
32 #include <linux/cpufreq.h>
34 #include <linux/rockchip/cpu.h>
35 #include <linux/rockchip/dvfs.h>
37 #define GPUCLK_NAME "clk_gpu"
38 #define GPUCLK_PD_NAME "pd_gpu"
39 #define GPU_MHZ 1000000
41 static struct dvfs_node *mali_clock = 0;
42 static struct clk *mali_clock_pd = 0;
43 static struct clk *audis_gpu_clk = 0;
45 #define MALI_DVFS_DEFAULT_STEP 0 // 50Mhz default
47 u32 mali_dvfs[] = {50, 100, 133, 160, 200, 266, 400};
50 u32 mali_init_clock = 50;
51 static int minuend = 0;
53 static struct cpufreq_frequency_table *freq_table = NULL;
55 module_param_array(mali_dvfs, int, &num_clock,S_IRUGO | S_IWUSR);
56 MODULE_PARM_DESC(mali_dvfs,"mali clock table");
58 module_param(mali_init_clock, int,S_IRUGO | S_IWUSR);
59 MODULE_PARM_DESC(mali_init_clock,"mali init clock value");
61 u32 mali_group_error = 0;
63 u32 gpu_power_state = 0;
64 static u32 utilization_global = 0;
66 u32 mali_utilization_timeout = 10;
67 u32 sampling_enable = 1;
69 #define mali_freq_workqueue_name "mali_freq_workqueue"
70 #define mali_freq_work_name "mali_freq_work"
72 struct mali_freq_data {
73 struct workqueue_struct *wq;
74 struct work_struct work;
78 typedef struct mali_dvfs_tableTag {
83 typedef struct mali_dvfs_statusTag {
85 mali_dvfs_table * pCurrentDvfs;
89 mali_dvfs_status maliDvfsStatus;
91 #define GPU_DVFS_UP_THRESHOLD ((int)((255*50)/100))
92 #define GPU_DVFS_DOWN_THRESHOLD ((int)((255*35)/100))
94 _mali_osk_mutex_t *clockSetlock;
96 struct clk* mali_clk_get(unsigned char *name)
99 clk = clk_get(NULL,name);
102 unsigned long mali_clk_get_rate(struct dvfs_node *clk)
104 return dvfs_clk_get_rate(clk);
107 void mali_clk_set_rate(struct dvfs_node *clk, u32 value)
109 unsigned long rate = (unsigned long)value * GPU_MHZ;
110 dvfs_clk_set_rate(clk, rate);
111 rate = mali_clk_get_rate(clk);
114 static struct kobject *mali400_utility_object;
116 static u32 get_mali_dvfs_status(void)
118 return maliDvfsStatus.currentStep;
120 static void set_mali_dvfs_step(u32 value)
122 maliDvfsStatus.currentStep = value;
125 static void scale_enable_set(u32 value)
127 scale_enable = value;
129 static u32 mali_dvfs_search(u32 value)
133 for (i=0;i<num_clock;i++) {
134 if (clock == mali_dvfs[i]) {
135 _mali_osk_mutex_wait(clockSetlock);
136 mali_clk_set_rate(mali_clock,clock);
137 _mali_osk_mutex_signal(clockSetlock);
138 set_mali_dvfs_step(i);
143 MALI_DEBUG_PRINT(2,("USER set clock not in the mali_dvfs table\r\n"));
148 static int mali400_utility_show(struct device *dev,struct device_attribute *attr, char *buf)
150 return sprintf(buf, "%d\n", utilization_global);
152 static int mali400_clock_set(struct device *dev,struct device_attribute *attr, const char *buf,u32 count)
157 clock = simple_strtoul(buf, NULL, 10);
158 currentStep = get_mali_dvfs_status();
159 timeValue = _mali_osk_time_get_ns();
160 /*MALI_PRINT(("USER SET CLOCK,%d\r\n",clock));*/
164 mali_dvfs_search(clock);
168 static int clock_show(struct device *dev,struct device_attribute *attr, char *buf)
172 pos += snprintf(pos,PAGE_SIZE,"%d,",num_clock);
173 for(i=0;i<(num_clock-1);i++) {
174 pos += snprintf(pos,PAGE_SIZE,"%d,",mali_dvfs[i]);
176 pos +=snprintf(pos,PAGE_SIZE,"%d\n",mali_dvfs[i]);
179 static int sampling_timeout_show(struct device *dev,struct device_attribute *attr, char *buf)
181 return sprintf(buf, "mali_utilization_timeout = %d\n", mali_utilization_timeout);
183 static int sampling_timeout_set(struct device *dev,struct device_attribute *attr,
184 const char *buf,u32 count)
187 sampling = simple_strtoul(buf, NULL, 10);
189 if (sampling == 0 ) {
191 MALI_PRINT(("disable mali clock frequency scalling\r\n"));
193 mali_utilization_timeout = sampling;
195 MALI_PRINT(("enable mali clock frequency scalling ,mali_utilization_timeout : %dms\r\n",
196 mali_utilization_timeout));
200 static int error_count_show(struct device *dev,struct device_attribute *attr, char *buf)
202 return sprintf(buf, "%d\n", mali_group_error);
205 static DEVICE_ATTR(utility, 0644, mali400_utility_show, mali400_clock_set);
206 static DEVICE_ATTR(param, 0644, clock_show, NULL);
207 static DEVICE_ATTR(sampling_timeout, 0644, sampling_timeout_show, sampling_timeout_set);
208 static DEVICE_ATTR(error_count, 0644, error_count_show, NULL);
211 static mali_bool mali400_utility_sysfs_init(void)
215 mali400_utility_object = kobject_create_and_add("mali400_utility", NULL);
216 if (mali400_utility_object == NULL) {
219 ret = sysfs_create_file(mali400_utility_object, &dev_attr_utility.attr);
223 ret = sysfs_create_file(mali400_utility_object, &dev_attr_param.attr);
227 ret = sysfs_create_file(mali400_utility_object, &dev_attr_sampling_timeout.attr);
231 ret = sysfs_create_file(mali400_utility_object, &dev_attr_error_count.attr);
237 static unsigned int decideNextStatus(unsigned int utilization)
241 if(utilization > GPU_DVFS_UP_THRESHOLD &&
242 maliDvfsStatus.currentStep == 0 &&
243 maliDvfsStatus.currentStep < (num_clock-minuend))
245 else if (utilization > GPU_DVFS_UP_THRESHOLD &&
246 maliDvfsStatus.currentStep == 1 &&
247 maliDvfsStatus.currentStep < (num_clock-minuend))
249 else if (utilization > GPU_DVFS_UP_THRESHOLD &&
250 maliDvfsStatus.currentStep == 2 &&
251 maliDvfsStatus.currentStep < (num_clock-minuend))
253 else if (utilization > GPU_DVFS_UP_THRESHOLD &&
254 maliDvfsStatus.currentStep == 3 &&
255 maliDvfsStatus.currentStep < (num_clock-minuend))
257 else if (utilization > GPU_DVFS_UP_THRESHOLD &&
258 maliDvfsStatus.currentStep == 4 &&
259 maliDvfsStatus.currentStep < (num_clock-minuend))
261 else if (utilization > GPU_DVFS_UP_THRESHOLD &&
262 maliDvfsStatus.currentStep == 5 &&
263 maliDvfsStatus.currentStep < (num_clock-minuend))
266 determined by minuend to up to level 6
268 else if(utilization < GPU_DVFS_DOWN_THRESHOLD &&
269 maliDvfsStatus.currentStep == 6)
271 else if(utilization < GPU_DVFS_DOWN_THRESHOLD &&
272 maliDvfsStatus.currentStep == 5)
274 else if(utilization < GPU_DVFS_DOWN_THRESHOLD &&
275 maliDvfsStatus.currentStep == 4)
277 else if(utilization < GPU_DVFS_DOWN_THRESHOLD &&
278 maliDvfsStatus.currentStep == 3)
280 else if(utilization < GPU_DVFS_DOWN_THRESHOLD &&
281 maliDvfsStatus.currentStep == 2)
283 else if(utilization < GPU_DVFS_DOWN_THRESHOLD &&
284 maliDvfsStatus.currentStep == 1)
287 level = maliDvfsStatus.currentStep;
291 static mali_bool set_mali_dvfs_status(u32 step)
293 u32 validatedStep=step;
295 _mali_osk_mutex_wait(clockSetlock);
296 mali_clk_set_rate(mali_clock, mali_dvfs[validatedStep]);
297 _mali_osk_mutex_signal(clockSetlock);
298 set_mali_dvfs_step(validatedStep);
303 static mali_bool change_mali_dvfs_status(u32 step)
305 if(!set_mali_dvfs_status(step)) {
306 MALI_DEBUG_PRINT(2,("error on set_mali_dvfs_status: %d\n",step));
313 static void mali_freq_scale_work(struct work_struct *work)
319 curStatus = get_mali_dvfs_status();
320 nextStatus = decideNextStatus(utilization_global);
322 if (curStatus!=nextStatus) {
323 if (!change_mali_dvfs_status(nextStatus)) {
324 MALI_DEBUG_PRINT(1, ("error on change_mali_dvfs_status \n"));
328 static mali_bool init_mali_clock(void)
330 mali_bool ret = MALI_TRUE;
333 if (mali_clock != 0 || mali_clock_pd != 0)
336 mali_clock_pd = clk_get(NULL,GPUCLK_PD_NAME);
337 if (IS_ERR(mali_clock_pd)) {
338 MALI_PRINT( ("MALI Error : failed to get source mali pd\n"));
342 clk_prepare_enable(mali_clock_pd);
344 mali_clock = clk_get_dvfs_node(GPUCLK_NAME);
345 if (IS_ERR(mali_clock)) {
346 MALI_PRINT( ("MALI Error : failed to get source mali clock\n"));
350 dvfs_clk_prepare_enable(mali_clock);
351 freq_table = dvfs_get_freq_volt_table(mali_clock);
353 MALI_PRINT(("Stop,dvfs table should be set in dts\n"));
356 for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
357 mali_dvfs[i] = freq_table[i].frequency/1000;
359 mali_init_clock = mali_dvfs[0];
362 MALI_PRINT(("Mali400 inside of rk3126\r\n"));
364 mali_clk_set_rate(mali_clock, mali_init_clock);
370 MALI_PRINT(("::clk_put:: %s mali_clock\n", __FUNCTION__));
373 clk_disable_unprepare(mali_clock_pd);
375 dvfs_clk_disable_unprepare(mali_clock);
381 static mali_bool deinit_mali_clock(void)
383 if (mali_clock == 0 && mali_clock_pd == 0)
385 dvfs_clk_disable_unprepare(mali_clock);
387 clk_disable_unprepare(mali_clock_pd);
397 mali_bool init_mali_dvfs_status(int step)
399 set_mali_dvfs_step(step);
403 #ifdef CONFIG_HAS_EARLYSUSPEND
404 static void mali_pm_early_suspend(struct early_suspend *mali_dev)
408 static void mali_pm_late_resume(struct early_suspend *mali_dev)
412 static struct early_suspend mali_dev_early_suspend = {
413 .suspend = mali_pm_early_suspend,
414 .resume = mali_pm_late_resume,
415 .level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
417 #endif /* CONFIG_HAS_EARLYSUSPEND */
419 _mali_osk_errcode_t mali_platform_init(void)
421 if (cpu_is_rk3036()) {
422 audis_gpu_clk = clk_get(NULL,"clk_gpu");
424 if (IS_ERR(audis_gpu_clk)) {
425 MALI_PRINT( ("MALI Error : failed to get audis mali clk\n"));
430 clk_prepare_enable(audis_gpu_clk);
434 MALI_CHECK(init_mali_clock(), _MALI_OSK_ERR_FAULT);
436 clockSetlock = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED,_MALI_OSK_LOCK_ORDER_UTILIZATION);
438 if(!init_mali_dvfs_status(MALI_DVFS_DEFAULT_STEP))
439 MALI_DEBUG_PRINT(1, ("init_mali_dvfs_status failed\n"));
441 if(mali400_utility_sysfs_init())
442 MALI_PRINT(("mali400_utility_sysfs_init error\r\n"));
444 mali_freq_data = kmalloc(sizeof(struct mali_freq_data), GFP_KERNEL);
445 if(!mali_freq_data) {
446 MALI_PRINT(("kmalloc error\r\n"));
449 mali_freq_data->wq = create_workqueue(mali_freq_workqueue_name);
450 if(!mali_freq_data->wq)
452 INIT_WORK(&mali_freq_data->work,mali_freq_scale_work);
454 #ifdef CONFIG_HAS_EARLYSUSPEND
455 register_early_suspend(&mali_dev_early_suspend);
461 _mali_osk_errcode_t mali_platform_deinit(void)
463 if (cpu_is_rk3036()) {
464 clk_disable_unprepare(audis_gpu_clk);
469 _mali_osk_mutex_term(clockSetlock);
473 _mali_osk_errcode_t mali_power_domain_control(u32 bpower_off)
476 if (!gpu_power_state) {
477 if (cpu_is_rk3036()) {
478 clk_prepare_enable(audis_gpu_clk);
481 clk_prepare_enable(mali_clock_pd);
483 dvfs_clk_prepare_enable(mali_clock);
485 gpu_power_state = 1 ;
487 } else if (bpower_off == 2) {
489 } else if (bpower_off == 1) {
490 if(gpu_power_state) {
491 if (cpu_is_rk3036()) {
492 clk_disable_unprepare(audis_gpu_clk);
494 dvfs_clk_disable_unprepare(mali_clock);
496 clk_disable_unprepare(mali_clock_pd);
505 _mali_osk_errcode_t mali_platform_power_mode_change(mali_power_mode power_mode)
509 case MALI_POWER_MODE_ON:
510 MALI_DEBUG_PRINT(2,("MALI_POWER_MODE_ON\r\n"));
511 mali_power_domain_control(MALI_POWER_MODE_ON);
513 case MALI_POWER_MODE_LIGHT_SLEEP:
514 MALI_DEBUG_PRINT(2,("MALI_POWER_MODE_LIGHT_SLEEP\r\n"));
515 mali_power_domain_control(MALI_POWER_MODE_LIGHT_SLEEP);
517 case MALI_POWER_MODE_DEEP_SLEEP:
518 MALI_DEBUG_PRINT(2,("MALI_POWER_MODE_DEEP_SLEEP\r\n"));
519 mali_power_domain_control(MALI_POWER_MODE_DEEP_SLEEP);
522 MALI_DEBUG_PRINT(2,("mali_platform_power_mode_change:power_mode(%d) not support \r\n",power_mode));
528 void mali_gpu_utilization_handler(struct mali_gpu_utilization_data *data)
533 if(data->utilization_pp > 256)
535 utilization_global = data->utilization_pp;
537 MALI_PRINT(("utilization_global = %d\r\n",utilization_global));
539 if(scale_enable && sampling_enable)
540 queue_work(mali_freq_data->wq,&mali_freq_data->work);