162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/clk.h> 562306a36Sopenharmony_ci#include <linux/interconnect.h> 662306a36Sopenharmony_ci#include <linux/of_platform.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci#include <linux/pm_domain.h> 962306a36Sopenharmony_ci#include <linux/pm_opp.h> 1062306a36Sopenharmony_ci#include <soc/qcom/cmd-db.h> 1162306a36Sopenharmony_ci#include <drm/drm_gem.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "a6xx_gpu.h" 1462306a36Sopenharmony_ci#include "a6xx_gmu.xml.h" 1562306a36Sopenharmony_ci#include "msm_gem.h" 1662306a36Sopenharmony_ci#include "msm_gpu_trace.h" 1762306a36Sopenharmony_ci#include "msm_mmu.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic void a6xx_gmu_fault(struct a6xx_gmu *gmu) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 2262306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 2362306a36Sopenharmony_ci struct msm_gpu *gpu = &adreno_gpu->base; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci /* FIXME: add a banner here */ 2662306a36Sopenharmony_ci gmu->hung = true; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* Turn off the hangcheck timer while we are resetting */ 2962306a36Sopenharmony_ci del_timer(&gpu->hangcheck_timer); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* Queue the GPU handler because we need to treat this as a recovery */ 3262306a36Sopenharmony_ci kthread_queue_work(gpu->worker, &gpu->recover_work); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic irqreturn_t a6xx_gmu_irq(int irq, void *data) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct a6xx_gmu *gmu = data; 3862306a36Sopenharmony_ci u32 status; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci status = gmu_read(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_STATUS); 4162306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, status); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE) { 4462306a36Sopenharmony_ci dev_err_ratelimited(gmu->dev, "GMU watchdog expired\n"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci a6xx_gmu_fault(gmu); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR) 5062306a36Sopenharmony_ci dev_err_ratelimited(gmu->dev, "GMU AHB bus error\n"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_FENCE_ERR) 5362306a36Sopenharmony_ci dev_err_ratelimited(gmu->dev, "GMU fence error: 0x%x\n", 5462306a36Sopenharmony_ci gmu_read(gmu, REG_A6XX_GMU_AHB_FENCE_STATUS)); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return IRQ_HANDLED; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic irqreturn_t a6xx_hfi_irq(int irq, void *data) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct a6xx_gmu *gmu = data; 6262306a36Sopenharmony_ci u32 status; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci status = gmu_read(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO); 6562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, status); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (status & A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) { 6862306a36Sopenharmony_ci dev_err_ratelimited(gmu->dev, "GMU firmware fault\n"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci a6xx_gmu_fault(gmu); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return IRQ_HANDLED; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cibool a6xx_gmu_sptprac_is_on(struct a6xx_gmu *gmu) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u32 val; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* This can be called from gpu state code so make sure GMU is valid */ 8162306a36Sopenharmony_ci if (!gmu->initialized) 8262306a36Sopenharmony_ci return false; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci val = gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return !(val & 8762306a36Sopenharmony_ci (A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_SPTPRAC_GDSC_POWER_OFF | 8862306a36Sopenharmony_ci A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_SP_CLOCK_OFF)); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Check to see if the GX rail is still powered */ 9262306a36Sopenharmony_cibool a6xx_gmu_gx_is_on(struct a6xx_gmu *gmu) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci u32 val; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* This can be called from gpu state code so make sure GMU is valid */ 9762306a36Sopenharmony_ci if (!gmu->initialized) 9862306a36Sopenharmony_ci return false; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci val = gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return !(val & 10362306a36Sopenharmony_ci (A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_GDSC_POWER_OFF | 10462306a36Sopenharmony_ci A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_GX_HM_CLK_OFF)); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_civoid a6xx_gmu_set_freq(struct msm_gpu *gpu, struct dev_pm_opp *opp, 10862306a36Sopenharmony_ci bool suspended) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); 11162306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); 11262306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 11362306a36Sopenharmony_ci u32 perf_index; 11462306a36Sopenharmony_ci unsigned long gpu_freq; 11562306a36Sopenharmony_ci int ret = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci gpu_freq = dev_pm_opp_get_freq(opp); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (gpu_freq == gmu->freq) 12062306a36Sopenharmony_ci return; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for (perf_index = 0; perf_index < gmu->nr_gpu_freqs - 1; perf_index++) 12362306a36Sopenharmony_ci if (gpu_freq == gmu->gpu_freqs[perf_index]) 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci gmu->current_perf_index = perf_index; 12762306a36Sopenharmony_ci gmu->freq = gmu->gpu_freqs[perf_index]; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci trace_msm_gmu_freq_change(gmu->freq, perf_index); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * This can get called from devfreq while the hardware is idle. Don't 13362306a36Sopenharmony_ci * bring up the power if it isn't already active. All we're doing here 13462306a36Sopenharmony_ci * is updating the frequency so that when we come back online we're at 13562306a36Sopenharmony_ci * the right rate. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (suspended) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!gmu->legacy) { 14162306a36Sopenharmony_ci a6xx_hfi_set_freq(gmu, perf_index); 14262306a36Sopenharmony_ci dev_pm_opp_set_opp(&gpu->pdev->dev, opp); 14362306a36Sopenharmony_ci return; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_DCVS_ACK_OPTION, 0); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_DCVS_PERF_SETTING, 14962306a36Sopenharmony_ci ((3 & 0xf) << 28) | perf_index); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * Send an invalid index as a vote for the bus bandwidth and let the 15362306a36Sopenharmony_ci * firmware decide on the right vote 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_DCVS_BW_SETTING, 0xff); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Set and clear the OOB for DCVS to trigger the GMU */ 15862306a36Sopenharmony_ci a6xx_gmu_set_oob(gmu, GMU_OOB_DCVS_SET); 15962306a36Sopenharmony_ci a6xx_gmu_clear_oob(gmu, GMU_OOB_DCVS_SET); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = gmu_read(gmu, REG_A6XX_GMU_DCVS_RETURN); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci dev_pm_opp_set_opp(&gpu->pdev->dev, opp); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciunsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); 17162306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); 17262306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return gmu->freq; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci u32 val; 18062306a36Sopenharmony_ci int local = gmu->idle_level; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* SPTP and IFPC both report as IFPC */ 18362306a36Sopenharmony_ci if (gmu->idle_level == GMU_IDLE_STATE_SPTP) 18462306a36Sopenharmony_ci local = GMU_IDLE_STATE_IFPC; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (val == local) { 18962306a36Sopenharmony_ci if (gmu->idle_level != GMU_IDLE_STATE_IFPC || 19062306a36Sopenharmony_ci !a6xx_gmu_gx_is_on(gmu)) 19162306a36Sopenharmony_ci return true; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return false; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* Wait for the GMU to get to its most idle state */ 19862306a36Sopenharmony_ciint a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return spin_until(a6xx_gmu_check_idle_level(gmu)); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int a6xx_gmu_start(struct a6xx_gmu *gmu) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int ret; 20662306a36Sopenharmony_ci u32 val; 20762306a36Sopenharmony_ci u32 mask, reset_val; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci val = gmu_read(gmu, REG_A6XX_GMU_CM3_DTCM_START + 0xff8); 21062306a36Sopenharmony_ci if (val <= 0x20010004) { 21162306a36Sopenharmony_ci mask = 0xffffffff; 21262306a36Sopenharmony_ci reset_val = 0xbabeface; 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci mask = 0x1ff; 21562306a36Sopenharmony_ci reset_val = 0x100; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 1); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Set the log wptr index 22162306a36Sopenharmony_ci * note: downstream saves the value in poweroff and restores it here 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_RESP, 0); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 0); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_CM3_FW_INIT_RESULT, val, 22862306a36Sopenharmony_ci (val & mask) == reset_val, 100, 10000); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (ret) 23162306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "GMU firmware initialization timed out\n"); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int a6xx_gmu_hfi_start(struct a6xx_gmu *gmu) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci u32 val; 23962306a36Sopenharmony_ci int ret; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_HFI_CTRL_INIT, 1); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_HFI_CTRL_STATUS, val, 24462306a36Sopenharmony_ci val & 1, 100, 10000); 24562306a36Sopenharmony_ci if (ret) 24662306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "Unable to start the HFI queues\n"); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return ret; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistruct a6xx_gmu_oob_bits { 25262306a36Sopenharmony_ci int set, ack, set_new, ack_new, clear, clear_new; 25362306a36Sopenharmony_ci const char *name; 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* These are the interrupt / ack bits for each OOB request that are set 25762306a36Sopenharmony_ci * in a6xx_gmu_set_oob and a6xx_clear_oob 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_cistatic const struct a6xx_gmu_oob_bits a6xx_gmu_oob_bits[] = { 26062306a36Sopenharmony_ci [GMU_OOB_GPU_SET] = { 26162306a36Sopenharmony_ci .name = "GPU_SET", 26262306a36Sopenharmony_ci .set = 16, 26362306a36Sopenharmony_ci .ack = 24, 26462306a36Sopenharmony_ci .set_new = 30, 26562306a36Sopenharmony_ci .ack_new = 31, 26662306a36Sopenharmony_ci .clear = 24, 26762306a36Sopenharmony_ci .clear_new = 31, 26862306a36Sopenharmony_ci }, 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci [GMU_OOB_PERFCOUNTER_SET] = { 27162306a36Sopenharmony_ci .name = "PERFCOUNTER", 27262306a36Sopenharmony_ci .set = 17, 27362306a36Sopenharmony_ci .ack = 25, 27462306a36Sopenharmony_ci .set_new = 28, 27562306a36Sopenharmony_ci .ack_new = 30, 27662306a36Sopenharmony_ci .clear = 25, 27762306a36Sopenharmony_ci .clear_new = 29, 27862306a36Sopenharmony_ci }, 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci [GMU_OOB_BOOT_SLUMBER] = { 28162306a36Sopenharmony_ci .name = "BOOT_SLUMBER", 28262306a36Sopenharmony_ci .set = 22, 28362306a36Sopenharmony_ci .ack = 30, 28462306a36Sopenharmony_ci .clear = 30, 28562306a36Sopenharmony_ci }, 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci [GMU_OOB_DCVS_SET] = { 28862306a36Sopenharmony_ci .name = "GPU_DCVS", 28962306a36Sopenharmony_ci .set = 23, 29062306a36Sopenharmony_ci .ack = 31, 29162306a36Sopenharmony_ci .clear = 31, 29262306a36Sopenharmony_ci }, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* Trigger a OOB (out of band) request to the GMU */ 29662306a36Sopenharmony_ciint a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int ret; 29962306a36Sopenharmony_ci u32 val; 30062306a36Sopenharmony_ci int request, ack; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci WARN_ON_ONCE(!mutex_is_locked(&gmu->lock)); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (state >= ARRAY_SIZE(a6xx_gmu_oob_bits)) 30562306a36Sopenharmony_ci return -EINVAL; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (gmu->legacy) { 30862306a36Sopenharmony_ci request = a6xx_gmu_oob_bits[state].set; 30962306a36Sopenharmony_ci ack = a6xx_gmu_oob_bits[state].ack; 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci request = a6xx_gmu_oob_bits[state].set_new; 31262306a36Sopenharmony_ci ack = a6xx_gmu_oob_bits[state].ack_new; 31362306a36Sopenharmony_ci if (!request || !ack) { 31462306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, 31562306a36Sopenharmony_ci "Invalid non-legacy GMU request %s\n", 31662306a36Sopenharmony_ci a6xx_gmu_oob_bits[state].name); 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Trigger the equested OOB operation */ 32262306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET, 1 << request); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Wait for the acknowledge interrupt */ 32562306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val, 32662306a36Sopenharmony_ci val & (1 << ack), 100, 10000); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (ret) 32962306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, 33062306a36Sopenharmony_ci "Timeout waiting for GMU OOB set %s: 0x%x\n", 33162306a36Sopenharmony_ci a6xx_gmu_oob_bits[state].name, 33262306a36Sopenharmony_ci gmu_read(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO)); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Clear the acknowledge interrupt */ 33562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, 1 << ack); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* Clear a pending OOB state in the GMU */ 34162306a36Sopenharmony_civoid a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int bit; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci WARN_ON_ONCE(!mutex_is_locked(&gmu->lock)); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (state >= ARRAY_SIZE(a6xx_gmu_oob_bits)) 34862306a36Sopenharmony_ci return; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (gmu->legacy) 35162306a36Sopenharmony_ci bit = a6xx_gmu_oob_bits[state].clear; 35262306a36Sopenharmony_ci else 35362306a36Sopenharmony_ci bit = a6xx_gmu_oob_bits[state].clear_new; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET, 1 << bit); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/* Enable CPU control of SPTP power power collapse */ 35962306a36Sopenharmony_ciint a6xx_sptprac_enable(struct a6xx_gmu *gmu) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci int ret; 36262306a36Sopenharmony_ci u32 val; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (!gmu->legacy) 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, 0x778000); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, val, 37062306a36Sopenharmony_ci (val & 0x38) == 0x28, 1, 100); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (ret) { 37362306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "Unable to power on SPTPRAC: 0x%x\n", 37462306a36Sopenharmony_ci gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS)); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* Disable CPU control of SPTP power power collapse */ 38162306a36Sopenharmony_civoid a6xx_sptprac_disable(struct a6xx_gmu *gmu) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci u32 val; 38462306a36Sopenharmony_ci int ret; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!gmu->legacy) 38762306a36Sopenharmony_ci return; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Make sure retention is on */ 39062306a36Sopenharmony_ci gmu_rmw(gmu, REG_A6XX_GPU_CC_GX_GDSCR, 0, (1 << 11)); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GX_SPTPRAC_POWER_CONTROL, 0x778001); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, val, 39562306a36Sopenharmony_ci (val & 0x04), 100, 10000); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "failed to power off SPTPRAC: 0x%x\n", 39962306a36Sopenharmony_ci gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS)); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* Let the GMU know we are starting a boot sequence */ 40362306a36Sopenharmony_cistatic int a6xx_gmu_gfx_rail_on(struct a6xx_gmu *gmu) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci u32 vote; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Let the GMU know we are getting ready for boot */ 40862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_BOOT_SLUMBER_OPTION, 0); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Choose the "default" power level as the highest available */ 41162306a36Sopenharmony_ci vote = gmu->gx_arc_votes[gmu->nr_gpu_freqs - 1]; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GX_VOTE_IDX, vote & 0xff); 41462306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_MX_VOTE_IDX, (vote >> 8) & 0xff); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Let the GMU know the boot sequence has started */ 41762306a36Sopenharmony_ci return a6xx_gmu_set_oob(gmu, GMU_OOB_BOOT_SLUMBER); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci/* Let the GMU know that we are about to go into slumber */ 42162306a36Sopenharmony_cistatic int a6xx_gmu_notify_slumber(struct a6xx_gmu *gmu) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci int ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Disable the power counter so the GMU isn't busy */ 42662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Disable SPTP_PC if the CPU is responsible for it */ 42962306a36Sopenharmony_ci if (gmu->idle_level < GMU_IDLE_STATE_SPTP) 43062306a36Sopenharmony_ci a6xx_sptprac_disable(gmu); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!gmu->legacy) { 43362306a36Sopenharmony_ci ret = a6xx_hfi_send_prep_slumber(gmu); 43462306a36Sopenharmony_ci goto out; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Tell the GMU to get ready to slumber */ 43862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_BOOT_SLUMBER_OPTION, 1); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ret = a6xx_gmu_set_oob(gmu, GMU_OOB_BOOT_SLUMBER); 44162306a36Sopenharmony_ci a6xx_gmu_clear_oob(gmu, GMU_OOB_BOOT_SLUMBER); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!ret) { 44462306a36Sopenharmony_ci /* Check to see if the GMU really did slumber */ 44562306a36Sopenharmony_ci if (gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE) 44662306a36Sopenharmony_ci != 0x0f) { 44762306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "The GMU did not go into slumber\n"); 44862306a36Sopenharmony_ci ret = -ETIMEDOUT; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciout: 45362306a36Sopenharmony_ci /* Put fence into allow mode */ 45462306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0); 45562306a36Sopenharmony_ci return ret; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int a6xx_rpmh_start(struct a6xx_gmu *gmu) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci int ret; 46162306a36Sopenharmony_ci u32 val; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 1 << 1); 46462306a36Sopenharmony_ci /* Wait for the register to finish posting */ 46562306a36Sopenharmony_ci wmb(); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_RSCC_CONTROL_ACK, val, 46862306a36Sopenharmony_ci val & (1 << 1), 100, 10000); 46962306a36Sopenharmony_ci if (ret) { 47062306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "Unable to power on the GPU RSC\n"); 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_SEQ_BUSY_DRV0, val, 47562306a36Sopenharmony_ci !val, 100, 10000); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (ret) { 47862306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "GPU RSC sequence stuck while waking up the GPU\n"); 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 0); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void a6xx_rpmh_stop(struct a6xx_gmu *gmu) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci int ret; 49062306a36Sopenharmony_ci u32 val; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 1); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret = gmu_poll_timeout_rscc(gmu, REG_A6XX_GPU_RSCC_RSC_STATUS0_DRV0, 49562306a36Sopenharmony_ci val, val & (1 << 16), 100, 10000); 49662306a36Sopenharmony_ci if (ret) 49762306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "Unable to power off the GPU RSC\n"); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 0); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic inline void pdc_write(void __iomem *ptr, u32 offset, u32 value) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci msm_writel(value, ptr + (offset << 2)); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic void __iomem *a6xx_gmu_get_mmio(struct platform_device *pdev, 50862306a36Sopenharmony_ci const char *name); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 51362306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 51462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(gmu->dev); 51562306a36Sopenharmony_ci void __iomem *pdcptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc"); 51662306a36Sopenharmony_ci void __iomem *seqptr = NULL; 51762306a36Sopenharmony_ci uint32_t pdc_address_offset; 51862306a36Sopenharmony_ci bool pdc_in_aop = false; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (IS_ERR(pdcptr)) 52162306a36Sopenharmony_ci goto err; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (adreno_is_a650(adreno_gpu) || adreno_is_a660_family(adreno_gpu)) 52462306a36Sopenharmony_ci pdc_in_aop = true; 52562306a36Sopenharmony_ci else if (adreno_is_a618(adreno_gpu) || adreno_is_a640_family(adreno_gpu)) 52662306a36Sopenharmony_ci pdc_address_offset = 0x30090; 52762306a36Sopenharmony_ci else if (adreno_is_a619(adreno_gpu)) 52862306a36Sopenharmony_ci pdc_address_offset = 0x300a0; 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci pdc_address_offset = 0x30080; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (!pdc_in_aop) { 53362306a36Sopenharmony_ci seqptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc_seq"); 53462306a36Sopenharmony_ci if (IS_ERR(seqptr)) 53562306a36Sopenharmony_ci goto err; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* Disable SDE clock gating */ 53962306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_GPU_RSCC_RSC_STATUS0_DRV0, BIT(24)); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Setup RSC PDC handshake for sleep and wakeup */ 54262306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_SLAVE_ID_DRV0, 1); 54362306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA, 0); 54462306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR, 0); 54562306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 2, 0); 54662306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + 2, 0); 54762306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 4, 0x80000000); 54862306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + 4, 0); 54962306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_OVERRIDE_START_ADDR, 0); 55062306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_SEQ_START_ADDR, 0x4520); 55162306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_MATCH_VALUE_LO, 0x4510); 55262306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_MATCH_VALUE_HI, 0x4514); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Load RSC sequencer uCode for sleep and wakeup */ 55562306a36Sopenharmony_ci if (adreno_is_a650_family(adreno_gpu)) { 55662306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0, 0xeaaae5a0); 55762306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 1, 0xe1a1ebab); 55862306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 2, 0xa2e0a581); 55962306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 3, 0xecac82e2); 56062306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 4, 0x0020edad); 56162306a36Sopenharmony_ci } else { 56262306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0, 0xa7a506a0); 56362306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 1, 0xa1e6a6e7); 56462306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 2, 0xa2e081e1); 56562306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 3, 0xe9a982e2); 56662306a36Sopenharmony_ci gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 4, 0x0020e8a8); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (pdc_in_aop) 57062306a36Sopenharmony_ci goto setup_pdc; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Load PDC sequencer uCode for power up and power down sequence */ 57362306a36Sopenharmony_ci pdc_write(seqptr, REG_A6XX_PDC_GPU_SEQ_MEM_0, 0xfebea1e1); 57462306a36Sopenharmony_ci pdc_write(seqptr, REG_A6XX_PDC_GPU_SEQ_MEM_0 + 1, 0xa5a4a3a2); 57562306a36Sopenharmony_ci pdc_write(seqptr, REG_A6XX_PDC_GPU_SEQ_MEM_0 + 2, 0x8382a6e0); 57662306a36Sopenharmony_ci pdc_write(seqptr, REG_A6XX_PDC_GPU_SEQ_MEM_0 + 3, 0xbce3e284); 57762306a36Sopenharmony_ci pdc_write(seqptr, REG_A6XX_PDC_GPU_SEQ_MEM_0 + 4, 0x002081fc); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Set TCS commands used by PDC sequence for low power modes */ 58062306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD_ENABLE_BANK, 7); 58162306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD_WAIT_FOR_CMPL_BANK, 0); 58262306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CONTROL, 0); 58362306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_MSGID, 0x10108); 58462306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_ADDR, 0x30010); 58562306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_DATA, 1); 58662306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_MSGID + 4, 0x10108); 58762306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_ADDR + 4, 0x30000); 58862306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_DATA + 4, 0x0); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_MSGID + 8, 0x10108); 59162306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_ADDR + 8, pdc_address_offset); 59262306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS1_CMD0_DATA + 8, 0x0); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD_ENABLE_BANK, 7); 59562306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK, 0); 59662306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CONTROL, 0); 59762306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_MSGID, 0x10108); 59862306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_ADDR, 0x30010); 59962306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_DATA, 2); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_MSGID + 4, 0x10108); 60262306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_ADDR + 4, 0x30000); 60362306a36Sopenharmony_ci if (adreno_is_a618(adreno_gpu) || adreno_is_a619(adreno_gpu) || 60462306a36Sopenharmony_ci adreno_is_a650_family(adreno_gpu)) 60562306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_DATA + 4, 0x2); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_DATA + 4, 0x3); 60862306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_MSGID + 8, 0x10108); 60962306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_ADDR + 8, pdc_address_offset); 61062306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_TCS3_CMD0_DATA + 8, 0x3); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Setup GPU PDC */ 61362306a36Sopenharmony_cisetup_pdc: 61462306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_SEQ_START_ADDR, 0); 61562306a36Sopenharmony_ci pdc_write(pdcptr, REG_A6XX_PDC_GPU_ENABLE_PDC, 0x80000001); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* ensure no writes happen before the uCode is fully written */ 61862306a36Sopenharmony_ci wmb(); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci a6xx_rpmh_stop(gmu); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cierr: 62362306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(pdcptr)) 62462306a36Sopenharmony_ci iounmap(pdcptr); 62562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(seqptr)) 62662306a36Sopenharmony_ci iounmap(seqptr); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* 63062306a36Sopenharmony_ci * The lowest 16 bits of this value are the number of XO clock cycles for main 63162306a36Sopenharmony_ci * hysteresis which is set at 0x1680 cycles (300 us). The higher 16 bits are 63262306a36Sopenharmony_ci * for the shorter hysteresis that happens after main - this is 0xa (.5 us) 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci#define GMU_PWR_COL_HYST 0x000a1680 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* Set up the idle state for the GMU */ 63862306a36Sopenharmony_cistatic void a6xx_gmu_power_config(struct a6xx_gmu *gmu) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci /* Disable GMU WB/RB buffer */ 64162306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_SYS_BUS_CONFIG, 0x1); 64262306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_ICACHE_CONFIG, 0x1); 64362306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_DCACHE_CONFIG, 0x1); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0x9c40400); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci switch (gmu->idle_level) { 64862306a36Sopenharmony_ci case GMU_IDLE_STATE_IFPC: 64962306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_PWR_COL_INTER_FRAME_HYST, 65062306a36Sopenharmony_ci GMU_PWR_COL_HYST); 65162306a36Sopenharmony_ci gmu_rmw(gmu, REG_A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0, 65262306a36Sopenharmony_ci A6XX_GMU_PWR_COL_INTER_FRAME_CTRL_IFPC_ENABLE | 65362306a36Sopenharmony_ci A6XX_GMU_PWR_COL_INTER_FRAME_CTRL_HM_POWER_COLLAPSE_ENABLE); 65462306a36Sopenharmony_ci fallthrough; 65562306a36Sopenharmony_ci case GMU_IDLE_STATE_SPTP: 65662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_PWR_COL_SPTPRAC_HYST, 65762306a36Sopenharmony_ci GMU_PWR_COL_HYST); 65862306a36Sopenharmony_ci gmu_rmw(gmu, REG_A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0, 65962306a36Sopenharmony_ci A6XX_GMU_PWR_COL_INTER_FRAME_CTRL_IFPC_ENABLE | 66062306a36Sopenharmony_ci A6XX_GMU_PWR_COL_INTER_FRAME_CTRL_SPTPRAC_POWER_CONTROL_ENABLE); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Enable RPMh GPU client */ 66462306a36Sopenharmony_ci gmu_rmw(gmu, REG_A6XX_GMU_RPMH_CTRL, 0, 66562306a36Sopenharmony_ci A6XX_GMU_RPMH_CTRL_RPMH_INTERFACE_ENABLE | 66662306a36Sopenharmony_ci A6XX_GMU_RPMH_CTRL_LLC_VOTE_ENABLE | 66762306a36Sopenharmony_ci A6XX_GMU_RPMH_CTRL_DDR_VOTE_ENABLE | 66862306a36Sopenharmony_ci A6XX_GMU_RPMH_CTRL_MX_VOTE_ENABLE | 66962306a36Sopenharmony_ci A6XX_GMU_RPMH_CTRL_CX_VOTE_ENABLE | 67062306a36Sopenharmony_ci A6XX_GMU_RPMH_CTRL_GFX_VOTE_ENABLE); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistruct block_header { 67462306a36Sopenharmony_ci u32 addr; 67562306a36Sopenharmony_ci u32 size; 67662306a36Sopenharmony_ci u32 type; 67762306a36Sopenharmony_ci u32 value; 67862306a36Sopenharmony_ci u32 data[]; 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic bool fw_block_mem(struct a6xx_gmu_bo *bo, const struct block_header *blk) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci if (!in_range(blk->addr, bo->iova, bo->size)) 68462306a36Sopenharmony_ci return false; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci memcpy(bo->virt + blk->addr - bo->iova, blk->data, blk->size); 68762306a36Sopenharmony_ci return true; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int a6xx_gmu_fw_load(struct a6xx_gmu *gmu) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 69362306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 69462306a36Sopenharmony_ci const struct firmware *fw_image = adreno_gpu->fw[ADRENO_FW_GMU]; 69562306a36Sopenharmony_ci const struct block_header *blk; 69662306a36Sopenharmony_ci u32 reg_offset; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci u32 itcm_base = 0x00000000; 69962306a36Sopenharmony_ci u32 dtcm_base = 0x00040000; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (adreno_is_a650_family(adreno_gpu)) 70262306a36Sopenharmony_ci dtcm_base = 0x10004000; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (gmu->legacy) { 70562306a36Sopenharmony_ci /* Sanity check the size of the firmware that was loaded */ 70662306a36Sopenharmony_ci if (fw_image->size > 0x8000) { 70762306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, 70862306a36Sopenharmony_ci "GMU firmware is bigger than the available region\n"); 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci gmu_write_bulk(gmu, REG_A6XX_GMU_CM3_ITCM_START, 71362306a36Sopenharmony_ci (u32*) fw_image->data, fw_image->size); 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci for (blk = (const struct block_header *) fw_image->data; 71962306a36Sopenharmony_ci (const u8*) blk < fw_image->data + fw_image->size; 72062306a36Sopenharmony_ci blk = (const struct block_header *) &blk->data[blk->size >> 2]) { 72162306a36Sopenharmony_ci if (blk->size == 0) 72262306a36Sopenharmony_ci continue; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (in_range(blk->addr, itcm_base, SZ_16K)) { 72562306a36Sopenharmony_ci reg_offset = (blk->addr - itcm_base) >> 2; 72662306a36Sopenharmony_ci gmu_write_bulk(gmu, 72762306a36Sopenharmony_ci REG_A6XX_GMU_CM3_ITCM_START + reg_offset, 72862306a36Sopenharmony_ci blk->data, blk->size); 72962306a36Sopenharmony_ci } else if (in_range(blk->addr, dtcm_base, SZ_16K)) { 73062306a36Sopenharmony_ci reg_offset = (blk->addr - dtcm_base) >> 2; 73162306a36Sopenharmony_ci gmu_write_bulk(gmu, 73262306a36Sopenharmony_ci REG_A6XX_GMU_CM3_DTCM_START + reg_offset, 73362306a36Sopenharmony_ci blk->data, blk->size); 73462306a36Sopenharmony_ci } else if (!fw_block_mem(&gmu->icache, blk) && 73562306a36Sopenharmony_ci !fw_block_mem(&gmu->dcache, blk) && 73662306a36Sopenharmony_ci !fw_block_mem(&gmu->dummy, blk)) { 73762306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, 73862306a36Sopenharmony_ci "failed to match fw block (addr=%.8x size=%d data[0]=%.8x)\n", 73962306a36Sopenharmony_ci blk->addr, blk->size, blk->data[0]); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 74962306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 75062306a36Sopenharmony_ci int ret; 75162306a36Sopenharmony_ci u32 chipid; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (adreno_is_a650_family(adreno_gpu)) { 75462306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, 1); 75562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FAL_INTF, 1); 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (state == GMU_WARM_BOOT) { 75962306a36Sopenharmony_ci ret = a6xx_rpmh_start(gmu); 76062306a36Sopenharmony_ci if (ret) 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci } else { 76362306a36Sopenharmony_ci if (WARN(!adreno_gpu->fw[ADRENO_FW_GMU], 76462306a36Sopenharmony_ci "GMU firmware is not loaded\n")) 76562306a36Sopenharmony_ci return -ENOENT; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* Turn on register retention */ 76862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ret = a6xx_rpmh_start(gmu); 77162306a36Sopenharmony_ci if (ret) 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ret = a6xx_gmu_fw_load(gmu); 77562306a36Sopenharmony_ci if (ret) 77662306a36Sopenharmony_ci return ret; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CM3_FW_INIT_RESULT, 0); 78062306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CM3_BOOT_CONFIG, 0x02); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* Write the iova of the HFI table */ 78362306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_ADDR, gmu->hfi.iova); 78462306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_INFO, 1); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AHB_FENCE_RANGE_0, 78762306a36Sopenharmony_ci (1 << 31) | (0xa << 18) | (0xa0)); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* 79062306a36Sopenharmony_ci * Snapshots toggle the NMI bit which will result in a jump to the NMI 79162306a36Sopenharmony_ci * handler instead of __main. Set the M3 config value to avoid that. 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CM3_CFG, 0x4052); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* 79662306a36Sopenharmony_ci * Note that the GMU has a slightly different layout for 79762306a36Sopenharmony_ci * chip_id, for whatever reason, so a bit of massaging 79862306a36Sopenharmony_ci * is needed. The upper 16b are the same, but minor and 79962306a36Sopenharmony_ci * patchid are packed in four bits each with the lower 80062306a36Sopenharmony_ci * 8b unused: 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci chipid = adreno_gpu->chip_id & 0xffff0000; 80362306a36Sopenharmony_ci chipid |= (adreno_gpu->chip_id << 4) & 0xf000; /* minor */ 80462306a36Sopenharmony_ci chipid |= (adreno_gpu->chip_id << 8) & 0x0f00; /* patchid */ 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_HFI_SFR_ADDR, chipid); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_MSG, 80962306a36Sopenharmony_ci gmu->log.iova | (gmu->log.size / SZ_4K - 1)); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Set up the lowest idle level on the GMU */ 81262306a36Sopenharmony_ci a6xx_gmu_power_config(gmu); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci ret = a6xx_gmu_start(gmu); 81562306a36Sopenharmony_ci if (ret) 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (gmu->legacy) { 81962306a36Sopenharmony_ci ret = a6xx_gmu_gfx_rail_on(gmu); 82062306a36Sopenharmony_ci if (ret) 82162306a36Sopenharmony_ci return ret; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* Enable SPTP_PC if the CPU is responsible for it */ 82562306a36Sopenharmony_ci if (gmu->idle_level < GMU_IDLE_STATE_SPTP) { 82662306a36Sopenharmony_ci ret = a6xx_sptprac_enable(gmu); 82762306a36Sopenharmony_ci if (ret) 82862306a36Sopenharmony_ci return ret; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = a6xx_gmu_hfi_start(gmu); 83262306a36Sopenharmony_ci if (ret) 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* FIXME: Do we need this wmb() here? */ 83662306a36Sopenharmony_ci wmb(); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci#define A6XX_HFI_IRQ_MASK \ 84262306a36Sopenharmony_ci (A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci#define A6XX_GMU_IRQ_MASK \ 84562306a36Sopenharmony_ci (A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE | \ 84662306a36Sopenharmony_ci A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR | \ 84762306a36Sopenharmony_ci A6XX_GMU_AO_HOST_INTERRUPT_STATUS_FENCE_ERR) 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci disable_irq(gmu->gmu_irq); 85262306a36Sopenharmony_ci disable_irq(gmu->hfi_irq); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_MASK, ~0); 85562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~0); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci u32 val; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* Make sure there are no outstanding RPMh votes */ 86362306a36Sopenharmony_ci gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS0_DRV0_STATUS, val, 86462306a36Sopenharmony_ci (val & 1), 100, 10000); 86562306a36Sopenharmony_ci gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS1_DRV0_STATUS, val, 86662306a36Sopenharmony_ci (val & 1), 100, 10000); 86762306a36Sopenharmony_ci gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS2_DRV0_STATUS, val, 86862306a36Sopenharmony_ci (val & 1), 100, 10000); 86962306a36Sopenharmony_ci gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS, val, 87062306a36Sopenharmony_ci (val & 1), 100, 1000); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/* Force the GMU off in case it isn't responsive */ 87462306a36Sopenharmony_cistatic void a6xx_gmu_force_off(struct a6xx_gmu *gmu) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 87762306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 87862306a36Sopenharmony_ci struct msm_gpu *gpu = &adreno_gpu->base; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * Turn off keep alive that might have been enabled by the hang 88262306a36Sopenharmony_ci * interrupt 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_ci gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_GMU_PWR_COL_KEEPALIVE, 0); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* Flush all the queues */ 88762306a36Sopenharmony_ci a6xx_hfi_stop(gmu); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* Stop the interrupts */ 89062306a36Sopenharmony_ci a6xx_gmu_irq_disable(gmu); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Force off SPTP in case the GMU is managing it */ 89362306a36Sopenharmony_ci a6xx_sptprac_disable(gmu); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Make sure there are no outstanding RPMh votes */ 89662306a36Sopenharmony_ci a6xx_gmu_rpmh_off(gmu); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* Clear the WRITEDROPPED fields and put fence into allow mode */ 89962306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AHB_FENCE_STATUS_CLR, 0x7); 90062306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* Make sure the above writes go through */ 90362306a36Sopenharmony_ci wmb(); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* Halt the gmu cm3 core */ 90662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 1); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci a6xx_bus_clear_pending_transactions(adreno_gpu, true); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* Reset GPU core blocks */ 91162306a36Sopenharmony_ci a6xx_gpu_sw_reset(gpu, true); 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic void a6xx_gmu_set_initial_freq(struct msm_gpu *gpu, struct a6xx_gmu *gmu) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct dev_pm_opp *gpu_opp; 91762306a36Sopenharmony_ci unsigned long gpu_freq = gmu->gpu_freqs[gmu->current_perf_index]; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci gpu_opp = dev_pm_opp_find_freq_exact(&gpu->pdev->dev, gpu_freq, true); 92062306a36Sopenharmony_ci if (IS_ERR(gpu_opp)) 92162306a36Sopenharmony_ci return; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci gmu->freq = 0; /* so a6xx_gmu_set_freq() doesn't exit early */ 92462306a36Sopenharmony_ci a6xx_gmu_set_freq(gpu, gpu_opp, false); 92562306a36Sopenharmony_ci dev_pm_opp_put(gpu_opp); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic void a6xx_gmu_set_initial_bw(struct msm_gpu *gpu, struct a6xx_gmu *gmu) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct dev_pm_opp *gpu_opp; 93162306a36Sopenharmony_ci unsigned long gpu_freq = gmu->gpu_freqs[gmu->current_perf_index]; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci gpu_opp = dev_pm_opp_find_freq_exact(&gpu->pdev->dev, gpu_freq, true); 93462306a36Sopenharmony_ci if (IS_ERR(gpu_opp)) 93562306a36Sopenharmony_ci return; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci dev_pm_opp_set_opp(&gpu->pdev->dev, gpu_opp); 93862306a36Sopenharmony_ci dev_pm_opp_put(gpu_opp); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ciint a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 94462306a36Sopenharmony_ci struct msm_gpu *gpu = &adreno_gpu->base; 94562306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 94662306a36Sopenharmony_ci int status, ret; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (WARN(!gmu->initialized, "The GMU is not set up yet\n")) 94962306a36Sopenharmony_ci return -EINVAL; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci gmu->hung = false; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Turn on the resources */ 95462306a36Sopenharmony_ci pm_runtime_get_sync(gmu->dev); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * "enable" the GX power domain which won't actually do anything but it 95862306a36Sopenharmony_ci * will make sure that the refcounting is correct in case we need to 95962306a36Sopenharmony_ci * bring down the GX after a GMU failure 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(gmu->gxpd)) 96262306a36Sopenharmony_ci pm_runtime_get_sync(gmu->gxpd); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Use a known rate to bring up the GMU */ 96562306a36Sopenharmony_ci clk_set_rate(gmu->core_clk, 200000000); 96662306a36Sopenharmony_ci clk_set_rate(gmu->hub_clk, 150000000); 96762306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks); 96862306a36Sopenharmony_ci if (ret) { 96962306a36Sopenharmony_ci pm_runtime_put(gmu->gxpd); 97062306a36Sopenharmony_ci pm_runtime_put(gmu->dev); 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* Set the bus quota to a reasonable value for boot */ 97562306a36Sopenharmony_ci a6xx_gmu_set_initial_bw(gpu, gmu); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* Enable the GMU interrupt */ 97862306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, ~0); 97962306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_MASK, ~A6XX_GMU_IRQ_MASK); 98062306a36Sopenharmony_ci enable_irq(gmu->gmu_irq); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* Check to see if we are doing a cold or warm boot */ 98362306a36Sopenharmony_ci status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ? 98462306a36Sopenharmony_ci GMU_WARM_BOOT : GMU_COLD_BOOT; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* 98762306a36Sopenharmony_ci * Warm boot path does not work on newer GPUs 98862306a36Sopenharmony_ci * Presumably this is because icache/dcache regions must be restored 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci if (!gmu->legacy) 99162306a36Sopenharmony_ci status = GMU_COLD_BOOT; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci ret = a6xx_gmu_fw_start(gmu, status); 99462306a36Sopenharmony_ci if (ret) 99562306a36Sopenharmony_ci goto out; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ret = a6xx_hfi_start(gmu, status); 99862306a36Sopenharmony_ci if (ret) 99962306a36Sopenharmony_ci goto out; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * Turn on the GMU firmware fault interrupt after we know the boot 100362306a36Sopenharmony_ci * sequence is successful 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, ~0); 100662306a36Sopenharmony_ci gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~A6XX_HFI_IRQ_MASK); 100762306a36Sopenharmony_ci enable_irq(gmu->hfi_irq); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* Set the GPU to the current freq */ 101062306a36Sopenharmony_ci a6xx_gmu_set_initial_freq(gpu, gmu); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ciout: 101362306a36Sopenharmony_ci /* On failure, shut down the GMU to leave it in a good state */ 101462306a36Sopenharmony_ci if (ret) { 101562306a36Sopenharmony_ci disable_irq(gmu->gmu_irq); 101662306a36Sopenharmony_ci a6xx_rpmh_stop(gmu); 101762306a36Sopenharmony_ci pm_runtime_put(gmu->gxpd); 101862306a36Sopenharmony_ci pm_runtime_put(gmu->dev); 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return ret; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cibool a6xx_gmu_isidle(struct a6xx_gmu *gmu) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci u32 reg; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (!gmu->initialized) 102962306a36Sopenharmony_ci return true; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci reg = gmu_read(gmu, REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (reg & A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS_GPUBUSYIGNAHB) 103462306a36Sopenharmony_ci return false; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci return true; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/* Gracefully try to shut down the GMU and by extension the GPU */ 104062306a36Sopenharmony_cistatic void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 104362306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 104462306a36Sopenharmony_ci u32 val; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* 104762306a36Sopenharmony_ci * The GMU may still be in slumber unless the GPU started so check and 104862306a36Sopenharmony_ci * skip putting it back into slumber if so 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_ci val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (val != 0xf) { 105362306a36Sopenharmony_ci int ret = a6xx_gmu_wait_for_idle(gmu); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* If the GMU isn't responding assume it is hung */ 105662306a36Sopenharmony_ci if (ret) { 105762306a36Sopenharmony_ci a6xx_gmu_force_off(gmu); 105862306a36Sopenharmony_ci return; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci a6xx_bus_clear_pending_transactions(adreno_gpu, a6xx_gpu->hung); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* tell the GMU we want to slumber */ 106462306a36Sopenharmony_ci ret = a6xx_gmu_notify_slumber(gmu); 106562306a36Sopenharmony_ci if (ret) { 106662306a36Sopenharmony_ci a6xx_gmu_force_off(gmu); 106762306a36Sopenharmony_ci return; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci ret = gmu_poll_timeout(gmu, 107162306a36Sopenharmony_ci REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, val, 107262306a36Sopenharmony_ci !(val & A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS_GPUBUSYIGNAHB), 107362306a36Sopenharmony_ci 100, 10000); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* 107662306a36Sopenharmony_ci * Let the user know we failed to slumber but don't worry too 107762306a36Sopenharmony_ci * much because we are powering down anyway 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (ret) 108162306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, 108262306a36Sopenharmony_ci "Unable to slumber GMU: status = 0%x/0%x\n", 108362306a36Sopenharmony_ci gmu_read(gmu, 108462306a36Sopenharmony_ci REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS), 108562306a36Sopenharmony_ci gmu_read(gmu, 108662306a36Sopenharmony_ci REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS2)); 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Turn off HFI */ 109062306a36Sopenharmony_ci a6xx_hfi_stop(gmu); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci /* Stop the interrupts and mask the hardware */ 109362306a36Sopenharmony_ci a6xx_gmu_irq_disable(gmu); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* Tell RPMh to power off the GPU */ 109662306a36Sopenharmony_ci a6xx_rpmh_stop(gmu); 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ciint a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 110362306a36Sopenharmony_ci struct msm_gpu *gpu = &a6xx_gpu->base.base; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (!pm_runtime_active(gmu->dev)) 110662306a36Sopenharmony_ci return 0; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* 110962306a36Sopenharmony_ci * Force the GMU off if we detected a hang, otherwise try to shut it 111062306a36Sopenharmony_ci * down gracefully 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ci if (gmu->hung) 111362306a36Sopenharmony_ci a6xx_gmu_force_off(gmu); 111462306a36Sopenharmony_ci else 111562306a36Sopenharmony_ci a6xx_gmu_shutdown(gmu); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* Remove the bus vote */ 111862306a36Sopenharmony_ci dev_pm_opp_set_opp(&gpu->pdev->dev, NULL); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* 112162306a36Sopenharmony_ci * Make sure the GX domain is off before turning off the GMU (CX) 112262306a36Sopenharmony_ci * domain. Usually the GMU does this but only if the shutdown sequence 112362306a36Sopenharmony_ci * was successful 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(gmu->gxpd)) 112662306a36Sopenharmony_ci pm_runtime_put_sync(gmu->gxpd); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci pm_runtime_put_sync(gmu->dev); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic void a6xx_gmu_memory_free(struct a6xx_gmu *gmu) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci msm_gem_kernel_put(gmu->hfi.obj, gmu->aspace); 113862306a36Sopenharmony_ci msm_gem_kernel_put(gmu->debug.obj, gmu->aspace); 113962306a36Sopenharmony_ci msm_gem_kernel_put(gmu->icache.obj, gmu->aspace); 114062306a36Sopenharmony_ci msm_gem_kernel_put(gmu->dcache.obj, gmu->aspace); 114162306a36Sopenharmony_ci msm_gem_kernel_put(gmu->dummy.obj, gmu->aspace); 114262306a36Sopenharmony_ci msm_gem_kernel_put(gmu->log.obj, gmu->aspace); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci gmu->aspace->mmu->funcs->detach(gmu->aspace->mmu); 114562306a36Sopenharmony_ci msm_gem_address_space_put(gmu->aspace); 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic int a6xx_gmu_memory_alloc(struct a6xx_gmu *gmu, struct a6xx_gmu_bo *bo, 114962306a36Sopenharmony_ci size_t size, u64 iova, const char *name) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 115262306a36Sopenharmony_ci struct drm_device *dev = a6xx_gpu->base.base.dev; 115362306a36Sopenharmony_ci uint32_t flags = MSM_BO_WC; 115462306a36Sopenharmony_ci u64 range_start, range_end; 115562306a36Sopenharmony_ci int ret; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci size = PAGE_ALIGN(size); 115862306a36Sopenharmony_ci if (!iova) { 115962306a36Sopenharmony_ci /* no fixed address - use GMU's uncached range */ 116062306a36Sopenharmony_ci range_start = 0x60000000 + PAGE_SIZE; /* skip dummy page */ 116162306a36Sopenharmony_ci range_end = 0x80000000; 116262306a36Sopenharmony_ci } else { 116362306a36Sopenharmony_ci /* range for fixed address */ 116462306a36Sopenharmony_ci range_start = iova; 116562306a36Sopenharmony_ci range_end = iova + size; 116662306a36Sopenharmony_ci /* use IOMMU_PRIV for icache/dcache */ 116762306a36Sopenharmony_ci flags |= MSM_BO_MAP_PRIV; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci bo->obj = msm_gem_new(dev, size, flags); 117162306a36Sopenharmony_ci if (IS_ERR(bo->obj)) 117262306a36Sopenharmony_ci return PTR_ERR(bo->obj); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci ret = msm_gem_get_and_pin_iova_range(bo->obj, gmu->aspace, &bo->iova, 117562306a36Sopenharmony_ci range_start, range_end); 117662306a36Sopenharmony_ci if (ret) { 117762306a36Sopenharmony_ci drm_gem_object_put(bo->obj); 117862306a36Sopenharmony_ci return ret; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci bo->virt = msm_gem_get_vaddr(bo->obj); 118262306a36Sopenharmony_ci bo->size = size; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci msm_gem_object_set_name(bo->obj, name); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic int a6xx_gmu_memory_probe(struct a6xx_gmu *gmu) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct msm_mmu *mmu; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci mmu = msm_iommu_new(gmu->dev, 0); 119462306a36Sopenharmony_ci if (!mmu) 119562306a36Sopenharmony_ci return -ENODEV; 119662306a36Sopenharmony_ci if (IS_ERR(mmu)) 119762306a36Sopenharmony_ci return PTR_ERR(mmu); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci gmu->aspace = msm_gem_address_space_create(mmu, "gmu", 0x0, 0x80000000); 120062306a36Sopenharmony_ci if (IS_ERR(gmu->aspace)) 120162306a36Sopenharmony_ci return PTR_ERR(gmu->aspace); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci return 0; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci/* Return the 'arc-level' for the given frequency */ 120762306a36Sopenharmony_cistatic unsigned int a6xx_gmu_get_arc_level(struct device *dev, 120862306a36Sopenharmony_ci unsigned long freq) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct dev_pm_opp *opp; 121162306a36Sopenharmony_ci unsigned int val; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (!freq) 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_exact(dev, freq, true); 121762306a36Sopenharmony_ci if (IS_ERR(opp)) 121862306a36Sopenharmony_ci return 0; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci val = dev_pm_opp_get_level(opp); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci dev_pm_opp_put(opp); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci return val; 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes, 122862306a36Sopenharmony_ci unsigned long *freqs, int freqs_count, const char *id) 122962306a36Sopenharmony_ci{ 123062306a36Sopenharmony_ci int i, j; 123162306a36Sopenharmony_ci const u16 *pri, *sec; 123262306a36Sopenharmony_ci size_t pri_count, sec_count; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci pri = cmd_db_read_aux_data(id, &pri_count); 123562306a36Sopenharmony_ci if (IS_ERR(pri)) 123662306a36Sopenharmony_ci return PTR_ERR(pri); 123762306a36Sopenharmony_ci /* 123862306a36Sopenharmony_ci * The data comes back as an array of unsigned shorts so adjust the 123962306a36Sopenharmony_ci * count accordingly 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci pri_count >>= 1; 124262306a36Sopenharmony_ci if (!pri_count) 124362306a36Sopenharmony_ci return -EINVAL; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci sec = cmd_db_read_aux_data("mx.lvl", &sec_count); 124662306a36Sopenharmony_ci if (IS_ERR(sec)) 124762306a36Sopenharmony_ci return PTR_ERR(sec); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci sec_count >>= 1; 125062306a36Sopenharmony_ci if (!sec_count) 125162306a36Sopenharmony_ci return -EINVAL; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* Construct a vote for each frequency */ 125462306a36Sopenharmony_ci for (i = 0; i < freqs_count; i++) { 125562306a36Sopenharmony_ci u8 pindex = 0, sindex = 0; 125662306a36Sopenharmony_ci unsigned int level = a6xx_gmu_get_arc_level(dev, freqs[i]); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* Get the primary index that matches the arc level */ 125962306a36Sopenharmony_ci for (j = 0; j < pri_count; j++) { 126062306a36Sopenharmony_ci if (pri[j] >= level) { 126162306a36Sopenharmony_ci pindex = j; 126262306a36Sopenharmony_ci break; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (j == pri_count) { 126762306a36Sopenharmony_ci DRM_DEV_ERROR(dev, 126862306a36Sopenharmony_ci "Level %u not found in the RPMh list\n", 126962306a36Sopenharmony_ci level); 127062306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "Available levels:\n"); 127162306a36Sopenharmony_ci for (j = 0; j < pri_count; j++) 127262306a36Sopenharmony_ci DRM_DEV_ERROR(dev, " %u\n", pri[j]); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci return -EINVAL; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * Look for a level in in the secondary list that matches. If 127962306a36Sopenharmony_ci * nothing fits, use the maximum non zero vote 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci for (j = 0; j < sec_count; j++) { 128362306a36Sopenharmony_ci if (sec[j] >= level) { 128462306a36Sopenharmony_ci sindex = j; 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci } else if (sec[j]) { 128762306a36Sopenharmony_ci sindex = j; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* Construct the vote */ 129262306a36Sopenharmony_ci votes[i] = ((pri[pindex] & 0xffff) << 16) | 129362306a36Sopenharmony_ci (sindex << 8) | pindex; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci return 0; 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci/* 130062306a36Sopenharmony_ci * The GMU votes with the RPMh for itself and on behalf of the GPU but we need 130162306a36Sopenharmony_ci * to construct the list of votes on the CPU and send it over. Query the RPMh 130262306a36Sopenharmony_ci * voltage levels and build the votes 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic int a6xx_gmu_rpmh_votes_init(struct a6xx_gmu *gmu) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 130862306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 130962306a36Sopenharmony_ci struct msm_gpu *gpu = &adreno_gpu->base; 131062306a36Sopenharmony_ci int ret; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* Build the GX votes */ 131362306a36Sopenharmony_ci ret = a6xx_gmu_rpmh_arc_votes_init(&gpu->pdev->dev, gmu->gx_arc_votes, 131462306a36Sopenharmony_ci gmu->gpu_freqs, gmu->nr_gpu_freqs, "gfx.lvl"); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci /* Build the CX votes */ 131762306a36Sopenharmony_ci ret |= a6xx_gmu_rpmh_arc_votes_init(gmu->dev, gmu->cx_arc_votes, 131862306a36Sopenharmony_ci gmu->gmu_freqs, gmu->nr_gmu_freqs, "cx.lvl"); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return ret; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic int a6xx_gmu_build_freq_table(struct device *dev, unsigned long *freqs, 132462306a36Sopenharmony_ci u32 size) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci int count = dev_pm_opp_get_opp_count(dev); 132762306a36Sopenharmony_ci struct dev_pm_opp *opp; 132862306a36Sopenharmony_ci int i, index = 0; 132962306a36Sopenharmony_ci unsigned long freq = 1; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci /* 133262306a36Sopenharmony_ci * The OPP table doesn't contain the "off" frequency level so we need to 133362306a36Sopenharmony_ci * add 1 to the table size to account for it 133462306a36Sopenharmony_ci */ 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (WARN(count + 1 > size, 133762306a36Sopenharmony_ci "The GMU frequency table is being truncated\n")) 133862306a36Sopenharmony_ci count = size - 1; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* Set the "off" frequency */ 134162306a36Sopenharmony_ci freqs[index++] = 0; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci for (i = 0; i < count; i++) { 134462306a36Sopenharmony_ci opp = dev_pm_opp_find_freq_ceil(dev, &freq); 134562306a36Sopenharmony_ci if (IS_ERR(opp)) 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci dev_pm_opp_put(opp); 134962306a36Sopenharmony_ci freqs[index++] = freq++; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci return index; 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic int a6xx_gmu_pwrlevels_probe(struct a6xx_gmu *gmu) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); 135862306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 135962306a36Sopenharmony_ci struct msm_gpu *gpu = &adreno_gpu->base; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci int ret = 0; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci /* 136462306a36Sopenharmony_ci * The GMU handles its own frequency switching so build a list of 136562306a36Sopenharmony_ci * available frequencies to send during initialization 136662306a36Sopenharmony_ci */ 136762306a36Sopenharmony_ci ret = devm_pm_opp_of_add_table(gmu->dev); 136862306a36Sopenharmony_ci if (ret) { 136962306a36Sopenharmony_ci DRM_DEV_ERROR(gmu->dev, "Unable to set the OPP table for the GMU\n"); 137062306a36Sopenharmony_ci return ret; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci gmu->nr_gmu_freqs = a6xx_gmu_build_freq_table(gmu->dev, 137462306a36Sopenharmony_ci gmu->gmu_freqs, ARRAY_SIZE(gmu->gmu_freqs)); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* 137762306a36Sopenharmony_ci * The GMU also handles GPU frequency switching so build a list 137862306a36Sopenharmony_ci * from the GPU OPP table 137962306a36Sopenharmony_ci */ 138062306a36Sopenharmony_ci gmu->nr_gpu_freqs = a6xx_gmu_build_freq_table(&gpu->pdev->dev, 138162306a36Sopenharmony_ci gmu->gpu_freqs, ARRAY_SIZE(gmu->gpu_freqs)); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci gmu->current_perf_index = gmu->nr_gpu_freqs - 1; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci /* Build the list of RPMh votes that we'll send to the GMU */ 138662306a36Sopenharmony_ci return a6xx_gmu_rpmh_votes_init(gmu); 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_cistatic int a6xx_gmu_clocks_probe(struct a6xx_gmu *gmu) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci int ret = devm_clk_bulk_get_all(gmu->dev, &gmu->clocks); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (ret < 1) 139462306a36Sopenharmony_ci return ret; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci gmu->nr_clocks = ret; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci gmu->core_clk = msm_clk_bulk_get_clock(gmu->clocks, 139962306a36Sopenharmony_ci gmu->nr_clocks, "gmu"); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci gmu->hub_clk = msm_clk_bulk_get_clock(gmu->clocks, 140262306a36Sopenharmony_ci gmu->nr_clocks, "hub"); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci return 0; 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic void __iomem *a6xx_gmu_get_mmio(struct platform_device *pdev, 140862306a36Sopenharmony_ci const char *name) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci void __iomem *ret; 141162306a36Sopenharmony_ci struct resource *res = platform_get_resource_byname(pdev, 141262306a36Sopenharmony_ci IORESOURCE_MEM, name); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (!res) { 141562306a36Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "Unable to find the %s registers\n", name); 141662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci ret = ioremap(res->start, resource_size(res)); 142062306a36Sopenharmony_ci if (!ret) { 142162306a36Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "Unable to map the %s registers\n", name); 142262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return ret; 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic int a6xx_gmu_get_irq(struct a6xx_gmu *gmu, struct platform_device *pdev, 142962306a36Sopenharmony_ci const char *name, irq_handler_t handler) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci int irq, ret; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, name); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci ret = request_irq(irq, handler, IRQF_TRIGGER_HIGH, name, gmu); 143662306a36Sopenharmony_ci if (ret) { 143762306a36Sopenharmony_ci DRM_DEV_ERROR(&pdev->dev, "Unable to get interrupt %s %d\n", 143862306a36Sopenharmony_ci name, ret); 143962306a36Sopenharmony_ci return ret; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci disable_irq(irq); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci return irq; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_civoid a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 145062306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 145162306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(gmu->dev); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci mutex_lock(&gmu->lock); 145462306a36Sopenharmony_ci if (!gmu->initialized) { 145562306a36Sopenharmony_ci mutex_unlock(&gmu->lock); 145662306a36Sopenharmony_ci return; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci gmu->initialized = false; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci mutex_unlock(&gmu->lock); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci pm_runtime_force_suspend(gmu->dev); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* 146662306a36Sopenharmony_ci * Since cxpd is a virt device, the devlink with gmu-dev will be removed 146762306a36Sopenharmony_ci * automatically when we do detach 146862306a36Sopenharmony_ci */ 146962306a36Sopenharmony_ci dev_pm_domain_detach(gmu->cxpd, false); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(gmu->gxpd)) { 147262306a36Sopenharmony_ci pm_runtime_disable(gmu->gxpd); 147362306a36Sopenharmony_ci dev_pm_domain_detach(gmu->gxpd, false); 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci iounmap(gmu->mmio); 147762306a36Sopenharmony_ci if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "rscc")) 147862306a36Sopenharmony_ci iounmap(gmu->rscc); 147962306a36Sopenharmony_ci gmu->mmio = NULL; 148062306a36Sopenharmony_ci gmu->rscc = NULL; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (!adreno_has_gmu_wrapper(adreno_gpu)) { 148362306a36Sopenharmony_ci a6xx_gmu_memory_free(gmu); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci free_irq(gmu->gmu_irq, gmu); 148662306a36Sopenharmony_ci free_irq(gmu->hfi_irq, gmu); 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci /* Drop reference taken in of_find_device_by_node */ 149062306a36Sopenharmony_ci put_device(gmu->dev); 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic int cxpd_notifier_cb(struct notifier_block *nb, 149462306a36Sopenharmony_ci unsigned long action, void *data) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci struct a6xx_gmu *gmu = container_of(nb, struct a6xx_gmu, pd_nb); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (action == GENPD_NOTIFY_OFF) 149962306a36Sopenharmony_ci complete_all(&gmu->pd_gate); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return 0; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ciint a6xx_gmu_wrapper_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node) 150562306a36Sopenharmony_ci{ 150662306a36Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(node); 150762306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 150862306a36Sopenharmony_ci int ret; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (!pdev) 151162306a36Sopenharmony_ci return -ENODEV; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci gmu->dev = &pdev->dev; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci of_dma_configure(gmu->dev, node, true); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci pm_runtime_enable(gmu->dev); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* Mark legacy for manual SPTPRAC control */ 152062306a36Sopenharmony_ci gmu->legacy = true; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* Map the GMU registers */ 152362306a36Sopenharmony_ci gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu"); 152462306a36Sopenharmony_ci if (IS_ERR(gmu->mmio)) { 152562306a36Sopenharmony_ci ret = PTR_ERR(gmu->mmio); 152662306a36Sopenharmony_ci goto err_mmio; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci gmu->cxpd = dev_pm_domain_attach_by_name(gmu->dev, "cx"); 153062306a36Sopenharmony_ci if (IS_ERR(gmu->cxpd)) { 153162306a36Sopenharmony_ci ret = PTR_ERR(gmu->cxpd); 153262306a36Sopenharmony_ci goto err_mmio; 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (!device_link_add(gmu->dev, gmu->cxpd, DL_FLAG_PM_RUNTIME)) { 153662306a36Sopenharmony_ci ret = -ENODEV; 153762306a36Sopenharmony_ci goto detach_cxpd; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci init_completion(&gmu->pd_gate); 154162306a36Sopenharmony_ci complete_all(&gmu->pd_gate); 154262306a36Sopenharmony_ci gmu->pd_nb.notifier_call = cxpd_notifier_cb; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* Get a link to the GX power domain to reset the GPU */ 154562306a36Sopenharmony_ci gmu->gxpd = dev_pm_domain_attach_by_name(gmu->dev, "gx"); 154662306a36Sopenharmony_ci if (IS_ERR(gmu->gxpd)) { 154762306a36Sopenharmony_ci ret = PTR_ERR(gmu->gxpd); 154862306a36Sopenharmony_ci goto err_mmio; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci gmu->initialized = true; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci return 0; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_cidetach_cxpd: 155662306a36Sopenharmony_ci dev_pm_domain_detach(gmu->cxpd, false); 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_cierr_mmio: 155962306a36Sopenharmony_ci iounmap(gmu->mmio); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci /* Drop reference taken in of_find_device_by_node */ 156262306a36Sopenharmony_ci put_device(gmu->dev); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci return ret; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ciint a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node) 156862306a36Sopenharmony_ci{ 156962306a36Sopenharmony_ci struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; 157062306a36Sopenharmony_ci struct a6xx_gmu *gmu = &a6xx_gpu->gmu; 157162306a36Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(node); 157262306a36Sopenharmony_ci int ret; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci if (!pdev) 157562306a36Sopenharmony_ci return -ENODEV; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci gmu->dev = &pdev->dev; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci of_dma_configure(gmu->dev, node, true); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci /* Fow now, don't do anything fancy until we get our feet under us */ 158262306a36Sopenharmony_ci gmu->idle_level = GMU_IDLE_STATE_ACTIVE; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci pm_runtime_enable(gmu->dev); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci /* Get the list of clocks */ 158762306a36Sopenharmony_ci ret = a6xx_gmu_clocks_probe(gmu); 158862306a36Sopenharmony_ci if (ret) 158962306a36Sopenharmony_ci goto err_put_device; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci ret = a6xx_gmu_memory_probe(gmu); 159262306a36Sopenharmony_ci if (ret) 159362306a36Sopenharmony_ci goto err_put_device; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci /* A660 now requires handling "prealloc requests" in GMU firmware 159762306a36Sopenharmony_ci * For now just hardcode allocations based on the known firmware. 159862306a36Sopenharmony_ci * note: there is no indication that these correspond to "dummy" or 159962306a36Sopenharmony_ci * "debug" regions, but this "guess" allows reusing these BOs which 160062306a36Sopenharmony_ci * are otherwise unused by a660. 160162306a36Sopenharmony_ci */ 160262306a36Sopenharmony_ci gmu->dummy.size = SZ_4K; 160362306a36Sopenharmony_ci if (adreno_is_a660_family(adreno_gpu)) { 160462306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_4K * 7, 160562306a36Sopenharmony_ci 0x60400000, "debug"); 160662306a36Sopenharmony_ci if (ret) 160762306a36Sopenharmony_ci goto err_memory; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci gmu->dummy.size = SZ_8K; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci /* Allocate memory for the GMU dummy page */ 161362306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->dummy, gmu->dummy.size, 161462306a36Sopenharmony_ci 0x60000000, "dummy"); 161562306a36Sopenharmony_ci if (ret) 161662306a36Sopenharmony_ci goto err_memory; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* Note that a650 family also includes a660 family: */ 161962306a36Sopenharmony_ci if (adreno_is_a650_family(adreno_gpu)) { 162062306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->icache, 162162306a36Sopenharmony_ci SZ_16M - SZ_16K, 0x04000, "icache"); 162262306a36Sopenharmony_ci if (ret) 162362306a36Sopenharmony_ci goto err_memory; 162462306a36Sopenharmony_ci /* 162562306a36Sopenharmony_ci * NOTE: when porting legacy ("pre-650-family") GPUs you may be tempted to add a condition 162662306a36Sopenharmony_ci * to allocate icache/dcache here, as per downstream code flow, but it may not actually be 162762306a36Sopenharmony_ci * necessary. If you omit this step and you don't get random pagefaults, you are likely 162862306a36Sopenharmony_ci * good to go without this! 162962306a36Sopenharmony_ci */ 163062306a36Sopenharmony_ci } else if (adreno_is_a640_family(adreno_gpu)) { 163162306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->icache, 163262306a36Sopenharmony_ci SZ_256K - SZ_16K, 0x04000, "icache"); 163362306a36Sopenharmony_ci if (ret) 163462306a36Sopenharmony_ci goto err_memory; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->dcache, 163762306a36Sopenharmony_ci SZ_256K - SZ_16K, 0x44000, "dcache"); 163862306a36Sopenharmony_ci if (ret) 163962306a36Sopenharmony_ci goto err_memory; 164062306a36Sopenharmony_ci } else if (adreno_is_a630_family(adreno_gpu)) { 164162306a36Sopenharmony_ci /* HFI v1, has sptprac */ 164262306a36Sopenharmony_ci gmu->legacy = true; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci /* Allocate memory for the GMU debug region */ 164562306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_16K, 0, "debug"); 164662306a36Sopenharmony_ci if (ret) 164762306a36Sopenharmony_ci goto err_memory; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci /* Allocate memory for the GMU log region */ 165162306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->log, SZ_16K, 0, "log"); 165262306a36Sopenharmony_ci if (ret) 165362306a36Sopenharmony_ci goto err_memory; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci /* Allocate memory for for the HFI queues */ 165662306a36Sopenharmony_ci ret = a6xx_gmu_memory_alloc(gmu, &gmu->hfi, SZ_16K, 0, "hfi"); 165762306a36Sopenharmony_ci if (ret) 165862306a36Sopenharmony_ci goto err_memory; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci /* Map the GMU registers */ 166162306a36Sopenharmony_ci gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu"); 166262306a36Sopenharmony_ci if (IS_ERR(gmu->mmio)) { 166362306a36Sopenharmony_ci ret = PTR_ERR(gmu->mmio); 166462306a36Sopenharmony_ci goto err_memory; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (adreno_is_a650_family(adreno_gpu)) { 166862306a36Sopenharmony_ci gmu->rscc = a6xx_gmu_get_mmio(pdev, "rscc"); 166962306a36Sopenharmony_ci if (IS_ERR(gmu->rscc)) { 167062306a36Sopenharmony_ci ret = -ENODEV; 167162306a36Sopenharmony_ci goto err_mmio; 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci } else { 167462306a36Sopenharmony_ci gmu->rscc = gmu->mmio + 0x23000; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci /* Get the HFI and GMU interrupts */ 167862306a36Sopenharmony_ci gmu->hfi_irq = a6xx_gmu_get_irq(gmu, pdev, "hfi", a6xx_hfi_irq); 167962306a36Sopenharmony_ci gmu->gmu_irq = a6xx_gmu_get_irq(gmu, pdev, "gmu", a6xx_gmu_irq); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (gmu->hfi_irq < 0 || gmu->gmu_irq < 0) { 168262306a36Sopenharmony_ci ret = -ENODEV; 168362306a36Sopenharmony_ci goto err_mmio; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci gmu->cxpd = dev_pm_domain_attach_by_name(gmu->dev, "cx"); 168762306a36Sopenharmony_ci if (IS_ERR(gmu->cxpd)) { 168862306a36Sopenharmony_ci ret = PTR_ERR(gmu->cxpd); 168962306a36Sopenharmony_ci goto err_mmio; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (!device_link_add(gmu->dev, gmu->cxpd, 169362306a36Sopenharmony_ci DL_FLAG_PM_RUNTIME)) { 169462306a36Sopenharmony_ci ret = -ENODEV; 169562306a36Sopenharmony_ci goto detach_cxpd; 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci init_completion(&gmu->pd_gate); 169962306a36Sopenharmony_ci complete_all(&gmu->pd_gate); 170062306a36Sopenharmony_ci gmu->pd_nb.notifier_call = cxpd_notifier_cb; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* 170362306a36Sopenharmony_ci * Get a link to the GX power domain to reset the GPU in case of GMU 170462306a36Sopenharmony_ci * crash 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_ci gmu->gxpd = dev_pm_domain_attach_by_name(gmu->dev, "gx"); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* Get the power levels for the GMU and GPU */ 170962306a36Sopenharmony_ci a6xx_gmu_pwrlevels_probe(gmu); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci /* Set up the HFI queues */ 171262306a36Sopenharmony_ci a6xx_hfi_init(gmu); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* Initialize RPMh */ 171562306a36Sopenharmony_ci a6xx_gmu_rpmh_init(gmu); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci gmu->initialized = true; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci return 0; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_cidetach_cxpd: 172262306a36Sopenharmony_ci dev_pm_domain_detach(gmu->cxpd, false); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cierr_mmio: 172562306a36Sopenharmony_ci iounmap(gmu->mmio); 172662306a36Sopenharmony_ci if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "rscc")) 172762306a36Sopenharmony_ci iounmap(gmu->rscc); 172862306a36Sopenharmony_ci free_irq(gmu->gmu_irq, gmu); 172962306a36Sopenharmony_ci free_irq(gmu->hfi_irq, gmu); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cierr_memory: 173262306a36Sopenharmony_ci a6xx_gmu_memory_free(gmu); 173362306a36Sopenharmony_cierr_put_device: 173462306a36Sopenharmony_ci /* Drop reference taken in of_find_device_by_node */ 173562306a36Sopenharmony_ci put_device(gmu->dev); 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci return ret; 173862306a36Sopenharmony_ci} 1739