aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/arm/mali/platform/mach-origen/mali_devfreq.c
blob: b9d23d61f6009d087dc70b30e66edbf6ac7e2258 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
 /*
 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com/
 *	Vikas Sajjan <vikas.sajjan@samsung.com>
 *
 * EXYNOS4 - MALI frequency/voltage scaling support in DEVFREQ framework
 *	This version supports only EXYNOS4412 only.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/io.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/suspend.h>
#include <linux/opp.h>
#include <linux/devfreq.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>

#include <mach/regs-clock.h>
#include "mali_kernel_common.h"
#include "mali_osk.h"
#include "mali_platform.h"
#include "mali_devfreq.h"
#include <plat/map-s5p.h>

/* dvfs status */
struct mali_dvfs_status maliDvfsStatus;

#define MAX_MALI_DVFS_STEPS 4
#define MALI_DVFS_STEPS 4

int mali_dvfs_control;

struct mali_dvfs_threshold_tbl {
	unsigned int downthreshold;
	unsigned int upthreshold;
};

struct mali_dvfs_staycount {
	unsigned int staycount;
};

struct mali_dvfs_threshold_tbl mali_dvfs_threshold[MALI_DVFS_STEPS] = {
	{0, 70},
	{50, 70},
	{50, 85},
	{75, 100}
};

struct mali_dvfs_staycount mali_dvfs_staycount[MALI_DVFS_STEPS] = {
	/*step 0*/{1},
	/*step 1*/{1},
	/*step 2*/{1},
	/*step 3*/{1}
};

/* dvfs information */
/* L0 = 440Mhz, 1.025V */
/* L1 = 350Mhz, 0.95V  */
/* L2 = 266Mhz, 0.90V  */
/* L3 = 160Mhz, 0.875V */

int step0_clk = 160;
int step0_vol = 875000;
int step1_clk = 266;
int step1_vol = 900000;
int step0_up = 70;
int step1_down = 50;
int step2_clk = 350;
int step2_vol = 950000;
int step1_up = 70;
int step2_down = 50;
int step3_clk = 440;
int step3_vol = 1025000;
int step2_up = 85;
int step3_down = 75;

struct mali_dvfs_stp {
	int clk;
	int vol;
};

struct mali_dvfs_tbl mali_dvfs_value[MALI_DVFS_STEPS] = {
	{ 160, 1000000, 875000 },
	{ 266, 1000000, 900000 },
	{ 350, 1000000, 950000 },
	{ 440, 1000000, 1025000} };

struct mali_dvfs_stp step[MALI_DVFS_STEPS] = {
	/* step 0 clk */ { 160, 875000 },
	/* step 1 clk */ { 266, 900000 },
	/* step 2 clk */ { 350, 950000 },
	/* step 3 clk */ { 440, 1025000 }
};

int change_dvfs_tableset(int change_clk, int change_step)
{
	if (change_clk < mali_dvfs_value[1].clock) {
		mali_dvfs_value[change_step].clock = mali_dvfs_value[0].clock;
		mali_dvfs_value[change_step].vol = mali_dvfs_value[0].vol;
	} else if (change_clk < mali_dvfs_value[2].clock && change_clk >=
						mali_dvfs_value[1].clock) {
		mali_dvfs_value[change_step].clock = mali_dvfs_value[1].clock;
		mali_dvfs_value[change_step].vol = mali_dvfs_value[1].vol;
	} else if (change_clk < mali_dvfs_value[3].clock && change_clk >=
						mali_dvfs_value[2].clock) {
		mali_dvfs_value[change_step].clock = mali_dvfs_value[2].clock;
		mali_dvfs_value[change_step].vol = mali_dvfs_value[2].vol;
	} else {
		mali_dvfs_value[change_step].clock = mali_dvfs_value[3].clock;
		mali_dvfs_value[change_step].vol = mali_dvfs_value[3].vol;
	}

	if (maliDvfsStatus.currentStep == change_step) {
		/* change the voltage */
		mali_regulator_set_voltage(mali_dvfs_value[change_step].vol,
					mali_dvfs_value[change_step].vol);
		/* change the clock */
		mali_clk_set_rate(mali_dvfs_value[change_step].clock,
					mali_dvfs_value[change_step].freq);
	}

	return mali_dvfs_value[change_step].clock;
}

mali_bool set_mali_dvfs_current_step(unsigned int step)
{
	_mali_osk_lock_wait(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW);
	maliDvfsStatus.currentStep = step;
	_mali_osk_lock_signal(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW);
	return MALI_TRUE;
}

static mali_bool set_mali_dvfs_status(u32 step, mali_bool boostup)
{
	u32 validatedStep = step;

	if (mali_regulator_get_usecount() == 0)
		return MALI_FALSE;

	if (boostup) {
		/* change the voltage */
		mali_regulator_set_voltage(mali_dvfs_value[step].vol,
						mali_dvfs_value[step].vol);
		/* change the clock */
		mali_clk_set_rate(mali_dvfs_value[step].clock,
						mali_dvfs_value[step].freq);
	} else {
		/* change the clock */
		mali_clk_set_rate(mali_dvfs_value[step].clock,
						mali_dvfs_value[step].freq);
		/* change the voltage */
		mali_regulator_set_voltage(mali_dvfs_value[step].vol,
						mali_dvfs_value[step].vol);
	}

	set_mali_dvfs_current_step(validatedStep);

	/* for future use */
	maliDvfsStatus.pCurrentDvfs = &mali_dvfs_value[validatedStep];

	return MALI_TRUE;
}

