msm/drm/a6xx: Turn off the GMU if resume fails
Currently if the GMU resume function fails all we try to do is clear the BOOT_SLUMBER oob which usually times out and ends up in a cycle of death. If the resume function fails at any point remove any RPMh votes that might have been added and try to shut down the GMU hardware cleanly. Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org> Signed-off-by: Rob Clark <robdclark@chromium.org>
This commit is contained in:
		
							parent
							
								
									e31fdb74c1
								
							
						
					
					
						commit
						41570b747c
					
				| @ -648,20 +648,6 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) | ||||
| 	 A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR | \ | ||||
| 	 A6XX_GMU_AO_HOST_INTERRUPT_STATUS_FENCE_ERR) | ||||
| 
 | ||||
| static void a6xx_gmu_irq_enable(struct a6xx_gmu *gmu) | ||||
| { | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, ~0); | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, ~0); | ||||
| 
 | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_MASK, | ||||
| 		~A6XX_GMU_IRQ_MASK); | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, | ||||
| 		~A6XX_HFI_IRQ_MASK); | ||||
| 
 | ||||
| 	enable_irq(gmu->gmu_irq); | ||||
| 	enable_irq(gmu->hfi_irq); | ||||
| } | ||||
| 
 | ||||
| static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu) | ||||
| { | ||||
| 	disable_irq(gmu->gmu_irq); | ||||
| @ -671,20 +657,10 @@ static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu) | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~0); | ||||
| } | ||||
| 
 | ||||
| /* Force the GMU off in case it isn't responsive */ | ||||
| static void a6xx_gmu_force_off(struct a6xx_gmu *gmu) | ||||
| static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	/* Flush all the queues */ | ||||
| 	a6xx_hfi_stop(gmu); | ||||
| 
 | ||||
| 	/* Stop the interrupts */ | ||||
| 	a6xx_gmu_irq_disable(gmu); | ||||
| 
 | ||||
| 	/* Force off SPTP in case the GMU is managing it */ | ||||
| 	a6xx_sptprac_disable(gmu); | ||||
| 
 | ||||
| 	/* Make sure there are no outstanding RPMh votes */ | ||||
| 	gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS0_DRV0_STATUS, val, | ||||
| 		(val & 1), 100, 10000); | ||||
| @ -696,6 +672,22 @@ static void a6xx_gmu_force_off(struct a6xx_gmu *gmu) | ||||
| 		(val & 1), 100, 1000); | ||||
| } | ||||
| 
 | ||||
| /* Force the GMU off in case it isn't responsive */ | ||||
| static void a6xx_gmu_force_off(struct a6xx_gmu *gmu) | ||||
| { | ||||
| 	/* Flush all the queues */ | ||||
| 	a6xx_hfi_stop(gmu); | ||||
| 
 | ||||
| 	/* Stop the interrupts */ | ||||
| 	a6xx_gmu_irq_disable(gmu); | ||||
| 
 | ||||
| 	/* Force off SPTP in case the GMU is managing it */ | ||||
| 	a6xx_sptprac_disable(gmu); | ||||
| 
 | ||||
| 	/* Make sure there are no outstanding RPMh votes */ | ||||
| 	a6xx_gmu_rpmh_off(gmu); | ||||
| } | ||||
| 
 | ||||
| int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) | ||||
| { | ||||
| 	struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; | ||||
| @ -714,13 +706,18 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) | ||||
| 	/* Use a known rate to bring up the GMU */ | ||||
| 	clk_set_rate(gmu->core_clk, 200000000); | ||||
| 	ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks); | ||||
| 	if (ret) | ||||
| 		goto out; | ||||
| 	if (ret) { | ||||
| 		pm_runtime_put(gmu->dev); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Set the bus quota to a reasonable value for boot */ | ||||
| 	icc_set_bw(gpu->icc_path, 0, MBps_to_icc(3072)); | ||||
| 
 | ||||
| 	a6xx_gmu_irq_enable(gmu); | ||||
| 	/* Enable the GMU interrupt */ | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, ~0); | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_MASK, ~A6XX_GMU_IRQ_MASK); | ||||
| 	enable_irq(gmu->gmu_irq); | ||||
| 
 | ||||
