]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_power.c
gpu: vivante: Update driver from Freescale 3.10.53-1.1-ga BSP
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_power.c
1 /****************************************************************************
2 *
3 *    Copyright (C) 2005 - 2014 by Vivante Corp.
4 *
5 *    This program is free software; you can redistribute it and/or modify
6 *    it under the terms of the GNU General Public License as published by
7 *    the Free Software Foundation; either version 2 of the license, or
8 *    (at your option) any later version.
9 *
10 *    This program is distributed in the hope that it will be useful,
11 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 *    GNU General Public License for more details.
14 *
15 *    You should have received a copy of the GNU General Public License
16 *    along with this program; if not write to the Free Software
17 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 *****************************************************************************/
20
21
22 #include "gc_hal_kernel_precomp.h"
23
24 #define _GC_OBJ_ZONE    gcvZONE_POWER
25
26 /******************************************************************************\
27 ************************ Dynamic Voltage Frequency Setting *********************
28 \******************************************************************************/
29 #if gcdDVFS
30 static gctUINT32
31 _GetLoadHistory(
32     IN gckDVFS Dvfs,
33     IN gctUINT32 Select,
34     IN gctUINT32 Index
35 )
36 {
37     return Dvfs->loads[Index];
38 }
39
40 static void
41 _IncreaseScale(
42     IN gckDVFS Dvfs,
43     IN gctUINT32 Load,
44     OUT gctUINT8 *Scale
45     )
46 {
47     if (Dvfs->currentScale < 32)
48     {
49         *Scale = Dvfs->currentScale + 8;
50     }
51     else
52     {
53         *Scale = Dvfs->currentScale + 8;
54         *Scale = gcmMIN(64, *Scale);
55     }
56 }
57
58 static void
59 _RecordFrequencyHistory(
60     gckDVFS Dvfs,
61     gctUINT32 Frequency
62     )
63 {
64     gctUINT32 i = 0;
65
66     struct _FrequencyHistory *history = Dvfs->frequencyHistory;
67
68     for (i = 0; i < 16; i++)
69     {
70         if (history->frequency == Frequency)
71         {
72             break;
73         }
74
75         if (history->frequency == 0)
76         {
77             history->frequency = Frequency;
78             break;
79         }
80
81         history++;
82     }
83
84     if (i < 16)
85     {
86         history->count++;
87     }
88 }
89
90 static gctUINT32
91 _GetFrequencyHistory(
92     gckDVFS Dvfs,
93     gctUINT32 Frequency
94     )
95 {
96     gctUINT32 i = 0;
97
98     struct _FrequencyHistory * history = Dvfs->frequencyHistory;
99
100     for (i = 0; i < 16; i++)
101     {
102         if (history->frequency == Frequency)
103         {
104             break;
105         }
106
107         history++;
108     }
109
110     if (i < 16)
111     {
112         return history->count;
113     }
114
115     return 0;
116 }
117
118 static void
119 _Policy(
120     IN gckDVFS Dvfs,
121     IN gctUINT32 Load,
122     OUT gctUINT8 *Scale
123     )
124 {
125     gctUINT8 load[4], nextLoad;
126     gctUINT8 scale;
127
128     /* Last 4 history. */
129     load[0] = (Load & 0xFF);
130     load[1] = (Load & 0xFF00) >> 8;
131     load[2] = (Load & 0xFF0000) >> 16;
132     load[3] = (Load & 0xFF000000) >> 24;
133
134     /* Determine target scale. */
135     if (load[0] > 54)
136     {
137         _IncreaseScale(Dvfs, Load, &scale);
138     }
139     else
140     {
141         nextLoad = (load[0] + load[1] + load[2] + load[3])/4;
142
143         scale = Dvfs->currentScale * (nextLoad) / 54;
144
145         scale = gcmMAX(1, scale);
146         scale = gcmMIN(64, scale);
147     }
148
149     Dvfs->totalConfig++;
150
151     Dvfs->loads[(load[0]-1)/8]++;
152
153     *Scale = scale;
154
155
156     if (Dvfs->totalConfig % 100 == 0)
157     {
158         gcmkPRINT("=======================================================");
159         gcmkPRINT("GPU Load:       %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d",
160                                    8, 16, 24, 32, 40, 48, 56, 64);
161         gcmkPRINT("                %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d",
162                   _GetLoadHistory(Dvfs,2, 0),
163                   _GetLoadHistory(Dvfs,2, 1),
164                   _GetLoadHistory(Dvfs,2, 2),
165                   _GetLoadHistory(Dvfs,2, 3),
166                   _GetLoadHistory(Dvfs,2, 4),
167                   _GetLoadHistory(Dvfs,2, 5),
168                   _GetLoadHistory(Dvfs,2, 6),
169                   _GetLoadHistory(Dvfs,2, 7)
170                   );
171
172         gcmkPRINT("Frequency(MHz)  %-8d %-8d %-8d %-8d %-8d",
173                   58, 120, 240, 360, 480);
174         gcmkPRINT("                %-8d %-8d %-8d %-8d %-8d",
175                   _GetFrequencyHistory(Dvfs, 58),
176                   _GetFrequencyHistory(Dvfs,120),
177                   _GetFrequencyHistory(Dvfs,240),
178                   _GetFrequencyHistory(Dvfs,360),
179                   _GetFrequencyHistory(Dvfs,480)
180                   );
181     }
182 }
183
184 static void
185 _TimerFunction(
186     gctPOINTER Data
187     )
188 {
189     gceSTATUS status;
190     gckDVFS dvfs = (gckDVFS) Data;
191     gckHARDWARE hardware = dvfs->hardware;
192     gctUINT32 value;
193     gctUINT32 frequency;
194     gctUINT8 scale;
195     gctUINT32 t1, t2, consumed;
196
197     gckOS_GetTicks(&t1);
198
199     gcmkONERROR(gckHARDWARE_QueryLoad(hardware, &value));
200
201     /* determine target sacle. */
202     _Policy(dvfs, value, &scale);
203
204     /* Set frequency and voltage. */
205     gcmkONERROR(gckOS_SetGPUFrequency(hardware->os, hardware->core, scale));
206
207     /* Query real frequency. */
208     gcmkONERROR(
209         gckOS_QueryGPUFrequency(hardware->os,
210                                 hardware->core,
211                                 &frequency,
212                                 &dvfs->currentScale));
213
214     _RecordFrequencyHistory(dvfs, frequency);
215
216     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_POWER,
217                    "Current frequency = %d",
218                    frequency);
219
220     /* Set period. */
221     gcmkONERROR(gckHARDWARE_SetDVFSPeroid(hardware, frequency));
222
223 OnError:
224     /* Determine next querying time. */
225     gckOS_GetTicks(&t2);
226
227     consumed = gcmMIN(((long)t2 - (long)t1), 5);
228
229     if (dvfs->stop == gcvFALSE)
230     {
231         gcmkVERIFY_OK(gckOS_StartTimer(hardware->os,
232                                        dvfs->timer,
233                                        dvfs->pollingTime - consumed));
234     }
235
236     return;
237 }
238
239 gceSTATUS
240 gckDVFS_Construct(
241     IN gckHARDWARE Hardware,
242     OUT gckDVFS * Dvfs
243     )
244 {
245     gceSTATUS status;
246     gctPOINTER pointer;
247     gckDVFS dvfs = gcvNULL;
248     gckOS os = Hardware->os;
249
250     gcmkHEADER_ARG("Hardware=0x%X", Hardware);
251
252     gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE);
253     gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL);
254
255     /* Allocate a gckDVFS manager. */
256     gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckDVFS), &pointer));
257
258     gckOS_ZeroMemory(pointer, gcmSIZEOF(struct _gckDVFS));
259
260     dvfs = pointer;
261
262     /* Initialization. */
263     dvfs->hardware = Hardware;
264     dvfs->pollingTime = gcdDVFS_POLLING_TIME;
265     dvfs->os = Hardware->os;
266     dvfs->currentScale = 64;
267
268     /* Create a polling timer. */
269     gcmkONERROR(gckOS_CreateTimer(os, _TimerFunction, pointer, &dvfs->timer));
270
271     /* Initialize frequency and voltage adjustment helper. */
272     gcmkONERROR(gckOS_PrepareGPUFrequency(os, Hardware->core));
273
274     /* Return result. */
275     *Dvfs = dvfs;
276
277     gcmkFOOTER_NO();
278     return gcvSTATUS_OK;
279
280 OnError:
281     /* Roll back. */
282     if (dvfs)
283     {
284         if (dvfs->timer)
285         {
286             gcmkVERIFY_OK(gckOS_DestroyTimer(os, dvfs->timer));
287         }
288
289         gcmkOS_SAFE_FREE(os, dvfs);
290     }
291
292     gcmkFOOTER();
293     return status;
294 }
295
296 gceSTATUS
297 gckDVFS_Destroy(
298     IN gckDVFS Dvfs
299     )
300 {
301     gcmkHEADER_ARG("Dvfs=0x%X", Dvfs);
302     gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL);
303
304     /* Deinitialize helper fuunction. */
305     gcmkVERIFY_OK(gckOS_FinishGPUFrequency(Dvfs->os, Dvfs->hardware->core));
306
307     /* DestroyTimer. */
308     gcmkVERIFY_OK(gckOS_DestroyTimer(Dvfs->os, Dvfs->timer));
309
310     gcmkOS_SAFE_FREE(Dvfs->os, Dvfs);
311
312     gcmkFOOTER_NO();
313     return gcvSTATUS_OK;
314 }
315
316 gceSTATUS
317 gckDVFS_Start(
318     IN gckDVFS Dvfs
319     )
320 {
321     gcmkHEADER_ARG("Dvfs=0x%X", Dvfs);
322     gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL);
323
324     gckHARDWARE_InitDVFS(Dvfs->hardware);
325
326     Dvfs->stop = gcvFALSE;
327
328     gckOS_StartTimer(Dvfs->os, Dvfs->timer, Dvfs->pollingTime);
329
330     gcmkFOOTER_NO();
331     return gcvSTATUS_OK;
332 }
333
334 gceSTATUS
335 gckDVFS_Stop(
336     IN gckDVFS Dvfs
337     )
338 {
339     gcmkHEADER_ARG("Dvfs=0x%X", Dvfs);
340     gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL);
341
342     Dvfs->stop = gcvTRUE;
343
344     gcmkFOOTER_NO();
345     return gcvSTATUS_OK;
346 }
347 #endif