/*************************************************************************/ /*!
|
@File
|
@Title Device specific power routines
|
@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
|
@Description Device specific functions
|
@License Dual MIT/GPLv2
|
|
The contents of this file are subject to the MIT license as set out below.
|
|
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.
|
|
Alternatively, the contents of this file may be used under the terms of
|
the GNU General Public License Version 2 ("GPL") in which case the provisions
|
of GPL are applicable instead of those above.
|
|
If you wish to allow use of your version of this file only under the terms of
|
GPL, and not to allow others to use your version of this file under the terms
|
of the MIT license, indicate your decision by deleting the provisions above
|
and replace them with the notice and other provisions required by GPL as set
|
out in the file called "GPL-COPYING" included in this distribution. If you do
|
not delete the provisions above, a recipient may use your version of this file
|
under the terms of either the MIT license or GPL.
|
|
This License is also included in this distribution in the file called
|
"MIT-COPYING".
|
|
EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) 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; AND (B) 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.
|
*/ /**************************************************************************/
|
|
#include <stddef.h>
|
#include "rgxpower.h"
|
#include "rgxinit.h"
|
#include "rgx_fwif_km.h"
|
#include "rgxfwutils.h"
|
#include "pdump_km.h"
|
#include "pvr_debug.h"
|
#include "osfunc.h"
|
#include "rgxdebug.h"
|
#include "devicemem.h"
|
#include "devicemem_pdump.h"
|
#include "rgxtimecorr.h"
|
#include "devicemem_utils.h"
|
#include "htbserver.h"
|
#include "rgxstartstop.h"
|
#include "sync.h"
|
#include "lists.h"
|
|
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
|
#include "process_stats.h"
|
#endif
|
#if defined(PVR_DVFS)
|
#include "pvr_dvfs_device.h"
|
#endif
|
|
static PVRSRV_ERROR RGXFWNotifyHostTimeout(PVRSRV_RGXDEV_INFO *psDevInfo)
|
{
|
PVRSRV_ERROR eError;
|
RGXFWIF_KCCB_CMD sCmd;
|
RGXFWIF_RUNTIME_CFG *psRuntimeCfg = psDevInfo->psRGXFWIfRuntimeCfg;
|
|
/* Send the Timeout notification to the FW */
|
/* Extending the APM Latency Change command structure with the notification boolean for
|
backwards compatibility reasons */
|
sCmd.eCmdType = RGXFWIF_KCCB_CMD_POW;
|
sCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_APM_LATENCY_CHANGE;
|
sCmd.uCmdData.sPowData.uPoweReqData.ui32ActivePMLatencyms = psRuntimeCfg->ui32ActivePMLatencyms;
|
sCmd.uCmdData.sPowData.bNotifyTimeout = IMG_TRUE;
|
|
/* Ensure the new APM latency is written to memory before requesting the FW to read it */
|
OSMemoryBarrier();
|
|
eError = RGXSendCommand(psDevInfo,
|
RGXFWIF_DM_GP,
|
&sCmd,
|
sizeof(sCmd),
|
PDUMP_FLAGS_NONE);
|
|
return eError;
|
}
|
|
static void _RGXUpdateGPUUtilStats(PVRSRV_RGXDEV_INFO *psDevInfo)
|
{
|
RGXFWIF_GPU_UTIL_FWCB *psUtilFWCb;
|
IMG_UINT64 *paui64StatsCounters;
|
IMG_UINT64 ui64LastPeriod;
|
IMG_UINT64 ui64LastState;
|
IMG_UINT64 ui64LastTime;
|
IMG_UINT64 ui64TimeNow;
|
|
psUtilFWCb = psDevInfo->psRGXFWIfGpuUtilFWCb;
|
paui64StatsCounters = &psUtilFWCb->aui64StatsCounters[0];
|
|
OSLockAcquire(psDevInfo->hGPUUtilLock);
|
|
ui64TimeNow = RGXFWIF_GPU_UTIL_GET_TIME(OSClockns64());
|
|
/* Update counters to account for the time since the last update */
|
ui64LastState = RGXFWIF_GPU_UTIL_GET_STATE(psUtilFWCb->ui64LastWord);
|
ui64LastTime = RGXFWIF_GPU_UTIL_GET_TIME(psUtilFWCb->ui64LastWord);
|
ui64LastPeriod = RGXFWIF_GPU_UTIL_GET_PERIOD(ui64TimeNow, ui64LastTime);
|
paui64StatsCounters[ui64LastState] += ui64LastPeriod;
|
|
/* Update state and time of the latest update */
|
psUtilFWCb->ui64LastWord = RGXFWIF_GPU_UTIL_MAKE_WORD(ui64TimeNow, ui64LastState);
|
|
OSLockRelease(psDevInfo->hGPUUtilLock);
|
}
|
|
|
static INLINE PVRSRV_ERROR RGXDoStop(PVRSRV_DEVICE_NODE *psDeviceNode)
|
{
|
PVRSRV_ERROR eError;
|
|
#if defined(SUPPORT_TRUSTED_DEVICE) && !defined(NO_HARDWARE)
|
PVRSRV_DEVICE_CONFIG *psDevConfig = psDeviceNode->psDevConfig;
|
|
if (psDevConfig->pfnTDRGXStop == NULL)
|
{
|
PVR_DPF((PVR_DBG_ERROR, "RGXPrePowerState: TDRGXStop not implemented!"));
|
return PVRSRV_ERROR_NOT_IMPLEMENTED;
|
}
|
|
eError = psDevConfig->pfnTDRGXStop(psDevConfig->hSysData);
|
#else
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
|
eError = RGXStop(&psDevInfo->sPowerParams);
|
#endif
|
|
return eError;
|
}
|
|
/*
|
RGXPrePowerState
|
*/
|
PVRSRV_ERROR RGXPrePowerState (IMG_HANDLE hDevHandle,
|
PVRSRV_DEV_POWER_STATE eNewPowerState,
|
PVRSRV_DEV_POWER_STATE eCurrentPowerState,
|
IMG_BOOL bForced)
|
{
|
PVRSRV_ERROR eError = PVRSRV_OK;
|
|
if ((eNewPowerState != eCurrentPowerState) &&
|
(eNewPowerState != PVRSRV_DEV_POWER_STATE_ON))
|
{
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGXFWIF_KCCB_CMD sPowCmd;
|
RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf;
|
|
/* Send the Power off request to the FW */
|
sPowCmd.eCmdType = RGXFWIF_KCCB_CMD_POW;
|
sPowCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_OFF_REQ;
|
sPowCmd.uCmdData.sPowData.uPoweReqData.bForced = bForced;
|
|
eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim",
|
__FUNCTION__));
|
return eError;
|
}
|
|
eError = RGXSendCommand(psDevInfo,
|
RGXFWIF_DM_GP,
|
&sPowCmd,
|
sizeof(sPowCmd),
|
PDUMP_FLAGS_NONE);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Failed to send Power off request"));
|
return eError;
|
}
|
|
/* Wait for the firmware to complete processing. It cannot use PVRSRVWaitForValueKM as it relies
|
on the EventObject which is signalled in this MISR */
|
eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 0x1, 0xFFFFFFFF);
|
|
/* Check the Power state after the answer */
|
if (eError == PVRSRV_OK)
|
{
|
/* Finally, de-initialise some registers. */
|
if (psFWTraceBuf->ePowState == RGXFWIF_POW_OFF)
|
{
|
#if !defined(NO_HARDWARE)
|
IMG_UINT32 ui32TID;
|
for (ui32TID = 0; ui32TID < RGXFW_THREAD_NUM; ui32TID++)
|
{
|
/* Wait for the pending META/MIPS to host interrupts to come back. */
|
eError = PVRSRVPollForValueKM(&psDevInfo->aui32SampleIRQCount[ui32TID],
|
psFWTraceBuf->aui32InterruptCount[ui32TID],
|
0xffffffff);
|
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR, \
|
"RGXPrePowerState: Wait for pending interrupts failed. Thread %u: Host:%u, FW: %u", \
|
ui32TID, \
|
psDevInfo->aui32SampleIRQCount[ui32TID], \
|
psFWTraceBuf->aui32InterruptCount[ui32TID]));
|
|
RGX_WaitForInterruptsTimeout(psDevInfo);
|
break;
|
}
|
}
|
#endif /* NO_HARDWARE */
|
|
/* Update GPU frequency and timer correlation related data */
|
RGXGPUFreqCalibratePrePowerOff(psDeviceNode);
|
|
/* Update GPU state counters */
|
_RGXUpdateGPUUtilStats(psDevInfo);
|
|
#if defined(PVR_DVFS)
|
eError = SuspendDVFS();
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Failed to suspend DVFS"));
|
return eError;
|
}
|
#endif
|
|
psDevInfo->bRGXPowered = IMG_FALSE;
|
|
eError = RGXDoStop(psDeviceNode);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR, "RGXPrePowerState: RGXDoStop failed (%s)",
|
PVRSRVGetErrorStringKM(eError)));
|
eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_FAILURE;
|
}
|
}
|
else
|
{
|
/* the sync was updated bu the pow state isn't off -> the FW denied the transition */
|
eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_DENIED;
|
|
if (bForced)
|
{ /* It is an error for a forced request to be denied */
|
PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Failure to power off during a forced power off. FW: %d", psFWTraceBuf->ePowState));
|
}
|
}
|
}
|
else if (eError == PVRSRV_ERROR_TIMEOUT)
|
{
|
/* timeout waiting for the FW to ack the request: return timeout */
|
PVR_DPF((PVR_DBG_WARNING,"RGXPrePowerState: Timeout waiting for powoff ack from the FW"));
|
}
|
else
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXPrePowerState: Error waiting for powoff ack from the FW (%s)", PVRSRVGetErrorStringKM(eError)));
|
eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_FAILURE;
|
}
|
|
}
|
|
return eError;
|
}
|
|
|
static INLINE PVRSRV_ERROR RGXDoStart(PVRSRV_DEVICE_NODE *psDeviceNode)
|
{
|
PVRSRV_ERROR eError;
|
|
#if defined(SUPPORT_TRUSTED_DEVICE) && !defined(NO_HARDWARE)
|
PVRSRV_DEVICE_CONFIG *psDevConfig = psDeviceNode->psDevConfig;
|
|
if (psDevConfig->pfnTDRGXStart == NULL)
|
{
|
PVR_DPF((PVR_DBG_ERROR, "RGXPostPowerState: TDRGXStart not implemented!"));
|
return PVRSRV_ERROR_NOT_IMPLEMENTED;
|
}
|
|
eError = psDevConfig->pfnTDRGXStart(psDevConfig->hSysData);
|
#else
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
|
eError = RGXStart(&psDevInfo->sPowerParams);
|
#endif
|
|
return eError;
|
}
|
|
/*
|
RGXPostPowerState
|
*/
|
PVRSRV_ERROR RGXPostPowerState (IMG_HANDLE hDevHandle,
|
PVRSRV_DEV_POWER_STATE eNewPowerState,
|
PVRSRV_DEV_POWER_STATE eCurrentPowerState,
|
IMG_BOOL bForced)
|
{
|
if ((eNewPowerState != eCurrentPowerState) &&
|
(eCurrentPowerState != PVRSRV_DEV_POWER_STATE_ON))
|
{
|
PVRSRV_ERROR eError;
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGXFWIF_INIT *psRGXFWInit;
|
|
if (eCurrentPowerState == PVRSRV_DEV_POWER_STATE_OFF)
|
{
|
/* Update GPU frequency and timer correlation related data */
|
RGXGPUFreqCalibratePostPowerOn(psDeviceNode);
|
|
/* Update GPU state counters */
|
_RGXUpdateGPUUtilStats(psDevInfo);
|
|
eError = RGXDoStart(psDeviceNode);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXPostPowerState: RGXDoStart failed"));
|
return eError;
|
}
|
|
OSMemoryBarrier();
|
|
#if defined(SUPPORT_EXTRA_METASP_DEBUG)
|
eError = ValidateFWImageWithSP(psDevInfo);
|
if (eError != PVRSRV_OK) return eError;
|
#endif
|
|
eError = DevmemAcquireCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc,
|
(void **)&psRGXFWInit);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"RGXPostPowerState: Failed to acquire kernel fw if ctl (%u)",
|
eError));
|
return eError;
|
}
|
|
/*
|
* Check whether the FW has started by polling on bFirmwareStarted flag
|
*/
|
if (PVRSRVPollForValueKM((IMG_UINT32 *)&psRGXFWInit->bFirmwareStarted,
|
IMG_TRUE,
|
0xFFFFFFFF) != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR, "RGXPostPowerState: Polling for 'FW started' flag failed."));
|
eError = PVRSRV_ERROR_TIMEOUT;
|
|
/*
|
* When bFirmwareStarted fails some info maybe gained by doing the following
|
* debug dump but unfortunately it could lockup some cores or cause other power
|
* lock issues. The code is placed here to provide a possible example approach
|
* when all other ideas have been tried.
|
*/
|
/*{
|
PVRSRV_POWER_DEV *psPowerDev = psDeviceNode->psPowerDev;
|
|
if (psPowerDev)
|
{
|
PVRSRV_DEV_POWER_STATE eOldPowerState = psPowerDev->eCurrentPowerState;
|
|
PVRSRVPowerUnlock(psDeviceNode);
|
psPowerDev->eCurrentPowerState = PVRSRV_DEV_POWER_STATE_ON;
|
RGXDumpDebugInfo(NULL, psDeviceNode->pvDevice);
|
psPowerDev->eCurrentPowerState = eOldPowerState;
|
PVRSRVPowerLock(psDeviceNode);
|
}
|
}*/
|
|
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc);
|
return eError;
|
}
|
|
#if defined(PDUMP)
|
PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "Wait for the Firmware to start.");
|
eError = DevmemPDumpDevmemPol32(psDevInfo->psRGXFWIfInitMemDesc,
|
offsetof(RGXFWIF_INIT, bFirmwareStarted),
|
IMG_TRUE,
|
0xFFFFFFFFU,
|
PDUMP_POLL_OPERATOR_EQUAL,
|
PDUMP_FLAGS_CONTINUOUS);
|
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"RGXPostPowerState: problem pdumping POL for psRGXFWIfInitMemDesc (%d)",
|
eError));
|
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc);
|
return eError;
|
}
|
#endif
|
|
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
|
SetFirmwareStartTime(psRGXFWInit->ui32FirmwareStartedTimeStamp);
|
#endif
|
|
HTBSyncPartitionMarker(psRGXFWInit->ui32MarkerVal);
|
|
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfInitMemDesc);
|
|
psDevInfo->bRGXPowered = IMG_TRUE;
|
|
#if defined(PVR_DVFS)
|
eError = ResumeDVFS();
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXPostPowerState: Failed to resume DVFS"));
|
return eError;
|
}
|
#endif
|
}
|
}
|
|
PDUMPCOMMENT("RGXPostPowerState: Current state: %d, New state: %d", eCurrentPowerState, eNewPowerState);
|
|
return PVRSRV_OK;
|
}
|
|
|
/*
|
RGXPreClockSpeedChange
|
*/
|
PVRSRV_ERROR RGXPreClockSpeedChange (IMG_HANDLE hDevHandle,
|
PVRSRV_DEV_POWER_STATE eCurrentPowerState)
|
{
|
PVRSRV_ERROR eError = PVRSRV_OK;
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGX_DATA *psRGXData = (RGX_DATA*)psDeviceNode->psDevConfig->hDevData;
|
RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf;
|
|
PVR_UNREFERENCED_PARAMETER(psRGXData);
|
|
PVR_DPF((PVR_DBG_MESSAGE,"RGXPreClockSpeedChange: RGX clock speed was %uHz",
|
psRGXData->psRGXTimingInfo->ui32CoreClockSpeed));
|
|
if ((eCurrentPowerState != PVRSRV_DEV_POWER_STATE_OFF)
|
&& (psFWTraceBuf->ePowState != RGXFWIF_POW_OFF))
|
{
|
/* Update GPU frequency and timer correlation related data */
|
RGXGPUFreqCalibratePreClockSpeedChange(psDeviceNode);
|
}
|
|
return eError;
|
}
|
|
|
/*
|
RGXPostClockSpeedChange
|
*/
|
PVRSRV_ERROR RGXPostClockSpeedChange (IMG_HANDLE hDevHandle,
|
PVRSRV_DEV_POWER_STATE eCurrentPowerState)
|
{
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGX_DATA *psRGXData = (RGX_DATA*)psDeviceNode->psDevConfig->hDevData;
|
PVRSRV_ERROR eError = PVRSRV_OK;
|
RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf;
|
IMG_UINT32 ui32NewClockSpeed = psRGXData->psRGXTimingInfo->ui32CoreClockSpeed;
|
|
/* Update runtime configuration with the new value */
|
psDevInfo->psRGXFWIfRuntimeCfg->ui32CoreClockSpeed = ui32NewClockSpeed;
|
|
if ((eCurrentPowerState != PVRSRV_DEV_POWER_STATE_OFF)
|
&& (psFWTraceBuf->ePowState != RGXFWIF_POW_OFF))
|
{
|
RGXFWIF_KCCB_CMD sCOREClkSpeedChangeCmd;
|
|
RGXGPUFreqCalibratePostClockSpeedChange(psDeviceNode, ui32NewClockSpeed);
|
|
sCOREClkSpeedChangeCmd.eCmdType = RGXFWIF_KCCB_CMD_CORECLKSPEEDCHANGE;
|
sCOREClkSpeedChangeCmd.uCmdData.sCORECLKSPEEDCHANGEData.ui32NewClockSpeed = ui32NewClockSpeed;
|
|
/* Ensure the new clock speed is written to memory before requesting the FW to read it */
|
OSMemoryBarrier();
|
|
PDUMPCOMMENT("Scheduling CORE clock speed change command");
|
|
PDUMPPOWCMDSTART();
|
eError = RGXSendCommand(psDeviceNode->pvDevice,
|
RGXFWIF_DM_GP,
|
&sCOREClkSpeedChangeCmd,
|
sizeof(sCOREClkSpeedChangeCmd),
|
PDUMP_FLAGS_NONE);
|
PDUMPPOWCMDEND();
|
|
if (eError != PVRSRV_OK)
|
{
|
PDUMPCOMMENT("Scheduling CORE clock speed change command failed");
|
PVR_DPF((PVR_DBG_ERROR, "RGXPostClockSpeedChange: Scheduling KCCB command failed. Error:%u", eError));
|
return eError;
|
}
|
|
PVR_DPF((PVR_DBG_MESSAGE,"RGXPostClockSpeedChange: RGX clock speed changed to %uHz",
|
psRGXData->psRGXTimingInfo->ui32CoreClockSpeed));
|
}
|
|
return eError;
|
}
|
|
|
/*!
|
******************************************************************************
|
|
@Function RGXDustCountChange
|
|
@Description
|
|
Does change of number of DUSTs
|
|
@Input hDevHandle : RGX Device Node
|
@Input ui32NumberOfDusts : Number of DUSTs to make transition to
|
|
@Return PVRSRV_ERROR :
|
|
******************************************************************************/
|
PVRSRV_ERROR RGXDustCountChange(IMG_HANDLE hDevHandle,
|
IMG_UINT32 ui32NumberOfDusts)
|
{
|
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
PVRSRV_ERROR eError;
|
RGXFWIF_KCCB_CMD sDustCountChange;
|
IMG_UINT32 ui32MaxAvailableDusts = MAX(1, (psDevInfo->sDevFeatureCfg.ui32NumClusters/2));
|
RGXFWIF_RUNTIME_CFG *psRuntimeCfg = psDevInfo->psRGXFWIfRuntimeCfg;
|
|
if (ui32NumberOfDusts > ui32MaxAvailableDusts)
|
{
|
eError = PVRSRV_ERROR_INVALID_PARAMS;
|
PVR_DPF((PVR_DBG_ERROR,
|
"RGXDustCountChange: Invalid number of DUSTs (%u) while expecting value within <0,%u>. Error:%u",
|
ui32NumberOfDusts,
|
ui32MaxAvailableDusts,
|
eError));
|
return eError;
|
}
|
|
#if defined(FIX_HW_BRN_59042)
|
if (ui32NumberOfDusts < ui32MaxAvailableDusts && (ui32NumberOfDusts & 0x1))
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"RGXDustCountChange: Invalid number of DUSTs (%u) due to HW restriction. Allowed values are :-",
|
ui32NumberOfDusts));
|
switch (ui32MaxAvailableDusts)
|
{
|
case 2: PVR_DPF((PVR_DBG_ERROR, "0, 2")); break;
|
case 3: PVR_DPF((PVR_DBG_ERROR, "0, 2, 3")); break;
|
case 4: PVR_DPF((PVR_DBG_ERROR, "0, 2, 4")); break;
|
case 5: PVR_DPF((PVR_DBG_ERROR, "0, 2, 4, 5")); break;
|
case 6: PVR_DPF((PVR_DBG_ERROR, "0, 2, 4, 6")); break;
|
default: break;
|
}
|
return PVRSRV_ERROR_INVALID_PARAMS;
|
}
|
#endif
|
|
psRuntimeCfg->ui32DefaultDustsNumInit = ui32NumberOfDusts;
|
|
#if !defined(NO_HARDWARE)
|
{
|
RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf;
|
|
if (psFWTraceBuf->ePowState == RGXFWIF_POW_OFF)
|
{
|
return PVRSRV_OK;
|
}
|
|
if (psFWTraceBuf->ePowState != RGXFWIF_POW_FORCED_IDLE)
|
{
|
eError = PVRSRV_ERROR_DEVICE_POWER_CHANGE_DENIED;
|
PVR_DPF((PVR_DBG_ERROR,"RGXDustCountChange: Attempt to change dust count when not IDLE"));
|
return eError;
|
}
|
}
|
#endif
|
|
eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim",
|
__FUNCTION__));
|
return eError;
|
}
|
|
sDustCountChange.eCmdType = RGXFWIF_KCCB_CMD_POW;
|
sDustCountChange.uCmdData.sPowData.ePowType = RGXFWIF_POW_NUMDUST_CHANGE;
|
sDustCountChange.uCmdData.sPowData.uPoweReqData.ui32NumOfDusts = ui32NumberOfDusts;
|
|
PDUMPCOMMENT("Scheduling command to change Dust Count to %u", ui32NumberOfDusts);
|
eError = RGXSendCommand(psDeviceNode->pvDevice,
|
RGXFWIF_DM_GP,
|
&sDustCountChange,
|
sizeof(sDustCountChange),
|
PDUMP_FLAGS_NONE);
|
|
if (eError != PVRSRV_OK)
|
{
|
PDUMPCOMMENT("Scheduling command to change Dust Count failed. Error:%u", eError);
|
PVR_DPF((PVR_DBG_ERROR, "RGXDustCountChange: Scheduling KCCB to change Dust Count failed. Error:%u", eError));
|
return eError;
|
}
|
|
/* Wait for the firmware to answer. */
|
eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 0x1, 0xFFFFFFFF);
|
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXDustCountChange: Timeout waiting for idle request"));
|
return eError;
|
}
|
|
#if defined(PDUMP)
|
PDUMPCOMMENT("RGXDustCountChange: Poll for Kernel SyncPrim [0x%p] on DM %d ", psDevInfo->psPowSyncPrim->pui32LinAddr, RGXFWIF_DM_GP);
|
|
SyncPrimPDumpPol(psDevInfo->psPowSyncPrim,
|
1,
|
0xffffffff,
|
PDUMP_POLL_OPERATOR_EQUAL,
|
0);
|
#endif
|
|
return PVRSRV_OK;
|
}
|
/*
|
@Function RGXAPMLatencyChange
|
*/
|
PVRSRV_ERROR RGXAPMLatencyChange(IMG_HANDLE hDevHandle,
|
IMG_UINT32 ui32ActivePMLatencyms,
|
IMG_BOOL bActivePMLatencyPersistant)
|
{
|
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
PVRSRV_ERROR eError;
|
RGXFWIF_RUNTIME_CFG *psRuntimeCfg = psDevInfo->psRGXFWIfRuntimeCfg;
|
PVRSRV_DEV_POWER_STATE ePowerState;
|
|
eError = PVRSRVPowerLock(psDeviceNode);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXAPMLatencyChange: Failed to acquire power lock"));
|
return eError;
|
}
|
|
/* Update runtime configuration with the new values */
|
psRuntimeCfg->ui32ActivePMLatencyms = ui32ActivePMLatencyms;
|
psRuntimeCfg->bActivePMLatencyPersistant = bActivePMLatencyPersistant;
|
|
eError = PVRSRVGetDevicePowerState(psDeviceNode, &ePowerState);
|
|
if ((eError == PVRSRV_OK) && (ePowerState != PVRSRV_DEV_POWER_STATE_OFF))
|
{
|
RGXFWIF_KCCB_CMD sActivePMLatencyChange;
|
sActivePMLatencyChange.eCmdType = RGXFWIF_KCCB_CMD_POW;
|
sActivePMLatencyChange.uCmdData.sPowData.bNotifyTimeout = IMG_FALSE;
|
sActivePMLatencyChange.uCmdData.sPowData.ePowType = RGXFWIF_POW_APM_LATENCY_CHANGE;
|
sActivePMLatencyChange.uCmdData.sPowData.uPoweReqData.ui32ActivePMLatencyms = ui32ActivePMLatencyms;
|
|
/* Ensure the new APM latency is written to memory before requesting the FW to read it */
|
OSMemoryBarrier();
|
|
PDUMPCOMMENT("Scheduling command to change APM latency to %u", ui32ActivePMLatencyms);
|
eError = RGXSendCommand(psDeviceNode->pvDevice,
|
RGXFWIF_DM_GP,
|
&sActivePMLatencyChange,
|
sizeof(sActivePMLatencyChange),
|
PDUMP_FLAGS_NONE);
|
|
if (eError != PVRSRV_OK)
|
{
|
PDUMPCOMMENT("Scheduling command to change APM latency failed. Error:%u", eError);
|
PVR_DPF((PVR_DBG_ERROR, "RGXAPMLatencyChange: Scheduling KCCB to change APM latency failed. Error:%u", eError));
|
return eError;
|
}
|
}
|
|
PVRSRVPowerUnlock(psDeviceNode);
|
|
return PVRSRV_OK;
|
}
|
|
/*
|
RGXActivePowerRequest
|
*/
|
PVRSRV_ERROR RGXActivePowerRequest(IMG_HANDLE hDevHandle)
|
{
|
PVRSRV_ERROR eError = PVRSRV_OK;
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf;
|
|
OSAcquireBridgeLock();
|
/* NOTE: If this function were to wait for event object attempt should be
|
made to prevent releasing bridge lock during sleep. Bridge lock should
|
be held during sleep. */
|
|
/* Powerlock to avoid further requests from racing with the FW hand-shake from now on
|
(previous kicks to this point are detected by the FW) */
|
eError = PVRSRVPowerLock(psDeviceNode);
|
if(eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,
|
"%s: Failed to acquire PowerLock (device: %p, error: %s)",
|
__func__, psDeviceNode, PVRSRVGetErrorStringKM(eError)));
|
goto _RGXActivePowerRequest_PowerLock_failed;
|
}
|
|
/* Check again for IDLE once we have the power lock */
|
if (psFWTraceBuf->ePowState == RGXFWIF_POW_IDLE)
|
{
|
|
psDevInfo->ui32ActivePMReqTotal++;
|
|
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
|
SetFirmwareHandshakeIdleTime(RGXReadHWTimerReg(psDevInfo)-psFWTraceBuf->ui64StartIdleTime);
|
#endif
|
|
PDUMPPOWCMDSTART();
|
eError = PVRSRVSetDevicePowerStateKM(psDeviceNode,
|
PVRSRV_DEV_POWER_STATE_OFF,
|
IMG_FALSE); /* forced */
|
PDUMPPOWCMDEND();
|
|
if (eError == PVRSRV_OK)
|
{
|
psDevInfo->ui32ActivePMReqOk++;
|
}
|
else if (eError == PVRSRV_ERROR_DEVICE_POWER_CHANGE_DENIED)
|
{
|
psDevInfo->ui32ActivePMReqDenied++;
|
}
|
|
}
|
|
PVRSRVPowerUnlock(psDeviceNode);
|
|
_RGXActivePowerRequest_PowerLock_failed:
|
OSReleaseBridgeLock();
|
|
return eError;
|
|
}
|
/*
|
RGXForcedIdleRequest
|
*/
|
|
#define RGX_FORCED_IDLE_RETRY_COUNT 10
|
|
PVRSRV_ERROR RGXForcedIdleRequest(IMG_HANDLE hDevHandle, IMG_BOOL bDeviceOffPermitted)
|
{
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGXFWIF_KCCB_CMD sPowCmd;
|
PVRSRV_ERROR eError;
|
IMG_UINT32 ui32RetryCount = 0;
|
|
#if !defined(NO_HARDWARE)
|
RGXFWIF_TRACEBUF *psFWTraceBuf = psDevInfo->psRGXFWIfTraceBuf;
|
|
/* Firmware already forced idle */
|
if (psFWTraceBuf->ePowState == RGXFWIF_POW_FORCED_IDLE)
|
{
|
return PVRSRV_OK;
|
}
|
|
/* Firmware is not powered. Sometimes this is permitted, for instance we were forcing idle to power down. */
|
if (psFWTraceBuf->ePowState == RGXFWIF_POW_OFF)
|
{
|
return (bDeviceOffPermitted) ? PVRSRV_OK : PVRSRV_ERROR_DEVICE_IDLE_REQUEST_DENIED;
|
}
|
#endif
|
|
eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim",
|
__FUNCTION__));
|
return eError;
|
}
|
sPowCmd.eCmdType = RGXFWIF_KCCB_CMD_POW;
|
sPowCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_FORCED_IDLE_REQ;
|
sPowCmd.uCmdData.sPowData.uPoweReqData.bCancelForcedIdle = IMG_FALSE;
|
|
PDUMPCOMMENT("RGXForcedIdleRequest: Sending forced idle command");
|
|
/* Send one forced IDLE command to GP */
|
eError = RGXSendCommand(psDevInfo,
|
RGXFWIF_DM_GP,
|
&sPowCmd,
|
sizeof(sPowCmd),
|
PDUMP_FLAGS_NONE);
|
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXForcedIdleRequest: Failed to send idle request"));
|
return eError;
|
}
|
|
/* Wait for GPU to finish current workload */
|
do {
|
eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 0x1, 0xFFFFFFFF);
|
if ((eError == PVRSRV_OK) || (ui32RetryCount == RGX_FORCED_IDLE_RETRY_COUNT))
|
{
|
break;
|
}
|
ui32RetryCount++;
|
PVR_DPF((PVR_DBG_WARNING,"RGXForcedIdleRequest: Request timeout. Retry %d of %d", ui32RetryCount, RGX_FORCED_IDLE_RETRY_COUNT));
|
} while (IMG_TRUE);
|
|
if (eError != PVRSRV_OK)
|
{
|
RGXFWNotifyHostTimeout(psDevInfo);
|
PVR_DPF((PVR_DBG_ERROR,"RGXForcedIdleRequest: Idle request failed. Firmware potentially left in forced idle state"));
|
return eError;
|
}
|
|
#if defined(PDUMP)
|
PDUMPCOMMENT("RGXForcedIdleRequest: Poll for Kernel SyncPrim [0x%p] on DM %d ", psDevInfo->psPowSyncPrim->pui32LinAddr, RGXFWIF_DM_GP);
|
|
SyncPrimPDumpPol(psDevInfo->psPowSyncPrim,
|
1,
|
0xffffffff,
|
PDUMP_POLL_OPERATOR_EQUAL,
|
0);
|
#endif
|
|
#if !defined(NO_HARDWARE)
|
/* Check the firmware state for idleness */
|
if (psFWTraceBuf->ePowState != RGXFWIF_POW_FORCED_IDLE)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXForcedIdleRequest: Failed to force IDLE"));
|
|
return PVRSRV_ERROR_DEVICE_IDLE_REQUEST_DENIED;
|
}
|
#endif
|
|
return PVRSRV_OK;
|
}
|
|
/*
|
RGXCancelForcedIdleRequest
|
*/
|
PVRSRV_ERROR RGXCancelForcedIdleRequest(IMG_HANDLE hDevHandle)
|
{
|
PVRSRV_DEVICE_NODE *psDeviceNode = hDevHandle;
|
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
|
RGXFWIF_KCCB_CMD sPowCmd;
|
PVRSRV_ERROR eError = PVRSRV_OK;
|
|
eError = SyncPrimSet(psDevInfo->psPowSyncPrim, 0);
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"%s: Failed to set Power sync prim",
|
__FUNCTION__));
|
goto ErrorExit;
|
}
|
|
/* Send the IDLE request to the FW */
|
sPowCmd.eCmdType = RGXFWIF_KCCB_CMD_POW;
|
sPowCmd.uCmdData.sPowData.ePowType = RGXFWIF_POW_FORCED_IDLE_REQ;
|
sPowCmd.uCmdData.sPowData.uPoweReqData.bCancelForcedIdle = IMG_TRUE;
|
|
PDUMPCOMMENT("RGXForcedIdleRequest: Sending cancel forced idle command");
|
|
/* Send cancel forced IDLE command to GP */
|
eError = RGXSendCommand(psDevInfo,
|
RGXFWIF_DM_GP,
|
&sPowCmd,
|
sizeof(sPowCmd),
|
PDUMP_FLAGS_NONE);
|
|
if (eError != PVRSRV_OK)
|
{
|
PDUMPCOMMENT("RGXCancelForcedIdleRequest: Failed to send cancel IDLE request for DM%d", RGXFWIF_DM_GP);
|
goto ErrorExit;
|
}
|
|
/* Wait for the firmware to answer. */
|
eError = PVRSRVPollForValueKM(psDevInfo->psPowSyncPrim->pui32LinAddr, 1, 0xFFFFFFFF);
|
|
if (eError != PVRSRV_OK)
|
{
|
PVR_DPF((PVR_DBG_ERROR,"RGXCancelForcedIdleRequest: Timeout waiting for cancel idle request"));
|
goto ErrorExit;
|
}
|
|
#if defined(PDUMP)
|
PDUMPCOMMENT("RGXCancelForcedIdleRequest: Poll for Kernel SyncPrim [0x%p] on DM %d ", psDevInfo->psPowSyncPrim->pui32LinAddr, RGXFWIF_DM_GP);
|
|
SyncPrimPDumpPol(psDevInfo->psPowSyncPrim,
|
1,
|
0xffffffff,
|
PDUMP_POLL_OPERATOR_EQUAL,
|
0);
|
#endif
|
|
return eError;
|
|
ErrorExit:
|
PVR_DPF((PVR_DBG_ERROR,"RGXCancelForcedIdleRequest: Firmware potentially left in forced idle state"));
|
return eError;
|
}
|
|
/*!
|
******************************************************************************
|
|
@Function PVRSRVGetNextDustCount
|
|
@Description
|
|
Calculate a sequence of dust counts to achieve full transition coverage.
|
We increment two counts of dusts and switch up and down between them.
|
It does contain a few redundant transitions. If two dust exist, the
|
output transitions should be as follows.
|
|
0->1, 0<-1, 0->2, 0<-2, (0->1)
|
1->1, 1->2, 1<-2, (1->2)
|
2->2, (2->0),
|
0->0. Repeat.
|
|
Redundant transitions in brackets.
|
|
@Input psDustReqState : Counter state used to calculate next dust count
|
@Input ui32DustCount : Number of dusts in the core
|
|
@Return PVRSRV_ERROR
|
|
******************************************************************************/
|
IMG_UINT32 RGXGetNextDustCount(RGX_DUST_STATE *psDustReqState, IMG_UINT32 ui32DustCount)
|
{
|
if (psDustReqState->bToggle)
|
{
|
psDustReqState->ui32DustCount2++;
|
}
|
|
if (psDustReqState->ui32DustCount2 > ui32DustCount)
|
{
|
psDustReqState->ui32DustCount1++;
|
psDustReqState->ui32DustCount2 = psDustReqState->ui32DustCount1;
|
}
|
|
if (psDustReqState->ui32DustCount1 > ui32DustCount)
|
{
|
psDustReqState->ui32DustCount1 = 0;
|
psDustReqState->ui32DustCount2 = 0;
|
}
|
|
psDustReqState->bToggle = !psDustReqState->bToggle;
|
|
return (psDustReqState->bToggle) ? psDustReqState->ui32DustCount1 : psDustReqState->ui32DustCount2;
|
}
|
|
/******************************************************************************
|
End of file (rgxpower.c)
|
******************************************************************************/
|