| 	/* Check to see if we are doing a cold or warm boot */ | ||||
| 	status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ? | ||||
| @ -731,6 +728,16 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	ret = a6xx_hfi_start(gmu, status); | ||||
| 	if (ret) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Turn on the GMU firmware fault interrupt after we know the boot | ||||
| 	 * sequence is successful | ||||
| 	 */ | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, ~0); | ||||
| 	gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~A6XX_HFI_IRQ_MASK); | ||||
| 	enable_irq(gmu->hfi_irq); | ||||
| 
 | ||||
| 	/* Set the GPU to the highest power frequency */ | ||||
| 	__a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1); | ||||
| @ -744,9 +751,12 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) | ||||
| 		pm_runtime_get(gmu->gxpd); | ||||
| 
 | ||||
| out: | ||||
| 	/* Make sure to turn off the boot OOB request on error */ | ||||
| 	if (ret) | ||||
| 		a6xx_gmu_clear_oob(gmu, GMU_OOB_BOOT_SLUMBER); | ||||
| 	/* On failure, shut down the GMU to leave it in a good state */ | ||||
| 	if (ret) { | ||||
| 		disable_irq(gmu->gmu_irq); | ||||
| 		a6xx_rpmh_stop(gmu); | ||||
| 		pm_runtime_put(gmu->dev); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| @ -769,6 +779,9 @@ bool a6xx_gmu_isidle(struct a6xx_gmu *gmu) | ||||
| /* Gracefully try to shut down the GMU and by extension the GPU */ | ||||
| static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) | ||||
| { | ||||
| 	struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); | ||||
| 	struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; | ||||
| 	struct msm_gpu *gpu = &adreno_gpu->base; | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	/*
 | ||||
| @ -786,6 +799,12 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu) | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		/* Clear the VBIF pipe before shutting down */ | ||||
| 		gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0xf); | ||||
| 		spin_until((gpu_read(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL1) & 0xf) | ||||
| 			== 0xf); | ||||
| 		gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0); | ||||
| 
 | ||||
| 		/* tell the GMU we want to slumber */ | ||||
| 		a6xx_gmu_notify_slumber(gmu); | ||||
| 
 | ||||
| @ -824,6 +843,9 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) | ||||
| 	struct a6xx_gmu *gmu = &a6xx_gpu->gmu; | ||||
| 	struct msm_gpu *gpu = &a6xx_gpu->base.base; | ||||
| 
 | ||||
| 	if (!pm_runtime_active(gmu->dev)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Force the GMU off if we detected a hang, otherwise try to shut it | ||||
| 	 * down gracefully | ||||
|  | ||||
| @ -678,13 +678,15 @@ static int a6xx_pm_resume(struct msm_gpu *gpu) | ||||
| 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = a6xx_gmu_resume(a6xx_gpu); | ||||
| 
 | ||||
| 	gpu->needs_hw_init = true; | ||||
| 
 | ||||
| 	ret = a6xx_gmu_resume(a6xx_gpu); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	msm_gpu_resume_devfreq(gpu); | ||||
| 
 | ||||
| 	return ret; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int a6xx_pm_suspend(struct msm_gpu *gpu) | ||||
| @ -694,18 +696,6 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu) | ||||
| 
 | ||||
| 	devfreq_suspend_device(gpu->devfreq.devfreq); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make sure the GMU is idle before continuing (because some transitions | ||||
| 	 * may use VBIF | ||||
| 	 */ | ||||
| 	a6xx_gmu_wait_for_idle(&a6xx_gpu->gmu); | ||||
| 
 | ||||
| 	/* Clear the VBIF pipe before shutting down */ | ||||
| 	/* FIXME: This accesses the GPU - do we need to make sure it is on? */ | ||||
| 	gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0xf); | ||||
| 	spin_until((gpu_read(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL1) & 0xf) == 0xf); | ||||
| 	gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0); | ||||
| 
 | ||||
| 	return a6xx_gmu_stop(a6xx_gpu); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -229,6 +229,7 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev) | ||||
| 
 | ||||
| 	ret = pm_runtime_get_sync(&pdev->dev); | ||||
| 	if (ret < 0) { | ||||
| 		pm_runtime_put_sync(&pdev->dev); | ||||
| 		DRM_DEV_ERROR(dev->dev, "Couldn't power up the GPU: %d\n", ret); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user