From: Imre Deak Date: Mon, 26 Jul 2010 19:22:07 +0000 (+0300) Subject: gpu: pvr: add dvfs lock X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b4013aaa5306088ec1c757b1085b0d3fab59e3f5;p=sgx.git gpu: pvr: add dvfs lock We need to protect the SGX HW access with a separate lock as the pvr_lock. This is to overcome a lockdep issue between the clock change notification lock and pvr_lock. Fixes: NB#181087 - pvrsrvkm: possible circular locking dependency detected Signed-off-by: Imre Deak --- diff --git a/pvr/pvr_bridge_k.c b/pvr/pvr_bridge_k.c index f252b0a..698e363 100644 --- a/pvr/pvr_bridge_k.c +++ b/pvr/pvr_bridge_k.c @@ -42,8 +42,8 @@ /* Global driver lock protecting all HW and SW state tracking objects. */ DEFINE_MUTEX(gPVRSRVLock); -int pvr_dvfs_active; -DECLARE_WAIT_QUEUE_HEAD(pvr_dvfs_wq); +static int pvr_dvfs_active; +static DECLARE_WAIT_QUEUE_HEAD(pvr_dvfs_wq); int pvr_disabled; /* @@ -63,22 +63,26 @@ int pvr_disabled; * lead to a dead lock though since at 3. we always release A, before it's * again acquired at 4. To avoid the warning use a wait queue based approach * so that we can unlock B before 3. - * - * Must be called with gPVRSRVLock held. */ -void pvr_dvfs_wait_active(void) +void pvr_dvfs_lock(void) { - while (pvr_dvfs_active) { + while (cmpxchg(&pvr_dvfs_active, 0, 1)) { DEFINE_WAIT(pvr_dvfs_wait); prepare_to_wait(&pvr_dvfs_wq, &pvr_dvfs_wait, TASK_UNINTERRUPTIBLE); - mutex_unlock(&gPVRSRVLock); - schedule(); - mutex_lock(&gPVRSRVLock); + if (pvr_dvfs_active) + schedule(); finish_wait(&pvr_dvfs_wq, &pvr_dvfs_wait); } } +void pvr_dvfs_unlock(void) +{ + BUG_ON(!pvr_dvfs_active); + pvr_dvfs_active = 0; + wake_up(&pvr_dvfs_wq); +} + #if defined(DEBUG_BRIDGE_KM) static off_t printLinuxBridgeStats(char *buffer, size_t size, off_t off); #endif diff --git a/pvr/pvr_bridge_km.h b/pvr/pvr_bridge_km.h index ea981ad..a338ff6 100644 --- a/pvr/pvr_bridge_km.h +++ b/pvr/pvr_bridge_km.h @@ -34,33 +34,14 @@ #include "pvr_bridge.h" #include "perproc.h" -extern int pvr_dvfs_active; +extern void pvr_dvfs_lock(void); +extern void pvr_dvfs_unlock(void); extern struct mutex gPVRSRVLock; -extern wait_queue_head_t pvr_dvfs_wq; extern int pvr_disabled; -void pvr_dvfs_wait_active(void); - -static inline void pvr_dvfs_lock(void) -{ - mutex_lock(&gPVRSRVLock); - pvr_dvfs_active = 1; - mutex_unlock(&gPVRSRVLock); -} - -static inline void pvr_dvfs_unlock(void) -{ - mutex_lock(&gPVRSRVLock); - pvr_dvfs_active = 0; - wake_up(&pvr_dvfs_wq); - mutex_unlock(&gPVRSRVLock); -} - static inline void pvr_lock(void) { mutex_lock(&gPVRSRVLock); - if (pvr_dvfs_active) - pvr_dvfs_wait_active(); } static inline void pvr_unlock(void) diff --git a/pvr/sgxinit.c b/pvr/sgxinit.c index 32f5c52..01b8697 100644 --- a/pvr/sgxinit.c +++ b/pvr/sgxinit.c @@ -452,9 +452,11 @@ enum PVRSRV_ERROR DevInitSGXPart2KM(struct PVRSRV_PER_PROCESS_DATA *psPerProc, PVR_ASSERT(psDeviceNode->pfnDeviceISR == SGX_ISRHandler); + pvr_dvfs_lock(); l = readl(&psDevInfo->psSGXHostCtl->ui32PowerStatus); l |= PVRSRV_USSE_EDM_POWMAN_NO_WORK; writel(l, &psDevInfo->psSGXHostCtl->ui32PowerStatus); + pvr_dvfs_unlock(); eDefaultPowerState = PVRSRV_POWER_STATE_D3; eError = PVRSRVRegisterPowerDevice(psDeviceNode->sDevId.ui32DeviceIndex, @@ -676,6 +678,7 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode) BUG_ON(!pvr_is_locked()); + pvr_dvfs_lock(); l = readl(&psSGXHostCtl->ui32InterruptClearFlags); l |= PVRSRV_USSE_EDM_INTERRUPT_HWR; writel(l, &psSGXHostCtl->ui32InterruptClearFlags); @@ -700,6 +703,7 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode) pvr_disable(); PDUMPRESUME(); + pvr_dvfs_unlock(); return; } @@ -708,6 +712,8 @@ void HWRecoveryResetSGX(struct PVRSRV_DEVICE_NODE *psDeviceNode) SGXScheduleProcessQueues(psDeviceNode); + pvr_dvfs_unlock(); + PVRSRVProcessQueues(IMG_TRUE); } @@ -745,6 +751,7 @@ static void SGXOSTimer(struct work_struct *work) if (bPoweredDown) { ui32LockupCounter = 0; } else { + pvr_dvfs_lock(); ui32CurrentEDMTasks = OSReadHWReg(psDevInfo->pvRegsBaseKM, psDevInfo->ui32EDMTaskReg0); if (psDevInfo->ui32EDMTaskReg1 != 0) @@ -767,6 +774,7 @@ static void SGXOSTimer(struct work_struct *work) ui32EDMTasks = ui32CurrentEDMTasks; ui32NumResets = psDevInfo->ui32NumResets; } + pvr_dvfs_unlock(); } bLockup |= cmpxchg(&sgx_reset_forced, 1, 0); @@ -776,9 +784,11 @@ static void SGXOSTimer(struct work_struct *work) psDevInfo->psSGXHostCtl; u32 l; + pvr_dvfs_lock(); l = readl(&psSGXHostCtl->ui32HostDetectedLockups); l++; writel(l, &psSGXHostCtl->ui32HostDetectedLockups); + pvr_dvfs_unlock(); HWRecoveryResetSGX(psDeviceNode); } @@ -906,6 +916,9 @@ static void SGX_MISRHandler(void *pvData) enum PVRSRV_ERROR err; dev_idx = psDeviceNode->sDevId.ui32DeviceIndex; + + pvr_dvfs_lock(); + err = PVRSRVSetDevicePowerStateKM(dev_idx, PVRSRV_POWER_STATE_D0); BUG_ON(err != PVRSRV_OK); @@ -919,6 +932,8 @@ static void SGX_MISRHandler(void *pvData) SGXScheduleProcessQueues(psDeviceNode); SGXTestActivePowerEvent(psDeviceNode); + + pvr_dvfs_unlock(); } enum PVRSRV_ERROR SGXRegisterDevice(struct PVRSRV_DEVICE_NODE *psDeviceNode) @@ -1394,6 +1409,7 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo, return PVRSRV_ERROR_INVALID_PARAMS; } + pvr_dvfs_lock(); ui32MatchingFlags = readl(&psDevInfo-> psSGXHostCtl->ui32HWPerfFlags); ui32MatchingFlags &= @@ -1411,6 +1427,7 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo, writel(psMiscInfo->uData.ui32NewHWPerfStatus, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags); + pvr_dvfs_unlock(); #if defined(PDUMP) PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "SGX ukernel HWPerf status %u\n", @@ -1437,15 +1454,19 @@ enum PVRSRV_ERROR SGXGetMiscInfoKM(struct PVRSRV_SGXDEV_INFO *psDevInfo, psHWPerfCB->ui32OrdinalGRAPHICS = 0xffffffffUL; + pvr_dvfs_lock();; l = readl(&psDevInfo->psSGXHostCtl->ui32HWPerfFlags); l |= PVRSRV_SGX_HWPERF_GRAPHICS_ON; writel(l, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags); + pvr_dvfs_unlock(); return PVRSRV_OK; } case SGX_MISC_INFO_REQUEST_HWPERF_CB_OFF: { + pvr_dvfs_lock(); writel(0, &psDevInfo->psSGXHostCtl->ui32HWPerfFlags); + pvr_dvfs_unlock(); return PVRSRV_OK; } @@ -1529,6 +1550,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg, *pbActive = bPowered; + pvr_dvfs_lock(); + { struct PVRSRV_SGXDEV_DIFF_INFO sNew, *psPrev = &psDevInfo->sDiffInfo; @@ -1597,6 +1620,8 @@ enum PVRSRV_ERROR SGXReadDiffCountersKM(void *hDevHandle, u32 ui32Reg, SGXTestActivePowerEvent(psDeviceNode); + pvr_dvfs_unlock(); + return PVRSRV_OK; } diff --git a/pvr/sgxutils.c b/pvr/sgxutils.c index 6f49578..616aad4 100644 --- a/pvr/sgxutils.c +++ b/pvr/sgxutils.c @@ -253,6 +253,8 @@ enum PVRSRV_ERROR SGXScheduleCCBCommandKM( PDUMPSUSPEND(); + pvr_dvfs_lock(); + eError = PVRSRVSetDevicePowerStateKM(psDeviceNode->sDevId.ui32DeviceIndex, PVRSRV_POWER_STATE_D0); @@ -264,6 +266,7 @@ enum PVRSRV_ERROR SGXScheduleCCBCommandKM( } else { PVR_DPF(PVR_DBG_ERROR, "%s: can't power on device (%d)", __func__, eError); + pvr_dvfs_unlock(); return eError; } @@ -273,6 +276,8 @@ enum PVRSRV_ERROR SGXScheduleCCBCommandKM( if (ui32CallerID != ISR_ID) SGXTestActivePowerEvent(psDeviceNode); + pvr_dvfs_unlock(); + return eError; } @@ -314,7 +319,9 @@ enum PVRSRV_ERROR SGXScheduleProcessQueuesKM(struct PVRSRV_DEVICE_NODE { enum PVRSRV_ERROR eError; + pvr_dvfs_lock(); eError = SGXScheduleProcessQueues(psDeviceNode); + pvr_dvfs_unlock(); return eError; } @@ -360,6 +367,7 @@ void SGXCleanupRequest(struct PVRSRV_DEVICE_NODE *psDeviceNode, #endif u32 l; + pvr_dvfs_lock(); if (readl(&psSGXHostCtl->ui32PowerStatus) & PVRSRV_USSE_EDM_POWMAN_NO_WORK) { ; @@ -448,6 +456,7 @@ void SGXCleanupRequest(struct PVRSRV_DEVICE_NODE *psDeviceNode, sizeof(u32), 0, hUniqueTag); #endif } + pvr_dvfs_unlock(); } struct SGX_HW_RENDER_CONTEXT_CLEANUP {