blob: b436cee82219e56cd944c76e4b06b3f9960d4a7f [file] [edit]
/*************************************************************************/ /*!
@File
@Title Rogue firmware utility routines
@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved
@Description Rogue firmware utility routines
@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.
*/ /**************************************************************************/
#if defined(LINUX)
#include <linux/stddef.h>
#else
#include <stddef.h>
#endif
#include "img_defs.h"
#include "rgxdefs_km.h"
#include "rgx_fwif_km.h"
#include "pdump_km.h"
#include "osfunc.h"
#include "oskm_apphint.h"
#include "cache_km.h"
#include "allocmem.h"
#include "physheap.h"
#include "devicemem.h"
#include "devicemem_pdump.h"
#include "devicemem_server.h"
#include "pvr_debug.h"
#include "pvr_notifier.h"
#include "rgxfwutils.h"
#include "rgx_options.h"
#include "rgx_fwif_alignchecks.h"
#include "rgx_fwif_resetframework.h"
#include "rgx_pdump_panics.h"
#include "fwtrace_string.h"
#include "rgxheapconfig.h"
#include "pvrsrv.h"
#include "rgxdebug.h"
#include "rgxhwperf.h"
#include "rgxccb.h"
#include "rgxcompute.h"
#include "rgxtransfer.h"
#include "rgxpower.h"
#include "rgxtdmtransfer.h"
#if defined(SUPPORT_DISPLAY_CLASS)
#include "dc_server.h"
#endif
#include "rgxmem.h"
#include "rgxta3d.h"
#include "rgxkicksync.h"
#include "rgxutils.h"
#include "rgxtimecorr.h"
#include "sync_internal.h"
#include "sync.h"
#include "sync_checkpoint.h"
#include "sync_checkpoint_external.h"
#include "tlstream.h"
#include "devicemem_server_utils.h"
#include "htbuffer.h"
#include "rgx_bvnc_defs_km.h"
#include "info_page.h"
#include "physmem_lma.h"
#include "physmem_osmem.h"
#ifdef __linux__
#include <linux/kernel.h> /* sprintf */
#include "rogue_trace_events.h"
#else
#include <stdio.h>
#include <string.h>
#endif
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
#include "process_stats.h"
#endif
#if defined(SUPPORT_WORKLOAD_ESTIMATION)
#include "rgxworkest.h"
#endif
#if defined(SUPPORT_PDVFS)
#include "rgxpdvfs.h"
#endif
#if defined(PVRSRV_SYNC_CHECKPOINT_CCB)
#if defined(SUPPORT_BUFFER_SYNC)
#include "pvr_buffer_sync.h"
#endif
#endif
#include "vz_vmm_pvz.h"
#include "rgx_heaps.h"
/*!
******************************************************************************
* HWPERF
*****************************************************************************/
/* Size of the Firmware L1 HWPERF buffer in bytes (2MB). Accessed by the
* Firmware and host driver. */
#define RGXFW_HWPERF_L1_SIZE_MIN (16U)
#define RGXFW_HWPERF_L1_SIZE_DEFAULT PVRSRV_APPHINT_HWPERFFWBUFSIZEINKB
#define RGXFW_HWPERF_L1_SIZE_MAX (12288U)
/* Kernel CCB length */
/* Reducing the size of the KCCB in an attempt to avoid flooding and overflowing the FW kick queue
* in the case of multiple OSes */
#define RGXFWIF_KCCB_NUMCMDS_LOG2_GPUVIRT_WITHOUT_FEATURE (6)
#define RGXFWIF_KCCB_NUMCMDS_LOG2_DEFAULT (7)
/* Firmware CCB length */
#if defined(NO_HARDWARE) && defined(PDUMP)
#define RGXFWIF_FWCCB_NUMCMDS_LOG2 (10)
#elif defined(SUPPORT_PDVFS)
#define RGXFWIF_FWCCB_NUMCMDS_LOG2 (8)
#else
#define RGXFWIF_FWCCB_NUMCMDS_LOG2 (5)
#endif
#if defined(RGX_FW_IRQ_OS_COUNTERS)
const IMG_UINT32 gaui32FwOsIrqCntRegAddr[RGXFW_MAX_NUM_OS] = {IRQ_COUNTER_STORAGE_REGS};
#endif
#if defined(PVRSRV_SYNC_CHECKPOINT_CCB)
/* Checkpoint CCB length */
#define RGXFWIF_CHECKPOINTCCB_NUMCMDS_LOG2 (10)
#endif
/*
* Maximum length of time a DM can run for before the DM will be marked
* as out-of-time. CDM has an increased value due to longer running kernels.
*
* These deadlines are increased on FPGA, EMU and VP due to the slower
* execution time of these platforms.
*/
#if defined(FPGA) || defined(EMULATOR) || defined(VIRTUAL_PLATFORM)
#define RGXFWIF_MAX_WORKLOAD_DEADLINE_MS (60000)
#define RGXFWIF_MAX_CDM_WORKLOAD_DEADLINE_MS (250000)
#else
#define RGXFWIF_MAX_WORKLOAD_DEADLINE_MS (30000)
#define RGXFWIF_MAX_CDM_WORKLOAD_DEADLINE_MS (90000)
#endif
/* Workload Estimation Firmware CCB length */
#define RGXFWIF_WORKEST_FWCCB_NUMCMDS_LOG2 (7)
/* Size of memory buffer for firmware gcov data
* The actual data size is several hundred kilobytes. The buffer is an order of magnitude larger. */
#define RGXFWIF_FIRMWARE_GCOV_BUFFER_SIZE (4*1024*1024)
#if defined(CONFIG_L4) && !defined(RGX_FEATURE_GPU_VIRTUALISATION) && defined(RGX_NUM_OS_SUPPORTED) && (RGX_NUM_OS_SUPPORTED > 1)
#define MTS_SCHEDULE_DM_VAL (RGXFWIF_DM_GP + PVRSRV_VZ_MODE_IS(GUEST) ? (1) : (0))
#else
#define MTS_SCHEDULE_DM_VAL (RGXFWIF_DM_GP)
#endif
typedef struct
{
RGXFWIF_KCCB_CMD sKCCBcmd;
DLLIST_NODE sListNode;
PDUMP_FLAGS_T uiPdumpFlags;
PVRSRV_RGXDEV_INFO *psDevInfo;
} RGX_DEFERRED_KCCB_CMD;
#if defined(PDUMP)
/* ensure PIDs are 32-bit because a 32-bit PDump load is generated for the
* PID filter example entries
*/
static_assert(sizeof(IMG_PID) == sizeof(IMG_UINT32),
"FW PID filtering assumes the IMG_PID type is 32-bits wide as it "
"generates WRW commands for loading the PID values");
#endif
static void RGXFreeFwOsData(PVRSRV_RGXDEV_INFO *psDevInfo);
static void RGXFreeFwSysData(PVRSRV_RGXDEV_INFO *psDevInfo);
static PVRSRV_ERROR _AllocateSLC3Fence(PVRSRV_RGXDEV_INFO* psDevInfo, RGXFWIF_SYSINIT* psFwSysInit)
{
PVRSRV_ERROR eError;
DEVMEM_MEMDESC** ppsSLC3FenceMemDesc = &psDevInfo->psSLC3FenceMemDesc;
IMG_UINT32 ui32CacheLineSize = GET_ROGUE_CACHE_LINE_SIZE(
RGX_GET_FEATURE_VALUE(psDevInfo, SLC_CACHE_LINE_SIZE_BITS));
PVR_DPF_ENTERED;
eError = DevmemAllocate(psDevInfo->psFirmwareMainHeap,
1,
ui32CacheLineSize,
PVRSRV_MEMALLOCFLAG_GPU_READABLE |
PVRSRV_MEMALLOCFLAG_GPU_WRITEABLE |
PVRSRV_MEMALLOCFLAG_UNCACHED |
PVRSRV_MEMALLOCFLAG_FW_ALLOC_MAIN,
"FwSLC3FenceWA",
ppsSLC3FenceMemDesc);
if (eError != PVRSRV_OK)
{
PVR_DPF_RETURN_RC(eError);
}
/* We need to map it so the heap for this allocation is set */
eError = DevmemMapToDevice(*ppsSLC3FenceMemDesc,
psDevInfo->psFirmwareMainHeap,
&psFwSysInit->sSLC3FenceDevVAddr);
if (eError != PVRSRV_OK)
{
DevmemFree(*ppsSLC3FenceMemDesc);
*ppsSLC3FenceMemDesc = NULL;
}
PVR_DPF_RETURN_RC1(eError, *ppsSLC3FenceMemDesc);
}
static void _FreeSLC3Fence(PVRSRV_RGXDEV_INFO* psDevInfo)
{
DEVMEM_MEMDESC* psSLC3FenceMemDesc = psDevInfo->psSLC3FenceMemDesc;
if (psSLC3FenceMemDesc)
{
DevmemReleaseDevVirtAddr(psSLC3FenceMemDesc);
DevmemFree(psSLC3FenceMemDesc);
}
}
static void __MTSScheduleWrite(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32Value)
{
/* ensure memory is flushed before kicking MTS */
OSWriteMemoryBarrier();
OSWriteHWReg32(psDevInfo->pvRegsBaseKM, RGX_CR_MTS_SCHEDULE, ui32Value);
/* ensure the MTS kick goes through before continuing */
OSMemoryBarrier();
}
/*************************************************************************/ /*!
@Function RGXSetupFwAllocation
@Description Sets a pointer in a firmware data structure.
@Input psDevInfo Device Info struct
@Input uiAllocFlags Flags determining type of memory allocation
@Input ui32Size Size of memory allocation
@Input pszName Allocation label
@Input ppsMemDesc pointer to the allocation's memory descriptor
@Input psFwPtr Address of the firmware pointer to set
@Input ppvCpuPtr Address of the cpu pointer to set
@Input ui32DevVAFlags Any combination of RFW_FWADDR_*_FLAG
@Return PVRSRV_ERROR
*/ /**************************************************************************/
PVRSRV_ERROR RGXSetupFwAllocation(PVRSRV_RGXDEV_INFO* psDevInfo,
DEVMEM_FLAGS_T uiAllocFlags,
IMG_UINT32 ui32Size,
const IMG_CHAR *pszName,
DEVMEM_MEMDESC **ppsMemDesc,
RGXFWIF_DEV_VIRTADDR *psFwPtr,
void **ppvCpuPtr,
IMG_UINT32 ui32DevVAFlags)
{
PVRSRV_ERROR eError;
#if defined(SUPPORT_AUTOVZ)
IMG_BOOL bClearByMemset = ((uiAllocFlags & PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC) != 0);
/* Under AutoVz the ZERO_ON_ALLOC flag is avoided as it causes the memory to
* be allocated from a different PMR than an allocation without the flag.
* When the content of an allocation needs to be recovered from physical memory
* on a later driver reboot, the memory then cannot be zeroed but the allocation
* addresses must still match.
* If the memory requires clearing, perform a memset after the allocation. */
uiAllocFlags &= ~PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC;
#endif
PDUMPCOMMENT("Allocate %s", pszName);
eError = DevmemFwAllocate(psDevInfo,
ui32Size,
uiAllocFlags,
pszName,
ppsMemDesc);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to allocate %u bytes for %s (%u)",
__func__,
ui32Size,
pszName,
eError));
goto fail_alloc;
}
if (psFwPtr)
{
eError = RGXSetFirmwareAddress(psFwPtr, *ppsMemDesc, 0, ui32DevVAFlags);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to acquire firmware virtual address for %s (%u)",
__func__,
pszName,
eError));
goto fail_fwaddr;
}
}
#if defined(SUPPORT_AUTOVZ)
if ((bClearByMemset) || (ppvCpuPtr))
#else
if (ppvCpuPtr)
#endif
{
void *pvTempCpuPtr;
eError = DevmemAcquireCpuVirtAddr(*ppsMemDesc, &pvTempCpuPtr);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to acquire CPU virtual address for %s (%u)",
__func__,
pszName,
eError));
goto fail_cpuva;
}
#if defined(SUPPORT_AUTOVZ)
if (bClearByMemset)
{
OSDeviceMemSet(pvTempCpuPtr, 0, ui32Size);
}
if (ppvCpuPtr)
#endif
{
*ppvCpuPtr = pvTempCpuPtr;
}
#if defined(SUPPORT_AUTOVZ)
else
{
DevmemReleaseCpuVirtAddr(*ppsMemDesc);
pvTempCpuPtr = NULL;
}
#endif
}
PVR_DPF((PVR_DBG_MESSAGE, "%s: %s set up at Fw VA 0x%x and CPU VA 0x%p",
__func__, pszName,
(psFwPtr) ? (psFwPtr->ui32Addr) : (0),
(ppvCpuPtr) ? (*ppvCpuPtr) : (NULL)));
return eError;
fail_cpuva:
if (psFwPtr)
{
RGXUnsetFirmwareAddress(*ppsMemDesc);
}
fail_fwaddr:
DevmemFree(*ppsMemDesc);
fail_alloc:
return eError;
}
/*************************************************************************/ /*!
@Function GetHwPerfBufferSize
@Description Computes the effective size of the HW Perf Buffer
@Input ui32HWPerfFWBufSizeKB Device Info struct
@Return HwPerfBufferSize
*/ /**************************************************************************/
static IMG_UINT32 GetHwPerfBufferSize(IMG_UINT32 ui32HWPerfFWBufSizeKB)
{
IMG_UINT32 HwPerfBufferSize;
/* HWPerf: Determine the size of the FW buffer */
if (ui32HWPerfFWBufSizeKB == 0 ||
ui32HWPerfFWBufSizeKB == RGXFW_HWPERF_L1_SIZE_DEFAULT)
{
/* Under pvrsrvctl 0 size implies AppHint not set or is set to zero,
* use default size from driver constant. Set it to the default
* size, no logging.
*/
HwPerfBufferSize = RGXFW_HWPERF_L1_SIZE_DEFAULT<<10;
}
else if (ui32HWPerfFWBufSizeKB > (RGXFW_HWPERF_L1_SIZE_MAX))
{
/* Size specified as a AppHint but it is too big */
PVR_DPF((PVR_DBG_WARNING,
"%s: HWPerfFWBufSizeInKB value (%u) too big, using maximum (%u)",
__func__,
ui32HWPerfFWBufSizeKB, RGXFW_HWPERF_L1_SIZE_MAX));
HwPerfBufferSize = RGXFW_HWPERF_L1_SIZE_MAX<<10;
}
else if (ui32HWPerfFWBufSizeKB > (RGXFW_HWPERF_L1_SIZE_MIN))
{
/* Size specified as in AppHint HWPerfFWBufSizeInKB */
PVR_DPF((PVR_DBG_WARNING,
"%s: Using HWPerf FW buffer size of %u KB",
__func__,
ui32HWPerfFWBufSizeKB));
HwPerfBufferSize = ui32HWPerfFWBufSizeKB<<10;
}
else
{
/* Size specified as a AppHint but it is too small */
PVR_DPF((PVR_DBG_WARNING,
"%s: HWPerfFWBufSizeInKB value (%u) too small, using minimum (%u)",
__func__,
ui32HWPerfFWBufSizeKB, RGXFW_HWPERF_L1_SIZE_MIN));
HwPerfBufferSize = RGXFW_HWPERF_L1_SIZE_MIN<<10;
}
return HwPerfBufferSize;
}
#if defined(PDUMP)
/*!
*******************************************************************************
@Function RGXFWSetupSignatureChecks
@Description
@Input psDevInfo
@Return PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXFWSetupSignatureChecks(PVRSRV_RGXDEV_INFO* psDevInfo,
DEVMEM_MEMDESC** ppsSigChecksMemDesc,
IMG_UINT32 ui32SigChecksBufSize,
RGXFWIF_SIGBUF_CTL* psSigBufCtl,
const IMG_CHAR* pszBufferName)
{
PVRSRV_ERROR eError;
/* Allocate memory for the checks */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS,
ui32SigChecksBufSize,
"FwSignatureChecks",
ppsSigChecksMemDesc,
&psSigBufCtl->sBuffer,
NULL,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
DevmemPDumpLoadMem( *ppsSigChecksMemDesc,
0,
ui32SigChecksBufSize,
PDUMP_FLAGS_CONTINUOUS);
psSigBufCtl->ui32LeftSizeInRegs = ui32SigChecksBufSize / sizeof(IMG_UINT32);
fail:
return eError;
}
#endif
#if defined(SUPPORT_FIRMWARE_GCOV)
/*!
*******************************************************************************
@Function RGXFWSetupFirmwareGcovBuffer
@Description
@Input psDevInfo
@Return PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXFWSetupFirmwareGcovBuffer(PVRSRV_RGXDEV_INFO* psDevInfo,
DEVMEM_MEMDESC** ppsBufferMemDesc,
IMG_UINT32 ui32FirmwareGcovBufferSize,
RGXFWIF_FIRMWARE_GCOV_CTL* psFirmwareGcovCtl,
const IMG_CHAR* pszBufferName)
{
PVRSRV_ERROR eError;
/* Allocate memory for gcov */
eError = RGXSetupFwAllocation(psDevInfo,
(RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED)),
ui32FirmwareGcovBufferSize,
pszBufferName,
ppsBufferMemDesc,
&psFirmwareGcovCtl->sBuffer,
NULL,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
psFirmwareGcovCtl->ui32Size = ui32FirmwareGcovBufferSize;
return PVRSRV_OK;
}
#endif
#if defined(SUPPORT_POWER_SAMPLING_VIA_DEBUGFS)
/*!
******************************************************************************
@Function RGXFWSetupCounterBuffer
@Description
@Input psDevInfo
@Return PVRSRV_ERROR
*****************************************************************************/
static PVRSRV_ERROR RGXFWSetupCounterBuffer(PVRSRV_RGXDEV_INFO* psDevInfo,
DEVMEM_MEMDESC** ppsBufferMemDesc,
IMG_UINT32 ui32CounterDataBufferSize,
RGXFWIF_COUNTER_DUMP_CTL* psCounterDumpCtl,
const IMG_CHAR* pszBufferName)
{
PVRSRV_ERROR eError;
eError = RGXSetupFwAllocation(psDevInfo,
(RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED)),
ui32CounterDataBufferSize,
"FwCounterBuffer",
ppsBufferMemDesc,
&psCounterDumpCtl->sBuffer,
NULL,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_RETURN_IF_ERROR(eError, "RGXSetupFwAllocation");
psCounterDumpCtl->ui32SizeInDwords = ui32CounterDataBufferSize >> 2;
return PVRSRV_OK;
}
#endif
/*!
******************************************************************************
@Function RGXFWSetupAlignChecks
@Description This functions allocates and fills memory needed for the
aligns checks of the UM and KM structures shared with the
firmware. The format of the data in the memory is as follows:
<number of elements in the KM array>
<array of KM structures' sizes and members' offsets>
<number of elements in the UM array>
<array of UM structures' sizes and members' offsets>
The UM array is passed from the user side. Now the firmware is
is responsible for filling this part of the memory. If that
happens the check of the UM structures will be performed
by the host driver on client's connect.
If the macro is not defined the client driver fills the memory
and the firmware checks for the alignment of all structures.
@Input psDeviceNode
@Return PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXFWSetupAlignChecks(PVRSRV_DEVICE_NODE *psDeviceNode,
RGXFWIF_DEV_VIRTADDR *psAlignChecksDevFW,
IMG_UINT32 *pui32RGXFWAlignChecks,
IMG_UINT32 ui32RGXFWAlignChecksArrLength)
{
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
IMG_UINT32 aui32RGXFWAlignChecksKM[] = { RGXFW_ALIGN_CHECKS_INIT_KM };
IMG_UINT32 ui32RGXFWAlingChecksTotal;
IMG_UINT32* paui32AlignChecks;
PVRSRV_ERROR eError;
/* In this case we don't know the number of elements in UM array.
* We have to assume something so we assume RGXFW_ALIGN_CHECKS_UM_MAX.
*/
PVR_ASSERT(ui32RGXFWAlignChecksArrLength == 0);
ui32RGXFWAlingChecksTotal = sizeof(aui32RGXFWAlignChecksKM)
+ RGXFW_ALIGN_CHECKS_UM_MAX * sizeof(IMG_UINT32)
+ 2 * sizeof(IMG_UINT32);
/* Allocate memory for the checks */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
ui32RGXFWAlingChecksTotal,
"FwAlignmentChecks",
&psDevInfo->psRGXFWAlignChecksMemDesc,
psAlignChecksDevFW,
(void**) &paui32AlignChecks,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
if (!psDeviceNode->bAutoVzFwIsUp)
{
/* Copy the values */
*paui32AlignChecks++ = ARRAY_SIZE(aui32RGXFWAlignChecksKM);
OSDeviceMemCopy(paui32AlignChecks, &aui32RGXFWAlignChecksKM[0], sizeof(aui32RGXFWAlignChecksKM));
paui32AlignChecks += ARRAY_SIZE(aui32RGXFWAlignChecksKM);
*paui32AlignChecks = 0;
}
DevmemPDumpLoadMem( psDevInfo->psRGXFWAlignChecksMemDesc,
0,
ui32RGXFWAlingChecksTotal,
PDUMP_FLAGS_CONTINUOUS);
return PVRSRV_OK;
fail:
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
static void RGXFWFreeAlignChecks(PVRSRV_RGXDEV_INFO* psDevInfo)
{
if (psDevInfo->psRGXFWAlignChecksMemDesc != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWAlignChecksMemDesc);
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWAlignChecksMemDesc);
psDevInfo->psRGXFWAlignChecksMemDesc = NULL;
}
}
PVRSRV_ERROR RGXSetFirmwareAddress(RGXFWIF_DEV_VIRTADDR *ppDest,
DEVMEM_MEMDESC *psSrc,
IMG_UINT32 uiExtraOffset,
IMG_UINT32 ui32Flags)
{
PVRSRV_ERROR eError;
IMG_DEV_VIRTADDR psDevVirtAddr;
PVRSRV_DEVICE_NODE *psDeviceNode;
PVRSRV_RGXDEV_INFO *psDevInfo;
psDeviceNode = (PVRSRV_DEVICE_NODE *) DevmemGetConnection(psSrc);
psDevInfo = (PVRSRV_RGXDEV_INFO *)psDeviceNode->pvDevice;
if (RGX_IS_FEATURE_VALUE_SUPPORTED(psDevInfo, META))
{
IMG_UINT32 ui32Offset;
IMG_BOOL bCachedInMETA;
DEVMEM_FLAGS_T uiDevFlags;
IMG_UINT32 uiGPUCacheMode;
eError = DevmemAcquireDevVirtAddr(psSrc, &psDevVirtAddr);
PVR_LOG_GOTO_IF_ERROR(eError, "DevmemAcquireDevVirtAddr", failDevVAAcquire);
/* Convert to an address in META memmap */
ui32Offset = psDevVirtAddr.uiAddr + uiExtraOffset - RGX_FIRMWARE_RAW_HEAP_BASE;
/* Check in the devmem flags whether this memory is cached/uncached */
DevmemGetFlags(psSrc, &uiDevFlags);
/* Honour the META cache flags */
bCachedInMETA = (PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) & uiDevFlags) != 0;
/* Honour the SLC cache flags */
eError = DevmemDeviceCacheMode(psDeviceNode, uiDevFlags, &uiGPUCacheMode);
PVR_LOG_GOTO_IF_ERROR(eError, "DevmemDeviceCacheMode", failDevCacheMode);
ui32Offset += RGXFW_SEGMMU_DATA_BASE_ADDRESS;
if (bCachedInMETA)
{
ui32Offset |= RGXFW_SEGMMU_DATA_META_CACHED;
}
else
{
ui32Offset |= RGXFW_SEGMMU_DATA_META_UNCACHED;
}
if (PVRSRV_CHECK_GPU_CACHED(uiGPUCacheMode))
{
ui32Offset |= RGXFW_SEGMMU_DATA_VIVT_SLC_CACHED;
}
else
{
ui32Offset |= RGXFW_SEGMMU_DATA_VIVT_SLC_UNCACHED;
}
ppDest->ui32Addr = ui32Offset;
}
else if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, MIPS))
{
eError = DevmemAcquireDevVirtAddr(psSrc, &psDevVirtAddr);
PVR_GOTO_IF_ERROR(eError, failDevVAAcquire);
ppDest->ui32Addr = (IMG_UINT32)((psDevVirtAddr.uiAddr + uiExtraOffset) & 0xFFFFFFFF);
}
else
{
IMG_UINT32 ui32Offset;
IMG_BOOL bCachedInRISCV;
DEVMEM_FLAGS_T uiDevFlags;
eError = DevmemAcquireDevVirtAddr(psSrc, &psDevVirtAddr);
PVR_LOG_GOTO_IF_ERROR(eError, "DevmemAcquireDevVirtAddr", failDevVAAcquire);
/* Convert to an address in RISCV memmap */
ui32Offset = psDevVirtAddr.uiAddr + uiExtraOffset - RGX_FIRMWARE_RAW_HEAP_BASE;
/* Check in the devmem flags whether this memory is cached/uncached */
DevmemGetFlags(psSrc, &uiDevFlags);
/* Honour the RISCV cache flags */
bCachedInRISCV = (PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) & uiDevFlags) != 0;
if (bCachedInRISCV)
{
ui32Offset |= RGXRISCVFW_SHARED_CACHED_DATA_BASE;
}
else
{
ui32Offset |= RGXRISCVFW_SHARED_UNCACHED_DATA_BASE;
}
ppDest->ui32Addr = ui32Offset;
}
if ((ppDest->ui32Addr & 0x3U) != 0)
{
IMG_CHAR *pszAnnotation;
/* It is expected that the annotation returned by DevmemGetAnnotation() is always valid */
DevmemGetAnnotation(psSrc, &pszAnnotation);
PVR_DPF((PVR_DBG_ERROR, "%s: %s @ 0x%x is not aligned to 32 bit",
__func__, pszAnnotation, ppDest->ui32Addr));
return PVRSRV_ERROR_INVALID_ALIGNMENT;
}
if (ui32Flags & RFW_FWADDR_NOREF_FLAG)
{
DevmemReleaseDevVirtAddr(psSrc);
}
return PVRSRV_OK;
failDevCacheMode:
DevmemReleaseDevVirtAddr(psSrc);
failDevVAAcquire:
return eError;
}
void RGXSetMetaDMAAddress(RGXFWIF_DMA_ADDR *psDest,
DEVMEM_MEMDESC *psSrcMemDesc,
RGXFWIF_DEV_VIRTADDR *psSrcFWDevVAddr,
IMG_UINT32 uiOffset)
{
PVRSRV_ERROR eError;
IMG_DEV_VIRTADDR sDevVirtAddr;
eError = DevmemAcquireDevVirtAddr(psSrcMemDesc, &sDevVirtAddr);
PVR_ASSERT(eError == PVRSRV_OK);
psDest->psDevVirtAddr.uiAddr = sDevVirtAddr.uiAddr;
psDest->psDevVirtAddr.uiAddr += uiOffset;
psDest->pbyFWAddr.ui32Addr = psSrcFWDevVAddr->ui32Addr;
DevmemReleaseDevVirtAddr(psSrcMemDesc);
}
void RGXUnsetFirmwareAddress(DEVMEM_MEMDESC *psSrc)
{
DevmemReleaseDevVirtAddr(psSrc);
}
struct _RGX_SERVER_COMMON_CONTEXT_ {
PVRSRV_RGXDEV_INFO *psDevInfo;
DEVMEM_MEMDESC *psFWCommonContextMemDesc;
PRGXFWIF_FWCOMMONCONTEXT sFWCommonContextFWAddr;
DEVMEM_MEMDESC *psFWMemContextMemDesc;
DEVMEM_MEMDESC *psFWFrameworkMemDesc;
DEVMEM_MEMDESC *psContextStateMemDesc;
RGX_CLIENT_CCB *psClientCCB;
DEVMEM_MEMDESC *psClientCCBMemDesc;
DEVMEM_MEMDESC *psClientCCBCtrlMemDesc;
IMG_BOOL bCommonContextMemProvided;
IMG_UINT32 ui32ContextID;
DLLIST_NODE sListNode;
RGXFWIF_CONTEXT_RESET_REASON eLastResetReason;
IMG_UINT32 ui32LastResetJobRef;
};
PVRSRV_ERROR FWCommonContextAllocate(CONNECTION_DATA *psConnection,
PVRSRV_DEVICE_NODE *psDeviceNode,
RGX_CCB_REQUESTOR_TYPE eRGXCCBRequestor,
RGXFWIF_DM eDM,
DEVMEM_MEMDESC *psAllocatedMemDesc,
IMG_UINT32 ui32AllocatedOffset,
DEVMEM_MEMDESC *psFWMemContextMemDesc,
DEVMEM_MEMDESC *psContextStateMemDesc,
IMG_UINT32 ui32CCBAllocSizeLog2,
IMG_UINT32 ui32CCBMaxAllocSizeLog2,
IMG_UINT32 ui32ContextFlags,
IMG_UINT32 ui32Priority,
IMG_UINT32 ui32MaxDeadlineMS,
IMG_UINT64 ui64RobustnessAddress,
RGX_COMMON_CONTEXT_INFO *psInfo,
RGX_SERVER_COMMON_CONTEXT **ppsServerCommonContext)
{
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
RGX_SERVER_COMMON_CONTEXT *psServerCommonContext;
RGXFWIF_FWCOMMONCONTEXT *psFWCommonContext;
IMG_UINT32 ui32FWCommonContextOffset;
IMG_UINT8 *pui8Ptr;
PVRSRV_ERROR eError;
/*
* Allocate all the resources that are required
*/
psServerCommonContext = OSAllocMem(sizeof(*psServerCommonContext));
if (psServerCommonContext == NULL)
{
eError = PVRSRV_ERROR_OUT_OF_MEMORY;
goto fail_alloc;
}
psServerCommonContext->psDevInfo = psDevInfo;
if (psAllocatedMemDesc)
{
PDUMPCOMMENT("Using existing MemDesc for Rogue firmware %s context (offset = %d)",
aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT],
ui32AllocatedOffset);
ui32FWCommonContextOffset = ui32AllocatedOffset;
psServerCommonContext->psFWCommonContextMemDesc = psAllocatedMemDesc;
psServerCommonContext->bCommonContextMemProvided = IMG_TRUE;
}
else
{
/* Allocate device memory for the firmware context */
PDUMPCOMMENT("Allocate Rogue firmware %s context", aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT]);
eError = DevmemFwAllocate(psDevInfo,
sizeof(*psFWCommonContext),
RGX_FWCOMCTX_ALLOCFLAGS,
"FwContext",
&psServerCommonContext->psFWCommonContextMemDesc);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to allocate firmware %s context (%s)",
__func__,
aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT],
PVRSRVGetErrorString(eError)));
goto fail_contextalloc;
}
ui32FWCommonContextOffset = 0;
psServerCommonContext->bCommonContextMemProvided = IMG_FALSE;
}
/* Record this context so we can refer to it if the FW needs to tell us it was reset. */
psServerCommonContext->eLastResetReason = RGXFWIF_CONTEXT_RESET_REASON_NONE;
psServerCommonContext->ui32LastResetJobRef = 0;
psServerCommonContext->ui32ContextID = psDevInfo->ui32CommonCtxtCurrentID++;
/*
* Temporarily map the firmware context to the kernel and initialise it
*/
eError = DevmemAcquireCpuVirtAddr(psServerCommonContext->psFWCommonContextMemDesc,
(void **)&pui8Ptr);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to map firmware %s context to CPU (%s)",
__func__,
aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT],
PVRSRVGetErrorString(eError)));
goto fail_cpuvirtacquire;
}
/* Allocate the client CCB */
eError = RGXCreateCCB(psDevInfo,
ui32CCBAllocSizeLog2,
ui32CCBMaxAllocSizeLog2,
ui32ContextFlags,
psConnection,
eRGXCCBRequestor,
psServerCommonContext,
&psServerCommonContext->psClientCCB,
&psServerCommonContext->psClientCCBMemDesc,
&psServerCommonContext->psClientCCBCtrlMemDesc);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: failed to create CCB for %s context (%s)",
__func__,
aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT],
PVRSRVGetErrorString(eError)));
goto fail_allocateccb;
}
psFWCommonContext = (RGXFWIF_FWCOMMONCONTEXT *) (pui8Ptr + ui32FWCommonContextOffset);
psFWCommonContext->eDM = eDM;
/* Set the firmware CCB device addresses in the firmware common context */
eError = RGXSetFirmwareAddress(&psFWCommonContext->psCCB,
psServerCommonContext->psClientCCBMemDesc,
0, RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:1", fail_cccbfwaddr);
eError = RGXSetFirmwareAddress(&psFWCommonContext->psCCBCtl,
psServerCommonContext->psClientCCBCtrlMemDesc,
0, RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:2", fail_cccbctrlfwaddr);
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, META_DMA))
{
RGXSetMetaDMAAddress(&psFWCommonContext->sCCBMetaDMAAddr,
psServerCommonContext->psClientCCBMemDesc,
&psFWCommonContext->psCCB,
0);
}
/* Set the memory context device address */
psServerCommonContext->psFWMemContextMemDesc = psFWMemContextMemDesc;
eError = RGXSetFirmwareAddress(&psFWCommonContext->psFWMemContext,
psFWMemContextMemDesc,
0, RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:3", fail_fwmemctxfwaddr);
/* Set the framework register updates address */
psServerCommonContext->psFWFrameworkMemDesc = psInfo->psFWFrameworkMemDesc;
if (psInfo->psFWFrameworkMemDesc != NULL)
{
eError = RGXSetFirmwareAddress(&psFWCommonContext->psRFCmd,
psInfo->psFWFrameworkMemDesc,
0, RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:4", fail_fwframeworkfwaddr);
}
else
{
/* This should never be touched in this contexts without a framework
* memdesc, but ensure it is zero so we see crashes if it is.
*/
psFWCommonContext->psRFCmd.ui32Addr = 0;
}
psFWCommonContext->ui32Priority = ui32Priority;
psFWCommonContext->ui32PrioritySeqNum = 0;
psFWCommonContext->ui32MaxDeadlineMS = MIN(ui32MaxDeadlineMS,
(eDM == RGXFWIF_DM_CDM ?
RGXFWIF_MAX_CDM_WORKLOAD_DEADLINE_MS :
RGXFWIF_MAX_WORKLOAD_DEADLINE_MS));
psFWCommonContext->ui64RobustnessAddress = ui64RobustnessAddress;
/* Store a references to Server Common Context and PID for notifications back from the FW. */
psFWCommonContext->ui32ServerCommonContextID = psServerCommonContext->ui32ContextID;
psFWCommonContext->ui32PID = OSGetCurrentClientProcessIDKM();
/* Set the firmware GPU context state buffer */
psServerCommonContext->psContextStateMemDesc = psContextStateMemDesc;
if (psContextStateMemDesc)
{
eError = RGXSetFirmwareAddress(&psFWCommonContext->psContextState,
psContextStateMemDesc,
0,
RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:5", fail_ctxstatefwaddr);
}
/*
* Dump the created context
*/
PDUMPCOMMENT("Dump %s context", aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT]);
DevmemPDumpLoadMem(psServerCommonContext->psFWCommonContextMemDesc,
ui32FWCommonContextOffset,
sizeof(*psFWCommonContext),
PDUMP_FLAGS_CONTINUOUS);
/* We've finished the setup so release the CPU mapping */
DevmemReleaseCpuVirtAddr(psServerCommonContext->psFWCommonContextMemDesc);
/* Map this allocation into the FW */
eError = RGXSetFirmwareAddress(&psServerCommonContext->sFWCommonContextFWAddr,
psServerCommonContext->psFWCommonContextMemDesc,
ui32FWCommonContextOffset,
RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:6", fail_fwcommonctxfwaddr);
#if defined(LINUX)
{
IMG_UINT32 ui32FWAddr;
switch (eDM) {
case RGXFWIF_DM_GEOM:
ui32FWAddr = (IMG_UINT32) ((uintptr_t) IMG_CONTAINER_OF((void *) ((uintptr_t)
psServerCommonContext->sFWCommonContextFWAddr.ui32Addr), RGXFWIF_FWRENDERCONTEXT, sTAContext));
break;
case RGXFWIF_DM_3D:
ui32FWAddr = (IMG_UINT32) ((uintptr_t) IMG_CONTAINER_OF((void *) ((uintptr_t)
psServerCommonContext->sFWCommonContextFWAddr.ui32Addr), RGXFWIF_FWRENDERCONTEXT, s3DContext));
break;
default:
ui32FWAddr = psServerCommonContext->sFWCommonContextFWAddr.ui32Addr;
break;
}
trace_rogue_create_fw_context(OSGetCurrentClientProcessNameKM(),
aszCCBRequestors[eRGXCCBRequestor][REQ_PDUMP_COMMENT],
ui32FWAddr);
}
#endif
/*Add the node to the list when finalised */
OSWRLockAcquireWrite(psDevInfo->hCommonCtxtListLock);
dllist_add_to_tail(&(psDevInfo->sCommonCtxtListHead), &(psServerCommonContext->sListNode));
OSWRLockReleaseWrite(psDevInfo->hCommonCtxtListLock);
*ppsServerCommonContext = psServerCommonContext;
return PVRSRV_OK;
fail_fwcommonctxfwaddr:
if (psContextStateMemDesc)
{
RGXUnsetFirmwareAddress(psContextStateMemDesc);
}
fail_ctxstatefwaddr:
if (psInfo->psFWFrameworkMemDesc != NULL)
{
RGXUnsetFirmwareAddress(psInfo->psFWFrameworkMemDesc);
}
fail_fwframeworkfwaddr:
RGXUnsetFirmwareAddress(psFWMemContextMemDesc);
fail_fwmemctxfwaddr:
RGXUnsetFirmwareAddress(psServerCommonContext->psClientCCBCtrlMemDesc);
fail_cccbctrlfwaddr:
RGXUnsetFirmwareAddress(psServerCommonContext->psClientCCBMemDesc);
fail_cccbfwaddr:
RGXDestroyCCB(psDevInfo, psServerCommonContext->psClientCCB);
fail_allocateccb:
DevmemReleaseCpuVirtAddr(psServerCommonContext->psFWCommonContextMemDesc);
fail_cpuvirtacquire:
if (!psServerCommonContext->bCommonContextMemProvided)
{
DevmemFwUnmapAndFree(psDevInfo, psServerCommonContext->psFWCommonContextMemDesc);
psServerCommonContext->psFWCommonContextMemDesc = NULL;
}
fail_contextalloc:
OSFreeMem(psServerCommonContext);
fail_alloc:
return eError;
}
void FWCommonContextFree(RGX_SERVER_COMMON_CONTEXT *psServerCommonContext)
{
OSWRLockAcquireWrite(psServerCommonContext->psDevInfo->hCommonCtxtListLock);
/* Remove the context from the list of all contexts. */
dllist_remove_node(&psServerCommonContext->sListNode);
OSWRLockReleaseWrite(psServerCommonContext->psDevInfo->hCommonCtxtListLock);
/*
Unmap the context itself and then all its resources
*/
/* Unmap the FW common context */
RGXUnsetFirmwareAddress(psServerCommonContext->psFWCommonContextMemDesc);
/* Umap context state buffer (if there was one) */
if (psServerCommonContext->psContextStateMemDesc)
{
RGXUnsetFirmwareAddress(psServerCommonContext->psContextStateMemDesc);
}
/* Unmap the framework buffer */
if (psServerCommonContext->psFWFrameworkMemDesc)
{
RGXUnsetFirmwareAddress(psServerCommonContext->psFWFrameworkMemDesc);
}
/* Unmap client CCB and CCB control */
RGXUnsetFirmwareAddress(psServerCommonContext->psClientCCBCtrlMemDesc);
RGXUnsetFirmwareAddress(psServerCommonContext->psClientCCBMemDesc);
/* Unmap the memory context */
RGXUnsetFirmwareAddress(psServerCommonContext->psFWMemContextMemDesc);
/* Destroy the client CCB */
RGXDestroyCCB(psServerCommonContext->psDevInfo, psServerCommonContext->psClientCCB);
/* Free the FW common context (if there was one) */
if (!psServerCommonContext->bCommonContextMemProvided)
{
DevmemFwUnmapAndFree(psServerCommonContext->psDevInfo,
psServerCommonContext->psFWCommonContextMemDesc);
psServerCommonContext->psFWCommonContextMemDesc = NULL;
}
/* Free the hosts representation of the common context */
OSFreeMem(psServerCommonContext);
}
PRGXFWIF_FWCOMMONCONTEXT FWCommonContextGetFWAddress(RGX_SERVER_COMMON_CONTEXT *psServerCommonContext)
{
return psServerCommonContext->sFWCommonContextFWAddr;
}
RGX_CLIENT_CCB *FWCommonContextGetClientCCB(RGX_SERVER_COMMON_CONTEXT *psServerCommonContext)
{
return psServerCommonContext->psClientCCB;
}
RGXFWIF_CONTEXT_RESET_REASON FWCommonContextGetLastResetReason(RGX_SERVER_COMMON_CONTEXT *psServerCommonContext,
IMG_UINT32 *pui32LastResetJobRef)
{
RGXFWIF_CONTEXT_RESET_REASON eLastResetReason;
PVR_ASSERT(psServerCommonContext != NULL);
PVR_ASSERT(pui32LastResetJobRef != NULL);
/* Take the most recent reason & job ref and reset for next time... */
eLastResetReason = psServerCommonContext->eLastResetReason;
*pui32LastResetJobRef = psServerCommonContext->ui32LastResetJobRef;
psServerCommonContext->eLastResetReason = RGXFWIF_CONTEXT_RESET_REASON_NONE;
psServerCommonContext->ui32LastResetJobRef = 0;
if (eLastResetReason == RGXFWIF_CONTEXT_RESET_REASON_HARD_CONTEXT_SWITCH)
{
PVR_DPF((PVR_DBG_WARNING,
"A Hard Context Switch was triggered on the GPU to ensure Quality of Service."));
}
return eLastResetReason;
}
PVRSRV_RGXDEV_INFO* FWCommonContextGetRGXDevInfo(RGX_SERVER_COMMON_CONTEXT *psServerCommonContext)
{
return psServerCommonContext->psDevInfo;
}
PVRSRV_ERROR FWCommonContextSetFlags(RGX_SERVER_COMMON_CONTEXT *psServerCommonContext,
IMG_UINT32 ui32ContextFlags)
{
return RGXSetCCBFlags(psServerCommonContext->psClientCCB,
ui32ContextFlags);
}
/*!
*******************************************************************************
@Function RGXFreeCCB
@Description Free the kernel or firmware CCB
@Input psDevInfo
@Input ppsCCBCtl
@Input ppsCCBCtlMemDesc
@Input ppsCCBMemDesc
@Input psCCBCtlFWAddr
******************************************************************************/
static void RGXFreeCCB(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_CCB_CTL **ppsCCBCtl,
DEVMEM_MEMDESC **ppsCCBCtlMemDesc,
IMG_UINT8 **ppui8CCB,
DEVMEM_MEMDESC **ppsCCBMemDesc)
{
if (*ppsCCBMemDesc != NULL)
{
if (*ppui8CCB != NULL)
{
DevmemReleaseCpuVirtAddr(*ppsCCBMemDesc);
*ppui8CCB = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, *ppsCCBMemDesc);
*ppsCCBMemDesc = NULL;
}
if (*ppsCCBCtlMemDesc != NULL)
{
if (*ppsCCBCtl != NULL)
{
DevmemReleaseCpuVirtAddr(*ppsCCBCtlMemDesc);
*ppsCCBCtl = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, *ppsCCBCtlMemDesc);
*ppsCCBCtlMemDesc = NULL;
}
}
/*!
*******************************************************************************
@Function RGXFreeCCBReturnSlots
@Description Free the kernel CCB's return slot array and associated mappings
@Input psDevInfo Device Info struct
@Input ppui32CCBRtnSlots CPU mapping of slot array
@Input ppsCCBRtnSlotsMemDesc Slot array's device memdesc
******************************************************************************/
static void RGXFreeCCBReturnSlots(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 **ppui32CCBRtnSlots,
DEVMEM_MEMDESC **ppsCCBRtnSlotsMemDesc)
{
/* Free the return slot array if allocated */
if (*ppsCCBRtnSlotsMemDesc != NULL)
{
/* Before freeing, ensure the CPU mapping as well is released */
if (*ppui32CCBRtnSlots != NULL)
{
DevmemReleaseCpuVirtAddr(*ppsCCBRtnSlotsMemDesc);
*ppui32CCBRtnSlots = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, *ppsCCBRtnSlotsMemDesc);
*ppsCCBRtnSlotsMemDesc = NULL;
}
}
/*!
*******************************************************************************
@Function RGXSetupCCB
@Description Allocate and initialise a circular command buffer
@Input psDevInfo
@Input ppsCCBCtl
@Input ppsCCBCtlMemDesc
@Input ppui8CCB
@Input ppsCCBMemDesc
@Input psCCBCtlFWAddr
@Input ui32NumCmdsLog2
@Input ui32CmdSize
@Input uiCCBMemAllocFlags
@Input pszName
@Return PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXSetupCCB(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_CCB_CTL **ppsCCBCtl,
DEVMEM_MEMDESC **ppsCCBCtlMemDesc,
IMG_UINT8 **ppui8CCB,
DEVMEM_MEMDESC **ppsCCBMemDesc,
PRGXFWIF_CCB_CTL *psCCBCtlFWAddr,
PRGXFWIF_CCB *psCCBFWAddr,
IMG_UINT32 ui32NumCmdsLog2,
IMG_UINT32 ui32CmdSize,
DEVMEM_FLAGS_T uiCCBMemAllocFlags,
const IMG_CHAR *pszName)
{
PVRSRV_ERROR eError;
RGXFWIF_CCB_CTL *psCCBCtl;
IMG_UINT32 ui32CCBSize = (1U << ui32NumCmdsLog2);
IMG_CHAR szCCBCtlName[DEVMEM_ANNOTATION_MAX_LEN];
IMG_INT32 iStrLen;
/* Append "Control" to the name for the control struct. */
iStrLen = OSSNPrintf(szCCBCtlName, sizeof(szCCBCtlName), "%sControl", pszName);
PVR_ASSERT(iStrLen < sizeof(szCCBCtlName));
if (unlikely(iStrLen < 0))
{
szCCBCtlName[0] = '\0';
}
/* Allocate memory for the CCB control.*/
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS,
sizeof(RGXFWIF_CCB_CTL),
szCCBCtlName,
ppsCCBCtlMemDesc,
psCCBCtlFWAddr,
(void**) ppsCCBCtl,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
/*
* Allocate memory for the CCB.
* (this will reference further command data in non-shared CCBs)
*/
eError = RGXSetupFwAllocation(psDevInfo,
uiCCBMemAllocFlags,
ui32CCBSize * ui32CmdSize,
pszName,
ppsCCBMemDesc,
psCCBFWAddr,
(void**) ppui8CCB,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
/*
* Initialise the CCB control.
*/
psCCBCtl = *ppsCCBCtl;
psCCBCtl->ui32WriteOffset = 0;
psCCBCtl->ui32ReadOffset = 0;
psCCBCtl->ui32WrapMask = ui32CCBSize - 1;
psCCBCtl->ui32CmdSize = ui32CmdSize;
/* Pdump the CCB control */
PDUMPCOMMENT("Initialise %s", szCCBCtlName);
DevmemPDumpLoadMem(*ppsCCBCtlMemDesc,
0,
sizeof(RGXFWIF_CCB_CTL),
0);
return PVRSRV_OK;
fail:
RGXFreeCCB(psDevInfo,
ppsCCBCtl,
ppsCCBCtlMemDesc,
ppui8CCB,
ppsCCBMemDesc);
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
static void RGXSetupFaultReadRegisterRollback(PVRSRV_RGXDEV_INFO *psDevInfo)
{
PMR *psPMR;
if (psDevInfo->psRGXFaultAddressMemDesc)
{
if (DevmemServerGetImportHandle(psDevInfo->psRGXFaultAddressMemDesc, (void **)&psPMR) == PVRSRV_OK)
{
PMRUnlockSysPhysAddresses(psPMR);
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFaultAddressMemDesc);
psDevInfo->psRGXFaultAddressMemDesc = NULL;
}
}
static PVRSRV_ERROR RGXSetupFaultReadRegister(PVRSRV_DEVICE_NODE *psDeviceNode, RGXFWIF_SYSINIT *psFwSysInit)
{
PVRSRV_ERROR eError = PVRSRV_OK;
IMG_UINT32 *pui32MemoryVirtAddr;
IMG_UINT32 i;
size_t ui32PageSize = OSGetPageSize();
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
PMR *psPMR;
/* Allocate page of memory to use for page faults on non-blocking memory transactions.
* Doesn't need to be cleared as it is initialised with the 0xDEADBEE0 pattern below. */
psDevInfo->psRGXFaultAddressMemDesc = NULL;
eError = DevmemFwAllocateExportable(psDeviceNode,
ui32PageSize,
ui32PageSize,
RGX_FWSHAREDMEM_ALLOCFLAGS & (~PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC),
"FwExFaultAddress",
&psDevInfo->psRGXFaultAddressMemDesc);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to allocate mem for fault address (%u)",
__func__, eError));
goto failFaultAddressDescAlloc;
}
eError = DevmemAcquireCpuVirtAddr(psDevInfo->psRGXFaultAddressMemDesc,
(void **)&pui32MemoryVirtAddr);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to acquire mem for fault address (%u)",
__func__, eError));
goto failFaultAddressDescAqCpuVirt;
}
if (!psDeviceNode->bAutoVzFwIsUp)
{
/* fill the page with a known pattern when booting the firmware */
for (i = 0; i < ui32PageSize/sizeof(IMG_UINT32); i++)
{
*(pui32MemoryVirtAddr + i) = 0xDEADBEE0;
}
}
eError = DevmemServerGetImportHandle(psDevInfo->psRGXFaultAddressMemDesc, (void **)&psPMR);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Error getting PMR for fault address (%u)",
__func__, eError));
goto failFaultAddressDescGetPMR;
}
else
{
IMG_BOOL bValid;
IMG_UINT32 ui32Log2PageSize = OSGetPageShift();
eError = PMRLockSysPhysAddresses(psPMR);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Error locking physical address for fault address MemDesc (%u)",
__func__, eError));
goto failFaultAddressDescLockPhys;
}
eError = PMR_DevPhysAddr(psPMR,ui32Log2PageSize, 1, 0, &(psFwSysInit->sFaultPhysAddr), &bValid);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Error getting physical address for fault address MemDesc (%u)",
__func__, eError));
goto failFaultAddressDescGetPhys;
}
if (!bValid)
{
psFwSysInit->sFaultPhysAddr.uiAddr = 0;
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed getting physical address for fault address MemDesc - invalid page (0x%" IMG_UINT64_FMTSPECX ")",
__func__, psFwSysInit->sFaultPhysAddr.uiAddr));
goto failFaultAddressDescGetPhys;
}
}
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFaultAddressMemDesc);
return PVRSRV_OK;
failFaultAddressDescGetPhys:
PMRUnlockSysPhysAddresses(psPMR);
failFaultAddressDescLockPhys:
failFaultAddressDescGetPMR:
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFaultAddressMemDesc);
failFaultAddressDescAqCpuVirt:
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFaultAddressMemDesc);
psDevInfo->psRGXFaultAddressMemDesc = NULL;
failFaultAddressDescAlloc:
return eError;
}
#if defined(PDUMP)
/* Replace the DevPhy address with the one Pdump allocates at pdump_player run time */
static PVRSRV_ERROR RGXPDumpFaultReadRegister(PVRSRV_RGXDEV_INFO *psDevInfo)
{
PVRSRV_ERROR eError;
PMR *psFWInitPMR, *psFaultAddrPMR;
IMG_UINT32 ui32Dstoffset;
psFWInitPMR = (PMR *)(psDevInfo->psRGXFWIfSysInitMemDesc->psImport->hPMR);
ui32Dstoffset = psDevInfo->psRGXFWIfSysInitMemDesc->uiOffset + offsetof(RGXFWIF_SYSINIT, sFaultPhysAddr.uiAddr);
psFaultAddrPMR = (PMR *)(psDevInfo->psRGXFaultAddressMemDesc->psImport->hPMR);
eError = PDumpMemLabelToMem64(psFaultAddrPMR,
psFWInitPMR,
0,
ui32Dstoffset,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Dump of Fault Page Phys address failed(%u)", __func__, eError));
}
return eError;
}
#endif
#if defined(SUPPORT_TBI_INTERFACE)
/*************************************************************************/ /*!
@Function RGXTBIBufferIsInitRequired
@Description Returns true if the firmware tbi buffer is not allocated and
might be required by the firmware soon. TBI buffer allocated
on-demand to reduce RAM footprint on systems not needing
tbi.
@Input psDevInfo RGX device info
@Return IMG_BOOL Whether on-demand allocation(s) is/are needed
or not
*/ /**************************************************************************/
INLINE IMG_BOOL RGXTBIBufferIsInitRequired(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFWIF_TRACEBUF* psTraceBufCtl = psDevInfo->psRGXFWIfTraceBufCtl;
/* The firmware expects a tbi buffer only when:
* - Logtype is "tbi"
*/
if ((psDevInfo->psRGXFWIfTBIBufferMemDesc == NULL)
&& (psTraceBufCtl->ui32LogType & ~RGXFWIF_LOG_TYPE_TRACE)
&& (psTraceBufCtl->ui32LogType & RGXFWIF_LOG_TYPE_GROUP_MASK))
{
return IMG_TRUE;
}
return IMG_FALSE;
}
/*************************************************************************/ /*!
@Function RGXTBIBufferDeinit
@Description Deinitialises all the allocations and references that are made
for the FW tbi buffer
@Input ppsDevInfo RGX device info
@Return void
*/ /**************************************************************************/
static void RGXTBIBufferDeinit(PVRSRV_RGXDEV_INFO *psDevInfo)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfTBIBufferMemDesc);
psDevInfo->psRGXFWIfTBIBufferMemDesc = NULL;
psDevInfo->ui32RGXFWIfHWPerfBufSize = 0;
}
/*************************************************************************/ /*!
@Function RGXTBIBufferInitOnDemandResources
@Description Allocates the firmware TBI buffer required for reading SFs
strings and initialize it with SFs.
@Input psDevInfo RGX device info
@Return PVRSRV_OK If all went good, PVRSRV_ERROR otherwise.
*/ /**************************************************************************/
PVRSRV_ERROR RGXTBIBufferInitOnDemandResources(PVRSRV_RGXDEV_INFO *psDevInfo)
{
PVRSRV_ERROR eError = PVRSRV_OK;
IMG_UINT32 i, ui32Len;
const IMG_UINT32 ui32FWTBIBufsize = g_ui32SFsCount * sizeof(RGXFW_STID_FMT);
RGXFW_STID_FMT *psFW_SFs = NULL;
/* Firmware address should not be already set */
if (psDevInfo->sRGXFWIfTBIBuffer.ui32Addr)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: FW address for FWTBI is already set. Resetting it with newly allocated one",
__func__));
}
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_GPU_RO_ALLOCFLAGS,
ui32FWTBIBufsize,
"FwTBIBuffer",
&psDevInfo->psRGXFWIfTBIBufferMemDesc,
&psDevInfo->sRGXFWIfTBIBuffer,
(void**)&psFW_SFs,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
/* Copy SFs entries to FW buffer */
for (i = 0; i < g_ui32SFsCount; i++)
{
OSDeviceMemCopy(&psFW_SFs[i].ui32Id, &SFs[i].ui32Id, sizeof(SFs[i].ui32Id));
ui32Len = OSStringLength(SFs[i].psName);
OSDeviceMemCopy(psFW_SFs[i].sName, SFs[i].psName, MIN(ui32Len, IMG_SF_STRING_MAX_SIZE - 1));
}
/* Set size of TBI buffer */
psDevInfo->ui32FWIfTBIBufferSize = ui32FWTBIBufsize;
/* release CPU mapping */
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfTBIBufferMemDesc);
return PVRSRV_OK;
fail:
RGXTBIBufferDeinit(psDevInfo);
return eError;
}
#endif
/*************************************************************************/ /*!
@Function RGXTraceBufferIsInitRequired
@Description Returns true if the firmware trace buffer is not allocated and
might be required by the firmware soon. Trace buffer allocated
on-demand to reduce RAM footprint on systems not needing
firmware trace.
@Input psDevInfo RGX device info
@Return IMG_BOOL Whether on-demand allocation(s) is/are needed
or not
*/ /**************************************************************************/
INLINE IMG_BOOL RGXTraceBufferIsInitRequired(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFWIF_TRACEBUF* psTraceBufCtl = psDevInfo->psRGXFWIfTraceBufCtl;
/* The firmware expects a trace buffer only when:
* - Logtype is "trace" AND
* - at least one LogGroup is configured
* - the Driver Mode is not Guest
*/
if ((psDevInfo->psRGXFWIfTraceBufferMemDesc[0] == NULL)
&& (psTraceBufCtl->ui32LogType & RGXFWIF_LOG_TYPE_TRACE)
&& (psTraceBufCtl->ui32LogType & RGXFWIF_LOG_TYPE_GROUP_MASK)
&& !PVRSRV_VZ_MODE_IS(GUEST))
{
return IMG_TRUE;
}
return IMG_FALSE;
}
/*************************************************************************/ /*!
@Function RGXTraceBufferDeinit
@Description Deinitialises all the allocations and references that are made
for the FW trace buffer(s)
@Input ppsDevInfo RGX device info
@Return void
*/ /**************************************************************************/
static void RGXTraceBufferDeinit(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFWIF_TRACEBUF* psTraceBufCtl = psDevInfo->psRGXFWIfTraceBufCtl;
IMG_UINT32 i;
for (i = 0; i < RGXFW_THREAD_NUM; i++)
{
if (psDevInfo->psRGXFWIfTraceBufferMemDesc[i])
{
if (psTraceBufCtl->sTraceBuf[i].pui32TraceBuffer != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfTraceBufferMemDesc[i]);
psTraceBufCtl->sTraceBuf[i].pui32TraceBuffer = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfTraceBufferMemDesc[i]);
psDevInfo->psRGXFWIfTraceBufferMemDesc[i] = NULL;
}
}
}
/*************************************************************************/ /*!
@Function RGXTraceBufferInitOnDemandResources
@Description Allocates the firmware trace buffer required for dumping trace
info from the firmware.
@Input psDevInfo RGX device info
@Return PVRSRV_OK If all went good, PVRSRV_ERROR otherwise.
*/ /**************************************************************************/
PVRSRV_ERROR RGXTraceBufferInitOnDemandResources(PVRSRV_RGXDEV_INFO* psDevInfo,
DEVMEM_FLAGS_T uiAllocFlags)
{
RGXFWIF_TRACEBUF* psTraceBufCtl = psDevInfo->psRGXFWIfTraceBufCtl;
PVRSRV_ERROR eError = PVRSRV_OK;
IMG_UINT32 ui32FwThreadNum;
IMG_UINT32 ui32DefaultTraceBufSize;
IMG_DEVMEM_SIZE_T uiTraceBufSizeInBytes;
void *pvAppHintState = NULL;
IMG_CHAR pszBufferName[] = "FwTraceBuffer_Thread0";
/* Check AppHint value for module-param FWTraceBufSizeInDWords */
OSCreateKMAppHintState(&pvAppHintState);
ui32DefaultTraceBufSize = RGXFW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
OSGetKMAppHintUINT32(pvAppHintState,
FWTraceBufSizeInDWords,
&ui32DefaultTraceBufSize,
&psTraceBufCtl->ui32TraceBufSizeInDWords);
OSFreeKMAppHintState(pvAppHintState);
pvAppHintState = NULL;
uiTraceBufSizeInBytes = psTraceBufCtl->ui32TraceBufSizeInDWords * sizeof(IMG_UINT32);
for (ui32FwThreadNum = 0; ui32FwThreadNum < RGXFW_THREAD_NUM; ui32FwThreadNum++)
{
#if !defined(SUPPORT_AUTOVZ)
/* Ensure allocation API is only called when not already allocated */
PVR_ASSERT(psDevInfo->psRGXFWIfTraceBufferMemDesc[ui32FwThreadNum] == NULL);
/* Firmware address should not be already set */
PVR_ASSERT(psTraceBufCtl->sTraceBuf[ui32FwThreadNum].pui32RGXFWIfTraceBuffer.ui32Addr == 0x0);
#endif
/* update the firmware thread number in the Trace Buffer's name */
pszBufferName[sizeof(pszBufferName) - 2] += ui32FwThreadNum;
eError = RGXSetupFwAllocation(psDevInfo,
uiAllocFlags,
uiTraceBufSizeInBytes,
pszBufferName,
&psDevInfo->psRGXFWIfTraceBufferMemDesc[ui32FwThreadNum],
&psTraceBufCtl->sTraceBuf[ui32FwThreadNum].pui32RGXFWIfTraceBuffer,
(void**)&psTraceBufCtl->sTraceBuf[ui32FwThreadNum].pui32TraceBuffer,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
}
return PVRSRV_OK;
fail:
RGXTraceBufferDeinit(psDevInfo);
return eError;
}
#if defined(PDUMP)
/*************************************************************************/ /*!
@Function RGXPDumpLoadFWInitData
@Description Allocates the firmware trace buffer required for dumping trace
info from the firmware.
@Input psDevInfo RGX device info
*/ /*************************************************************************/
static void RGXPDumpLoadFWInitData(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32HWPerfCountersDataSize,
IMG_BOOL bEnableSignatureChecks)
{
IMG_UINT32 ui32ConfigFlags = psDevInfo->psRGXFWIfFwSysData->ui32ConfigFlags;
IMG_UINT32 ui32FwOsCfgFlags = psDevInfo->psRGXFWIfFwOsData->ui32FwOsConfigFlags;
PDUMPCOMMENT("Dump RGXFW Init data");
if (!bEnableSignatureChecks)
{
PDUMPCOMMENT("(to enable rgxfw signatures place the following line after the RTCONF line)");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfSysInitMemDesc,
offsetof(RGXFWIF_SYSINIT, asSigBufCtl),
sizeof(RGXFWIF_SIGBUF_CTL)*(RGXFWIF_DM_MAX),
PDUMP_FLAGS_CONTINUOUS);
}
PDUMPCOMMENT("Dump initial state of FW runtime configuration");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfRuntimeCfgMemDesc,
0,
sizeof(RGXFWIF_RUNTIME_CFG),
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("Dump rgxfw hwperfctl structure");
DevmemPDumpLoadZeroMem(psDevInfo->psRGXFWIfHWPerfCountersMemDesc,
0,
ui32HWPerfCountersDataSize,
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("Dump rgxfw trace control structure");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfTraceBufCtlMemDesc,
0,
sizeof(RGXFWIF_TRACEBUF),
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("Dump firmware system data structure");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfFwSysDataMemDesc,
0,
sizeof(RGXFWIF_SYSDATA),
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("Dump firmware OS data structure");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfFwOsDataMemDesc,
0,
sizeof(RGXFWIF_OSDATA),
PDUMP_FLAGS_CONTINUOUS);
#if defined(SUPPORT_TBI_INTERFACE)
PDUMPCOMMENT("Dump rgx TBI buffer");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfTBIBufferMemDesc,
0,
psDevInfo->ui32FWIfTBIBufferSize,
PDUMP_FLAGS_CONTINUOUS);
#endif /* defined(SUPPORT_TBI_INTERFACE) */
#if defined(SUPPORT_USER_REGISTER_CONFIGURATION)
PDUMPCOMMENT("Dump rgxfw register configuration buffer");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfRegCfgMemDesc,
0,
sizeof(RGXFWIF_REG_CFG),
PDUMP_FLAGS_CONTINUOUS);
#endif /* defined(SUPPORT_USER_REGISTER_CONFIGURATION) */
PDUMPCOMMENT("Dump rgxfw system init structure");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfSysInitMemDesc,
0,
sizeof(RGXFWIF_SYSINIT),
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("Dump rgxfw os init structure");
DevmemPDumpLoadMem(psDevInfo->psRGXFWIfOsInitMemDesc,
0,
sizeof(RGXFWIF_OSINIT),
PDUMP_FLAGS_CONTINUOUS);
/* RGXFW Init structure needs to be loaded before we overwrite FaultPhysAddr, else this address patching won't have any effect */
PDUMPCOMMENT("Overwrite FaultPhysAddr of FwSysInit in pdump with actual physical address");
RGXPDumpFaultReadRegister(psDevInfo);
PDUMPCOMMENT("RTCONF: run-time configuration");
/* Dump the config options so they can be edited.
*
*/
PDUMPCOMMENT("(Set the FW system config options here)");
PDUMPCOMMENT("( Ctx Switch Rand mode: 0x%08x)", RGXFWIF_INICFG_CTXSWITCH_MODE_RAND);
PDUMPCOMMENT("( Ctx Switch Soft Reset Enable: 0x%08x)", RGXFWIF_INICFG_CTXSWITCH_SRESET_EN);
PDUMPCOMMENT("( Enable HWPerf: 0x%08x)", RGXFWIF_INICFG_HWPERF_EN);
PDUMPCOMMENT("( Enable generic DM Killing Rand mode: 0x%08x)", RGXFWIF_INICFG_DM_KILL_MODE_RAND_EN);
PDUMPCOMMENT("( Rascal+Dust Power Island: 0x%08x)", RGXFWIF_INICFG_POW_RASCALDUST);
PDUMPCOMMENT("( Enable HWR: 0x%08x)", RGXFWIF_INICFG_HWR_EN);
PDUMPCOMMENT("( FBCDC Version 3.1 Enable: 0x%08x)", RGXFWIF_INICFG_FBCDC_V3_1_EN);
PDUMPCOMMENT("( Check MList: 0x%08x)", RGXFWIF_INICFG_CHECK_MLIST_EN);
PDUMPCOMMENT("( Disable Auto Clock Gating: 0x%08x)", RGXFWIF_INICFG_DISABLE_CLKGATING_EN);
PDUMPCOMMENT("( Enable HWPerf Polling Perf Counter: 0x%08x)", RGXFWIF_INICFG_POLL_COUNTERS_EN);
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, VDM_OBJECT_LEVEL_LLS))
{
PDUMPCOMMENT("( Ctx Switch Object mode Index: 0x%08x)", RGXFWIF_INICFG_VDM_CTX_STORE_MODE_INDEX);
PDUMPCOMMENT("( Ctx Switch Object mode Instance: 0x%08x)", RGXFWIF_INICFG_VDM_CTX_STORE_MODE_INSTANCE);
PDUMPCOMMENT("( Ctx Switch Object mode List: 0x%08x)", RGXFWIF_INICFG_VDM_CTX_STORE_MODE_LIST);
}
PDUMPCOMMENT("( Enable register configuration: 0x%08x)", RGXFWIF_INICFG_REGCONFIG_EN);
PDUMPCOMMENT("( Assert on TA Out-of-Memory: 0x%08x)", RGXFWIF_INICFG_ASSERT_ON_OUTOFMEMORY);
PDUMPCOMMENT("( Disable HWPerf custom counter filter: 0x%08x)", RGXFWIF_INICFG_HWP_DISABLE_FILTER);
PDUMPCOMMENT("( Enable HWPerf custom performance timer: 0x%08x)", RGXFWIF_INICFG_CUSTOM_PERF_TIMER_EN);
PDUMPCOMMENT("( Enable Ctx Switch profile mode: 0x%08x (none=b'000, fast=b'001, medium=b'010, slow=b'011, nodelay=b'100))", RGXFWIF_INICFG_CTXSWITCH_PROFILE_MASK);
PDUMPCOMMENT("( Disable DM overlap (except TA during SPM): 0x%08x)", RGXFWIF_INICFG_DISABLE_DM_OVERLAP);
PDUMPCOMMENT("( Assert on HWR trigger (page fault, lockup, overrun or poll failure): 0x%08x)", RGXFWIF_INICFG_ASSERT_ON_HWR_TRIGGER);
PDUMPCOMMENT("( Enable coherent memory accesses: 0x%08x)", RGXFWIF_INICFG_FABRIC_COHERENCY_ENABLED);
PDUMPCOMMENT("( Enable IRQ validation: 0x%08x)", RGXFWIF_INICFG_VALIDATE_IRQ);
PDUMPCOMMENT("( SPU power state mask change Enable: 0x%08x)", RGXFWIF_INICFG_SPU_POWER_STATE_MASK_CHANGE_EN);
#if defined(SUPPORT_WORKLOAD_ESTIMATION)
PDUMPCOMMENT("( Enable Workload Estimation: 0x%08x)", RGXFWIF_INICFG_WORKEST);
#if defined(SUPPORT_PDVFS)
PDUMPCOMMENT("( Enable Proactive DVFS: 0x%08x)", RGXFWIF_INICFG_PDVFS);
#endif /* defined(SUPPORT_PDVFS) */
#endif /* defined(SUPPORT_WORKLOAD_ESTIMATION) */
PDUMPCOMMENT("( CDM Arbitration Mode (task demand=b'01, round robin=b'10): 0x%08x)", RGXFWIF_INICFG_CDM_ARBITRATION_MASK);
PDUMPCOMMENT("( ISP Scheduling Mode (v1=b'01, v2=b'10): 0x%08x)", RGXFWIF_INICFG_ISPSCHEDMODE_MASK);
PDUMPCOMMENT("( Validate SOC & USC timers: 0x%08x)", RGXFWIF_INICFG_VALIDATE_SOCUSC_TIMER);
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfFwSysDataMemDesc,
offsetof(RGXFWIF_SYSDATA, ui32ConfigFlags),
ui32ConfigFlags,
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("( Extended FW system config options not used.)");
PDUMPCOMMENT("(Set the FW OS config options here)");
PDUMPCOMMENT("( Ctx Switch TA Enable: 0x%08x)", RGXFWIF_INICFG_OS_CTXSWITCH_TA_EN);
PDUMPCOMMENT("( Ctx Switch 3D Enable: 0x%08x)", RGXFWIF_INICFG_OS_CTXSWITCH_3D_EN);
PDUMPCOMMENT("( Ctx Switch CDM Enable: 0x%08x)", RGXFWIF_INICFG_OS_CTXSWITCH_CDM_EN);
PDUMPCOMMENT("( Lower Priority Ctx Switch 2D Enable: 0x%08x)", RGXFWIF_INICFG_OS_LOW_PRIO_CS_TDM);
PDUMPCOMMENT("( Lower Priority Ctx Switch TA Enable: 0x%08x)", RGXFWIF_INICFG_OS_LOW_PRIO_CS_TA);
PDUMPCOMMENT("( Lower Priority Ctx Switch 3D Enable: 0x%08x)", RGXFWIF_INICFG_OS_LOW_PRIO_CS_3D);
PDUMPCOMMENT("( Lower Priority Ctx Switch CDM Enable: 0x%08x)", RGXFWIF_INICFG_OS_LOW_PRIO_CS_CDM);
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfFwOsDataMemDesc,
offsetof(RGXFWIF_OSDATA, ui32FwOsConfigFlags),
ui32FwOsCfgFlags,
PDUMP_FLAGS_CONTINUOUS);
#if defined(SUPPORT_SECURITY_VALIDATION)
PDUMPCOMMENT("(Select one or more security tests here)");
PDUMPCOMMENT("( Read/write FW private data from non-FW contexts: 0x%08x)", RGXFWIF_SECURE_ACCESS_TEST_READ_WRITE_FW_DATA);
PDUMPCOMMENT("( Read/write FW code from non-FW contexts: 0x%08x)", RGXFWIF_SECURE_ACCESS_TEST_READ_WRITE_FW_CODE);
PDUMPCOMMENT("( Execute FW code from non-secure memory: 0x%08x)", RGXFWIF_SECURE_ACCESS_TEST_RUN_FROM_NONSECURE);
PDUMPCOMMENT("( Execute FW code from secure (non-FW) memory: 0x%08x)", RGXFWIF_SECURE_ACCESS_TEST_RUN_FROM_SECURE);
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfSysInitMemDesc,
offsetof(RGXFWIF_SYSINIT, ui32SecurityTestFlags),
psDevInfo->psRGXFWIfSysInit->ui32SecurityTestFlags,
PDUMP_FLAGS_CONTINUOUS);
#endif
PDUMPCOMMENT("( PID filter type: %X=INCLUDE_ALL_EXCEPT, %X=EXCLUDE_ALL_EXCEPT)",
RGXFW_PID_FILTER_INCLUDE_ALL_EXCEPT,
RGXFW_PID_FILTER_EXCLUDE_ALL_EXCEPT);
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfSysInitMemDesc,
offsetof(RGXFWIF_SYSINIT, sPIDFilter.eMode),
psDevInfo->psRGXFWIfSysInit->sPIDFilter.eMode,
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("( PID filter PID/OSID list (Up to %u entries. Terminate with a zero PID))",
RGXFWIF_PID_FILTER_MAX_NUM_PIDS);
{
IMG_UINT32 i;
/* generate a few WRWs in the pdump stream as an example */
for (i = 0; i < MIN(RGXFWIF_PID_FILTER_MAX_NUM_PIDS, 8); i++)
{
/*
* Some compilers cannot cope with the uses of offsetof() below - the specific problem being the use of
* a non-const variable in the expression, which it needs to be const. Typical compiler output is
* "expression must have a constant value".
*/
const IMG_DEVMEM_OFFSET_T uiPIDOff
= (IMG_DEVMEM_OFFSET_T)(uintptr_t)&(((RGXFWIF_SYSINIT *)0)->sPIDFilter.asItems[i].uiPID);
const IMG_DEVMEM_OFFSET_T uiOSIDOff
= (IMG_DEVMEM_OFFSET_T)(uintptr_t)&(((RGXFWIF_SYSINIT *)0)->sPIDFilter.asItems[i].ui32OSID);
PDUMPCOMMENT("(PID and OSID pair %u)", i);
PDUMPCOMMENT("(PID)");
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfSysInitMemDesc,
uiPIDOff,
0,
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("(OSID)");
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfSysInitMemDesc,
uiOSIDOff,
0,
PDUMP_FLAGS_CONTINUOUS);
}
}
/*
* Dump the log config so it can be edited.
*/
PDUMPCOMMENT("(Set the log config here)");
PDUMPCOMMENT("( Log Type: set bit 0 for TRACE, reset for TBI)");
PDUMPCOMMENT("( MAIN Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_MAIN);
PDUMPCOMMENT("( MTS Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_MTS);
PDUMPCOMMENT("( CLEANUP Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_CLEANUP);
PDUMPCOMMENT("( CSW Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_CSW);
PDUMPCOMMENT("( BIF Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_BIF);
PDUMPCOMMENT("( PM Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_PM);
PDUMPCOMMENT("( RTD Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_RTD);
PDUMPCOMMENT("( SPM Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_SPM);
PDUMPCOMMENT("( POW Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_POW);
PDUMPCOMMENT("( HWR Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_HWR);
PDUMPCOMMENT("( HWP Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_HWP);
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, META_DMA))
{
PDUMPCOMMENT("( DMA Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_DMA);
}
PDUMPCOMMENT("( MISC Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_MISC);
PDUMPCOMMENT("( DEBUG Group Enable: 0x%08x)", RGXFWIF_LOG_TYPE_GROUP_DEBUG);
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfTraceBufCtlMemDesc,
offsetof(RGXFWIF_TRACEBUF, ui32LogType),
psDevInfo->psRGXFWIfTraceBufCtl->ui32LogType,
PDUMP_FLAGS_CONTINUOUS);
PDUMPCOMMENT("Set the HWPerf Filter config here");
DevmemPDumpLoadMemValue64(psDevInfo->psRGXFWIfSysInitMemDesc,
offsetof(RGXFWIF_SYSINIT, ui64HWPerfFilter),
psDevInfo->psRGXFWIfSysInit->ui64HWPerfFilter,
PDUMP_FLAGS_CONTINUOUS);
#if defined(SUPPORT_USER_REGISTER_CONFIGURATION)
PDUMPCOMMENT("(Number of registers configurations for types(byte index): pow on(%d), dust change(%d), ta(%d), 3d(%d), cdm(%d), tla(%d), TDM(%d))",
RGXFWIF_REG_CFG_TYPE_PWR_ON,
RGXFWIF_REG_CFG_TYPE_DUST_CHANGE,
RGXFWIF_REG_CFG_TYPE_TA,
RGXFWIF_REG_CFG_TYPE_3D,
RGXFWIF_REG_CFG_TYPE_CDM,
RGXFWIF_REG_CFG_TYPE_TLA,
RGXFWIF_REG_CFG_TYPE_TDM);
{
IMG_UINT32 i;
/* Write 32 bits in each iteration as required by PDUMP WRW command */
for (i = 0; i < RGXFWIF_REG_CFG_TYPE_ALL; i += sizeof(IMG_UINT32))
{
DevmemPDumpLoadMemValue32(psDevInfo->psRGXFWIfRegCfgMemDesc,
offsetof(RGXFWIF_REG_CFG, aui8NumRegsType[i]),
0,
PDUMP_FLAGS_CONTINUOUS);
}
}
PDUMPCOMMENT("(Set registers here: address, mask, value)");
DevmemPDumpLoadMemValue64(psDevInfo->psRGXFWIfRegCfgMemDesc,
offsetof(RGXFWIF_REG_CFG, asRegConfigs[0].ui64Addr),
0,
PDUMP_FLAGS_CONTINUOUS);
DevmemPDumpLoadMemValue64(psDevInfo->psRGXFWIfRegCfgMemDesc,
offsetof(RGXFWIF_REG_CFG, asRegConfigs[0].ui64Mask),
0,
PDUMP_FLAGS_CONTINUOUS);
DevmemPDumpLoadMemValue64(psDevInfo->psRGXFWIfRegCfgMemDesc,
offsetof(RGXFWIF_REG_CFG, asRegConfigs[0].ui64Value),
0,
PDUMP_FLAGS_CONTINUOUS);
#endif /* SUPPORT_USER_REGISTER_CONFIGURATION */
}
#endif /* defined(PDUMP) */
/*!
*******************************************************************************
@Function RGXSetupFwSysData
@Description Setups all system-wide firmware related data
@Input psDevInfo
@Return PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXSetupFwSysData(PVRSRV_DEVICE_NODE *psDeviceNode,
IMG_BOOL bEnableSignatureChecks,
IMG_UINT32 ui32SignatureChecksBufSize,
IMG_UINT32 ui32HWPerfFWBufSizeKB,
IMG_UINT64 ui64HWPerfFilter,
IMG_UINT32 ui32RGXFWAlignChecksArrLength,
IMG_UINT32 *pui32RGXFWAlignChecks,
IMG_UINT32 ui32ConfigFlags,
IMG_UINT32 ui32ConfigFlagsExt,
IMG_UINT32 ui32LogType,
IMG_UINT32 ui32FilterFlags,
IMG_UINT32 ui32JonesDisableMask,
IMG_UINT32 ui32HWPerfCountersDataSize,
IMG_UINT32 *pui32TPUTrilinearFracMask,
RGX_RD_POWER_ISLAND_CONF eRGXRDPowerIslandConf,
FW_PERF_CONF eFirmwarePerf)
{
PVRSRV_ERROR eError;
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
RGXFWIF_SYSINIT *psFwSysInitScratch = NULL;
psFwSysInitScratch = OSAllocZMem(sizeof(*psFwSysInitScratch));
PVR_LOG_GOTO_IF_NOMEM(psFwSysInitScratch, eError, fail);
/* Sys Fw init data */
eError = RGXSetupFwAllocation(psDevInfo,
(RGX_FWSHAREDMEM_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) |
PVRSRV_MEMALLOCFLAG_FW_ALLOC_CONFIG) &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(RGXFWIF_SYSINIT),
"FwSysInitStructure",
&psDevInfo->psRGXFWIfSysInitMemDesc,
NULL,
(void**) &psDevInfo->psRGXFWIfSysInit,
RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware Sys Init structure allocation", fail);
/* Setup Fault read register */
eError = RGXSetupFaultReadRegister(psDeviceNode, psFwSysInitScratch);
PVR_LOG_GOTO_IF_ERROR(eError, "Fault read register setup", fail);
/* RD Power Island */
{
RGX_DATA *psRGXData = (RGX_DATA*) psDeviceNode->psDevConfig->hDevData;
IMG_BOOL bSysEnableRDPowIsland = psRGXData->psRGXTimingInfo->bEnableRDPowIsland;
IMG_BOOL bEnableRDPowIsland = ((eRGXRDPowerIslandConf == RGX_RD_POWER_ISLAND_DEFAULT) && bSysEnableRDPowIsland) ||
(eRGXRDPowerIslandConf == RGX_RD_POWER_ISLAND_FORCE_ON);
ui32ConfigFlags |= bEnableRDPowIsland? RGXFWIF_INICFG_POW_RASCALDUST : 0;
}
#if defined(SUPPORT_WORKLOAD_ESTIMATION)
ui32ConfigFlags |= RGXFWIF_INICFG_WORKEST;
#if defined(SUPPORT_PDVFS)
{
RGXFWIF_PDVFS_OPP *psPDVFSOPPInfo;
IMG_DVFS_DEVICE_CFG *psDVFSDeviceCfg;
/* Pro-active DVFS depends on Workload Estimation */
psPDVFSOPPInfo = &psFwSysInitScratch->sPDVFSOPPInfo;
psDVFSDeviceCfg = &psDeviceNode->psDevConfig->sDVFS.sDVFSDeviceCfg;
PVR_LOG_IF_FALSE(psDVFSDeviceCfg->pasOPPTable, "RGXSetupFwSysData: Missing OPP Table");
if (psDVFSDeviceCfg->pasOPPTable != NULL)
{
if (psDVFSDeviceCfg->ui32OPPTableSize > ARRAY_SIZE(psPDVFSOPPInfo->asOPPValues))
{
PVR_DPF((PVR_DBG_ERROR,
"%s: OPP Table too large: Size = %u, Maximum size = %lu",
__func__,
psDVFSDeviceCfg->ui32OPPTableSize,
(unsigned long)(ARRAY_SIZE(psPDVFSOPPInfo->asOPPValues))));
eError = PVRSRV_ERROR_INVALID_PARAMS;
goto fail;
}
OSDeviceMemCopy(psPDVFSOPPInfo->asOPPValues,
psDVFSDeviceCfg->pasOPPTable,
sizeof(psPDVFSOPPInfo->asOPPValues));
psPDVFSOPPInfo->ui32MaxOPPPoint = psDVFSDeviceCfg->ui32OPPTableSize - 1;
ui32ConfigFlags |= RGXFWIF_INICFG_PDVFS;
}
}
#endif /* defined(SUPPORT_PDVFS) */
#endif /* defined(SUPPORT_WORKLOAD_ESTIMATION) */
/* FW trace control structure */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWINITDATA_WC_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(RGXFWIF_TRACEBUF),
"FwTraceCtlStruct",
&psDevInfo->psRGXFWIfTraceBufCtlMemDesc,
&psFwSysInitScratch->sTraceBufCtl,
(void**) &psDevInfo->psRGXFWIfTraceBufCtl,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
if (!psDeviceNode->bAutoVzFwIsUp)
{
/* Set initial firmware log type/group(s) */
if (ui32LogType & ~RGXFWIF_LOG_TYPE_MASK)
{
eError = PVRSRV_ERROR_INVALID_PARAMS;
PVR_DPF((PVR_DBG_ERROR,
"%s: Invalid initial log type (0x%X)",
__func__, ui32LogType));
goto fail;
}
psDevInfo->psRGXFWIfTraceBufCtl->ui32LogType = ui32LogType;
}
/* When PDUMP is enabled, ALWAYS allocate on-demand trace buffer resource
* (irrespective of loggroup(s) enabled), given that logtype/loggroups can
* be set during PDump playback in logconfig, at any point of time,
* Otherwise, allocate only if required. */
#if !defined(PDUMP)
#if defined(SUPPORT_AUTOVZ)
/* always allocate trace buffer for AutoVz Host drivers to allow
* deterministic addresses of all SysData structures */
if ((PVRSRV_VZ_MODE_IS(HOST)) || (RGXTraceBufferIsInitRequired(psDevInfo)))
#else
if (RGXTraceBufferIsInitRequired(psDevInfo))
#endif
#endif
{
eError = RGXTraceBufferInitOnDemandResources(psDevInfo,
RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp));
}
PVR_LOG_GOTO_IF_ERROR(eError, "RGXTraceBufferInitOnDemandResources", fail);
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(RGXFWIF_SYSDATA),
"FwSysData",
&psDevInfo->psRGXFWIfFwSysDataMemDesc,
&psFwSysInitScratch->sFwSysData,
(void**) &psDevInfo->psRGXFWIfFwSysData,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
/* GPIO validation setup */
psFwSysInitScratch->eGPIOValidationMode = RGXFWIF_GPIO_VAL_OFF;
#if defined(SUPPORT_VALIDATION)
{
IMG_INT32 ui32AppHintDefault;
IMG_INT32 ui32GPIOValidationMode;
void *pvAppHintState = NULL;
/* Check AppHint for GPIO validation mode */
OSCreateKMAppHintState(&pvAppHintState);
ui32AppHintDefault = PVRSRV_APPHINT_GPIOVALIDATIONMODE;
OSGetKMAppHintUINT32(pvAppHintState,
GPIOValidationMode,
&ui32AppHintDefault,
&ui32GPIOValidationMode);
OSFreeKMAppHintState(pvAppHintState);
pvAppHintState = NULL;
if (ui32GPIOValidationMode >= RGXFWIF_GPIO_VAL_LAST)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Invalid GPIO validation mode: %d, only valid if smaller than %d. Disabling GPIO validation.",
__func__,
ui32GPIOValidationMode,
RGXFWIF_GPIO_VAL_LAST));
}
else
{
psFwSysInitScratch->eGPIOValidationMode = (RGXFWIF_GPIO_VAL_MODE) ui32GPIOValidationMode;
}
psFwSysInitScratch->eGPIOValidationMode = ui32GPIOValidationMode;
}
#endif
#if defined(SUPPORT_POWER_SAMPLING_VIA_DEBUGFS)
eError = RGXFWSetupCounterBuffer(psDevInfo,
&psDevInfo->psCounterBufferMemDesc,
PAGE_SIZE,
&psFwSysInitScratch->sCounterDumpCtl,
"CounterBuffer");
PVR_LOG_GOTO_IF_ERROR(eError, "Counter Buffer allocation", fail);
#endif /* defined(SUPPORT_POWER_SAMPLING_VIA_DEBUGFS) */
#if defined(SUPPORT_FIRMWARE_GCOV)
eError = RGXFWSetupFirmwareGcovBuffer(psDevInfo,
&psDevInfo->psFirmwareGcovBufferMemDesc,
RGXFWIF_FIRMWARE_GCOV_BUFFER_SIZE,
&psFwSysInitScratch->sFirmwareGcovCtl,
"FirmwareGcovBuffer");
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware GCOV buffer allocation", fail);
psDevInfo->ui32FirmwareGcovSize = RGXFWIF_FIRMWARE_GCOV_BUFFER_SIZE;
#endif /* defined(SUPPORT_FIRMWARE_GCOV) */
#if defined(PDUMP)
/* Require a minimum amount of memory for the signature buffers */
if (ui32SignatureChecksBufSize < RGXFW_SIG_BUFFER_SIZE_MIN)
{
ui32SignatureChecksBufSize = RGXFW_SIG_BUFFER_SIZE_MIN;
}
/* Setup Signature and Checksum Buffers for TA and 3D */
eError = RGXFWSetupSignatureChecks(psDevInfo,
&psDevInfo->psRGXFWSigTAChecksMemDesc,
ui32SignatureChecksBufSize,
&psFwSysInitScratch->asSigBufCtl[RGXFWIF_DM_GEOM],
"TA");
PVR_LOG_GOTO_IF_ERROR(eError, "TA Signature check setup", fail);
psDevInfo->ui32SigTAChecksSize = ui32SignatureChecksBufSize;
eError = RGXFWSetupSignatureChecks(psDevInfo,
&psDevInfo->psRGXFWSig3DChecksMemDesc,
ui32SignatureChecksBufSize,
&psFwSysInitScratch->asSigBufCtl[RGXFWIF_DM_3D],
"3D");
PVR_LOG_GOTO_IF_ERROR(eError, "3D Signature check setup", fail);
psDevInfo->ui32Sig3DChecksSize = ui32SignatureChecksBufSize;
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, TDM_PDS_CHECKSUM))
{
/* Buffer allocated only when feature present because, all known TDM
* signature registers are dependent on this feature being present */
eError = RGXFWSetupSignatureChecks(psDevInfo,
&psDevInfo->psRGXFWSigTDM2DChecksMemDesc,
ui32SignatureChecksBufSize,
&psFwSysInitScratch->asSigBufCtl[RGXFWIF_DM_TDM],
"TDM");
PVR_LOG_GOTO_IF_ERROR(eError, "TDM Signature check setup", fail);
psDevInfo->ui32SigTDM2DChecksSize = ui32SignatureChecksBufSize;
}
else
{
psDevInfo->psRGXFWSigTDM2DChecksMemDesc = NULL;
psDevInfo->ui32SigTDM2DChecksSize = 0;
}
#endif
if (!bEnableSignatureChecks)
{
psFwSysInitScratch->asSigBufCtl[RGXFWIF_DM_TDM].sBuffer.ui32Addr = 0x0;
psFwSysInitScratch->asSigBufCtl[RGXFWIF_DM_GEOM].sBuffer.ui32Addr = 0x0;
psFwSysInitScratch->asSigBufCtl[RGXFWIF_DM_3D].sBuffer.ui32Addr = 0x0;
}
eError = RGXFWSetupAlignChecks(psDeviceNode,
&psFwSysInitScratch->sAlignChecks,
pui32RGXFWAlignChecks,
ui32RGXFWAlignChecksArrLength);
PVR_LOG_GOTO_IF_ERROR(eError, "Alignment checks setup", fail);
psFwSysInitScratch->ui32FilterFlags = ui32FilterFlags;
if (RGX_IS_BRN_SUPPORTED(psDevInfo, 65273))
{
/* Fill the remaining bits of fw the init data */
psFwSysInitScratch->sPDSExecBase.uiAddr = RGX_PDSCODEDATA_BRN_65273_HEAP_BASE;
psFwSysInitScratch->sUSCExecBase.uiAddr = RGX_USCCODE_BRN_65273_HEAP_BASE;
}
else
{
/* Fill the remaining bits of fw the init data */
psFwSysInitScratch->sPDSExecBase.uiAddr = RGX_PDSCODEDATA_HEAP_BASE;
psFwSysInitScratch->sUSCExecBase.uiAddr = RGX_USCCODE_HEAP_BASE;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, S7_TOP_INFRASTRUCTURE))
{
psFwSysInitScratch->ui32JonesDisableMask = ui32JonesDisableMask;
}
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, SLC_VIVT))
{
eError = _AllocateSLC3Fence(psDevInfo, psFwSysInitScratch);
PVR_LOG_GOTO_IF_ERROR(eError, "SLC3Fence memory allocation", fail);
}
#if defined(SUPPORT_PDVFS)
/* Core clock rate */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(IMG_UINT32),
"FwPDVFSCoreClkRate",
&psDevInfo->psRGXFWIFCoreClkRateMemDesc,
&psFwSysInitScratch->sCoreClockRate,
(void**) &psDevInfo->pui32RGXFWIFCoreClkRate,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "PDVFS core clock rate memory setup", fail);
#endif
#if defined(SUPPORT_TBI_INTERFACE)
#if !defined(PDUMP)
/* allocate only if required */
if (RGXTBIBufferIsInitRequired(psDevInfo))
#endif /* !defined(PDUMP) */
{
/* When PDUMP is enabled, ALWAYS allocate on-demand TBI buffer resource
* (irrespective of loggroup(s) enabled), given that logtype/loggroups
* can be set during PDump playback in logconfig, at any point of time
*/
eError = RGXTBIBufferInitOnDemandResources(psDevInfo);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXTBIBufferInitOnDemandResources", fail);
}
psFwSysInitScratch->sTBIBuf = psDevInfo->sRGXFWIfTBIBuffer;
#endif /* defined(SUPPORT_TBI_INTERFACE) */
/* Allocate shared buffer for GPU utilisation */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(RGXFWIF_GPU_UTIL_FWCB),
"FwGPUUtilisationBuffer",
&psDevInfo->psRGXFWIfGpuUtilFWCbCtlMemDesc,
&psFwSysInitScratch->sGpuUtilFWCbCtl,
(void**) &psDevInfo->psRGXFWIfGpuUtilFWCb,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "GPU Utilisation Buffer ctl allocation", fail);
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_GPU_RO_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(RGXFWIF_RUNTIME_CFG),
"FwRuntimeCfg",
&psDevInfo->psRGXFWIfRuntimeCfgMemDesc,
&psFwSysInitScratch->sRuntimeCfg,
(void**) &psDevInfo->psRGXFWIfRuntimeCfg,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware runtime configuration memory allocation", fail);
#if defined(SUPPORT_USER_REGISTER_CONFIGURATION)
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
sizeof(RGXFWIF_REG_CFG),
"FwRegisterConfigStructure",
&psDevInfo->psRGXFWIfRegCfgMemDesc,
&psFwSysInitScratch->sRegCfg,
NULL,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware register user configuration structure allocation", fail);
#endif
psDevInfo->ui32RGXFWIfHWPerfBufSize = GetHwPerfBufferSize(ui32HWPerfFWBufSizeKB);
/* Second stage initialisation or HWPerf, hHWPerfLock created in first
* stage. See RGXRegisterDevice() call to RGXHWPerfInit(). */
if (psDevInfo->ui64HWPerfFilter == 0)
{
psDevInfo->ui64HWPerfFilter = ui64HWPerfFilter;
psFwSysInitScratch->ui64HWPerfFilter = ui64HWPerfFilter;
}
else
{
/* The filter has already been modified. This can happen if
* pvr/apphint/EnableFTraceGPU was enabled. */
psFwSysInitScratch->ui64HWPerfFilter = psDevInfo->ui64HWPerfFilter;
}
#if !defined(PDUMP)
/* Allocate if HWPerf filter has already been set. This is possible either
* by setting a proper AppHint or enabling GPU ftrace events. */
if (psDevInfo->ui64HWPerfFilter != 0)
#endif
{
/* When PDUMP is enabled, ALWAYS allocate on-demand HWPerf resources
* (irrespective of HWPerf enabled or not), given that HWPerf can be
* enabled during PDump playback via RTCONF at any point of time. */
eError = RGXHWPerfInitOnDemandResources(psDevInfo);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXHWPerfInitOnDemandResources", fail);
}
RGXHWPerfInitAppHintCallbacks(psDeviceNode);
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWINITDATA_WC_ALLOCFLAGS &
RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp),
ui32HWPerfCountersDataSize,
"FwHWPerfControlStructure",
&psDevInfo->psRGXFWIfHWPerfCountersMemDesc,
&psFwSysInitScratch->sHWPerfCtl,
NULL,
RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware HW Perf control struct allocation", fail);
psDevInfo->bPDPEnabled = (ui32ConfigFlags & RGXFWIF_INICFG_DISABLE_PDP_EN)
? IMG_FALSE : IMG_TRUE;
psFwSysInitScratch->eFirmwarePerf = eFirmwarePerf;
#if defined(PDUMP)
/* default: no filter */
psFwSysInitScratch->sPIDFilter.eMode = RGXFW_PID_FILTER_INCLUDE_ALL_EXCEPT;
psFwSysInitScratch->sPIDFilter.asItems[0].uiPID = 0;
#endif
#if defined(SUPPORT_VALIDATION)
{
IMG_UINT32 dm;
/* TPU trilinear rounding mask override */
for (dm = 0; dm < RGXFWIF_TPU_DM_LAST; dm++)
{
psFwSysInitScratch->aui32TPUTrilinearFracMask[dm] = pui32TPUTrilinearFracMask[dm];
}
}
#endif
#if defined(SUPPORT_SECURITY_VALIDATION)
PDUMPCOMMENT("Allocate non-secure buffer for security validation test");
eError = DevmemFwAllocateExportable(psDeviceNode,
OSGetPageSize(),
OSGetPageSize(),
RGX_FWSHAREDMEM_ALLOCFLAGS,
"FwExNonSecureBuffer",
&psDevInfo->psRGXFWIfNonSecureBufMemDesc);
PVR_LOG_GOTO_IF_ERROR(eError, "Non-secure buffer allocation", fail);
eError = RGXSetFirmwareAddress(&psFwSysInitScratch->pbNonSecureBuffer,
psDevInfo->psRGXFWIfNonSecureBufMemDesc,
0, RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:1", fail);
PDUMPCOMMENT("Allocate secure buffer for security validation test");
eError = DevmemFwAllocateExportable(psDeviceNode,
OSGetPageSize(),
OSGetPageSize(),
RGX_FWSHAREDMEM_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_VAL_SECURE_BUFFER,
"FwExSecureBuffer",
&psDevInfo->psRGXFWIfSecureBufMemDesc);
PVR_LOG_GOTO_IF_ERROR(eError, "Secure buffer allocation", fail);
eError = RGXSetFirmwareAddress(&psFwSysInitScratch->pbSecureBuffer,
psDevInfo->psRGXFWIfSecureBufMemDesc,
0, RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetFirmwareAddress:2", fail);
#endif /* SUPPORT_SECURITY_VALIDATION */
/* Initialize FW started flag */
psFwSysInitScratch->bFirmwareStarted = IMG_FALSE;
psFwSysInitScratch->ui32MarkerVal = 1;
if (!psDeviceNode->bAutoVzFwIsUp)
{
RGX_DATA *psRGXData = (RGX_DATA*) psDeviceNode->psDevConfig->hDevData;
RGXFWIF_RUNTIME_CFG *psRuntimeCfg = psDevInfo->psRGXFWIfRuntimeCfg;
/* Required info by FW to calculate the ActivePM idle timer latency */
psFwSysInitScratch->ui32InitialCoreClockSpeed = psRGXData->psRGXTimingInfo->ui32CoreClockSpeed;
psFwSysInitScratch->ui32ActivePMLatencyms = psRGXData->psRGXTimingInfo->ui32ActivePMLatencyms;
/* Initialise variable runtime configuration to the system defaults */
psRuntimeCfg->ui32CoreClockSpeed = psFwSysInitScratch->ui32InitialCoreClockSpeed;
psRuntimeCfg->ui32ActivePMLatencyms = psFwSysInitScratch->ui32ActivePMLatencyms;
psRuntimeCfg->bActivePMLatencyPersistant = IMG_TRUE;
/* Initialize the DefaultDustsNumInit Field to Max Dusts */
psRuntimeCfg->ui32DefaultDustsNumInit = psDevInfo->sDevFeatureCfg.ui32MAXDustCount;
/* Setup FW coremem data */
if (psDevInfo->psRGXFWIfCorememDataStoreMemDesc)
{
IMG_BOOL bMetaDMA = RGX_IS_FEATURE_SUPPORTED(psDevInfo, META_DMA);
psFwSysInitScratch->sCorememDataStore.pbyFWAddr = psDevInfo->sFWCorememDataStoreFWAddr;
if (bMetaDMA)
{
RGXSetMetaDMAAddress(&psFwSysInitScratch->sCorememDataStore,
psDevInfo->psRGXFWIfCorememDataStoreMemDesc,
&psFwSysInitScratch->sCorememDataStore.pbyFWAddr,
0);
}
}
psDevInfo->psRGXFWIfFwSysData->ui32ConfigFlags = ui32ConfigFlags & RGXFWIF_INICFG_ALL;
psDevInfo->psRGXFWIfFwSysData->ui32ConfigFlagsExt = ui32ConfigFlagsExt & RGXFWIF_INICFG_EXT_ALL;
/* Initialise GPU utilisation buffer */
psDevInfo->psRGXFWIfGpuUtilFWCb->ui64LastWord =
RGXFWIF_GPU_UTIL_MAKE_WORD(OSClockns64(),RGXFWIF_GPU_UTIL_STATE_IDLE);
/* init HWPERF data */
psDevInfo->psRGXFWIfFwSysData->ui32HWPerfRIdx = 0;
psDevInfo->psRGXFWIfFwSysData->ui32HWPerfWIdx = 0;
psDevInfo->psRGXFWIfFwSysData->ui32HWPerfWrapCount = 0;
psDevInfo->psRGXFWIfFwSysData->ui32HWPerfSize = psDevInfo->ui32RGXFWIfHWPerfBufSize;
psDevInfo->psRGXFWIfFwSysData->ui32HWPerfUt = 0;
psDevInfo->psRGXFWIfFwSysData->ui32HWPerfDropCount = 0;
psDevInfo->psRGXFWIfFwSysData->ui32FirstDropOrdinal = 0;
psDevInfo->psRGXFWIfFwSysData->ui32LastDropOrdinal = 0;
#if defined(SUPPORT_POWMON_COMPONENT)
psDevInfo->psRGXFWIfFwSysData->ui32PowMonEstimate = 0;
#endif
/*Send through the BVNC Feature Flags*/
eError = RGXServerFeatureFlagsToHWPerfFlags(psDevInfo, &psFwSysInitScratch->sBvncKmFeatureFlags);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXServerFeatureFlagsToHWPerfFlags", fail);
/* populate the real FwOsInit structure with the values stored in the scratch copy */
OSDeviceMemCopy(psDevInfo->psRGXFWIfSysInit, psFwSysInitScratch, sizeof(RGXFWIF_SYSINIT));
}
OSFreeMem(psFwSysInitScratch);
return PVRSRV_OK;
fail:
if (psFwSysInitScratch)
{
OSFreeMem(psFwSysInitScratch);
}
RGXFreeFwSysData(psDevInfo);
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
/*!
*******************************************************************************
@Function RGXSetupFwOsData
@Description Sets up all os-specific firmware related data
@Input psDevInfo
@Return PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXSetupFwOsData(PVRSRV_DEVICE_NODE *psDeviceNode,
IMG_UINT32 ui32HWRDebugDumpLimit,
IMG_UINT32 ui32FwOsCfgFlags)
{
PVRSRV_ERROR eError;
RGXFWIF_OSINIT sFwOsInitScratch;
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
IMG_UINT32 ui32kCCBSize = (!PVRSRV_VZ_MODE_IS(NATIVE) &&
!(psDevInfo->sDevFeatureCfg.ui64Features & RGX_FEATURE_GPU_VIRTUALISATION_BIT_MASK)) ?
(RGXFWIF_KCCB_NUMCMDS_LOG2_GPUVIRT_WITHOUT_FEATURE) : (RGXFWIF_KCCB_NUMCMDS_LOG2_DEFAULT);
OSCachedMemSet(&sFwOsInitScratch, 0, sizeof(RGXFWIF_OSINIT));
/* Memory tracking the connection state should be non-volatile and
* is not cleared on allocation to prevent loss of pre-reset information */
eError = RGXSetupFwAllocation(psDevInfo,
(RGX_FWSHAREDMEM_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_FW_ALLOC_CONFIG)
& (~PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC),
sizeof(RGXFWIF_CONNECTION_CTL),
"FwConnectionCtl",
&psDevInfo->psRGXFWIfConnectionCtlMemDesc,
NULL,
(void**) &psDevInfo->psRGXFWIfConnectionCtl,
RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware Connection Control structure allocation", fail);
eError = RGXSetupFwAllocation(psDevInfo,
(RGX_FWSHAREDMEM_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED) |
PVRSRV_MEMALLOCFLAG_FW_ALLOC_CONFIG),
sizeof(RGXFWIF_OSINIT),
"FwOsInitStructure",
&psDevInfo->psRGXFWIfOsInitMemDesc,
NULL,
(void**) &psDevInfo->psRGXFWIfOsInit,
RFW_FWADDR_FLAG_NONE);
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware Os Init structure allocation", fail);
/* init HWR frame info */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS,
sizeof(RGXFWIF_HWRINFOBUF),
"FwHWRInfoBuffer",
&psDevInfo->psRGXFWIfHWRInfoBufCtlMemDesc,
&sFwOsInitScratch.sRGXFWIfHWRInfoBufCtl,
(void**) &psDevInfo->psRGXFWIfHWRInfoBufCtl,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "HWR Info Buffer allocation", fail);
/* Might be uncached. Be conservative and use a DeviceMemSet */
OSDeviceMemSet(psDevInfo->psRGXFWIfHWRInfoBufCtl, 0, sizeof(RGXFWIF_HWRINFOBUF));
/* Allocate a sync for power management */
eError = SyncPrimContextCreate(psDevInfo->psDeviceNode,
&psDevInfo->hSyncPrimContext);
PVR_LOG_GOTO_IF_ERROR(eError, "Sync primitive context allocation", fail);
eError = SyncPrimAlloc(psDevInfo->hSyncPrimContext, &psDevInfo->psPowSyncPrim, "fw power ack");
PVR_LOG_GOTO_IF_ERROR(eError, "Sync primitive allocation", fail);
/* Set up kernel CCB */
eError = RGXSetupCCB(psDevInfo,
&psDevInfo->psKernelCCBCtl,
&psDevInfo->psKernelCCBCtlMemDesc,
&psDevInfo->psKernelCCB,
&psDevInfo->psKernelCCBMemDesc,
&sFwOsInitScratch.psKernelCCBCtl,
&sFwOsInitScratch.psKernelCCB,
ui32kCCBSize,
sizeof(RGXFWIF_KCCB_CMD),
(RGX_FWSHAREDMEM_GPU_RO_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_DEVICE_FLAG(FIRMWARE_CACHED)),
"FwKernelCCB");
PVR_LOG_GOTO_IF_ERROR(eError, "Kernel CCB allocation", fail);
/* KCCB additionally uses a return slot array for FW to be able to send back
* return codes for each required command
*/
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS,
(1U << ui32kCCBSize) * sizeof(IMG_UINT32),
"FwKernelCCBRtnSlots",
&psDevInfo->psKernelCCBRtnSlotsMemDesc,
&sFwOsInitScratch.psKernelCCBRtnSlots,
(void**) &psDevInfo->pui32KernelCCBRtnSlots,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "Kernel CCB return slot array allocation", fail);
/* Set up firmware CCB */
eError = RGXSetupCCB(psDevInfo,
&psDevInfo->psFirmwareCCBCtl,
&psDevInfo->psFirmwareCCBCtlMemDesc,
&psDevInfo->psFirmwareCCB,
&psDevInfo->psFirmwareCCBMemDesc,
&sFwOsInitScratch.psFirmwareCCBCtl,
&sFwOsInitScratch.psFirmwareCCB,
RGXFWIF_FWCCB_NUMCMDS_LOG2,
sizeof(RGXFWIF_FWCCB_CMD),
RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS,
"FwCCB");
PVR_LOG_GOTO_IF_ERROR(eError, "Firmware CCB allocation", fail);
#if defined(PVRSRV_SYNC_CHECKPOINT_CCB)
/* Set up checkpoint CCB */
eError = RGXSetupCCB(psDevInfo,
&psDevInfo->psCheckpointCCBCtl,
&psDevInfo->psCheckpointCCBCtlMemDesc,
&psDevInfo->psCheckpointCCB,
&psDevInfo->psCheckpointCCBMemDesc,
&sFwOsInitScratch.psCheckpointCCBCtl,
&sFwOsInitScratch.psCheckpointCCB,
RGXFWIF_CHECKPOINTCCB_NUMCMDS_LOG2,
sizeof(PRGXFWIF_UFO_ADDR),
RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS,
"FwChkptCCB");
PVR_LOG_GOTO_IF_ERROR(eError, "Checkpoint CCB allocation", fail);
#endif /* defined(PVRSRV_SYNC_CHECKPOINT_CCB) */
eError = RGXSetupFwAllocation(psDevInfo,
RGX_FWSHAREDMEM_ALLOCFLAGS,
sizeof(RGXFWIF_OSDATA),
"FwOsData",
&psDevInfo->psRGXFWIfFwOsDataMemDesc,
&sFwOsInitScratch.sFwOsData,
(void**) &psDevInfo->psRGXFWIfFwOsData,
RFW_FWADDR_NOREF_FLAG);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSetupFwAllocation", fail);
psDevInfo->psRGXFWIfFwOsData->ui32FwOsConfigFlags = ui32FwOsCfgFlags & RGXFWIF_INICFG_OS_ALL;
eError = SyncPrimGetFirmwareAddr(psDevInfo->psPowSyncPrim, &psDevInfo->psRGXFWIfFwOsData->sPowerSync.ui32Addr);
PVR_LOG_GOTO_IF_ERROR(eError, "Get Sync Prim FW address", fail);
sFwOsInitScratch.ui32HWRDebugDumpLimit = ui32HWRDebugDumpLimit;
#if defined(SUPPORT_WORKLOAD_ESTIMATION)
/* Set up Workload Estimation firmware CCB */
eError = RGXSetupCCB(psDevInfo,
&psDevInfo->psWorkEstFirmwareCCBCtl,
&psDevInfo->psWorkEstFirmwareCCBCtlMemDesc,
&psDevInfo->psWorkEstFirmwareCCB,
&psDevInfo->psWorkEstFirmwareCCBMemDesc,
&sFwOsInitScratch.psWorkEstFirmwareCCBCtl,
&sFwOsInitScratch.psWorkEstFirmwareCCB,
RGXFWIF_WORKEST_FWCCB_NUMCMDS_LOG2,
sizeof(RGXFWIF_WORKEST_FWCCB_CMD),
RGX_FWSHAREDMEM_CPU_RO_ALLOCFLAGS,
"FwWEstCCB");
PVR_LOG_GOTO_IF_ERROR(eError, "Workload Estimation Firmware CCB allocation", fail);
#endif /* defined(SUPPORT_WORKLOAD_ESTIMATION) */
/* Initialise the compatibility check data */
RGXFWIF_COMPCHECKS_BVNC_INIT(sFwOsInitScratch.sRGXCompChecks.sFWBVNC);
RGXFWIF_COMPCHECKS_BVNC_INIT(sFwOsInitScratch.sRGXCompChecks.sHWBVNC);
/* populate the real FwOsInit structure with the values stored in the scratch copy */
OSDeviceMemCopy(psDevInfo->psRGXFWIfOsInit, &sFwOsInitScratch, sizeof(RGXFWIF_OSINIT));
#if defined(SUPPORT_AUTOVZ) && defined(SUPPORT_AUTOVZ_HW_REGS)
/* if hardware registers are used to store connection states,
* these can only be accessed if the GPU is powered up */
if (PVRSRV_VZ_MODE_IS(HOST) && (psDeviceNode->bAutoVzFwIsUp))
#endif /* defined(SUPPORT_AUTOVZ) && defined(SUPPORT_AUTOVZ_HW_REGS)*/
{
KM_SET_OS_CONNECTION(READY, psDevInfo);
}
return PVRSRV_OK;
fail:
RGXFreeFwOsData(psDevInfo);
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
/*!
*******************************************************************************
@Function RGXSetupFirmware
@Description Setups all firmware related data
@Input psDevInfo
@Return PVRSRV_ERROR
******************************************************************************/
PVRSRV_ERROR RGXSetupFirmware(PVRSRV_DEVICE_NODE *psDeviceNode,
IMG_BOOL bEnableSignatureChecks,
IMG_UINT32 ui32SignatureChecksBufSize,
IMG_UINT32 ui32HWPerfFWBufSizeKB,
IMG_UINT64 ui64HWPerfFilter,
IMG_UINT32 ui32RGXFWAlignChecksArrLength,
IMG_UINT32 *pui32RGXFWAlignChecks,
IMG_UINT32 ui32ConfigFlags,
IMG_UINT32 ui32ConfigFlagsExt,
IMG_UINT32 ui32FwOsCfgFlags,
IMG_UINT32 ui32LogType,
IMG_UINT32 ui32FilterFlags,
IMG_UINT32 ui32JonesDisableMask,
IMG_UINT32 ui32HWRDebugDumpLimit,
IMG_UINT32 ui32HWPerfCountersDataSize,
IMG_UINT32 *pui32TPUTrilinearFracMask,
RGX_RD_POWER_ISLAND_CONF eRGXRDPowerIslandConf,
FW_PERF_CONF eFirmwarePerf)
{
PVRSRV_ERROR eError;
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
eError = RGXSetupFwOsData(psDeviceNode, ui32HWRDebugDumpLimit, ui32FwOsCfgFlags);
PVR_LOG_GOTO_IF_ERROR(eError, "Setting up firmware os data", fail);
if (PVRSRV_VZ_MODE_IS(GUEST))
{
/* Guest drivers do not configure system-wide firmware data */
psDevInfo->psRGXFWIfSysInit = NULL;
}
else
{
/* Native and Host drivers must initialise the firmware's system data */
eError = RGXSetupFwSysData(psDeviceNode,
bEnableSignatureChecks,
ui32SignatureChecksBufSize,
ui32HWPerfFWBufSizeKB,
ui64HWPerfFilter,
ui32RGXFWAlignChecksArrLength,
pui32RGXFWAlignChecks,
ui32ConfigFlags,
ui32ConfigFlagsExt,
ui32LogType,
ui32FilterFlags,
ui32JonesDisableMask,
ui32HWPerfCountersDataSize,
pui32TPUTrilinearFracMask,
eRGXRDPowerIslandConf,
eFirmwarePerf);
PVR_LOG_GOTO_IF_ERROR(eError, "Setting up firmware system data", fail);
}
psDevInfo->bFirmwareInitialised = IMG_TRUE;
#if defined(PDUMP)
RGXPDumpLoadFWInitData(psDevInfo,
ui32HWPerfCountersDataSize,
bEnableSignatureChecks);
#endif /* PDUMP */
fail:
return eError;
}
/*!
*******************************************************************************
@Function RGXFreeFwSysData
@Description Frees all system-wide firmware related data
@Input psDevInfo
******************************************************************************/
static void RGXFreeFwSysData(PVRSRV_RGXDEV_INFO *psDevInfo)
{
psDevInfo->bFirmwareInitialised = IMG_FALSE;
if (psDevInfo->psRGXFWAlignChecksMemDesc)
{
RGXFWFreeAlignChecks(psDevInfo);
}
#if defined(PDUMP)
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, TDM_PDS_CHECKSUM) &&
psDevInfo->psRGXFWSigTDM2DChecksMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWSigTDM2DChecksMemDesc);
psDevInfo->psRGXFWSigTDM2DChecksMemDesc = NULL;
}
if (psDevInfo->psRGXFWSigTAChecksMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWSigTAChecksMemDesc);
psDevInfo->psRGXFWSigTAChecksMemDesc = NULL;
}
if (psDevInfo->psRGXFWSig3DChecksMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWSig3DChecksMemDesc);
psDevInfo->psRGXFWSig3DChecksMemDesc = NULL;
}
#endif
#if defined(SUPPORT_POWER_SAMPLING_VIA_DEBUGFS)
if (psDevInfo->psCounterBufferMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psCounterBufferMemDesc);
psDevInfo->psCounterBufferMemDesc = NULL;
}
#endif
#if defined(SUPPORT_FIRMWARE_GCOV)
if (psDevInfo->psFirmwareGcovBufferMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psFirmwareGcovBufferMemDesc);
psDevInfo->psFirmwareGcovBufferMemDesc = NULL;
}
#endif
RGXSetupFaultReadRegisterRollback(psDevInfo);
if (psDevInfo->psRGXFWIfGpuUtilFWCbCtlMemDesc)
{
if (psDevInfo->psRGXFWIfGpuUtilFWCb != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfGpuUtilFWCbCtlMemDesc);
psDevInfo->psRGXFWIfGpuUtilFWCb = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfGpuUtilFWCbCtlMemDesc);
psDevInfo->psRGXFWIfGpuUtilFWCbCtlMemDesc = NULL;
}
RGXHWPerfDeinit(psDevInfo);
if (psDevInfo->psRGXFWIfRuntimeCfgMemDesc)
{
if (psDevInfo->psRGXFWIfRuntimeCfg != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfRuntimeCfgMemDesc);
psDevInfo->psRGXFWIfRuntimeCfg = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfRuntimeCfgMemDesc);
psDevInfo->psRGXFWIfRuntimeCfgMemDesc = NULL;
}
if (psDevInfo->psRGXFWIfCorememDataStoreMemDesc)
{
psDevInfo->psRGXFWIfCorememDataStoreMemDesc = NULL;
}
if (psDevInfo->psRGXFWIfTraceBufCtlMemDesc)
{
if (psDevInfo->psRGXFWIfTraceBufCtl != NULL)
{
/* first deinit/free the tracebuffer allocation */
RGXTraceBufferDeinit(psDevInfo);
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfTraceBufCtlMemDesc);
psDevInfo->psRGXFWIfTraceBufCtl = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfTraceBufCtlMemDesc);
psDevInfo->psRGXFWIfTraceBufCtlMemDesc = NULL;
}
if (psDevInfo->psRGXFWIfFwSysDataMemDesc)
{
if (psDevInfo->psRGXFWIfFwSysData != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfFwSysDataMemDesc);
psDevInfo->psRGXFWIfFwSysData = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfFwSysDataMemDesc);
psDevInfo->psRGXFWIfFwSysDataMemDesc = NULL;
}
#if defined(SUPPORT_TBI_INTERFACE)
if (psDevInfo->psRGXFWIfTBIBufferMemDesc)
{
RGXTBIBufferDeinit(psDevInfo);
}
#endif
#if defined(SUPPORT_USER_REGISTER_CONFIGURATION)
if (psDevInfo->psRGXFWIfRegCfgMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfRegCfgMemDesc);
psDevInfo->psRGXFWIfRegCfgMemDesc = NULL;
}
#endif
if (psDevInfo->psRGXFWIfHWPerfCountersMemDesc)
{
RGXUnsetFirmwareAddress(psDevInfo->psRGXFWIfHWPerfCountersMemDesc);
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfHWPerfCountersMemDesc);
psDevInfo->psRGXFWIfHWPerfCountersMemDesc = NULL;
}
#if defined(SUPPORT_SECURITY_VALIDATION)
if (psDevInfo->psRGXFWIfNonSecureBufMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfNonSecureBufMemDesc);
psDevInfo->psRGXFWIfNonSecureBufMemDesc = NULL;
}
if (psDevInfo->psRGXFWIfSecureBufMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfSecureBufMemDesc);
psDevInfo->psRGXFWIfSecureBufMemDesc = NULL;
}
#endif
if (RGX_IS_FEATURE_SUPPORTED(psDevInfo, SLC_VIVT))
{
_FreeSLC3Fence(psDevInfo);
}
#if defined(SUPPORT_PDVFS)
if (psDevInfo->psRGXFWIFCoreClkRateMemDesc)
{
if (psDevInfo->pui32RGXFWIFCoreClkRate != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIFCoreClkRateMemDesc);
psDevInfo->pui32RGXFWIFCoreClkRate = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIFCoreClkRateMemDesc);
psDevInfo->psRGXFWIFCoreClkRateMemDesc = NULL;
}
#endif
}
/*!
*******************************************************************************
@Function RGXFreeFwOsData
@Description Frees all os-specific firmware related data
@Input psDevInfo
******************************************************************************/
static void RGXFreeFwOsData(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFreeCCBReturnSlots(psDevInfo,
&psDevInfo->pui32KernelCCBRtnSlots,
&psDevInfo->psKernelCCBRtnSlotsMemDesc);
RGXFreeCCB(psDevInfo,
&psDevInfo->psKernelCCBCtl,
&psDevInfo->psKernelCCBCtlMemDesc,
&psDevInfo->psKernelCCB,
&psDevInfo->psKernelCCBMemDesc);
RGXFreeCCB(psDevInfo,
&psDevInfo->psFirmwareCCBCtl,
&psDevInfo->psFirmwareCCBCtlMemDesc,
&psDevInfo->psFirmwareCCB,
&psDevInfo->psFirmwareCCBMemDesc);
#if defined(PVRSRV_SYNC_CHECKPOINT_CCB)
RGXFreeCCB(psDevInfo,
&psDevInfo->psCheckpointCCBCtl,
&psDevInfo->psCheckpointCCBCtlMemDesc,
&psDevInfo->psCheckpointCCB,
&psDevInfo->psCheckpointCCBMemDesc);
#endif
#if defined(SUPPORT_WORKLOAD_ESTIMATION)
RGXFreeCCB(psDevInfo,
&psDevInfo->psWorkEstFirmwareCCBCtl,
&psDevInfo->psWorkEstFirmwareCCBCtlMemDesc,
&psDevInfo->psWorkEstFirmwareCCB,
&psDevInfo->psWorkEstFirmwareCCBMemDesc);
#endif
if (psDevInfo->psPowSyncPrim != NULL)
{
SyncPrimFree(psDevInfo->psPowSyncPrim);
psDevInfo->psPowSyncPrim = NULL;
}
if (psDevInfo->hSyncPrimContext != (IMG_HANDLE) NULL)
{
SyncPrimContextDestroy(psDevInfo->hSyncPrimContext);
psDevInfo->hSyncPrimContext = (IMG_HANDLE) NULL;
}
if (psDevInfo->psRGXFWIfHWRInfoBufCtlMemDesc)
{
if (psDevInfo->psRGXFWIfHWRInfoBufCtl != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfHWRInfoBufCtlMemDesc);
psDevInfo->psRGXFWIfHWRInfoBufCtl = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfHWRInfoBufCtlMemDesc);
psDevInfo->psRGXFWIfHWRInfoBufCtlMemDesc = NULL;
}
if (psDevInfo->psRGXFWIfFwOsDataMemDesc)
{
if (psDevInfo->psRGXFWIfFwOsData != NULL)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfFwOsDataMemDesc);
psDevInfo->psRGXFWIfFwOsData = NULL;
}
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfFwOsDataMemDesc);
psDevInfo->psRGXFWIfFwOsDataMemDesc = NULL;
}
}
/*!
*******************************************************************************
@Function RGXFreeFirmware
@Description Frees all the firmware-related allocations
@Input psDevInfo
******************************************************************************/
void RGXFreeFirmware(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFreeFwOsData(psDevInfo);
if (psDevInfo->psRGXFWIfConnectionCtl)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfConnectionCtlMemDesc);
psDevInfo->psRGXFWIfConnectionCtl = NULL;
}
if (psDevInfo->psRGXFWIfConnectionCtlMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfConnectionCtlMemDesc);
psDevInfo->psRGXFWIfConnectionCtlMemDesc = NULL;
}
if (psDevInfo->psRGXFWIfOsInit)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfOsInitMemDesc);
psDevInfo->psRGXFWIfOsInit = NULL;
}
if (psDevInfo->psRGXFWIfOsInitMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfOsInitMemDesc);
psDevInfo->psRGXFWIfOsInitMemDesc = NULL;
}
RGXFreeFwSysData(psDevInfo);
if (psDevInfo->psRGXFWIfSysInit)
{
DevmemReleaseCpuVirtAddr(psDevInfo->psRGXFWIfSysInitMemDesc);
psDevInfo->psRGXFWIfSysInit = NULL;
}
if (psDevInfo->psRGXFWIfSysInitMemDesc)
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psRGXFWIfSysInitMemDesc);
psDevInfo->psRGXFWIfSysInitMemDesc = NULL;
}
}
/******************************************************************************
FUNCTION : RGXAcquireKernelCCBSlot
PURPOSE : Attempts to obtain a slot in the Kernel CCB
PARAMETERS : psCCB - the CCB
: Address of space if available, NULL otherwise
RETURNS : PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXAcquireKernelCCBSlot(DEVMEM_MEMDESC *psKCCBCtrlMemDesc,
RGXFWIF_CCB_CTL *psKCCBCtl,
IMG_UINT32 *pui32Offset)
{
IMG_UINT32 ui32OldWriteOffset, ui32NextWriteOffset;
ui32OldWriteOffset = psKCCBCtl->ui32WriteOffset;
ui32NextWriteOffset = (ui32OldWriteOffset + 1) & psKCCBCtl->ui32WrapMask;
/*
* Note: The MTS can queue up to 255 kicks (254 pending kicks and 1
* executing kick), hence the kernel CCB should not queue more than
* 254 commands.
*/
PVR_ASSERT(psKCCBCtl->ui32WrapMask < 255);
#if defined(PDUMP)
/* Wait for sufficient CCB space to become available */
PDUMPCOMMENTWITHFLAGS(0, "Wait for kCCB woff=%u", ui32NextWriteOffset);
DevmemPDumpCBP(psKCCBCtrlMemDesc,
offsetof(RGXFWIF_CCB_CTL, ui32ReadOffset),
ui32NextWriteOffset,
1,
(psKCCBCtl->ui32WrapMask + 1));
#endif
if (ui32NextWriteOffset == psKCCBCtl->ui32ReadOffset)
{
return PVRSRV_ERROR_KERNEL_CCB_FULL;
}
*pui32Offset = ui32NextWriteOffset;
return PVRSRV_OK;
}
/******************************************************************************
FUNCTION : RGXPollKernelCCBSlot
PURPOSE : Poll for space in Kernel CCB
PARAMETERS : psCCB - the CCB
: Address of space if available, NULL otherwise
RETURNS : PVRSRV_ERROR
******************************************************************************/
static PVRSRV_ERROR RGXPollKernelCCBSlot(DEVMEM_MEMDESC *psKCCBCtrlMemDesc,
RGXFWIF_CCB_CTL *psKCCBCtl)
{
IMG_UINT32 ui32OldWriteOffset, ui32NextWriteOffset;
ui32OldWriteOffset = psKCCBCtl->ui32WriteOffset;
ui32NextWriteOffset = (ui32OldWriteOffset + 1) & psKCCBCtl->ui32WrapMask;
/*
* Note: The MTS can queue up to 255 kicks (254 pending kicks and 1
* executing kick), hence the kernel CCB should not queue more than
* 254 commands.
*/
PVR_ASSERT(psKCCBCtl->ui32WrapMask < 255);
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
if (ui32NextWriteOffset != psKCCBCtl->ui32ReadOffset)
{
return PVRSRV_OK;
}
{
/*
* The following sanity check doesn't impact performance,
* since the CPU has to wait for the GPU anyway (full kernel CCB).
*/
if (PVRSRVGetPVRSRVData()->eServicesState != PVRSRV_SERVICES_STATE_OK)
{
return PVRSRV_ERROR_KERNEL_CCB_FULL;
}
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
return PVRSRV_ERROR_KERNEL_CCB_FULL;
}
/******************************************************************************
FUNCTION : RGXGetCmdMemCopySize
PURPOSE : Calculates actual size of KCCB command getting used
PARAMETERS : eCmdType Type of KCCB command
RETURNS : Returns actual size of KCCB command on success else zero
******************************************************************************/
static IMG_UINT32 RGXGetCmdMemCopySize(RGXFWIF_KCCB_CMD_TYPE eCmdType)
{
/* First get offset of uCmdData inside the struct RGXFWIF_KCCB_CMD
* This will account alignment requirement of uCmdData union
*
* Then add command-data size depending on command type to calculate actual
* command size required to do mem copy
*
* NOTE: Make sure that uCmdData is the last member of RGXFWIF_KCCB_CMD struct.
*/
switch (eCmdType)
{
case RGXFWIF_KCCB_CMD_KICK:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_KCCB_CMD_KICK_DATA);
}
case RGXFWIF_KCCB_CMD_COMBINED_TA_3D_KICK:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_KCCB_CMD_COMBINED_TA_3D_KICK_DATA);
}
case RGXFWIF_KCCB_CMD_MMUCACHE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_MMUCACHEDATA);
}
#if defined(SUPPORT_USC_BREAKPOINT)
case RGXFWIF_KCCB_CMD_BP:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_BPDATA);
}
#endif
case RGXFWIF_KCCB_CMD_SLCFLUSHINVAL:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_SLCFLUSHINVALDATA);
}
case RGXFWIF_KCCB_CMD_CLEANUP:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_CLEANUP_REQUEST);
}
case RGXFWIF_KCCB_CMD_POW:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_POWER_REQUEST);
}
case RGXFWIF_KCCB_CMD_ZSBUFFER_BACKING_UPDATE:
case RGXFWIF_KCCB_CMD_ZSBUFFER_UNBACKING_UPDATE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_ZSBUFFER_BACKING_DATA);
}
case RGXFWIF_KCCB_CMD_FREELIST_GROW_UPDATE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_FREELIST_GS_DATA);
}
case RGXFWIF_KCCB_CMD_FREELISTS_RECONSTRUCTION_UPDATE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_FREELISTS_RECONSTRUCTION_DATA);
}
case RGXFWIF_KCCB_CMD_NOTIFY_SIGNAL_UPDATE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_SIGNAL_UPDATE_DATA);
}
case RGXFWIF_KCCB_CMD_NOTIFY_WRITE_OFFSET_UPDATE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_WRITE_OFFSET_UPDATE_DATA);
}
case RGXFWIF_KCCB_CMD_FORCE_UPDATE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_KCCB_CMD_FORCE_UPDATE_DATA);
}
#if defined(SUPPORT_USER_REGISTER_CONFIGURATION)
case RGXFWIF_KCCB_CMD_REGCONFIG:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_REGCONFIG_DATA);
}
#endif
case RGXFWIF_KCCB_CMD_HWPERF_SELECT_CUSTOM_CNTRS:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_HWPERF_SELECT_CUSTOM_CNTRS);
}
#if defined(SUPPORT_PDVFS)
case RGXFWIF_KCCB_CMD_PDVFS_LIMIT_MAX_FREQ:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_PDVFS_MAX_FREQ_DATA);
}
#endif
case RGXFWIF_KCCB_CMD_OSID_PRIORITY_CHANGE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_OSID_PRIORITY_DATA);
}
case RGXFWIF_KCCB_CMD_HCS_SET_DEADLINE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_HCS_CTL);
}
case RGXFWIF_KCCB_CMD_OS_ISOLATION_GROUP_CHANGE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_OSID_ISOLATION_GROUP_DATA);
}
case RGXFWIF_KCCB_CMD_OS_ONLINE_STATE_CONFIGURE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_OS_STATE_CHANGE_DATA);
}
case RGXFWIF_KCCB_CMD_COUNTER_DUMP:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_COUNTER_DUMP_DATA);
}
case RGXFWIF_KCCB_CMD_HWPERF_UPDATE_CONFIG:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_HWPERF_CTRL);
}
case RGXFWIF_KCCB_CMD_HWPERF_CONFIG_ENABLE_BLKS:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_HWPERF_CONFIG_ENABLE_BLKS);
}
case RGXFWIF_KCCB_CMD_HWPERF_CTRL_BLKS:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_HWPERF_CTRL_BLKS);
}
case RGXFWIF_KCCB_CMD_CORECLKSPEEDCHANGE:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_CORECLKSPEEDCHANGE_DATA);
}
case RGXFWIF_KCCB_CMD_HEALTH_CHECK:
case RGXFWIF_KCCB_CMD_HWPERF_CONFIG_ENABLE_BLKS_DIRECT:
case RGXFWIF_KCCB_CMD_LOGTYPE_UPDATE:
case RGXFWIF_KCCB_CMD_STATEFLAGS_CTRL:
{
/* No command specific data */
return offsetof(RGXFWIF_KCCB_CMD, uCmdData);
}
case RGXFWIF_KCCB_CMD_PHR_CFG:
{
return offsetof(RGXFWIF_KCCB_CMD, uCmdData) + sizeof(RGXFWIF_KCCB_CMD_PHR_CFG_DATA);
}
default:
{
/* Invalid (OR) Unused (OR) Newly added command type */
return 0; /* Error */
}
}
}
PVRSRV_ERROR RGXWaitForKCCBSlotUpdate(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32SlotNum,
IMG_UINT32 ui32PDumpFlags)
{
PVRSRV_ERROR eError;
eError = PVRSRVWaitForValueKM(
(IMG_UINT32 __iomem *)&psDevInfo->pui32KernelCCBRtnSlots[ui32SlotNum],
RGXFWIF_KCCB_RTN_SLOT_CMD_EXECUTED,
RGXFWIF_KCCB_RTN_SLOT_CMD_EXECUTED);
PVR_LOG_RETURN_IF_ERROR(eError, "PVRSRVWaitForValueKM");
#if defined(PDUMP)
/* PDumping conditions same as RGXSendCommandRaw for the actual command and poll command to go in harmony */
if (PDumpIsContCaptureOn())
{
IMG_BOOL bIsInCaptureRange;
PDumpIsCaptureFrameKM(&bIsInCaptureRange);
if ((bIsInCaptureRange || PDUMP_IS_CONTINUOUS(ui32PDumpFlags)) && !PDUMPPOWCMDINTRANS())
{
PDUMPCOMMENT("Poll on KCCB slot %u for value %u (mask: 0x%x)", ui32SlotNum,
RGXFWIF_KCCB_RTN_SLOT_CMD_EXECUTED, RGXFWIF_KCCB_RTN_SLOT_CMD_EXECUTED);
eError = DevmemPDumpDevmemPol32(psDevInfo->psKernelCCBRtnSlotsMemDesc,
ui32SlotNum * sizeof(IMG_UINT32),
RGXFWIF_KCCB_RTN_SLOT_CMD_EXECUTED,
RGXFWIF_KCCB_RTN_SLOT_CMD_EXECUTED,
PDUMP_POLL_OPERATOR_EQUAL,
ui32PDumpFlags);
PVR_LOG_IF_ERROR(eError, "DevmemPDumpDevmemPol32");
}
}
#else
PVR_UNREFERENCED_PARAMETER(ui32PDumpFlags);
#endif
return eError;
}
static PVRSRV_ERROR RGXSendCommandRaw(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_KCCB_CMD *psKCCBCmd,
IMG_UINT32 uiPdumpFlags,
IMG_UINT32 *pui32CmdKCCBSlot)
{
PVRSRV_ERROR eError;
PVRSRV_DEVICE_NODE *psDeviceNode = psDevInfo->psDeviceNode;
RGXFWIF_CCB_CTL *psKCCBCtl = psDevInfo->psKernelCCBCtl;
IMG_UINT8 *pui8KCCB = psDevInfo->psKernelCCB;
IMG_UINT32 ui32NewWriteOffset;
IMG_UINT32 ui32OldWriteOffset = psKCCBCtl->ui32WriteOffset;
IMG_UINT32 ui32CmdMemCopySize;
#if !defined(PDUMP)
PVR_UNREFERENCED_PARAMETER(uiPdumpFlags);
#else
IMG_BOOL bPdumpEnabled = IMG_FALSE;
IMG_BOOL bPDumpPowTrans = PDUMPPOWCMDINTRANS();
IMG_BOOL bContCaptureOn = PDumpIsContCaptureOn(); /* client connected or in pdump init phase */
if (bContCaptureOn)
{
IMG_BOOL bIsInCaptureRange;
PDumpIsCaptureFrameKM(&bIsInCaptureRange);
bPdumpEnabled = (bIsInCaptureRange || PDUMP_IS_CONTINUOUS(uiPdumpFlags)) && !bPDumpPowTrans;
/* in capture range */
if (bPdumpEnabled)
{
if (!psDevInfo->bDumpedKCCBCtlAlready)
{
/* entering capture range */
psDevInfo->bDumpedKCCBCtlAlready = IMG_TRUE;
/* Wait for the live FW to catch up */
PVR_DPF((PVR_DBG_MESSAGE, "%s: waiting on fw to catch-up, roff: %d, woff: %d",
__func__,
psKCCBCtl->ui32ReadOffset, ui32OldWriteOffset));
PVRSRVPollForValueKM(psDevInfo->psDeviceNode,
(IMG_UINT32 __iomem *)&psKCCBCtl->ui32ReadOffset,
ui32OldWriteOffset, 0xFFFFFFFF,
POLL_FLAG_LOG_ERROR | POLL_FLAG_DEBUG_DUMP);
/* Dump Init state of Kernel CCB control (read and write offset) */
PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "Initial state of kernel CCB Control, roff: %d, woff: %d",
psKCCBCtl->ui32ReadOffset, psKCCBCtl->ui32WriteOffset);
DevmemPDumpLoadMem(psDevInfo->psKernelCCBCtlMemDesc,
0,
sizeof(RGXFWIF_CCB_CTL),
PDUMP_FLAGS_CONTINUOUS);
}
}
}
#endif
#if defined(SUPPORT_AUTOVZ)
if (!((KM_FW_CONNECTION_IS(READY, psDevInfo) && KM_OS_CONNECTION_IS(READY, psDevInfo)) ||
(KM_FW_CONNECTION_IS(ACTIVE, psDevInfo) && KM_OS_CONNECTION_IS(ACTIVE, psDevInfo))))
{
PVR_DPF((PVR_DBG_ERROR, "%s: The firmware-driver connection is invalid:"
"driver state = %u / firmware state = %u;"
"expected READY (%u/%u) or ACTIVE (%u/%u);",
__func__, KM_GET_OS_CONNECTION(psDevInfo), KM_GET_FW_CONNECTION(psDevInfo),
RGXFW_CONNECTION_OS_READY, RGXFW_CONNECTION_FW_READY,
RGXFW_CONNECTION_OS_ACTIVE, RGXFW_CONNECTION_FW_ACTIVE));
eError = PVRSRV_ERROR_PVZ_OSID_IS_OFFLINE;
goto _RGXSendCommandRaw_Exit;
}
#endif
PVR_ASSERT(sizeof(RGXFWIF_KCCB_CMD) == psKCCBCtl->ui32CmdSize);
if (!OSLockIsLocked(psDeviceNode->hPowerLock))
{
PVR_DPF((PVR_DBG_ERROR,
"%s called without power lock held!",
__func__));
PVR_ASSERT(OSLockIsLocked(psDeviceNode->hPowerLock));
}
/* Acquire a slot in the CCB */
eError = RGXAcquireKernelCCBSlot(psDevInfo->psKernelCCBCtlMemDesc, psKCCBCtl, &ui32NewWriteOffset);
if (eError != PVRSRV_OK)
{
goto _RGXSendCommandRaw_Exit;
}
/* Calculate actual size of command to optimize device mem copy */
ui32CmdMemCopySize = RGXGetCmdMemCopySize(psKCCBCmd->eCmdType);
PVR_LOG_RETURN_IF_FALSE(ui32CmdMemCopySize !=0, "RGXGetCmdMemCopySize failed", PVRSRV_ERROR_INVALID_CCB_COMMAND);
/* Copy the command into the CCB */
OSDeviceMemCopy(&pui8KCCB[ui32OldWriteOffset * psKCCBCtl->ui32CmdSize],
psKCCBCmd, ui32CmdMemCopySize);
/* If non-NULL pui32CmdKCCBSlot passed-in, return the kCCB slot in which the command was enqueued */
if (pui32CmdKCCBSlot)
{
*pui32CmdKCCBSlot = ui32OldWriteOffset;
/* Each such command enqueue needs to reset the slot value first. This is so that a caller
* doesn't get to see stale/false value in allotted slot */
psDevInfo->pui32KernelCCBRtnSlots[ui32OldWriteOffset] = RGXFWIF_KCCB_RTN_SLOT_NO_RESPONSE;
#if defined(PDUMP)
PDUMPCOMMENTWITHFLAGS(uiPdumpFlags, "Reset kCCB slot number %u", ui32OldWriteOffset);
DevmemPDumpLoadMem(psDevInfo->psKernelCCBRtnSlotsMemDesc,
ui32OldWriteOffset * sizeof(IMG_UINT32),
sizeof(IMG_UINT32),
uiPdumpFlags);
#endif
PVR_DPF((PVR_DBG_MESSAGE, "%s: Device (%p) KCCB slot %u reset with value %u for command type %u",
__func__, psDevInfo, ui32OldWriteOffset, RGXFWIF_KCCB_RTN_SLOT_NO_RESPONSE, psKCCBCmd->eCmdType));
}
/* ensure kCCB data is written before the offsets */
OSWriteMemoryBarrier();
/* Move past the current command */
psKCCBCtl->ui32WriteOffset = ui32NewWriteOffset;
/* Force a read-back to memory to avoid posted writes on certain buses */
(void) psKCCBCtl->ui32WriteOffset;
#if defined(PDUMP)
if (bContCaptureOn)
{
/* in capture range */
if (bPdumpEnabled)
{
/* Dump new Kernel CCB content */
PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "Dump kCCB cmd woff = %d",
ui32OldWriteOffset);
DevmemPDumpLoadMem(psDevInfo->psKernelCCBMemDesc,
ui32OldWriteOffset * psKCCBCtl->ui32CmdSize,
ui32CmdMemCopySize,
PDUMP_FLAGS_CONTINUOUS);
/* Dump new kernel CCB write offset */
PDUMPCOMMENTWITHFLAGS(uiPdumpFlags, "Dump kCCBCtl woff: %d",
ui32NewWriteOffset);
DevmemPDumpLoadMem(psDevInfo->psKernelCCBCtlMemDesc,
offsetof(RGXFWIF_CCB_CTL, ui32WriteOffset),
sizeof(IMG_UINT32),
uiPdumpFlags);
/* mimic the read-back of the write from above */
DevmemPDumpDevmemPol32(psDevInfo->psKernelCCBCtlMemDesc,
offsetof(RGXFWIF_CCB_CTL, ui32WriteOffset),
ui32NewWriteOffset,
0xFFFFFFFF,
PDUMP_POLL_OPERATOR_EQUAL,
uiPdumpFlags);
}
/* out of capture range */
else
{
eError = RGXPdumpDrainKCCB(psDevInfo, ui32OldWriteOffset);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXPdumpDrainKCCB", _RGXSendCommandRaw_Exit);
}
}
#endif
PDUMPCOMMENTWITHFLAGS(uiPdumpFlags, "MTS kick for kernel CCB");
/*
* Kick the MTS to schedule the firmware.
*/
__MTSScheduleWrite(psDevInfo, MTS_SCHEDULE_DM_VAL & ~RGX_CR_MTS_SCHEDULE_DM_CLRMSK);
PDUMPREG32(RGX_PDUMPREG_NAME, RGX_CR_MTS_SCHEDULE, MTS_SCHEDULE_DM_VAL & ~RGX_CR_MTS_SCHEDULE_DM_CLRMSK, uiPdumpFlags);
#if defined(NO_HARDWARE)
/* keep the roff updated because fw isn't there to update it */
psKCCBCtl->ui32ReadOffset = psKCCBCtl->ui32WriteOffset;
#endif
_RGXSendCommandRaw_Exit:
return eError;
}
/******************************************************************************
FUNCTION : _AllocDeferredCommand
PURPOSE : Allocate a KCCB command and add it to KCCB deferred list
PARAMETERS : psDevInfo RGX device info
: eKCCBType Firmware Command type
: psKCCBCmd Firmware Command
: uiPdumpFlags Pdump flags
RETURNS : PVRSRV_OK If all went good, PVRSRV_ERROR_RETRY otherwise.
******************************************************************************/
static PVRSRV_ERROR _AllocDeferredCommand(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_KCCB_CMD *psKCCBCmd,
IMG_UINT32 uiPdumpFlags)
{
RGX_DEFERRED_KCCB_CMD *psDeferredCommand;
OS_SPINLOCK_FLAGS uiFlags;
psDeferredCommand = OSAllocMem(sizeof(*psDeferredCommand));
if (!psDeferredCommand)
{
PVR_DPF((PVR_DBG_ERROR,
"Deferring a KCCB command failed: allocation failure: requesting retry"));
return PVRSRV_ERROR_RETRY;
}
psDeferredCommand->sKCCBcmd = *psKCCBCmd;
psDeferredCommand->uiPdumpFlags = uiPdumpFlags;
psDeferredCommand->psDevInfo = psDevInfo;
OSSpinLockAcquire(psDevInfo->hLockKCCBDeferredCommandsList, uiFlags);
dllist_add_to_tail(&(psDevInfo->sKCCBDeferredCommandsListHead), &(psDeferredCommand->sListNode));
psDevInfo->ui32KCCBDeferredCommandsCount++;
OSSpinLockRelease(psDevInfo->hLockKCCBDeferredCommandsList, uiFlags);
return PVRSRV_OK;
}
/******************************************************************************
FUNCTION : _FreeDeferredCommand
PURPOSE : Remove from the deferred list the sent deferred KCCB command
PARAMETERS : psNode Node in deferred list
: psDeferredKCCBCmd KCCB Command to free
RETURNS : None
******************************************************************************/
static void _FreeDeferredCommand(DLLIST_NODE *psNode, RGX_DEFERRED_KCCB_CMD *psDeferredKCCBCmd)
{
dllist_remove_node(psNode);
psDeferredKCCBCmd->psDevInfo->ui32KCCBDeferredCommandsCount--;
OSFreeMem(psDeferredKCCBCmd);
}
/******************************************************************************
FUNCTION : RGXSendCommandsFromDeferredList
PURPOSE : Try send KCCB commands in deferred list to KCCB
Should be called by holding PowerLock
PARAMETERS : psDevInfo RGX device info
: bPoll Poll for space in KCCB
RETURNS : PVRSRV_OK If all commands in deferred list are sent to KCCB,
PVRSRV_ERROR_KERNEL_CCB_FULL otherwise.
******************************************************************************/
PVRSRV_ERROR RGXSendCommandsFromDeferredList(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_BOOL bPoll)
{
PVRSRV_ERROR eError = PVRSRV_OK;
DLLIST_NODE *psNode, *psNext;
RGX_DEFERRED_KCCB_CMD *psTempDeferredKCCBCmd;
DLLIST_NODE sCommandList;
OS_SPINLOCK_FLAGS uiFlags;
PVR_ASSERT(PVRSRVPwrLockIsLockedByMe(psDevInfo->psDeviceNode));
/* !!! Important !!!
*
* The idea of moving the whole list hLockKCCBDeferredCommandsList below
* to the temporary list is only valid under the principle that all of the
* operations are also protected by the power lock. It must be held
* so that the order of the commands doesn't get messed up while we're
* performing the operations on the local list.
*
* The necessity of releasing the hLockKCCBDeferredCommandsList comes from
* the fact that _FreeDeferredCommand() is allocating memory and it can't
* be done in atomic context (inside section protected by a spin lock).
*
* We're using spin lock here instead of mutex to quickly perform a check
* if the list is empty in MISR without a risk that the MISR is going
* to sleep due to a lock.
*/
/* move the whole list to a local list so it can be processed without lock */
OSSpinLockAcquire(psDevInfo->hLockKCCBDeferredCommandsList, uiFlags);
dllist_replace_head(&psDevInfo->sKCCBDeferredCommandsListHead, &sCommandList);
OSSpinLockRelease(psDevInfo->hLockKCCBDeferredCommandsList, uiFlags);
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
if (dllist_is_empty(&sCommandList))
{
return PVRSRV_OK;
}
/* For every deferred KCCB command, try to send it*/
dllist_foreach_node(&sCommandList, psNode, psNext)
{
psTempDeferredKCCBCmd = IMG_CONTAINER_OF(psNode, RGX_DEFERRED_KCCB_CMD, sListNode);
eError = RGXSendCommandRaw(psTempDeferredKCCBCmd->psDevInfo,
&psTempDeferredKCCBCmd->sKCCBcmd,
psTempDeferredKCCBCmd->uiPdumpFlags,
NULL /* We surely aren't interested in kCCB slot number of deferred command */);
if (eError != PVRSRV_OK)
{
if (!bPoll)
{
eError = PVRSRV_ERROR_KERNEL_CCB_FULL;
goto cleanup_;
}
break;
}
_FreeDeferredCommand(psNode, psTempDeferredKCCBCmd);
}
if (bPoll)
{
PVRSRV_ERROR eErrPollForKCCBSlot;
/* Don't overwrite eError because if RGXPollKernelCCBSlot returns OK and the
* outer loop times-out, we'll still want to return KCCB_FULL to caller
*/
eErrPollForKCCBSlot = RGXPollKernelCCBSlot(psDevInfo->psKernelCCBCtlMemDesc,
psDevInfo->psKernelCCBCtl);
if (eErrPollForKCCBSlot == PVRSRV_ERROR_KERNEL_CCB_FULL)
{
eError = PVRSRV_ERROR_KERNEL_CCB_FULL;
goto cleanup_;
}
}
} END_LOOP_UNTIL_TIMEOUT();
cleanup_:
/* if the local list is not empty put it back to the deferred list head
* so that the old order of commands is retained */
OSSpinLockAcquire(psDevInfo->hLockKCCBDeferredCommandsList, uiFlags);
dllist_insert_list_at_head(&psDevInfo->sKCCBDeferredCommandsListHead, &sCommandList);
OSSpinLockRelease(psDevInfo->hLockKCCBDeferredCommandsList, uiFlags);
return eError;
}
PVRSRV_ERROR RGXSendCommandAndGetKCCBSlot(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_KCCB_CMD *psKCCBCmd,
IMG_UINT32 uiPdumpFlags,
IMG_UINT32 *pui32CmdKCCBSlot)
{
IMG_BOOL bPoll = (pui32CmdKCCBSlot != NULL);
PVRSRV_ERROR eError;
/*
* First try to Flush all the cmds in deferred list.
*
* We cannot defer an incoming command if the caller is interested in
* knowing the command's kCCB slot: it plans to poll/wait for a
* response from the FW just after the command is enqueued, so we must
* poll for space to be available.
*/
eError = RGXSendCommandsFromDeferredList(psDevInfo, bPoll);
if (eError == PVRSRV_OK)
{
eError = RGXSendCommandRaw(psDevInfo,
psKCCBCmd,
uiPdumpFlags,
pui32CmdKCCBSlot);
}
/*
* If we don't manage to enqueue one of the deferred commands or the command
* passed as argument because the KCCB is full, insert the latter into the deferred commands list.
* The deferred commands will also be flushed eventually by:
* - one more KCCB command sent for any DM
* - RGX_MISRHandler_CheckFWActivePowerState
*/
if (eError == PVRSRV_ERROR_KERNEL_CCB_FULL)
{
if (pui32CmdKCCBSlot == NULL)
{
eError = _AllocDeferredCommand(psDevInfo, psKCCBCmd, uiPdumpFlags);
}
else
{
/* Let the caller retry. Otherwise if we deferred the command and returned OK,
* the caller can end up looking in a stale CCB slot.
*/
PVR_DPF((PVR_DBG_ERROR, "%s: Couldn't flush the deferred queue for a command (Type:%d) "
"that needed the kCCB command slot number! Returning kCCB FULL",
__func__, psKCCBCmd->eCmdType));
}
}
return eError;
}
PVRSRV_ERROR RGXSendCommandWithPowLockAndGetKCCBSlot(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_KCCB_CMD *psKCCBCmd,
IMG_UINT32 ui32PDumpFlags,
IMG_UINT32 *pui32CmdKCCBSlot)
{
PVRSRV_ERROR eError;
PVRSRV_DEVICE_NODE *psDeviceNode = psDevInfo->psDeviceNode;
/* Ensure Rogue is powered up before kicking MTS */
eError = PVRSRVPowerLock(psDeviceNode);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING,
"%s: failed to acquire powerlock (%s)",
__func__,
PVRSRVGetErrorString(eError)));
goto _PVRSRVPowerLock_Exit;
}
PDUMPPOWCMDSTART();
eError = PVRSRVSetDevicePowerStateKM(psDeviceNode,
PVRSRV_DEV_POWER_STATE_ON,
IMG_FALSE);
PDUMPPOWCMDEND();
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING, "%s: failed to transition Rogue to ON (%s)",
__func__,
PVRSRVGetErrorString(eError)));
goto _PVRSRVSetDevicePowerStateKM_Exit;
}
eError = RGXSendCommandAndGetKCCBSlot(psDevInfo,
psKCCBCmd,
ui32PDumpFlags,
pui32CmdKCCBSlot);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: failed to schedule command (%s)",
__func__,
PVRSRVGetErrorString(eError)));
#if defined(DEBUG)
/* PVRSRVDebugRequest must be called without powerlock */
PVRSRVPowerUnlock(psDeviceNode);
PVRSRVDebugRequest(psDeviceNode, DEBUG_REQUEST_VERBOSITY_MAX, NULL, NULL);
goto _PVRSRVPowerLock_Exit;
#endif
}
_PVRSRVSetDevicePowerStateKM_Exit:
PVRSRVPowerUnlock(psDeviceNode);
_PVRSRVPowerLock_Exit:
return eError;
}
void RGXScheduleProcessQueuesKM(PVRSRV_CMDCOMP_HANDLE hCmdCompHandle)
{
PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE*) hCmdCompHandle;
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
OSScheduleMISR(psDevInfo->hProcessQueuesMISR);
}
/*!
*******************************************************************************
@Function RGX_MISRHandler_ScheduleProcessQueues
@Description - Sends uncounted kick to all the DMs (the FW will process all
the queue for all the DMs)
******************************************************************************/
static void RGX_MISRHandler_ScheduleProcessQueues(void *pvData)
{
PVRSRV_DEVICE_NODE *psDeviceNode = pvData;
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
PVRSRV_ERROR eError;
PVRSRV_DEV_POWER_STATE ePowerState;
eError = PVRSRVPowerLock(psDeviceNode);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING, "%s: failed to acquire powerlock (%s)",
__func__, PVRSRVGetErrorString(eError)));
return;
}
/* Check whether it's worth waking up the GPU */
eError = PVRSRVGetDevicePowerState(psDeviceNode, &ePowerState);
if (!PVRSRV_VZ_MODE_IS(GUEST) &&
(eError == PVRSRV_OK) && (ePowerState == PVRSRV_DEV_POWER_STATE_OFF))
{
/* For now, guest drivers will always wake-up the GPU */
RGXFWIF_GPU_UTIL_FWCB *psUtilFWCb = psDevInfo->psRGXFWIfGpuUtilFWCb;
IMG_BOOL bGPUHasWorkWaiting;
bGPUHasWorkWaiting =
(RGXFWIF_GPU_UTIL_GET_STATE(psUtilFWCb->ui64LastWord) == RGXFWIF_GPU_UTIL_STATE_BLOCKED);
if (!bGPUHasWorkWaiting)
{
/* all queues are empty, don't wake up the GPU */
PVRSRVPowerUnlock(psDeviceNode);
return;
}
}
PDUMPPOWCMDSTART();
/* wake up the GPU */
eError = PVRSRVSetDevicePowerStateKM(psDeviceNode,
PVRSRV_DEV_POWER_STATE_ON,
IMG_FALSE);
PDUMPPOWCMDEND();
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING, "%s: failed to transition Rogue to ON (%s)",
__func__, PVRSRVGetErrorString(eError)));
PVRSRVPowerUnlock(psDeviceNode);
return;
}
/* uncounted kick to the FW */
HTBLOGK(HTB_SF_MAIN_KICK_UNCOUNTED);
__MTSScheduleWrite(psDevInfo, (MTS_SCHEDULE_DM_VAL & ~RGX_CR_MTS_SCHEDULE_DM_CLRMSK) | RGX_CR_MTS_SCHEDULE_TASK_NON_COUNTED);
PVRSRVPowerUnlock(psDeviceNode);
}
PVRSRV_ERROR RGXInstallProcessQueuesMISR(IMG_HANDLE *phMISR, PVRSRV_DEVICE_NODE *psDeviceNode)
{
return OSInstallMISR(phMISR,
RGX_MISRHandler_ScheduleProcessQueues,
psDeviceNode,
"RGX_ScheduleProcessQueues");
}
PVRSRV_ERROR RGXScheduleCommandAndGetKCCBSlot(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_DM eKCCBType,
RGXFWIF_KCCB_CMD *psKCCBCmd,
IMG_UINT32 ui32CacheOpFence,
IMG_UINT32 ui32PDumpFlags,
IMG_UINT32 *pui32CmdKCCBSlot)
{
PVRSRV_ERROR eError;
IMG_UINT32 uiMMUSyncUpdate;
/* Don't send the command/power up request if the device is de-initialising.
* The de-init thread could destroy the device whilst the power up
* sequence below is accessing the HW registers.
*/
if (unlikely((psDevInfo == NULL) ||
(psDevInfo->psDeviceNode == NULL) ||
(psDevInfo->psDeviceNode->eDevState == PVRSRV_DEVICE_STATE_DEINIT)))
{
return PVRSRV_ERROR_INVALID_DEVICE;
}
eError = CacheOpFence(eKCCBType, ui32CacheOpFence);
if (unlikely(eError != PVRSRV_OK)) goto RGXScheduleCommand_exit;
#if defined(SUPPORT_VALIDATION)
/* For validation, force the core to different dust count states with each kick */
if ((eKCCBType == RGXFWIF_DM_GEOM) || (eKCCBType == RGXFWIF_DM_CDM))
{
if (psDevInfo->ui32DeviceFlags & RGXKM_DEVICE_STATE_GPU_UNITS_POWER_CHANGE_EN)
{
IMG_UINT32 ui32NumDusts = RGXGetNextDustCount(&psDevInfo->sDustReqState, psDevInfo->sDevFeatureCfg.ui32MAXDustCount);
PVRSRVDeviceGPUUnitsPowerChange(psDevInfo->psDeviceNode, ui32NumDusts);
}
}
#endif
/* PVRSRVPowerLock guarantees atomicity between commands. This is helpful
in a scenario with several applications allocating resources. */
eError = PVRSRVPowerLock(psDevInfo->psDeviceNode);
if (unlikely(eError != PVRSRV_OK))
{
PVR_DPF((PVR_DBG_WARNING, "%s: failed to acquire powerlock (%s)",
__func__, PVRSRVGetErrorString(eError)));
/* If system is found powered OFF, Retry scheduling the command */
if (likely(eError == PVRSRV_ERROR_SYSTEM_STATE_POWERED_OFF))
{
eError = PVRSRV_ERROR_RETRY;
}
goto RGXScheduleCommand_exit;
}
if (unlikely(psDevInfo->psDeviceNode->eDevState == PVRSRV_DEVICE_STATE_DEINIT))
{
/* If we have the power lock the device is valid but the deinit
* thread could be waiting for the lock. */
PVRSRVPowerUnlock(psDevInfo->psDeviceNode);
return PVRSRV_ERROR_INVALID_DEVICE;
}
/* Ensure device is powered up before sending any commands */
PDUMPPOWCMDSTART();
eError = PVRSRVSetDevicePowerStateKM(psDevInfo->psDeviceNode,
PVRSRV_DEV_POWER_STATE_ON,
IMG_FALSE);
PDUMPPOWCMDEND();
if (unlikely(eError != PVRSRV_OK))
{
PVR_DPF((PVR_DBG_WARNING, "%s: failed to transition RGX to ON (%s)",
__func__, PVRSRVGetErrorString(eError)));
goto _PVRSRVSetDevicePowerStateKM_Exit;
}
eError = RGXPreKickCacheCommand(psDevInfo, NULL, eKCCBType, &uiMMUSyncUpdate, IMG_FALSE);
if (unlikely(eError != PVRSRV_OK)) goto _PVRSRVSetDevicePowerStateKM_Exit;
eError = RGXSendCommandAndGetKCCBSlot(psDevInfo, psKCCBCmd, ui32PDumpFlags, pui32CmdKCCBSlot);
if (unlikely(eError != PVRSRV_OK)) goto _PVRSRVSetDevicePowerStateKM_Exit;
_PVRSRVSetDevicePowerStateKM_Exit:
PVRSRVPowerUnlock(psDevInfo->psDeviceNode);
RGXScheduleCommand_exit:
return eError;
}
#if defined(PVRSRV_SYNC_CHECKPOINT_CCB)
/*
* RGXCheckCheckpointCCB
*/
void RGXCheckCheckpointCCB(PVRSRV_DEVICE_NODE *psDeviceNode)
{
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
IMG_BOOL bSignal = IMG_FALSE;
PRGXFWIF_UFO_ADDR *psFwUFOAddr;
RGXFWIF_CCB_CTL *psChptCCBCtl = psDevInfo->psCheckpointCCBCtl;
IMG_UINT8 *psChptCCB = psDevInfo->psCheckpointCCB;
IMG_UINT32 ui32WriteOffset, ui32ReadOffset, ui32WrapMask = psChptCCBCtl->ui32WrapMask;
IMG_UINT32 uiFwAddr;
PVRSRV_SYNC_CHECKPOINT_STATE uiChptState;
/*
* Check if the firmware has signalled a full sync state check.
*/
if (psDevInfo->psRGXFWIfFwOsData->ui32FWSyncCheckMark != psDevInfo->psRGXFWIfFwOsData->ui32HostSyncCheckMark)
{
/*
* Update the offsets first so that if the firmware tries to write
* another checkpoint it is not missed by the check state.
*/
psDevInfo->psRGXFWIfFwOsData->ui32HostSyncCheckMark = psDevInfo->psRGXFWIfFwOsData->ui32FWSyncCheckMark;
psChptCCBCtl->ui32ReadOffset = psChptCCBCtl->ui32WriteOffset;
PVR_DPF((PVR_DBG_MESSAGE, "%s: Checkpoint CCB full, performing full sync checkpoint state check", __func__));
SyncCheckpointCheckState();
bSignal = IMG_TRUE;
#if defined(SUPPORT_BUFFER_SYNC)
pvr_buffer_sync_check_state();
#endif
goto exit_signal;
}
/*
* Take a snapshot of the current CCB ctl pointers at the start of
* processing.
*/
ui32WriteOffset = psChptCCBCtl->ui32WriteOffset;
ui32ReadOffset = psChptCCBCtl->ui32ReadOffset;
ui32WrapMask = psChptCCBCtl->ui32WrapMask;
while (ui32ReadOffset != ui32WriteOffset)
{
/* Point to the next checkpoint address */
psFwUFOAddr = ((PRGXFWIF_UFO_ADDR *)psChptCCB) + ui32ReadOffset;
/*
* State is encoded in bit 1 of ufo address
* 1 = signalled, 0 = errored
*/
uiChptState = PVRSRV_SYNC_CHECKPOINT_ERRORED;
uiFwAddr = psFwUFOAddr->ui32Addr;
if (uiFwAddr & 0x1U)
{
uiChptState = PVRSRV_SYNC_CHECKPOINT_SIGNALLED;
}
uiFwAddr |= 0x1U;
if (SyncCheckpointUFOHasSignalled(psDeviceNode, uiFwAddr, uiChptState))
{
bSignal = IMG_TRUE;
}
else
#if defined(SUPPORT_BUFFER_SYNC)
if (pvr_buffer_sync_checkpoint_ufo_has_signalled(uiFwAddr, uiChptState))
{
/* Buffer sync does not need a signal call. */
}
else
#endif
{
PVR_DPF((PVR_DBG_MESSAGE, "%s: Firmware signalled checkpoint (%#08X) with no host backing", __func__, uiFwAddr));
}
/* Update read offset */
ui32ReadOffset = (ui32ReadOffset + 1) & ui32WrapMask;
}
psChptCCBCtl->ui32ReadOffset = ui32ReadOffset;
exit_signal:
if (bSignal)
{
SyncCheckpointSignalWaiters();
}
}
#endif /* defined(PVRSRV_SYNC_CHECKPOINT_CCB) */
/*
* RGXCheckFirmwareCCB
*/
void RGXCheckFirmwareCCB(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFWIF_FWCCB_CMD *psFwCCBCmd;
RGXFWIF_CCB_CTL *psFWCCBCtl = psDevInfo->psFirmwareCCBCtl;
IMG_UINT8 *psFWCCB = psDevInfo->psFirmwareCCB;
while (psFWCCBCtl->ui32ReadOffset != psFWCCBCtl->ui32WriteOffset)
{
/* Point to the next command */
psFwCCBCmd = ((RGXFWIF_FWCCB_CMD *)psFWCCB) + psFWCCBCtl->ui32ReadOffset;
HTBLOGK(HTB_SF_MAIN_FWCCB_CMD, psFwCCBCmd->eCmdType);
switch (psFwCCBCmd->eCmdType)
{
case RGXFWIF_FWCCB_CMD_ZSBUFFER_BACKING:
{
if (psDevInfo->bPDPEnabled)
{
PDUMP_PANIC(ZSBUFFER_BACKING, "Request to add backing to ZSBuffer");
}
RGXProcessRequestZSBufferBacking(psDevInfo,
psFwCCBCmd->uCmdData.sCmdZSBufferBacking.ui32ZSBufferID);
break;
}
case RGXFWIF_FWCCB_CMD_ZSBUFFER_UNBACKING:
{
if (psDevInfo->bPDPEnabled)
{
PDUMP_PANIC(ZSBUFFER_UNBACKING, "Request to remove backing from ZSBuffer");
}
RGXProcessRequestZSBufferUnbacking(psDevInfo,
psFwCCBCmd->uCmdData.sCmdZSBufferBacking.ui32ZSBufferID);
break;
}
case RGXFWIF_FWCCB_CMD_FREELIST_GROW:
{
if (psDevInfo->bPDPEnabled)
{
PDUMP_PANIC(FREELIST_GROW, "Request to grow the free list");
}
RGXProcessRequestGrow(psDevInfo,
psFwCCBCmd->uCmdData.sCmdFreeListGS.ui32FreelistID);
break;
}
case RGXFWIF_FWCCB_CMD_FREELISTS_RECONSTRUCTION:
{
if (psDevInfo->bPDPEnabled)
{
PDUMP_PANIC(FREELISTS_RECONSTRUCTION, "Request to reconstruct free lists");
}
if (PVRSRV_VZ_MODE_IS(GUEST))
{
PVR_DPF((PVR_DBG_MESSAGE, "%s: Freelist reconstruction request (%d) for %d freelists",
__func__,
psFwCCBCmd->uCmdData.sCmdFreeListsReconstruction.ui32HwrCounter+1,
psFwCCBCmd->uCmdData.sCmdFreeListsReconstruction.ui32FreelistsCount));
}
else
{
PVR_ASSERT(psDevInfo->psRGXFWIfHWRInfoBufCtl);
PVR_DPF((PVR_DBG_MESSAGE, "%s: Freelist reconstruction request (%d/%d) for %d freelists",
__func__,
psFwCCBCmd->uCmdData.sCmdFreeListsReconstruction.ui32HwrCounter+1,
psDevInfo->psRGXFWIfHWRInfoBufCtl->ui32HwrCounter+1,
psFwCCBCmd->uCmdData.sCmdFreeListsReconstruction.ui32FreelistsCount));
}
RGXProcessRequestFreelistsReconstruction(psDevInfo,
psFwCCBCmd->uCmdData.sCmdFreeListsReconstruction.ui32FreelistsCount,
psFwCCBCmd->uCmdData.sCmdFreeListsReconstruction.aui32FreelistIDs);
break;
}
case RGXFWIF_FWCCB_CMD_CONTEXT_RESET_NOTIFICATION:
{
DLLIST_NODE *psNode, *psNext;
RGXFWIF_FWCCB_CMD_CONTEXT_RESET_DATA *psCmdContextResetNotification =
&psFwCCBCmd->uCmdData.sCmdContextResetNotification;
IMG_UINT32 ui32ServerCommonContextID =
psCmdContextResetNotification->ui32ServerCommonContextID;
RGX_SERVER_COMMON_CONTEXT *psServerCommonContext = NULL;
OSWRLockAcquireRead(psDevInfo->hCommonCtxtListLock);
dllist_foreach_node(&psDevInfo->sCommonCtxtListHead, psNode, psNext)
{
RGX_SERVER_COMMON_CONTEXT *psThisContext =
IMG_CONTAINER_OF(psNode, RGX_SERVER_COMMON_CONTEXT, sListNode);
if (psThisContext->ui32ContextID == ui32ServerCommonContextID)
{
psServerCommonContext = psThisContext;
break;
}
}
PVR_DPF((PVR_DBG_MESSAGE, "%s: Context 0x%p reset (ID=0x%08x, Reason=%d, JobRef=0x%08x)",
__func__,
psServerCommonContext,
psCmdContextResetNotification->ui32ServerCommonContextID,
(IMG_UINT32)(psCmdContextResetNotification->eResetReason),
psCmdContextResetNotification->ui32ResetJobRef));
if (psServerCommonContext != NULL)
{
psServerCommonContext->eLastResetReason = psCmdContextResetNotification->eResetReason;
psServerCommonContext->ui32LastResetJobRef = psCmdContextResetNotification->ui32ResetJobRef;
}
OSWRLockReleaseRead(psDevInfo->hCommonCtxtListLock);
if (psCmdContextResetNotification->bPageFault)
{
DevmemIntPFNotify(psDevInfo->psDeviceNode,
psCmdContextResetNotification->ui64PCAddress,
psCmdContextResetNotification->sFaultAddress);
}
break;
}
case RGXFWIF_FWCCB_CMD_DEBUG_DUMP:
{
PVRSRV_ERROR eError;
PVRSRV_DATA *psPVRSRVData = PVRSRVGetPVRSRVData();
OSAtomicWrite(&psDevInfo->psDeviceNode->eDebugDumpRequested, PVRSRV_DEVICE_DEBUG_DUMP_CAPTURE);
eError = OSEventObjectSignal(psPVRSRVData->hDevicesWatchdogEvObj);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to signal FW Cmd debug dump event, dumping now instead", __func__));
PVRSRVDebugRequest(psDevInfo->psDeviceNode, DEBUG_REQUEST_VERBOSITY_MAX, NULL, NULL);
}
break;
}
case RGXFWIF_FWCCB_CMD_UPDATE_STATS:
{
#if defined(PVRSRV_ENABLE_PROCESS_STATS)
IMG_PID pidTmp = psFwCCBCmd->uCmdData.sCmdUpdateStatsData.pidOwner;
IMG_INT32 i32AdjustmentValue = psFwCCBCmd->uCmdData.sCmdUpdateStatsData.i32AdjustmentValue;
switch (psFwCCBCmd->uCmdData.sCmdUpdateStatsData.eElementToUpdate)
{
case RGXFWIF_FWCCB_CMD_UPDATE_NUM_PARTIAL_RENDERS:
{
PVRSRVStatsUpdateRenderContextStats(i32AdjustmentValue,0,0,0,0,0,pidTmp);
break;
}
case RGXFWIF_FWCCB_CMD_UPDATE_NUM_OUT_OF_MEMORY:
{
PVRSRVStatsUpdateRenderContextStats(0,i32AdjustmentValue,0,0,0,0,pidTmp);
break;
}
case RGXFWIF_FWCCB_CMD_UPDATE_NUM_TA_STORES:
{
PVRSRVStatsUpdateRenderContextStats(0,0,i32AdjustmentValue,0,0,0,pidTmp);
break;
}
case RGXFWIF_FWCCB_CMD_UPDATE_NUM_3D_STORES:
{
PVRSRVStatsUpdateRenderContextStats(0,0,0,i32AdjustmentValue,0,0,pidTmp);
break;
}
case RGXFWIF_FWCCB_CMD_UPDATE_NUM_CDM_STORES:
{
PVRSRVStatsUpdateRenderContextStats(0,0,0,0,i32AdjustmentValue,0,pidTmp);
break;
}
case RGXFWIF_FWCCB_CMD_UPDATE_NUM_TDM_STORES:
{
PVRSRVStatsUpdateRenderContextStats(0,0,0,0,0,i32AdjustmentValue,pidTmp);
break;
}
}
#endif
break;
}
case RGXFWIF_FWCCB_CMD_CORE_CLK_RATE_CHANGE:
{
#if defined(SUPPORT_PDVFS)
PDVFS_PROCESS_CORE_CLK_RATE_CHANGE(psDevInfo,
psFwCCBCmd->uCmdData.sCmdCoreClkRateChange.ui32CoreClkRate);
#endif
break;
}
case RGXFWIF_FWCCB_CMD_REQUEST_GPU_RESTART:
{
if (psDevInfo->psRGXFWIfFwSysData != NULL &&
psDevInfo->psRGXFWIfFwSysData->ePowState != RGXFWIF_POW_OFF)
{
PVRSRV_ERROR eError;
/* Power down... */
eError = PVRSRVSetDeviceSystemPowerState(psDevInfo->psDeviceNode,
PVRSRV_SYS_POWER_STATE_OFF);
if (eError == PVRSRV_OK)
{
/* Clear the FW faulted flags... */
psDevInfo->psRGXFWIfFwSysData->ui32HWRStateFlags &= ~(RGXFWIF_HWR_FW_FAULT|RGXFWIF_HWR_RESTART_REQUESTED);
/* Power back up again... */
eError = PVRSRVSetDeviceSystemPowerState(psDevInfo->psDeviceNode,
PVRSRV_SYS_POWER_STATE_ON);
/* Send a dummy KCCB command to ensure the FW wakes up and checks the queues... */
if (eError == PVRSRV_OK)
{
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXFWHealthCheckCmd(psDevInfo);
if (eError != PVRSRV_ERROR_RETRY)
{
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
}
}
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed firmware restart (%s)",
__func__, PVRSRVGetErrorString(eError)));
}
}
break;
}
default:
{
/* unknown command */
PVR_DPF((PVR_DBG_WARNING, "%s: Unknown Command (eCmdType=0x%08x)",
__func__, psFwCCBCmd->eCmdType));
/* Assert on magic value corruption */
PVR_ASSERT((((IMG_UINT32)psFwCCBCmd->eCmdType & RGX_CMD_MAGIC_DWORD_MASK) >> RGX_CMD_MAGIC_DWORD_SHIFT) == RGX_CMD_MAGIC_DWORD);
}
}
/* Update read offset */
psFWCCBCtl->ui32ReadOffset = (psFWCCBCtl->ui32ReadOffset + 1) & psFWCCBCtl->ui32WrapMask;
}
}
/*
* PVRSRVRGXFrameworkCopyCommand
*/
PVRSRV_ERROR PVRSRVRGXFrameworkCopyCommand(DEVMEM_MEMDESC *psFWFrameworkMemDesc,
IMG_PBYTE pbyGPUFRegisterList,
IMG_UINT32 ui32FrameworkRegisterSize)
{
PVRSRV_ERROR eError;
RGXFWIF_RF_REGISTERS *psRFReg;
eError = DevmemAcquireCpuVirtAddr(psFWFrameworkMemDesc,
(void **)&psRFReg);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to map firmware render context state (%u)",
__func__, eError));
return eError;
}
OSDeviceMemCopy(psRFReg, pbyGPUFRegisterList, ui32FrameworkRegisterSize);
/* Release the CPU mapping */
DevmemReleaseCpuVirtAddr(psFWFrameworkMemDesc);
/*
* Dump the FW framework buffer
*/
#if defined(PDUMP)
PDUMPCOMMENT("Dump FWFramework buffer");
DevmemPDumpLoadMem(psFWFrameworkMemDesc, 0, ui32FrameworkRegisterSize, PDUMP_FLAGS_CONTINUOUS);
#endif
return PVRSRV_OK;
}
/*
* PVRSRVRGXFrameworkCreateKM
*/
PVRSRV_ERROR PVRSRVRGXFrameworkCreateKM(PVRSRV_DEVICE_NODE *psDeviceNode,
DEVMEM_MEMDESC **ppsFWFrameworkMemDesc,
IMG_UINT32 ui32FrameworkCommandSize)
{
PVRSRV_ERROR eError;
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
/*
Allocate device memory for the firmware GPU framework state.
Sufficient info to kick one or more DMs should be contained in this buffer
*/
PDUMPCOMMENT("Allocate Rogue firmware framework state");
eError = DevmemFwAllocate(psDevInfo,
ui32FrameworkCommandSize,
RGX_FWCOMCTX_ALLOCFLAGS,
"FwGPUFrameworkState",
ppsFWFrameworkMemDesc);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to allocate firmware framework state (%u)",
__func__, eError));
return eError;
}
return PVRSRV_OK;
}
PVRSRV_ERROR IMG_CALLCONV RGXPollForGPCommandCompletion(PVRSRV_DEVICE_NODE *psDevNode,
volatile IMG_UINT32 __iomem *pui32LinMemAddr,
IMG_UINT32 ui32Value,
IMG_UINT32 ui32Mask)
{
PVRSRV_ERROR eError = PVRSRV_OK;
RGXFWIF_CCB_CTL *psKCCBCtl;
IMG_UINT32 ui32CurrentQueueLength, ui32MaxRetries;
PVRSRV_RGXDEV_INFO *psDevInfo = psDevNode->pvDevice;
psKCCBCtl = psDevInfo->psKernelCCBCtl;
ui32CurrentQueueLength = (psKCCBCtl->ui32WrapMask+1 +
psKCCBCtl->ui32WriteOffset -
psKCCBCtl->ui32ReadOffset) & psKCCBCtl->ui32WrapMask;
ui32CurrentQueueLength += psDevInfo->ui32KCCBDeferredCommandsCount;
for (ui32MaxRetries = ui32CurrentQueueLength + 1;
ui32MaxRetries > 0;
ui32MaxRetries--)
{
/*
* PVRSRVPollForValueKM flags are set to POLL_FLAG_NONE in this case so that the function
* does not generate an error message. In this case, the PollForValueKM is expected to
* timeout as there is work ongoing on the GPU which may take longer than the timeout period.
*/
eError = PVRSRVPollForValueKM(psDevNode, pui32LinMemAddr, ui32Value, ui32Mask, POLL_FLAG_NONE);
if (eError != PVRSRV_ERROR_TIMEOUT)
{
break;
}
RGXSendCommandsFromDeferredList(psDevInfo, IMG_FALSE);
}
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed! Error(%s) CPU linear address(%p) Expected value(%u)",
__func__, PVRSRVGetErrorString(eError),
pui32LinMemAddr, ui32Value));
}
return eError;
}
PVRSRV_ERROR RGXStateFlagCtrl(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32Config,
IMG_UINT32 *pui32ConfigState,
IMG_BOOL bSetNotClear)
{
PVRSRV_ERROR eError;
PVRSRV_DEV_POWER_STATE ePowerState;
RGXFWIF_KCCB_CMD sStateFlagCmd = { 0 };
PVRSRV_DEVICE_NODE *psDeviceNode;
RGXFWIF_SYSDATA *psSysData;
IMG_UINT32 ui32kCCBCommandSlot;
IMG_BOOL bWaitForFwUpdate = IMG_FALSE;
if (!psDevInfo)
{
return PVRSRV_ERROR_INVALID_PARAMS;
}
psDeviceNode = psDevInfo->psDeviceNode;
psSysData = psDevInfo->psRGXFWIfFwSysData;
if (NULL == psSysData)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Fw Sys Config is not mapped into CPU space", __func__));
return PVRSRV_ERROR_INVALID_CPU_ADDR;
}
/* apply change and ensure the new data is written to memory
* before requesting the FW to read it
*/
ui32Config = ui32Config & RGXFWIF_INICFG_ALL;
if (bSetNotClear)
{
psSysData->ui32ConfigFlags |= ui32Config;
}
else
{
psSysData->ui32ConfigFlags &= ~ui32Config;
}
/* return current/new value to caller */
if (pui32ConfigState)
{
*pui32ConfigState = psSysData->ui32ConfigFlags;
}
OSMemoryBarrier();
eError = PVRSRVPowerLock(psDeviceNode);
PVR_LOG_RETURN_IF_ERROR(eError, "PVRSRVPowerLock");
/* notify FW to update setting */
eError = PVRSRVGetDevicePowerState(psDeviceNode, &ePowerState);
if ((eError == PVRSRV_OK) && (ePowerState != PVRSRV_DEV_POWER_STATE_OFF))
{
/* Ask the FW to update its cached version of the value */
sStateFlagCmd.eCmdType = RGXFWIF_KCCB_CMD_STATEFLAGS_CTRL;
eError = RGXSendCommandAndGetKCCBSlot(psDevInfo,
&sStateFlagCmd,
PDUMP_FLAGS_CONTINUOUS,
&ui32kCCBCommandSlot);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXSendCommandAndGetKCCBSlot", unlock);
bWaitForFwUpdate = IMG_TRUE;
}
unlock:
PVRSRVPowerUnlock(psDeviceNode);
if (bWaitForFwUpdate)
{
/* Wait for the value to be updated as the FW validates
* the parameters and modifies the ui32ConfigFlags
* accordingly
* (for completeness as registered callbacks should also
* not permit invalid transitions)
*/
eError = RGXWaitForKCCBSlotUpdate(psDevInfo, ui32kCCBCommandSlot, PDUMP_FLAGS_CONTINUOUS);
PVR_LOG_IF_ERROR(eError, "RGXWaitForKCCBSlotUpdate");
}
return eError;
}
static
PVRSRV_ERROR RGXScheduleCleanupCommand(PVRSRV_RGXDEV_INFO *psDevInfo,
RGXFWIF_DM eDM,
RGXFWIF_KCCB_CMD *psKCCBCmd,
RGXFWIF_CLEANUP_TYPE eCleanupType,
IMG_UINT32 ui32PDumpFlags)
{
PVRSRV_ERROR eError;
IMG_UINT32 ui32kCCBCommandSlot;
psKCCBCmd->eCmdType = RGXFWIF_KCCB_CMD_CLEANUP;
psKCCBCmd->uCmdData.sCleanupData.eCleanupType = eCleanupType;
/*
Send the cleanup request to the firmware. If the resource is still busy
the firmware will tell us and we'll drop out with a retry.
*/
eError = RGXScheduleCommandAndGetKCCBSlot(psDevInfo,
eDM,
psKCCBCmd,
0,
ui32PDumpFlags,
&ui32kCCBCommandSlot);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXScheduleCommandAndGetKCCBSlot", fail_command);
/* Wait for command kCCB slot to be updated by FW */
PDUMPCOMMENT("Wait for the firmware to reply to the cleanup command");
eError = RGXWaitForKCCBSlotUpdate(psDevInfo, ui32kCCBCommandSlot,
ui32PDumpFlags);
/*
If the firmware hasn't got back to us in a timely manner
then bail and let the caller retry the command.
*/
if (eError == PVRSRV_ERROR_TIMEOUT)
{
PVR_DPF((PVR_DBG_WARNING,
"%s: RGXWaitForKCCBSlotUpdate timed out. Dump debug information.",
__func__));
eError = PVRSRV_ERROR_RETRY;
#if defined(DEBUG)
PVRSRVDebugRequest(psDevInfo->psDeviceNode,
DEBUG_REQUEST_VERBOSITY_MAX, NULL, NULL);
#endif
goto fail_poll;
}
else if (eError != PVRSRV_OK)
{
goto fail_poll;
}
#if defined(PDUMP)
/*
* The cleanup request to the firmware will tell us if a given resource is busy or not.
* If the RGXFWIF_KCCB_RTN_SLOT_CLEANUP_BUSY flag is set, this means that the resource is
* still in use. In this case we return a PVRSRV_ERROR_RETRY error to the client drivers
* and they will re-issue the cleanup request until it succeed.
*
* Since this retry mechanism doesn't work for pdumps, client drivers should ensure
* that cleanup requests are only submitted if the resource is unused.
* If this is not the case, the following poll will block infinitely, making sure
* the issue doesn't go unnoticed.
*/
PDUMPCOMMENT("Cleanup: If this poll fails, the following resource is still in use (DM=%u, type=%u, address=0x%08x), which is incorrect in pdumps",
eDM,
psKCCBCmd->uCmdData.sCleanupData.eCleanupType,
psKCCBCmd->uCmdData.sCleanupData.uCleanupData.psContext.ui32Addr);
eError = DevmemPDumpDevmemPol32(psDevInfo->psKernelCCBRtnSlotsMemDesc,
ui32kCCBCommandSlot * sizeof(IMG_UINT32),
0,
RGXFWIF_KCCB_RTN_SLOT_CLEANUP_BUSY,
PDUMP_POLL_OPERATOR_EQUAL,
ui32PDumpFlags);
PVR_LOG_IF_ERROR(eError, "DevmemPDumpDevmemPol32");
#endif
/*
If the command has was run but a resource was busy, then the request
will need to be retried.
*/
if (unlikely(psDevInfo->pui32KernelCCBRtnSlots[ui32kCCBCommandSlot] & RGXFWIF_KCCB_RTN_SLOT_CLEANUP_BUSY))
{
if (psDevInfo->pui32KernelCCBRtnSlots[ui32kCCBCommandSlot] & RGXFWIF_KCCB_RTN_SLOT_POLL_FAILURE)
{
PVR_DPF((PVR_DBG_WARNING, "%s: FW poll on a HW operation failed", __func__));
}
eError = PVRSRV_ERROR_RETRY;
goto fail_requestbusy;
}
return PVRSRV_OK;
fail_requestbusy:
fail_poll:
fail_command:
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
/*
RGXRequestCommonContextCleanUp
*/
PVRSRV_ERROR RGXFWRequestCommonContextCleanUp(PVRSRV_DEVICE_NODE *psDeviceNode,
RGX_SERVER_COMMON_CONTEXT *psServerCommonContext,
RGXFWIF_DM eDM,
IMG_UINT32 ui32PDumpFlags)
{
RGXFWIF_KCCB_CMD sRCCleanUpCmd = {0};
PVRSRV_ERROR eError;
PRGXFWIF_FWCOMMONCONTEXT psFWCommonContextFWAddr;
PVRSRV_RGXDEV_INFO *psDevInfo = (PVRSRV_RGXDEV_INFO*)psDeviceNode->pvDevice;
/* Force retry if this context's CCB is currently being dumped
* as part of the stalled CCB debug */
if (psDevInfo->pvEarliestStalledClientCCB == (void*)psServerCommonContext->psClientCCB)
{
PVR_DPF((PVR_DBG_WARNING,
"%s: Forcing retry as psDevInfo->pvEarliestStalledClientCCB = psServerCommonContext->psClientCCB <%p>",
__func__,
(void*)psServerCommonContext->psClientCCB));
return PVRSRV_ERROR_RETRY;
}
psFWCommonContextFWAddr = FWCommonContextGetFWAddress(psServerCommonContext);
#if defined(PDUMP)
PDUMPCOMMENT("Common ctx cleanup Request DM%d [context = 0x%08x]",
eDM, psFWCommonContextFWAddr.ui32Addr);
PDUMPCOMMENT("Wait for CCB to be empty before common ctx cleanup");
RGXCCBPDumpDrainCCB(FWCommonContextGetClientCCB(psServerCommonContext), ui32PDumpFlags);
#endif
/* Setup our command data, the cleanup call will fill in the rest */
sRCCleanUpCmd.uCmdData.sCleanupData.uCleanupData.psContext = psFWCommonContextFWAddr;
/* Request cleanup of the firmware resource */
eError = RGXScheduleCleanupCommand(psDeviceNode->pvDevice,
eDM,
&sRCCleanUpCmd,
RGXFWIF_CLEANUP_FWCOMMONCONTEXT,
ui32PDumpFlags);
if ((eError != PVRSRV_OK) && (eError != PVRSRV_ERROR_RETRY))
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to schedule a memory context cleanup with error (%u)",
__func__, eError));
}
return eError;
}
/*
* RGXFWRequestHWRTDataCleanUp
*/
PVRSRV_ERROR RGXFWRequestHWRTDataCleanUp(PVRSRV_DEVICE_NODE *psDeviceNode,
PRGXFWIF_HWRTDATA psHWRTData)
{
RGXFWIF_KCCB_CMD sHWRTDataCleanUpCmd = {0};
PVRSRV_ERROR eError;
PDUMPCOMMENT("HW RTData cleanup Request [HWRTData = 0x%08x]", psHWRTData.ui32Addr);
sHWRTDataCleanUpCmd.uCmdData.sCleanupData.uCleanupData.psHWRTData = psHWRTData;
eError = RGXScheduleCleanupCommand(psDeviceNode->pvDevice,
RGXFWIF_DM_GP,
&sHWRTDataCleanUpCmd,
RGXFWIF_CLEANUP_HWRTDATA,
PDUMP_FLAGS_NONE);
if ((eError != PVRSRV_OK) && (eError != PVRSRV_ERROR_RETRY))
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to schedule a HWRTData cleanup with error (%u)",
__func__, eError));
}
return eError;
}
/*
RGXFWRequestFreeListCleanUp
*/
PVRSRV_ERROR RGXFWRequestFreeListCleanUp(PVRSRV_RGXDEV_INFO *psDevInfo,
PRGXFWIF_FREELIST psFWFreeList)
{
RGXFWIF_KCCB_CMD sFLCleanUpCmd = {0};
PVRSRV_ERROR eError;
PDUMPCOMMENT("Free list cleanup Request [FreeList = 0x%08x]", psFWFreeList.ui32Addr);
/* Setup our command data, the cleanup call will fill in the rest */
sFLCleanUpCmd.uCmdData.sCleanupData.uCleanupData.psFreelist = psFWFreeList;
/* Request cleanup of the firmware resource */
eError = RGXScheduleCleanupCommand(psDevInfo,
RGXFWIF_DM_GP,
&sFLCleanUpCmd,
RGXFWIF_CLEANUP_FREELIST,
PDUMP_FLAGS_NONE);
if ((eError != PVRSRV_OK) && (eError != PVRSRV_ERROR_RETRY))
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to schedule a memory context cleanup with error (%u)",
__func__, eError));
}
return eError;
}
/*
RGXFWRequestZSBufferCleanUp
*/
PVRSRV_ERROR RGXFWRequestZSBufferCleanUp(PVRSRV_RGXDEV_INFO *psDevInfo,
PRGXFWIF_ZSBUFFER psFWZSBuffer)
{
RGXFWIF_KCCB_CMD sZSBufferCleanUpCmd = {0};
PVRSRV_ERROR eError;
PDUMPCOMMENT("ZS Buffer cleanup Request [ZS Buffer = 0x%08x]", psFWZSBuffer.ui32Addr);
/* Setup our command data, the cleanup call will fill in the rest */
sZSBufferCleanUpCmd.uCmdData.sCleanupData.uCleanupData.psZSBuffer = psFWZSBuffer;
/* Request cleanup of the firmware resource */
eError = RGXScheduleCleanupCommand(psDevInfo,
RGXFWIF_DM_3D,
&sZSBufferCleanUpCmd,
RGXFWIF_CLEANUP_ZSBUFFER,
PDUMP_FLAGS_NONE);
if ((eError != PVRSRV_OK) && (eError != PVRSRV_ERROR_RETRY))
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to schedule a memory context cleanup with error (%u)",
__func__, eError));
}
return eError;
}
PVRSRV_ERROR RGXFWSetHCSDeadline(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32HCSDeadlineMs)
{
PVRSRV_ERROR eError;
RGXFWIF_KCCB_CMD sSetHCSDeadline = { 0 };
sSetHCSDeadline.eCmdType = RGXFWIF_KCCB_CMD_HCS_SET_DEADLINE;
sSetHCSDeadline.uCmdData.sHCSCtrl.ui32HCSDeadlineMS = ui32HCSDeadlineMs;
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXScheduleCommand(psDevInfo,
RGXFWIF_DM_GP,
&sSetHCSDeadline,
0,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_ERROR_RETRY)
{
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
return eError;
}
PVRSRV_ERROR RGXFWHealthCheckCmd(PVRSRV_RGXDEV_INFO *psDevInfo)
{
RGXFWIF_KCCB_CMD sCmpKCCBCmd = { 0 };
sCmpKCCBCmd.eCmdType = RGXFWIF_KCCB_CMD_HEALTH_CHECK;
return RGXScheduleCommand(psDevInfo,
RGXFWIF_DM_GP,
&sCmpKCCBCmd,
0,
PDUMP_FLAGS_CONTINUOUS);
}
PVRSRV_ERROR RGXFWSetOSIsolationThreshold(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32IsolationPriorityThreshold)
{
PVRSRV_ERROR eError;
RGXFWIF_KCCB_CMD sOSidIsoConfCmd = { 0 };
sOSidIsoConfCmd.eCmdType = RGXFWIF_KCCB_CMD_OS_ISOLATION_GROUP_CHANGE;
sOSidIsoConfCmd.uCmdData.sCmdOSidIsolationData.ui32IsolationPriorityThreshold = ui32IsolationPriorityThreshold;
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXScheduleCommand(psDevInfo,
RGXFWIF_DM_GP,
&sOSidIsoConfCmd,
0,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_ERROR_RETRY)
{
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
return eError;
}
PVRSRV_ERROR RGXFWSetFwOsState(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32OSid,
RGXFWIF_OS_STATE_CHANGE eOSOnlineState)
{
PVRSRV_ERROR eError = PVRSRV_OK;
RGXFWIF_KCCB_CMD sOSOnlineStateCmd = { 0 };
RGXFWIF_SYSDATA *psFwSysData = psDevInfo->psRGXFWIfFwSysData;
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_OK);
sOSOnlineStateCmd.eCmdType = RGXFWIF_KCCB_CMD_OS_ONLINE_STATE_CONFIGURE;
sOSOnlineStateCmd.uCmdData.sCmdOSOnlineStateData.ui32OSid = ui32OSid;
sOSOnlineStateCmd.uCmdData.sCmdOSOnlineStateData.eNewOSState = eOSOnlineState;
if (eOSOnlineState == RGXFWIF_OS_ONLINE)
{
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXScheduleCommand(psDevInfo,
RGXFWIF_DM_GP,
&sOSOnlineStateCmd,
0,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_ERROR_RETRY) break;
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
}
else if (psFwSysData)
{
IMG_UINT32 ui32kCCBCommandSlot;
volatile RGXFWIF_OS_RUNTIME_FLAGS *psFwRunFlags;
psFwRunFlags = (volatile RGXFWIF_OS_RUNTIME_FLAGS*) &psFwSysData->asOsRuntimeFlagsMirror[ui32OSid];
/* Attempt several times until the FW manages to offload the OS */
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
/* Send request */
eError = RGXScheduleCommandAndGetKCCBSlot(psDevInfo,
RGXFWIF_DM_GP,
&sOSOnlineStateCmd,
0,
PDUMP_FLAGS_CONTINUOUS,
&ui32kCCBCommandSlot);
if (unlikely(eError == PVRSRV_ERROR_RETRY)) continue;
PVR_LOG_GOTO_IF_ERROR(eError, "RGXScheduleCommand", return_);
/* Wait for FW to process the cmd */
eError = RGXWaitForKCCBSlotUpdate(psDevInfo, ui32kCCBCommandSlot, PDUMP_FLAGS_CONTINUOUS);
PVR_LOG_GOTO_IF_ERROR(eError, "RGXWaitForKCCBSlotUpdate", return_);
/* read the OS state */
OSMemoryBarrier();
/* check if FW finished offloading the OSID and is stopped */
if (psFwRunFlags->bfOsState == RGXFW_CONNECTION_FW_OFFLINE)
{
eError = PVRSRV_OK;
break;
}
else
{
eError = PVRSRV_ERROR_TIMEOUT;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
}
else
{
eError = PVRSRV_ERROR_NOT_INITIALISED;
}
return_ :
return eError;
}
PVRSRV_ERROR RGXFWChangeOSidPriority(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32OSid,
IMG_UINT32 ui32Priority)
{
PVRSRV_ERROR eError;
RGXFWIF_KCCB_CMD sOSidPriorityCmd = { 0 };
sOSidPriorityCmd.eCmdType = RGXFWIF_KCCB_CMD_OSID_PRIORITY_CHANGE;
sOSidPriorityCmd.uCmdData.sCmdOSidPriorityData.ui32OSidNum = ui32OSid;
sOSidPriorityCmd.uCmdData.sCmdOSidPriorityData.ui32Priority = ui32Priority;
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXScheduleCommand(psDevInfo,
RGXFWIF_DM_GP,
&sOSidPriorityCmd,
0,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_ERROR_RETRY)
{
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
return eError;
}
PVRSRV_ERROR ContextSetPriority(RGX_SERVER_COMMON_CONTEXT *psContext,
CONNECTION_DATA *psConnection,
PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32Priority,
RGXFWIF_DM eDM)
{
IMG_UINT32 ui32CmdSize;
IMG_UINT8 *pui8CmdPtr;
RGXFWIF_KCCB_CMD sPriorityCmd = { 0 };
RGXFWIF_CCB_CMD_HEADER *psCmdHeader;
RGXFWIF_CMD_PRIORITY *psCmd;
PVRSRV_ERROR eError;
RGX_CLIENT_CCB *psClientCCB = FWCommonContextGetClientCCB(psContext);
/*
Get space for command
*/
ui32CmdSize = RGX_CCB_FWALLOC_ALIGN(sizeof(RGXFWIF_CCB_CMD_HEADER) + sizeof(RGXFWIF_CMD_PRIORITY));
eError = RGXAcquireCCB(psClientCCB,
ui32CmdSize,
(void **) &pui8CmdPtr,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_OK)
{
if (eError != PVRSRV_ERROR_RETRY)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to acquire space for client CCB", __func__));
}
goto fail_ccbacquire;
}
/*
Write the command header and command
*/
psCmdHeader = (RGXFWIF_CCB_CMD_HEADER *) pui8CmdPtr;
psCmdHeader->eCmdType = RGXFWIF_CCB_CMD_TYPE_PRIORITY;
psCmdHeader->ui32CmdSize = RGX_CCB_FWALLOC_ALIGN(sizeof(RGXFWIF_CMD_PRIORITY));
pui8CmdPtr += sizeof(*psCmdHeader);
psCmd = (RGXFWIF_CMD_PRIORITY *) pui8CmdPtr;
psCmd->ui32Priority = ui32Priority;
pui8CmdPtr += sizeof(*psCmd);
/*
We should reserved space in the kernel CCB here and fill in the command
directly.
This is so if there isn't space in the kernel CCB we can return with
retry back to services client before we take any operations
*/
/*
Submit the command
*/
RGXReleaseCCB(psClientCCB,
ui32CmdSize,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: Failed to release space in client CCB", __func__));
return eError;
}
/* Construct the priority command. */
sPriorityCmd.eCmdType = RGXFWIF_KCCB_CMD_KICK;
sPriorityCmd.uCmdData.sCmdKickData.psContext = FWCommonContextGetFWAddress(psContext);
sPriorityCmd.uCmdData.sCmdKickData.ui32CWoffUpdate = RGXGetHostWriteOffsetCCB(psClientCCB);
sPriorityCmd.uCmdData.sCmdKickData.ui32CWrapMaskUpdate = RGXGetWrapMaskCCB(psClientCCB);
sPriorityCmd.uCmdData.sCmdKickData.ui32NumCleanupCtl = 0;
sPriorityCmd.uCmdData.sCmdKickData.ui32WorkEstCmdHeaderOffset = 0;
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXScheduleCommand(psDevInfo,
eDM,
&sPriorityCmd,
0,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_ERROR_RETRY)
{
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Failed to submit set priority command with error (%u)",
__func__,
eError));
}
return PVRSRV_OK;
fail_ccbacquire:
PVR_ASSERT(eError != PVRSRV_OK);
return eError;
}
PVRSRV_ERROR RGXFWConfigPHR(PVRSRV_RGXDEV_INFO *psDevInfo,
IMG_UINT32 ui32PHRMode)
{
PVRSRV_ERROR eError;
RGXFWIF_KCCB_CMD sCfgPHRCmd = { 0 };
sCfgPHRCmd.eCmdType = RGXFWIF_KCCB_CMD_PHR_CFG;
sCfgPHRCmd.uCmdData.sPeriodicHwResetCfg.ui32PHRMode = ui32PHRMode;
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
eError = RGXScheduleCommand(psDevInfo,
RGXFWIF_DM_GP,
&sCfgPHRCmd,
0,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_ERROR_RETRY)
{
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
return eError;
}
/*
RGXReadMETAAddr
*/
PVRSRV_ERROR RGXReadMETAAddr(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32METAAddr, IMG_UINT32 *pui32Value)
{
IMG_UINT8 __iomem *pui8RegBase = psDevInfo->pvRegsBaseKM;
IMG_UINT32 ui32Value;
/* Wait for Slave Port to be Ready */
if (PVRSRVPollForValueKM(psDevInfo->psDeviceNode,
(IMG_UINT32 __iomem *) (pui8RegBase + RGX_CR_META_SP_MSLVCTRL1),
RGX_CR_META_SP_MSLVCTRL1_READY_EN|RGX_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
RGX_CR_META_SP_MSLVCTRL1_READY_EN|RGX_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
POLL_FLAG_LOG_ERROR) != PVRSRV_OK)
{
return PVRSRV_ERROR_TIMEOUT;
}
/* Issue the Read */
OSWriteHWReg32(
psDevInfo->pvRegsBaseKM,
RGX_CR_META_SP_MSLVCTRL0,
ui32METAAddr | RGX_CR_META_SP_MSLVCTRL0_RD_EN);
(void) OSReadHWReg32(psDevInfo->pvRegsBaseKM, RGX_CR_META_SP_MSLVCTRL0);
/* Wait for Slave Port to be Ready: read complete */
if (PVRSRVPollForValueKM(psDevInfo->psDeviceNode,
(IMG_UINT32 __iomem *) (pui8RegBase + RGX_CR_META_SP_MSLVCTRL1),
RGX_CR_META_SP_MSLVCTRL1_READY_EN|RGX_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
RGX_CR_META_SP_MSLVCTRL1_READY_EN|RGX_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
POLL_FLAG_LOG_ERROR) != PVRSRV_OK)
{
return PVRSRV_ERROR_TIMEOUT;
}
/* Read the value */
ui32Value = OSReadHWReg32(psDevInfo->pvRegsBaseKM, RGX_CR_META_SP_MSLVDATAX);
*pui32Value = ui32Value;
return PVRSRV_OK;
}
/*
RGXWriteMETAAddr
*/
PVRSRV_ERROR RGXWriteMETAAddr(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32METAAddr, IMG_UINT32 ui32Value)
{
IMG_UINT8 __iomem *pui8RegBase = psDevInfo->pvRegsBaseKM;
/* Wait for Slave Port to be Ready */
if (PVRSRVPollForValueKM(psDevInfo->psDeviceNode,
(IMG_UINT32 __iomem *)(pui8RegBase + RGX_CR_META_SP_MSLVCTRL1),
RGX_CR_META_SP_MSLVCTRL1_READY_EN|RGX_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
RGX_CR_META_SP_MSLVCTRL1_READY_EN|RGX_CR_META_SP_MSLVCTRL1_GBLPORT_IDLE_EN,
POLL_FLAG_LOG_ERROR) != PVRSRV_OK)
{
return PVRSRV_ERROR_TIMEOUT;
}
/* Issue the Write */
OSWriteHWReg32(psDevInfo->pvRegsBaseKM, RGX_CR_META_SP_MSLVCTRL0, ui32METAAddr);
OSWriteHWReg32(psDevInfo->pvRegsBaseKM, RGX_CR_META_SP_MSLVDATAT, ui32Value);
return PVRSRV_OK;
}
void RGXCheckForStalledClientContexts(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_BOOL bIgnorePrevious)
{
/* Attempt to detect and deal with any stalled client contexts.
* bIgnorePrevious may be set by the caller if they know a context to be
* stalled, as otherwise this function will only identify stalled
* contexts which have not been previously reported.
*/
IMG_UINT32 ui32StalledClientMask = 0;
if (!(OSTryLockAcquire(psDevInfo->hCCBStallCheckLock)))
{
PVR_LOG(("RGXCheckForStalledClientContexts: Failed to acquire hCCBStallCheckLock, returning..."));
return;
}
ui32StalledClientMask |= CheckForStalledClientTransferCtxt(psDevInfo);
ui32StalledClientMask |= CheckForStalledClientRenderCtxt(psDevInfo);
ui32StalledClientMask |= CheckForStalledClientKickSyncCtxt(psDevInfo);
if (psDevInfo->sDevFeatureCfg.ui64Features & RGX_FEATURE_COMPUTE_BIT_MASK)
{
ui32StalledClientMask |= CheckForStalledClientComputeCtxt(psDevInfo);
}
/* If at least one DM stalled bit is different than before */
if (bIgnorePrevious || (psDevInfo->ui32StalledClientMask != ui32StalledClientMask))//(psDevInfo->ui32StalledClientMask ^ ui32StalledClientMask))
{
if (ui32StalledClientMask > 0)
{
static __maybe_unused const char *pszStalledAction =
#if defined(PVRSRV_STALLED_CCB_ACTION)
"force";
#else
"warn";
#endif
/* Print all the stalled DMs */
PVR_LOG(("Possible stalled client RGX contexts detected: %s%s%s%s%s%s%s%s%s",
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_GP),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_TDM_2D),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_TA),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_3D),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_CDM),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_RTU),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_SHG),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_TQ2D),
RGX_STRINGIFY_KICK_TYPE_DM_IF_SET(ui32StalledClientMask, RGX_KICK_TYPE_DM_TQ3D)));
PVR_LOG(("Trying to identify stalled context...(%s) [%d]",
pszStalledAction, bIgnorePrevious));
DumpStalledContextInfo(psDevInfo);
}
else
{
if (psDevInfo->ui32StalledClientMask> 0)
{
/* Indicate there are no stalled DMs */
PVR_LOG(("No further stalled client contexts exist"));
}
}
psDevInfo->ui32StalledClientMask = ui32StalledClientMask;
psDevInfo->pvEarliestStalledClientCCB = NULL;
}
OSLockRelease(psDevInfo->hCCBStallCheckLock);
}
/*
RGXUpdateHealthStatus
*/
PVRSRV_ERROR RGXUpdateHealthStatus(PVRSRV_DEVICE_NODE* psDevNode,
IMG_BOOL bCheckAfterTimePassed)
{
PVRSRV_DATA* psPVRSRVData = PVRSRVGetPVRSRVData();
PVRSRV_DEVICE_HEALTH_STATUS eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_OK;
PVRSRV_DEVICE_HEALTH_REASON eNewReason = PVRSRV_DEVICE_HEALTH_REASON_NONE;
PVRSRV_RGXDEV_INFO* psDevInfo;
RGXFWIF_TRACEBUF* psRGXFWIfTraceBufCtl;
RGXFWIF_SYSDATA* psFwSysData;
RGXFWIF_CCB_CTL *psKCCBCtl;
IMG_UINT32 ui32ThreadCount;
IMG_BOOL bKCCBCmdsWaiting;
PVR_ASSERT(psDevNode != NULL);
psDevInfo = psDevNode->pvDevice;
/* If the firmware is not yet initialised or has already deinitialised, stop here */
if (psDevInfo == NULL || !psDevInfo->bFirmwareInitialised || psDevInfo->pvRegsBaseKM == NULL ||
psDevInfo->psDeviceNode == NULL || psDevInfo->psDeviceNode->eDevState == PVRSRV_DEVICE_STATE_DEINIT)
{
return PVRSRV_OK;
}
psRGXFWIfTraceBufCtl = psDevInfo->psRGXFWIfTraceBufCtl;
psFwSysData = psDevInfo->psRGXFWIfFwSysData;
#if defined(SUPPORT_AUTOVZ)
if (KM_FW_CONNECTION_IS(ACTIVE, psDevInfo) && KM_OS_CONNECTION_IS(ACTIVE, psDevInfo))
{
/* read and write back the alive token value to confirm to the
* virtualisation watchdog that this connection is healthy */
KM_SET_OS_ALIVE_TOKEN(KM_GET_FW_ALIVE_TOKEN(psDevInfo), psDevInfo);
}
#endif
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_OK);
/* If this is a quick update, then include the last current value... */
if (!bCheckAfterTimePassed)
{
eNewStatus = OSAtomicRead(&psDevNode->eHealthStatus);
eNewReason = OSAtomicRead(&psDevNode->eHealthReason);
}
/* Decrement the SLR holdoff counter (if non-zero) */
if (psDevInfo->ui32SLRHoldoffCounter > 0)
{
psDevInfo->ui32SLRHoldoffCounter--;
}
/* If Rogue is not powered on, just skip ahead and check for stalled client CCBs */
if (PVRSRVIsDevicePowered(psDevNode))
{
if (psRGXFWIfTraceBufCtl != NULL)
{
/*
Firmware thread checks...
*/
for (ui32ThreadCount = 0; ui32ThreadCount < RGXFW_THREAD_NUM; ui32ThreadCount++)
{
IMG_CHAR* pszTraceAssertInfo = psRGXFWIfTraceBufCtl->sTraceBuf[ui32ThreadCount].sAssertBuf.szInfo;
/*
Check if the FW has hit an assert...
*/
if (*pszTraceAssertInfo != '\0')
{
PVR_DPF((PVR_DBG_WARNING, "%s: Firmware thread %d has asserted: %s (%s:%d)",
__func__, ui32ThreadCount, pszTraceAssertInfo,
psRGXFWIfTraceBufCtl->sTraceBuf[ui32ThreadCount].sAssertBuf.szPath,
psRGXFWIfTraceBufCtl->sTraceBuf[ui32ThreadCount].sAssertBuf.ui32LineNum));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_DEAD;
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_ASSERTED;
goto _RGXUpdateHealthStatus_Exit;
}
/*
Check the threads to see if they are in the same poll locations as last time...
*/
if (bCheckAfterTimePassed)
{
if (psFwSysData->aui32CrPollAddr[ui32ThreadCount] != 0 &&
psFwSysData->aui32CrPollCount[ui32ThreadCount] == psDevInfo->aui32CrLastPollCount[ui32ThreadCount])
{
PVR_DPF((PVR_DBG_WARNING, "%s: Firmware stuck on CR poll: T%u polling %s (reg:0x%08X mask:0x%08X)",
__func__, ui32ThreadCount,
((psFwSysData->aui32CrPollAddr[ui32ThreadCount] & RGXFW_POLL_TYPE_SET)?("set"):("unset")),
psFwSysData->aui32CrPollAddr[ui32ThreadCount] & ~RGXFW_POLL_TYPE_SET,
psFwSysData->aui32CrPollMask[ui32ThreadCount]));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_NOT_RESPONDING;
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_POLL_FAILING;
goto _RGXUpdateHealthStatus_Exit;
}
psDevInfo->aui32CrLastPollCount[ui32ThreadCount] = psFwSysData->aui32CrPollCount[ui32ThreadCount];
}
}
/*
Check if the FW has faulted...
*/
if (psFwSysData->ui32HWRStateFlags & RGXFWIF_HWR_FW_FAULT)
{
PVR_DPF((PVR_DBG_WARNING,
"%s: Firmware has faulted and needs to restart",
__func__));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_FAULT;
if (psFwSysData->ui32HWRStateFlags & RGXFWIF_HWR_RESTART_REQUESTED)
{
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_RESTARTING;
}
else
{
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_IDLING;
}
goto _RGXUpdateHealthStatus_Exit;
}
}
/*
Event Object Timeouts check...
*/
if (!bCheckAfterTimePassed)
{
if (psDevInfo->ui32GEOTimeoutsLastTime > 1 && psPVRSRVData->ui32GEOConsecutiveTimeouts > psDevInfo->ui32GEOTimeoutsLastTime)
{
PVR_DPF((PVR_DBG_WARNING, "%s: Global Event Object Timeouts have risen (from %d to %d)",
__func__,
psDevInfo->ui32GEOTimeoutsLastTime, psPVRSRVData->ui32GEOConsecutiveTimeouts));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_NOT_RESPONDING;
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_TIMEOUTS;
}
psDevInfo->ui32GEOTimeoutsLastTime = psPVRSRVData->ui32GEOConsecutiveTimeouts;
}
/*
Check the Kernel CCB pointer is valid. If any commands were waiting last time, then check
that some have executed since then.
*/
bKCCBCmdsWaiting = IMG_FALSE;
psKCCBCtl = psDevInfo->psKernelCCBCtl;
if (psKCCBCtl != NULL)
{
if (psKCCBCtl->ui32ReadOffset > psKCCBCtl->ui32WrapMask ||
psKCCBCtl->ui32WriteOffset > psKCCBCtl->ui32WrapMask)
{
PVR_DPF((PVR_DBG_WARNING, "%s: KCCB has invalid offset (ROFF=%d WOFF=%d)",
__func__, psKCCBCtl->ui32ReadOffset, psKCCBCtl->ui32WriteOffset));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_DEAD;
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_QUEUE_CORRUPT;
}
if (psKCCBCtl->ui32ReadOffset != psKCCBCtl->ui32WriteOffset)
{
bKCCBCmdsWaiting = IMG_TRUE;
}
}
if (bCheckAfterTimePassed && psDevInfo->psRGXFWIfFwOsData != NULL)
{
IMG_UINT32 ui32KCCBCmdsExecuted = psDevInfo->psRGXFWIfFwOsData->ui32KCCBCmdsExecuted;
if (psDevInfo->ui32KCCBCmdsExecutedLastTime == ui32KCCBCmdsExecuted)
{
/*
If something was waiting last time then the Firmware has stopped processing commands.
*/
if (psDevInfo->bKCCBCmdsWaitingLastTime)
{
PVR_DPF((PVR_DBG_WARNING, "%s: No KCCB commands executed since check!",
__func__));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_NOT_RESPONDING;
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_QUEUE_STALLED;
}
/*
If no commands are currently pending and nothing happened since the last poll, then
schedule a dummy command to ping the firmware so we know it is alive and processing.
*/
if (!bKCCBCmdsWaiting)
{
/* Protect the PDumpLoadMem. RGXScheduleCommand() cannot take the
* PMR lock itself, because some bridge functions will take the PMR lock
* before calling RGXScheduleCommand
*/
PVRSRV_ERROR eError = RGXFWHealthCheckCmd(psDevNode->pvDevice);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING, "%s: Cannot schedule Health Check command! (0x%x)",
__func__, eError));
}
else
{
bKCCBCmdsWaiting = IMG_TRUE;
}
}
}
psDevInfo->bKCCBCmdsWaitingLastTime = bKCCBCmdsWaiting;
psDevInfo->ui32KCCBCmdsExecutedLastTime = ui32KCCBCmdsExecuted;
}
}
/*
Interrupt counts check...
*/
if (bCheckAfterTimePassed && psDevInfo->psRGXFWIfFwSysData != NULL)
{
IMG_UINT32 ui32LISRCount = 0;
IMG_UINT32 ui32FWCount = 0;
IMG_UINT32 ui32MissingInts = 0;
/* Add up the total number of interrupts issued, sampled/received and missed... */
#if defined(RGX_FW_IRQ_OS_COUNTERS)
/* Only the Host OS has a sample count, so only one counter to check. */
ui32LISRCount += psDevInfo->aui32SampleIRQCount[RGXFW_HOST_OS];
ui32FWCount += OSReadHWReg32(psDevInfo->pvRegsBaseKM, gaui32FwOsIrqCntRegAddr[RGXFW_HOST_OS]);
#else
IMG_UINT32 ui32Index;
for (ui32Index = 0; ui32Index < RGXFW_THREAD_NUM; ui32Index++)
{
ui32LISRCount += psDevInfo->aui32SampleIRQCount[ui32Index];
ui32FWCount += psDevInfo->psRGXFWIfFwSysData->aui32InterruptCount[ui32Index];
}
#endif /* RGX_FW_IRQ_OS_COUNTERS */
if (ui32LISRCount < ui32FWCount)
{
ui32MissingInts = (ui32FWCount-ui32LISRCount);
}
if (ui32LISRCount == psDevInfo->ui32InterruptCountLastTime &&
ui32MissingInts >= psDevInfo->ui32MissingInterruptsLastTime &&
psDevInfo->ui32MissingInterruptsLastTime > 1)
{
PVR_DPF((PVR_DBG_ERROR, "%s: LISR has not received the last %d interrupts",
__func__, ui32MissingInts));
eNewStatus = PVRSRV_DEVICE_HEALTH_STATUS_NOT_RESPONDING;
eNewReason = PVRSRV_DEVICE_HEALTH_REASON_MISSING_INTERRUPTS;
/* Schedule the MISRs to help mitigate the problems of missing interrupts. */
OSScheduleMISR(psDevInfo->pvMISRData);
if (psDevInfo->pvAPMISRData != NULL)
{
OSScheduleMISR(psDevInfo->pvAPMISRData);
}
}
psDevInfo->ui32InterruptCountLastTime = ui32LISRCount;
psDevInfo->ui32MissingInterruptsLastTime = ui32MissingInts;
}
/*
Stalled CCB check...
*/
if (bCheckAfterTimePassed && (PVRSRV_DEVICE_HEALTH_STATUS_OK==eNewStatus))
{
RGXCheckForStalledClientContexts(psDevInfo, IMG_FALSE);
}
/*
Finished, save the new status...
*/
_RGXUpdateHealthStatus_Exit:
OSAtomicWrite(&psDevNode->eHealthStatus, eNewStatus);
OSAtomicWrite(&psDevNode->eHealthReason, eNewReason);
RGXSRV_HWPERF_DEVICE_INFO(psDevInfo, RGX_HWPERF_DEV_INFO_EV_HEALTH, eNewStatus, eNewReason);
/*
* Attempt to service the HWPerf buffer to regularly transport idle/periodic
* packets to host buffer.
*/
if (psDevNode->pfnServiceHWPerf != NULL)
{
PVRSRV_ERROR eError = psDevNode->pfnServiceHWPerf(psDevNode);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_WARNING, "%s: "
"Error occurred when servicing HWPerf buffer (%d)",
__func__, eError));
}
}
/* Attempt to refresh timer correlation data */
RGXTimeCorrRestartPeriodic(psDevNode);
return PVRSRV_OK;
} /* RGXUpdateHealthStatus */
PVRSRV_ERROR CheckStalledClientCommonContext(RGX_SERVER_COMMON_CONTEXT *psCurrentServerCommonContext, RGX_KICK_TYPE_DM eKickTypeDM)
{
if (psCurrentServerCommonContext == NULL)
{
/* the context has already been freed so there is nothing to do here */
return PVRSRV_OK;
}
return CheckForStalledCCB(psCurrentServerCommonContext->psDevInfo->psDeviceNode,
psCurrentServerCommonContext->psClientCCB,
eKickTypeDM);
}
void DumpFWCommonContextInfo(RGX_SERVER_COMMON_CONTEXT *psCurrentServerCommonContext,
DUMPDEBUG_PRINTF_FUNC *pfnDumpDebugPrintf,
void *pvDumpDebugFile,
IMG_UINT32 ui32VerbLevel)
{
if (psCurrentServerCommonContext == NULL)
{
/* the context has already been freed so there is nothing to do here */
return;
}
if (DD_VERB_LVL_ENABLED(ui32VerbLevel, DEBUG_REQUEST_VERBOSITY_HIGH))
{
/* If high verbosity requested, dump whole CCB */
DumpCCB(psCurrentServerCommonContext->psDevInfo,
psCurrentServerCommonContext->sFWCommonContextFWAddr,
psCurrentServerCommonContext->psClientCCB,
pfnDumpDebugPrintf,
pvDumpDebugFile);
}
else
{
/* Otherwise, only dump first stalled command in the CCB */
DumpStalledCCBCommand(psCurrentServerCommonContext->sFWCommonContextFWAddr,
psCurrentServerCommonContext->psClientCCB,
pfnDumpDebugPrintf,
pvDumpDebugFile);
}
}
PVRSRV_ERROR AttachKickResourcesCleanupCtls(PRGXFWIF_CLEANUP_CTL *apsCleanupCtl,
IMG_UINT32 *pui32NumCleanupCtl,
RGXFWIF_DM eDM,
IMG_BOOL bKick,
RGX_KM_HW_RT_DATASET *psKMHWRTDataSet,
RGX_ZSBUFFER_DATA *psZSBuffer,
RGX_ZSBUFFER_DATA *psMSAAScratchBuffer)
{
PVRSRV_ERROR eError;
PRGXFWIF_CLEANUP_CTL *psCleanupCtlWrite = apsCleanupCtl;
PVR_ASSERT((eDM == RGXFWIF_DM_GEOM) || (eDM == RGXFWIF_DM_3D));
PVR_RETURN_IF_INVALID_PARAM((eDM == RGXFWIF_DM_GEOM) || (eDM == RGXFWIF_DM_3D));
if (bKick)
{
if (psKMHWRTDataSet)
{
PRGXFWIF_CLEANUP_CTL psCleanupCtl;
eError = RGXSetFirmwareAddress(&psCleanupCtl, psKMHWRTDataSet->psHWRTDataFwMemDesc,
offsetof(RGXFWIF_HWRTDATA, sCleanupState),
RFW_FWADDR_NOREF_FLAG);
PVR_RETURN_IF_ERROR(eError);
*(psCleanupCtlWrite++) = psCleanupCtl;
}
if (eDM == RGXFWIF_DM_3D)
{
RGXFWIF_PRBUFFER_TYPE eBufferType;
RGX_ZSBUFFER_DATA *psBuffer = NULL;
for (eBufferType = RGXFWIF_PRBUFFER_START; eBufferType < RGXFWIF_PRBUFFER_MAXSUPPORTED; eBufferType++)
{
switch (eBufferType)
{
case RGXFWIF_PRBUFFER_ZSBUFFER:
psBuffer = psZSBuffer;
break;
case RGXFWIF_PRBUFFER_MSAABUFFER:
psBuffer = psMSAAScratchBuffer;
break;
case RGXFWIF_PRBUFFER_MAXSUPPORTED:
psBuffer = NULL;
break;
}
if (psBuffer)
{
(psCleanupCtlWrite++)->ui32Addr = psBuffer->sZSBufferFWDevVAddr.ui32Addr +
offsetof(RGXFWIF_PRBUFFER, sCleanupState);
psBuffer = NULL;
}
}
}
}
*pui32NumCleanupCtl = psCleanupCtlWrite - apsCleanupCtl;
PVR_ASSERT(*pui32NumCleanupCtl <= RGXFWIF_KCCB_CMD_KICK_DATA_MAX_NUM_CLEANUP_CTLS);
return PVRSRV_OK;
}
PVRSRV_ERROR RGXResetHWRLogs(PVRSRV_DEVICE_NODE *psDevNode)
{
PVRSRV_RGXDEV_INFO *psDevInfo;
RGXFWIF_HWRINFOBUF *psHWRInfoBuf;
IMG_UINT32 i;
if (psDevNode->pvDevice == NULL)
{
return PVRSRV_ERROR_INVALID_DEVINFO;
}
psDevInfo = psDevNode->pvDevice;
psHWRInfoBuf = psDevInfo->psRGXFWIfHWRInfoBufCtl;
for (i = 0 ; i < RGXFWIF_DM_MAX ; i++)
{
/* Reset the HWR numbers */
psHWRInfoBuf->aui32HwrDmLockedUpCount[i] = 0;
psHWRInfoBuf->aui32HwrDmFalseDetectCount[i] = 0;
psHWRInfoBuf->aui32HwrDmRecoveredCount[i] = 0;
psHWRInfoBuf->aui32HwrDmOverranCount[i] = 0;
}
for (i = 0 ; i < RGXFWIF_HWINFO_MAX ; i++)
{
psHWRInfoBuf->sHWRInfo[i].ui32HWRNumber = 0;
}
psHWRInfoBuf->ui32WriteIndex = 0;
psHWRInfoBuf->ui32DDReqCount = 0;
return PVRSRV_OK;
}
PVRSRV_ERROR RGXGetPhyAddr(PMR *psPMR,
IMG_DEV_PHYADDR *psPhyAddr,
IMG_UINT32 ui32LogicalOffset,
IMG_UINT32 ui32Log2PageSize,
IMG_UINT32 ui32NumOfPages,
IMG_BOOL *bValid)
{
PVRSRV_ERROR eError;
eError = PMRLockSysPhysAddresses(psPMR);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: PMRLockSysPhysAddresses failed (%u)",
__func__,
eError));
return eError;
}
eError = PMR_DevPhysAddr(psPMR,
ui32Log2PageSize,
ui32NumOfPages,
ui32LogicalOffset,
psPhyAddr,
bValid);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: PMR_DevPhysAddr failed (%u)",
__func__,
eError));
return eError;
}
eError = PMRUnlockSysPhysAddresses(psPMR);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: PMRUnLockSysPhysAddresses failed (%u)",
__func__,
eError));
return eError;
}
return eError;
}
#if defined(PDUMP)
PVRSRV_ERROR RGXPdumpDrainKCCB(PVRSRV_RGXDEV_INFO *psDevInfo, IMG_UINT32 ui32WriteOffset)
{
RGXFWIF_CCB_CTL *psKCCBCtl = psDevInfo->psKernelCCBCtl;
PVRSRV_ERROR eError = PVRSRV_OK;
if (psDevInfo->bDumpedKCCBCtlAlready)
{
/* exiting capture range or pdump block */
psDevInfo->bDumpedKCCBCtlAlready = IMG_FALSE;
/* make sure previous cmd is drained in pdump in case we will 'jump' over some future cmds */
PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS | PDUMP_FLAGS_POWER,
"kCCB(%p): Draining rgxfw_roff (0x%x) == woff (0x%x)",
psKCCBCtl,
ui32WriteOffset,
ui32WriteOffset);
eError = DevmemPDumpDevmemPol32(psDevInfo->psKernelCCBCtlMemDesc,
offsetof(RGXFWIF_CCB_CTL, ui32ReadOffset),
ui32WriteOffset,
0xffffffff,
PDUMP_POLL_OPERATOR_EQUAL,
PDUMP_FLAGS_CONTINUOUS | PDUMP_FLAGS_POWER);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR, "%s: problem pdumping POL for kCCBCtl (%d)", __func__, eError));
}
}
return eError;
}
#endif
/*!
*******************************************************************************
@Function RGXClientConnectCompatCheck_ClientAgainstFW
@Description
Check compatibility of client and firmware (build options)
at the connection time.
@Input psDeviceNode - device node
@Input ui32ClientBuildOptions - build options for the client
@Return PVRSRV_ERROR - depending on mismatch found
******************************************************************************/
PVRSRV_ERROR IMG_CALLCONV RGXClientConnectCompatCheck_ClientAgainstFW(PVRSRV_DEVICE_NODE *psDeviceNode, IMG_UINT32 ui32ClientBuildOptions)
{
#if !defined(NO_HARDWARE) || defined(PDUMP)
#if !defined(NO_HARDWARE)
IMG_UINT32 ui32BuildOptionsMismatch;
IMG_UINT32 ui32BuildOptionsFW;
#endif
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
#endif
PVRSRV_VZ_RET_IF_MODE(GUEST, PVRSRV_OK);
#if !defined(NO_HARDWARE)
if (psDevInfo == NULL || psDevInfo->psRGXFWIfOsInitMemDesc == NULL)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: Cannot acquire kernel fw compatibility check info, RGXFWIF_OSINIT structure not allocated.",
__func__));
return PVRSRV_ERROR_NOT_INITIALISED;
}
LOOP_UNTIL_TIMEOUT(MAX_HW_TIME_US)
{
if (*((volatile IMG_BOOL *) &psDevInfo->psRGXFWIfOsInit->sRGXCompChecks.bUpdated))
{
/* No need to wait if the FW has already updated the values */
break;
}
OSWaitus(MAX_HW_TIME_US/WAIT_TRY_COUNT);
} END_LOOP_UNTIL_TIMEOUT();
#endif
#if defined(PDUMP)
{
PVRSRV_ERROR eError;
PDUMPCOMMENT("Compatibility check: client and FW build options");
eError = DevmemPDumpDevmemPol32(psDevInfo->psRGXFWIfOsInitMemDesc,
offsetof(RGXFWIF_OSINIT, sRGXCompChecks) +
offsetof(RGXFWIF_COMPCHECKS, ui32BuildOptions),
ui32ClientBuildOptions,
0xffffffff,
PDUMP_POLL_OPERATOR_EQUAL,
PDUMP_FLAGS_CONTINUOUS);
if (eError != PVRSRV_OK)
{
PVR_DPF((PVR_DBG_ERROR,
"%s: problem pdumping POL for psRGXFWIfOsInitMemDesc (%d)",
__func__,
eError));
return eError;
}
}
#endif
#if !defined(NO_HARDWARE)
ui32BuildOptionsFW = psDevInfo->psRGXFWIfOsInit->sRGXCompChecks.ui32BuildOptions;
ui32BuildOptionsMismatch = ui32ClientBuildOptions ^ ui32BuildOptionsFW;
if (ui32BuildOptionsMismatch != 0)
{
if ((ui32ClientBuildOptions & ui32BuildOptionsMismatch) != 0)
{
PVR_LOG(("(FAIL) RGXDevInitCompatCheck: Mismatch in Firmware and client build options; "
"extra options present in client: (0x%x). Please check rgx_options.h",
ui32ClientBuildOptions & ui32BuildOptionsMismatch ));
}
if ((ui32BuildOptionsFW & ui32BuildOptionsMismatch) != 0)
{
PVR_LOG(("(FAIL) RGXDevInitCompatCheck: Mismatch in Firmware and client build options; "
"extra options present in Firmware: (0x%x). Please check rgx_options.h",
ui32BuildOptionsFW & ui32BuildOptionsMismatch ));
}
return PVRSRV_ERROR_BUILD_OPTIONS_MISMATCH;
}
else
{
PVR_DPF((PVR_DBG_MESSAGE, "%s: Firmware and client build options match. [ OK ]", __func__));
}
#endif
return PVRSRV_OK;
}
/*!
*******************************************************************************
@Function RGXFwRawHeapAllocMap
@Description Register firmware heap for the specified guest OSID
@Input psDeviceNode - device node
@Input ui32OSID - Guest OSID
@Input sDevPAddr - Heap address
@Input ui64DevPSize - Heap size
@Return PVRSRV_ERROR - PVRSRV_OK if heap setup was successful.
******************************************************************************/
PVRSRV_ERROR RGXFwRawHeapAllocMap(PVRSRV_DEVICE_NODE *psDeviceNode,
IMG_UINT32 ui32OSID,
IMG_DEV_PHYADDR sDevPAddr,
IMG_UINT64 ui64DevPSize)
{
PVRSRV_ERROR eError;
IMG_CHAR szRegionRAName[PVRSRV_MAX_RA_NAME_LENGTH];
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
IMG_UINT32 ui32RawFwHeapAllocFlags = (RGX_FWSHAREDMEM_ALLOCFLAGS |
PVRSRV_MEMALLOCFLAG_FW_ALLOC_RAW |
PVRSRV_MEMALLOCFLAG_FW_RAW_ALLOC_OSID(ui32OSID));
PVRSRV_VZ_RET_IF_NOT_MODE(HOST, PVRSRV_OK);
OSSNPrintf(szRegionRAName, sizeof(szRegionRAName), RGX_FIRMWARE_GUEST_RAW_HEAP_IDENT, ui32OSID);
if (!ui64DevPSize ||
!sDevPAddr.uiAddr ||
ui32OSID >= RGX_NUM_OS_SUPPORTED ||
ui64DevPSize != RGX_FIRMWARE_RAW_HEAP_SIZE)
{
PVR_DPF((PVR_DBG_ERROR, "Invalid parameters for %s", szRegionRAName));
return PVRSRV_ERROR_INVALID_PARAMS;
}
eError = PVRSRVCreateRegionRA(psDeviceNode->psDevConfig,
&psDeviceNode->psKernelFwRawMemArena[ui32OSID],
psDeviceNode->szKernelFwRawRAName[ui32OSID],
0,
sDevPAddr.uiAddr,
RGX_FIRMWARE_RAW_HEAP_SIZE,
0,
szRegionRAName);
PVR_LOG_RETURN_IF_ERROR(eError, "PVRSRVCreateRegionRA");
PDUMPCOMMENT("Allocate and map raw firmware heap for OSID: [%d]", ui32OSID);
#if (RGX_NUM_OS_SUPPORTED > 1)
/* don't clear the heap of other guests on allocation */
ui32RawFwHeapAllocFlags &= (ui32OSID > RGXFW_HOST_OS) ? (~PVRSRV_MEMALLOCFLAG_ZERO_ON_ALLOC) : (~0);
#endif
/* if the firmware is already powered up, consider the firmware heaps are pre-mapped. */
if (psDeviceNode->bAutoVzFwIsUp)
{
ui32RawFwHeapAllocFlags &= RGX_AUTOVZ_KEEP_FW_DATA_MASK(psDeviceNode->bAutoVzFwIsUp);
DevmemHeapSetPremapStatus(psDevInfo->psGuestFirmwareRawHeap[ui32OSID], IMG_TRUE);
}
eError = DevmemFwAllocate(psDevInfo,
RGX_FIRMWARE_RAW_HEAP_SIZE,
ui32RawFwHeapAllocFlags,
psDevInfo->psGuestFirmwareRawHeap[ui32OSID]->pszName,
&psDevInfo->psGuestFirmwareRawMemDesc[ui32OSID]);
PVR_LOG_RETURN_IF_ERROR(eError, "DevmemFwAllocate");
/* Mark this devmem heap as premapped so allocations will not require device mapping. */
DevmemHeapSetPremapStatus(psDevInfo->psGuestFirmwareRawHeap[ui32OSID], IMG_TRUE);
if (ui32OSID == RGXFW_HOST_OS)
{
/* if the Host's raw fw heap is premapped, mark its main & config sub-heaps accordingly
* No memory allocated from these sub-heaps will be individually mapped into the device's
* address space so they can remain marked permanently as premapped. */
DevmemHeapSetPremapStatus(psDevInfo->psFirmwareMainHeap, IMG_TRUE);
DevmemHeapSetPremapStatus(psDevInfo->psFirmwareConfigHeap, IMG_TRUE);
}
return eError;
}
/*!
*******************************************************************************
@Function RGXFwRawHeapUnmapFree
@Description Unregister firmware heap for the specified guest OSID
@Input psDeviceNode - device node
@Input ui32OSID - Guest OSID
******************************************************************************/
void RGXFwRawHeapUnmapFree(PVRSRV_DEVICE_NODE *psDeviceNode,
IMG_UINT32 ui32OSID)
{
PVRSRV_RGXDEV_INFO *psDevInfo = psDeviceNode->pvDevice;
/* remove the premap status, so the heap can be unmapped and freed */
if (psDevInfo->psGuestFirmwareRawHeap[ui32OSID])
{
DevmemHeapSetPremapStatus(psDevInfo->psGuestFirmwareRawHeap[ui32OSID], IMG_FALSE);
}
if (psDevInfo->psGuestFirmwareRawMemDesc[ui32OSID])
{
DevmemFwUnmapAndFree(psDevInfo, psDevInfo->psGuestFirmwareRawMemDesc[ui32OSID]);
psDevInfo->psGuestFirmwareRawMemDesc[ui32OSID] = NULL;
}
if (psDeviceNode->psKernelFwRawMemArena[ui32OSID])
{
RA_Delete(psDeviceNode->psKernelFwRawMemArena[ui32OSID]);
}
psDeviceNode->psKernelFwRawMemArena[ui32OSID] = NULL;
}
/******************************************************************************
End of file (rgxfwutils.c)
******************************************************************************/