diff options
Diffstat (limited to 'drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_power.c')
-rw-r--r-- | drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_power.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_power.c b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_power.c new file mode 100644 index 000000000000..92839cb6227f --- /dev/null +++ b/drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_power.c @@ -0,0 +1,381 @@ +/**************************************************************************** +* +* The MIT License (MIT) +* +* Copyright (c) 2014 - 2018 Vivante Corporation +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +* +***************************************************************************** +* +* The GPL License (GPL) +* +* Copyright (C) 2014 - 2018 Vivante Corporation +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +***************************************************************************** +* +* Note: This software is released under dual MIT and GPL licenses. A +* recipient may use this file under the terms of either the MIT license or +* GPL License. If you wish to use only one license not the other, you can +* indicate your decision by deleting one of the above license notices in your +* version of this file. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_POWER + +/******************************************************************************\ +************************ Dynamic Voltage Frequency Setting ********************* +\******************************************************************************/ +#if gcdDVFS +static gctUINT32 +_GetLoadHistory( + IN gckDVFS Dvfs, + IN gctUINT32 Select, + IN gctUINT32 Index +) +{ + return Dvfs->loads[Index]; +} + +static void +_IncreaseScale( + IN gckDVFS Dvfs, + IN gctUINT32 Load, + OUT gctUINT8 *Scale + ) +{ + if (Dvfs->currentScale < 32) + { + *Scale = Dvfs->currentScale + 8; + } + else + { + *Scale = Dvfs->currentScale + 8; + *Scale = gcmMIN(64, *Scale); + } +} + +static void +_RecordFrequencyHistory( + gckDVFS Dvfs, + gctUINT32 Frequency + ) +{ + gctUINT32 i = 0; + + struct _FrequencyHistory *history = Dvfs->frequencyHistory; + + for (i = 0; i < 16; i++) + { + if (history->frequency == Frequency) + { + break; + } + + if (history->frequency == 0) + { + history->frequency = Frequency; + break; + } + + history++; + } + + if (i < 16) + { + history->count++; + } +} + +static gctUINT32 +_GetFrequencyHistory( + gckDVFS Dvfs, + gctUINT32 Frequency + ) +{ + gctUINT32 i = 0; + + struct _FrequencyHistory * history = Dvfs->frequencyHistory; + + for (i = 0; i < 16; i++) + { + if (history->frequency == Frequency) + { + break; + } + + history++; + } + + if (i < 16) + { + return history->count; + } + + return 0; +} + +static void +_Policy( + IN gckDVFS Dvfs, + IN gctUINT32 Load, + OUT gctUINT8 *Scale + ) +{ + gctUINT8 load[4], nextLoad; + gctUINT8 scale; + + /* Last 4 history. */ + load[0] = (Load & 0xFF); + load[1] = (Load & 0xFF00) >> 8; + load[2] = (Load & 0xFF0000) >> 16; + load[3] = (Load & 0xFF000000) >> 24; + + /* Determine target scale. */ + if (load[0] > 54) + { + _IncreaseScale(Dvfs, Load, &scale); + } + else + { + nextLoad = (load[0] + load[1] + load[2] + load[3])/4; + + scale = Dvfs->currentScale * (nextLoad) / 54; + + scale = gcmMAX(1, scale); + scale = gcmMIN(64, scale); + } + + Dvfs->totalConfig++; + + Dvfs->loads[(load[0]-1)/8]++; + + *Scale = scale; + + + if (Dvfs->totalConfig % 100 == 0) + { + gcmkPRINT("======================================================="); + gcmkPRINT("GPU Load: %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d", + 8, 16, 24, 32, 40, 48, 56, 64); + gcmkPRINT(" %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d", + _GetLoadHistory(Dvfs,2, 0), + _GetLoadHistory(Dvfs,2, 1), + _GetLoadHistory(Dvfs,2, 2), + _GetLoadHistory(Dvfs,2, 3), + _GetLoadHistory(Dvfs,2, 4), + _GetLoadHistory(Dvfs,2, 5), + _GetLoadHistory(Dvfs,2, 6), + _GetLoadHistory(Dvfs,2, 7) + ); + + gcmkPRINT("Frequency(MHz) %-8d %-8d %-8d %-8d %-8d", + 58, 120, 240, 360, 480); + gcmkPRINT(" %-8d %-8d %-8d %-8d %-8d", + _GetFrequencyHistory(Dvfs, 58), + _GetFrequencyHistory(Dvfs,120), + _GetFrequencyHistory(Dvfs,240), + _GetFrequencyHistory(Dvfs,360), + _GetFrequencyHistory(Dvfs,480) + ); + } +} + +static void +_TimerFunction( + gctPOINTER Data + ) +{ + gceSTATUS status; + gckDVFS dvfs = (gckDVFS) Data; + gckHARDWARE hardware = dvfs->hardware; + gctUINT32 value; + gctUINT32 frequency; + gctUINT8 scale; + gctUINT32 t1, t2, consumed; + + gckOS_GetTicks(&t1); + + gcmkONERROR(gckHARDWARE_QueryLoad(hardware, &value)); + + /* determine target sacle. */ + _Policy(dvfs, value, &scale); + + /* Set frequency and voltage. */ + gcmkONERROR(gckOS_SetGPUFrequency(hardware->os, hardware->core, scale)); + + /* Query real frequency. */ + gcmkONERROR( + gckOS_QueryGPUFrequency(hardware->os, + hardware->core, + &frequency, + &dvfs->currentScale)); + + _RecordFrequencyHistory(dvfs, frequency); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_POWER, + "Current frequency = %d", + frequency); + + /* Set period. */ + gcmkONERROR(gckHARDWARE_SetDVFSPeroid(hardware, frequency)); + +OnError: + /* Determine next querying time. */ + gckOS_GetTicks(&t2); + + consumed = gcmMIN(((long)t2 - (long)t1), 5); + + if (dvfs->stop == gcvFALSE) + { + gcmkVERIFY_OK(gckOS_StartTimer(hardware->os, + dvfs->timer, + dvfs->pollingTime - consumed)); + } + + return; +} + +gceSTATUS +gckDVFS_Construct( + IN gckHARDWARE Hardware, + OUT gckDVFS * Dvfs + ) +{ + gceSTATUS status; + gctPOINTER pointer; + gckDVFS dvfs = gcvNULL; + gckOS os = Hardware->os; + + gcmkHEADER_ARG("Hardware=0x%X", Hardware); + + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + /* Allocate a gckDVFS manager. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckDVFS), &pointer)); + + gckOS_ZeroMemory(pointer, gcmSIZEOF(struct _gckDVFS)); + + dvfs = pointer; + + /* Initialization. */ + dvfs->hardware = Hardware; + dvfs->pollingTime = gcdDVFS_POLLING_TIME; + dvfs->os = Hardware->os; + dvfs->currentScale = 64; + + /* Create a polling timer. */ + gcmkONERROR(gckOS_CreateTimer(os, _TimerFunction, pointer, &dvfs->timer)); + + /* Initialize frequency and voltage adjustment helper. */ + gcmkONERROR(gckOS_PrepareGPUFrequency(os, Hardware->core)); + + /* Return result. */ + *Dvfs = dvfs; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (dvfs) + { + if (dvfs->timer) + { + gcmkVERIFY_OK(gckOS_DestroyTimer(os, dvfs->timer)); + } + + gcmkOS_SAFE_FREE(os, dvfs); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckDVFS_Destroy( + IN gckDVFS Dvfs + ) +{ + gcmkHEADER_ARG("Dvfs=0x%X", Dvfs); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + /* Deinitialize helper fuunction. */ + gcmkVERIFY_OK(gckOS_FinishGPUFrequency(Dvfs->os, Dvfs->hardware->core)); + + /* DestroyTimer. */ + gcmkVERIFY_OK(gckOS_DestroyTimer(Dvfs->os, Dvfs->timer)); + + gcmkOS_SAFE_FREE(Dvfs->os, Dvfs); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckDVFS_Start( + IN gckDVFS Dvfs + ) +{ + gcmkHEADER_ARG("Dvfs=0x%X", Dvfs); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + gckHARDWARE_InitDVFS(Dvfs->hardware); + + Dvfs->stop = gcvFALSE; + + gckOS_StartTimer(Dvfs->os, Dvfs->timer, Dvfs->pollingTime); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckDVFS_Stop( + IN gckDVFS Dvfs + ) +{ + gcmkHEADER_ARG("Dvfs=0x%X", Dvfs); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + Dvfs->stop = gcvTRUE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif |