blob: 46f497fbdd7799fe37586f29ed65f348bf5bbf72 [file] [log] [blame]
/****************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2014 - 2020 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 - 2020 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