static void mali_platform_waiting(u32 msec)
{
	unsigned int read_val;
	while (1) {
			read_val = _mali_osk_mem_ioread32(
					clk_register_map,
					0x00);
			if ((read_val & 0x8000) == 0x0000)
				break;
			/* 1000 -> 100 : 20101218 */
			_mali_osk_time_ubusydelay(100);
		}
}

static mali_bool change_mali_dvfs_status(u32 step, mali_bool boostup)
{
	if (!set_mali_dvfs_status(step, boostup))
		return MALI_FALSE;

	/* wait until clock and voltage is stablized */
	mali_platform_waiting(MALI_DVFS_WAITING); /* msec */
	return MALI_TRUE;
}

static	unsigned int decideNextStatus(unsigned int mali_dvfs_freq)
{
	static unsigned int level; /* 0:stay, 1:up */
	static int mali_dvfs_clk;

	if (!mali_dvfs_control && level == maliDvfsStatus.currentStep) {
		if (mali_dvfs_freq >
		(int)((mali_dvfs_value[maliDvfsStatus.currentStep].clock *
			mali_dvfs_value[maliDvfsStatus.currentStep].freq))
			&& level < MALI_DVFS_STEPS - 1) {
			level++;
		}
		if (mali_dvfs_freq <
		(int)((mali_dvfs_value[maliDvfsStatus.currentStep].clock *
			mali_dvfs_value[maliDvfsStatus.currentStep].freq))
			&& level > 0) {
			level--;
		}
	} else if (mali_dvfs_control == 999) {
		int i = 0;
		for (i = 0; i < MALI_DVFS_STEPS; i++)
			step[i].clk = mali_dvfs_value[i].clock;
#ifdef EXYNOS4_ASV_ENABLED
		mali_dvfs_table_update();
#endif
		i = 0;
		for (i = 0; i < MALI_DVFS_STEPS; i++)
			mali_dvfs_value[i].clock = step[i].clk;

		mali_dvfs_control = 0;
		level = 0;

		step0_clk = step[0].clk;
		change_dvfs_tableset(step0_clk, 0);

		step1_clk = step[1].clk;
		change_dvfs_tableset(step1_clk, 1);

		step2_clk = step[2].clk;
		change_dvfs_tableset(step2_clk, 2);

		step3_clk = step[3].clk;
		change_dvfs_tableset(step3_clk, 3);

	} else if (mali_dvfs_control != mali_dvfs_clk && mali_dvfs_control
			!= 999) {
		if (mali_dvfs_control < mali_dvfs_value[1].clock
						&& mali_dvfs_control > 0) {
			int i = 0;
			for (i = 0; i < MALI_DVFS_STEPS; i++)
				step[i].clk = mali_dvfs_value[0].clock;
		} else if (mali_dvfs_control < mali_dvfs_value[2].clock
			&& mali_dvfs_control >= mali_dvfs_value[1].clock) {
			int i = 0;
			for (i = 0; i < MALI_DVFS_STEPS; i++)
				step[i].clk = mali_dvfs_value[1].clock;
		} else if (mali_dvfs_control < mali_dvfs_value[3].clock
			&& mali_dvfs_control >= mali_dvfs_value[2].clock) {
			int i = 0;
			for (i = 0; i < MALI_DVFS_STEPS; i++)
				step[i].clk = mali_dvfs_value[2].clock;
		} else {
			int i = 0;
			for (i = 0; i < MALI_DVFS_STEPS; i++)
				step[i].clk  = mali_dvfs_value[3].clock;
		}

		step0_clk = step[0].clk;
		change_dvfs_tableset(step0_clk, 0);
		step1_clk = step[1].clk;
		change_dvfs_tableset(step1_clk, 1);
		step2_clk = step[2].clk;
		change_dvfs_tableset(step2_clk, 2);
		step3_clk = step[3].clk;
		change_dvfs_tableset(step3_clk, 3);
		level = maliDvfsStatus.currentStep;
	}

	mali_dvfs_clk = mali_dvfs_control;
	return level;
}

static unsigned int get_mali_dvfs_status(void)
{
	return maliDvfsStatus.currentStep;
}

