Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b6a673a authored by Kyle Piefer's avatar Kyle Piefer Committed by Raghu Ananya Arabolu
Browse files

msm: kgsl: Dump IFPC abort data after timeout



When KGSL times out waiting for the lowest idle level,
it usually means that the GMU is stuck in a IFPC
abort loop. To make this situation easier to debug,
read and print out the values of the registers
that the GMU checks before choosing to abort.

Change-Id: Ic30f4bc766f72a9eb1fbc2bb94be70ef5c0252ba
Signed-off-by: default avatarKyle Piefer <kpiefer@codeaurora.org>
Signed-off-by: default avatarRaghu Ananya Arabolu <rarabolu@codeaurora.org>
parent b12776c8
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -50,9 +50,11 @@
#define A6XX_CP_RB_RPTR                  0x806
#define A6XX_CP_RB_WPTR                  0x807
#define A6XX_CP_SQE_CNTL                 0x808
#define A6XX_CP_CP2GMU_STATUS            0x812
#define A6XX_CP_HW_FAULT                 0x821
#define A6XX_CP_INTERRUPT_STATUS         0x823
#define A6XX_CP_PROTECT_STATUS           0X824
#define A6XX_CP_PROTECT_STATUS           0x824
#define A6XX_CP_STATUS_1                 0x825
#define A6XX_CP_SQE_INSTR_BASE_LO        0x830
#define A6XX_CP_SQE_INSTR_BASE_HI        0x831
#define A6XX_CP_MISC_CNTL                0x840
+32 −3
Original line number Diff line number Diff line
@@ -849,7 +849,7 @@ static bool idle_trandition_complete(unsigned int idle_level,
static int a6xx_gmu_wait_for_lowest_idle(struct kgsl_device *device)
{
	struct gmu_device *gmu = KGSL_GMU_DEVICE(device);
	unsigned int reg, reg1;
	unsigned int reg, reg1, reg2, reg3, reg4, reg5, reg6, reg7, reg8;
	unsigned long t;
	uint64_t ts1, ts2, ts3;

@@ -881,9 +881,38 @@ static int a6xx_gmu_wait_for_lowest_idle(struct kgsl_device *device)
		return 0;

	ts3 = read_AO_counter(device);
	WARN(1, "Timeout waiting for lowest idle: %08x %llx %llx %llx %x\n",
		reg, ts1, ts2, ts3, reg1);

	/* Collect abort data to help with debugging */
	gmu_core_regread(device, A6XX_GPU_GMU_AO_GPU_CX_BUSY_STATUS, &reg2);
	gmu_core_regread(device, A6XX_CP_STATUS_1, &reg3);
	gmu_core_regread(device, A6XX_GMU_RBBM_INT_UNMASKED_STATUS, &reg4);
	gmu_core_regread(device, A6XX_GMU_GMU_PWR_COL_KEEPALIVE, &reg5);
	gmu_core_regread(device, A6XX_CP_CP2GMU_STATUS, &reg6);
	gmu_core_regread(device, A6XX_CP_CONTEXT_SWITCH_CNTL, &reg7);
	gmu_core_regread(device, A6XX_GMU_AO_SPARE_CNTL, &reg8);

	dev_err(&gmu->pdev->dev,
		"----------------------[ GMU error ]----------------------\n");
	dev_err(&gmu->pdev->dev,
		"Timeout waiting for lowest idle level %s\n",
		gpu_idle_level_names[gmu->idle_level]);
	dev_err(&gmu->pdev->dev, "Start: %llx (absolute ticks)\n", ts1);
	dev_err(&gmu->pdev->dev, "Poll: %llx (ticks relative to start)\n",
		ts2-ts1);
	dev_err(&gmu->pdev->dev, "Retry: %llx (ticks relative to poll)\n",
		ts3-ts2);
	dev_err(&gmu->pdev->dev,
		"RPMH_POWER_STATE=%x SPTPRAC_PWR_CLK_STATUS=%x\n", reg, reg1);
	dev_err(&gmu->pdev->dev,
		"CX_BUSY_STATUS=%x CP_STATUS_1=%x\n", reg2, reg3);
	dev_err(&gmu->pdev->dev,
		"RBBM_INT_UNMASKED_STATUS=%x PWR_COL_KEEPALIVE=%x\n",
		reg4, reg5);
	dev_err(&gmu->pdev->dev,
		"CP2GMU_STATUS=%x CONTEXT_SWITCH_CNTL=%x AO_SPARE_CNTL=%x\n",
		reg6, reg7, reg8);

	WARN_ON(1);
	return -ETIMEDOUT;
}

+10 −0
Original line number Diff line number Diff line
@@ -88,6 +88,16 @@ enum gpu_idle_level {
	GPU_HW_SLUMBER = 0xF
};

static const char * const gpu_idle_level_names[] = {
	[GPU_HW_ACTIVE] = "GPU_HW_ACTIVE",
	[GPU_HW_SPTP_PC] = "GPU_HW_SPTP_PC",
	[GPU_HW_IFPC] = "GPU_HW_IFPC",
	[GPU_HW_NAP] = "GPU_HW_NAP",
	[GPU_HW_MIN_VOLT] = "GPU_HW_MIN_VOLT",
	[GPU_HW_MIN_DDR] = "GPU_HW_MIN_DDR",
	[GPU_HW_SLUMBER] = "GPU_HW_SLUMBER"
};

/*
 * Wait time before trying to write the register again.
 * Hopefully the GMU has finished waking up during this delay.