From 7a3b72cf0c3a6e8236d92cb8096be8c5846d526b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Nicu=C8=99or=20C=C3=AE=C8=9Bu?= Date: Mon, 5 Apr 2021 09:56:06 +0300 Subject: [PATCH] MGS-6197 [#imx-2532] GPU crash when changing gpu_govern mode MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add gckGALDEVICE_Suspend() and gckGALDEVICE_Resume() to be used by both gpu_suspend/gpu_resume as well as by gpu_govern_store. The suspendSemaphore will take care that those are kept running on race. Signed-off-by: Nicușor Cîțu --- .../os/linux/kernel/gc_hal_kernel_device.c | 257 ++++++++++++++++++ .../os/linux/kernel/gc_hal_kernel_device.h | 12 + .../os/linux/kernel/gc_hal_kernel_driver.c | 154 ++--------- .../freescale/gc_hal_kernel_platform_imx.c | 27 ++ 4 files changed, 319 insertions(+), 131 deletions(-) diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c index d3d4da12b06b..ebbc31d393ce 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c @@ -2465,6 +2465,9 @@ gckGALDEVICE_Construct( gcmkONERROR(_SetupExternalSRAMVidMem(device)); #endif + /* Create the suspend semaphore. */ + gcmkONERROR(gckOS_CreateSemaphore(device->os, &device->suspendSemaphore)); + /* Initialize the kernel thread semaphores. */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { @@ -2740,6 +2743,11 @@ gckGALDEVICE_Destroy( } } + /* Destroy the suspend semaphore. */ + if (Device->suspendSemaphore) + { + gcmkVERIFY_OK(gckOS_DestroySemaphore(Device->os, Device->suspendSemaphore)); + } if (Device->taos) { @@ -2924,3 +2932,252 @@ OnError: return status; } +/******************************************************************************* +** +** gckGALDEVICE_Suspend +** +** Suspend the gal device to specific state. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** gceCHIPPOWERSTATE State +** State to suspend. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** gcvSTATUS_OK +** Suspend successfully. +*/ +gceSTATUS +gckGALDEVICE_Suspend( + IN gckGALDEVICE Device, + IN gceCHIPPOWERSTATE State + ) +{ + gctUINT i; + gceSTATUS status; + gckHARDWARE hardware; + gceCHIPPOWERSTATE currentState = gcvPOWER_INVALID; + + gcmkHEADER_ARG("Device=%p", Device); + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (Device->kernels[i] == gcvNULL) + { + continue; + } + Device->statesStored[i] = gcvPOWER_INVALID; + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (Device->kernels[i] == gcvNULL) + { + continue; + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + hardware = Device->kernels[i]->vg->hardware; + } + else +#endif + { + hardware = Device->kernels[i]->hardware; + } + + /* Query state. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + gcmkONERROR(gckVGHARDWARE_QueryPowerManagementState(hardware, + ¤tState)); + } + else +#endif + { + gcmkONERROR(gckHARDWARE_QueryPowerState(hardware, ¤tState)); + } + + /* Store state. */ + Device->statesStored[i] = currentState; + + /* Pull up power to flush GPU command buffer before suspending. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + gcmkONERROR(gckVGHARDWARE_SetPowerState(hardware, gcvPOWER_ON)); + } + else +#endif + { + gcmkONERROR(gckHARDWARE_SetPowerState(hardware, gcvPOWER_ON)); + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + gcmkONERROR(gckVGHARDWARE_SetPowerState(hardware, State)); + } + else +#endif + { + gcmkONERROR(gckHARDWARE_SetPowerState(hardware, State)); + } + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Roll back the state for touched cores. */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (Device->kernels[i] == gcvNULL) + { + continue; + } + + if (Device->statesStored[i] == gcvPOWER_INVALID) + { + continue; + } + + /* Reset stored state. */ + Device->statesStored[i] = gcvPOWER_INVALID; + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckGALDEVICE_Resume +** +** Resume the gal device. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** gcvSTATUS_OK +** Resume successfully. +*/ +gceSTATUS +gckGALDEVICE_Resume( + IN gckGALDEVICE Device + ) +{ + gctUINT i; + gceSTATUS status; + gckHARDWARE hardware; + gceCHIPPOWERSTATE state; + + gcmkHEADER_ARG("Device=%p", Device); + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (Device->kernels[i] == gcvNULL) + { + continue; + } + + if (Device->statesStored[i] == gcvPOWER_INVALID) + { + continue; + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + hardware = Device->kernels[i]->vg->hardware; + } + else +#endif + { + hardware = Device->kernels[i]->hardware; + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + gcmkERR_RETURN(gckVGHARDWARE_SetPowerState(hardware, gcvPOWER_ON)); + } + else +#endif + { + gcmkERR_RETURN(gckHARDWARE_SetPowerState(hardware, gcvPOWER_ON)); + } + + /* Convert global state to corresponding internal state. */ + switch (Device->statesStored[i]) + { + case gcvPOWER_ON: + state = gcvPOWER_ON_AUTO; + break; + case gcvPOWER_IDLE: + state = gcvPOWER_IDLE_BROADCAST; + break; + case gcvPOWER_SUSPEND: + state = gcvPOWER_SUSPEND_BROADCAST; + break; + case gcvPOWER_OFF: + state = gcvPOWER_OFF_BROADCAST; + break; + default: + state = Device->statesStored[i]; + break; + } + + /* Restore state. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + gcmkERR_RETURN(gckVGHARDWARE_SetPowerState(hardware, state)); + } + else +#endif + { + gctINT j = 0; + + for (; j < 100; j++) + { + status = gckHARDWARE_SetPowerState(hardware, state); + + if ((state != gcvPOWER_OFF_BROADCAST && + state != gcvPOWER_SUSPEND_BROADCAST) || + status != gcvSTATUS_CHIP_NOT_READY) + { + break; + } + + gcmkVERIFY_OK(gckOS_Delay(hardware->os, 10)); + } + + gcmkERR_RETURN(status); + } + + /* Reset stored state. */ + Device->statesStored[i] = gcvPOWER_INVALID; + } + + gcmkFOOTER(); + return status; +} diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h index e5233258c518..45e70fa3142e 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h @@ -131,6 +131,7 @@ typedef struct _gckGALDEVICE /* States before suspend. */ gceCHIPPOWERSTATE statesStored[gcdMAX_GPU_COUNT]; + gctPOINTER suspendSemaphore; gcsDEBUGFS_DIR debugfsDir; @@ -160,6 +161,17 @@ typedef struct _gcsHAL_PRIVATE_DATA } gcsHAL_PRIVATE_DATA, * gcsHAL_PRIVATE_DATA_PTR; +gceSTATUS +gckGALDEVICE_Suspend( + IN gckGALDEVICE Device, + IN gceCHIPPOWERSTATE State + ); + +gceSTATUS +gckGALDEVICE_Resume( + IN gckGALDEVICE Device + ); + gceSTATUS gckGALDEVICE_Start( IN gckGALDEVICE Device diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c index 7f6f2acb2708..7e941f54eb8a 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c @@ -1402,162 +1402,54 @@ static int gpu_remove(struct platform_device *pdev) static int gpu_suspend(struct platform_device *dev, pm_message_t state) { gceSTATUS status; - gckGALDEVICE device; - gctINT i; - - device = platform_get_drvdata(dev); + gctBOOL sem_acquired = gcvFALSE; + gckGALDEVICE device = platform_get_drvdata(dev); if (!device) { return -1; } - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (device->kernels[i] != gcvNULL) - { - /* Store states. */ -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_QueryPowerManagementState(device->kernels[i]->vg->hardware, &device->statesStored[i]); - } - else -#endif - { - status = gckHARDWARE_QueryPowerState(device->kernels[i]->hardware, &device->statesStored[i]); - } - - if (gcmIS_ERROR(status)) - { - return -1; - } - - /* need pull up power to flush gpu command buffer before suspend */ -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, gcvPOWER_ON); - } - else -#endif - { - status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, gcvPOWER_ON); - } + /* Acquire the suspend management semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(device->os, + device->suspendSemaphore)); - if (gcmIS_ERROR(status)) - { - return -1; - } + sem_acquired = gcvTRUE; -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, gcvPOWER_OFF); - } - else -#endif - { - status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, gcvPOWER_OFF); - } + /* Suspend the GPU to off state. */ + gcmkONERROR(gckGALDEVICE_Suspend(device, gcvPOWER_OFF)); - if (gcmIS_ERROR(status)) - { - return -1; - } + return 0; - } +OnError: + /* Release the suspend semaphore. */ + if (sem_acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseSemaphore(device->os, + device->suspendSemaphore)); } - return 0; + return -1; } static int gpu_resume(struct platform_device *dev) { gceSTATUS status; - gckGALDEVICE device; - gctINT i; - gceCHIPPOWERSTATE statesStored; - - device = platform_get_drvdata(dev); + gckGALDEVICE device = platform_get_drvdata(dev); if (!device) { return -1; } - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (device->kernels[i] != gcvNULL) - { -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, gcvPOWER_ON); - } - else -#endif - { - status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, gcvPOWER_ON); - } - - if (gcmIS_ERROR(status)) - { - return -1; - } - - /* Convert global state to crossponding internal state. */ - switch(device->statesStored[i]) - { - case gcvPOWER_ON: - statesStored = gcvPOWER_ON_AUTO; - break; - case gcvPOWER_IDLE: - statesStored = gcvPOWER_IDLE_BROADCAST; - break; - case gcvPOWER_SUSPEND: - statesStored = gcvPOWER_SUSPEND_BROADCAST; - break; - case gcvPOWER_OFF: - statesStored = gcvPOWER_OFF_BROADCAST; - break; - default: - statesStored = device->statesStored[i]; - break; - } - - /* Restore states. */ -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, statesStored); - } - else -#endif - { - gctINT j = 0; - - for (; j < 100; j++) - { - status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, statesStored); - - if (( statesStored != gcvPOWER_OFF_BROADCAST - && statesStored != gcvPOWER_SUSPEND_BROADCAST) - || status != gcvSTATUS_CHIP_NOT_READY) - { - break; - } + /* Resume GPU to previous state. */ + status = gckGALDEVICE_Resume(device); - gcmkVERIFY_OK(gckOS_Delay(device->kernels[i]->os, 10)); - }; - } + /* Release the suspend semaphore. */ + gcmkVERIFY_OK(gckOS_ReleaseSemaphore(device->os, device->suspendSemaphore)); - if (gcmIS_ERROR(status)) - { - return -1; - } - } - } + if (gcmIS_ERROR(status)) + return -1; return 0; } diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c index 97e88a978e9f..924691ef047c 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c @@ -554,6 +554,14 @@ static ssize_t gpu_govern_store(struct device_driver *dev, const char *buf, size struct imx_priv *priv = &imxPriv; int core = gcvCORE_MAJOR; int i; + gckGALDEVICE device = platform_get_drvdata(pdevice); + gctBOOL sem_acquired = gcvFALSE; + gceSTATUS status; + + if (!device) + { + return count; + } for (i = 0; i < GOVERN_COUNT; i++) { @@ -568,6 +576,14 @@ static ssize_t gpu_govern_store(struct device_driver *dev, const char *buf, size return count; } + /* Acquire the suspend semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(device->os, device->suspendSemaphore)); + + sem_acquired = gcvTRUE; + + /* Suspend the GPU to idle state. */ + gcmkONERROR(gckGALDEVICE_Suspend(device, gcvPOWER_IDLE)); + core_freq = priv->imx_gpu_govern.core_clk_freq[i]; shader_freq = priv->imx_gpu_govern.shader_clk_freq[i]; priv->imx_gpu_govern.current_mode = i; @@ -591,6 +607,17 @@ static ssize_t gpu_govern_store(struct device_driver *dev, const char *buf, size } } + /* Resume GPU to previous state. */ + gcmVERIFY_OK(gckGALDEVICE_Resume(device)); + +OnError: + /* Release the suspend semaphore. */ + if (sem_acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseSemaphore(device->os, + device->suspendSemaphore)); + } + return count; } -- 2.17.1