static mali_bool mali_dvfs_status(u32 mali_dvfs_freq)
{
	unsigned int nextStatus = 0;
	unsigned int curStatus = 0;
	mali_bool boostup = MALI_FALSE;
	static int stay_count;
#ifdef EXYNOS4_ASV_ENABLED
	static mali_bool asv_applied = MALI_FALSE;
#endif
#ifdef EXYNOS4_ASV_ENABLED
	if (asv_applied == MALI_FALSE) {
		mali_dvfs_table_update();
		change_mali_dvfs_status(1, 0);
		asv_applied = MALI_TRUE;
		return MALI_TRUE;
	}
#endif
	/* decide next step */
	curStatus = get_mali_dvfs_status();
	nextStatus = decideNextStatus(mali_dvfs_freq);

	/* if next status is same with current status, don't change anything */
	if ((curStatus != nextStatus && stay_count == 0)) {
		/* check if boost up or not */
		if (nextStatus > maliDvfsStatus.currentStep)
			boostup = 1;
		/* change mali dvfs status */
		if (!change_mali_dvfs_status(nextStatus, boostup))
			return MALI_FALSE;
		stay_count =
		mali_dvfs_staycount[maliDvfsStatus.currentStep].staycount;
	} else {
		if (stay_count > 0)
			stay_count--;
	}
	return MALI_TRUE;
}

mali_bool init_mali_dvfs_status(int step)
{
	set_mali_dvfs_current_step(step);
	return MALI_TRUE;
}

void deinit_mali_dvfs_status(void)
{
	if (clk_register_map) {
		_mali_osk_mem_unmapioregion(CLK_DIV_STAT_G3D,
							0x20,
							clk_register_map);
		clk_register_map = 0;
	}
}

void mali_default_step_set(int step, mali_bool boostup)
{
	mali_clk_set_rate(mali_dvfs_value[step].clock,
				mali_dvfs_value[step].freq);
	if (maliDvfsStatus.currentStep == 1)
		set_mali_dvfs_status(step, boostup);
}

void mali_gpu_utilization_handler(u32 mali_dvfs_freq)
{
	int change_clk = 0;
	int change_step = 0;

	/* dvfs table change when clock was changed */
	if (step0_clk != mali_dvfs_value[0].clock) {
		MALI_PRINT(("::: step0_clk change to %d Mhz\n", step0_clk));
		change_clk = step0_clk;
		change_step = 0;
		step0_clk = change_dvfs_tableset(change_clk, change_step);
	}
	if (step1_clk != mali_dvfs_value[1].clock) {
		MALI_PRINT(("::: step1_clk change to %d Mhz\n", step1_clk));
		change_clk = step1_clk;
		change_step = 1;
		step1_clk = change_dvfs_tableset(change_clk, change_step);
	}
	if (step0_up != mali_dvfs_threshold[0].upthreshold) {
		MALI_PRINT(("::: step0_up change to %d %\n", step0_up));
		mali_dvfs_threshold[0].upthreshold = step0_up;
	}
	if (step1_down != mali_dvfs_threshold[1].downthreshold) {
		MALI_PRINT((":::step1_down change to %d %\n", step1_down));
		mali_dvfs_threshold[1].downthreshold = step1_down;
	}
	if (step2_clk != mali_dvfs_value[2].clock) {
		MALI_PRINT(("::: step2_clk change to %d Mhz\n", step2_clk));
		change_clk = step2_clk;
		change_step = 2;
		step2_clk = change_dvfs_tableset(change_clk, change_step);
	}
	if (step1_up != mali_dvfs_threshold[1].upthreshold) {
		MALI_PRINT((":::step1_up change to %d %\n", step1_up));
		mali_dvfs_threshold[1].upthreshold = step1_up;
	}
	if (step2_down != mali_dvfs_threshold[2].downthreshold) {
		MALI_PRINT((":::step2_down change to %d %\n", step2_down));
		mali_dvfs_threshold[2].downthreshold = step2_down;
	}
	if (step3_clk != mali_dvfs_value[3].clock) {
		MALI_PRINT(("::: step3_clk change to %d Mhz\n", step3_clk));
		change_clk = step3_clk;
		change_step = 3;
		step3_clk = change_dvfs_tableset(change_clk, change_step);
	}
	if (step2_up != mali_dvfs_threshold[2].upthreshold) {
		MALI_PRINT((":::step2_up change to %d %\n", step2_up));
		mali_dvfs_threshold[2].upthreshold = step2_up;
	}
	if (step3_down != mali_dvfs_threshold[3].downthreshold) {
		MALI_PRINT((":::step3_down change to %d %\n", step3_down));
		mali_dvfs_threshold[3].downthreshold = step3_down;
	}
#ifdef DEBUG
	mali_dvfs_value[0].vol = step0_vol;
	mali_dvfs_value[1].vol = step1_vol;
	mali_dvfs_value[2].vol = step2_vol;
	mali_dvfs_value[3].vol = step3_vol;
#endif
	MALI_DEBUG_PRINT(3, ("=== mali_dvfs_work_handler\n"));

	if (!mali_dvfs_status(mali_dvfs_freq))
		MALI_DEBUG_PRINT(1, ("error on mali dvfs status"
					"in mali_dvfs_work_handler"));

}
/** @brief Get MALI current running frequency
 *
 * This function gets the current running frequency of MALI
 *
 * @return frequency in Hz
 */
unsigned long get_mali_platform_cur_freq(void)
{
	unsigned long rate = 0;
	rate  = mali_clk_get_rate();
	return rate;
}