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

Commit cf55519e authored by Yizheng Zhou's avatar Yizheng Zhou Committed by Tarun Karra
Browse files

msm: kgsl: GPMU interrupt handling code



This patch updates KGSL interrupt code to handle GPMU voltage
droop and watchdog bite. Bit 28 of the RBBM interrupt status
register indicates a voltage droop issue, and driver needs to
print out an error message. Bit 29 of this status register
together with Bit 31 of the GPMU interrupt information register
indicate a GPMU watchdog timeout, which requires a reset of GPMU.

Change-Id: I72a5ecf74d694979ce1b90cba58a255710caa8a0
Signed-off-by: default avatarYizheng Zhou <yizhengz@codeaurora.org>
parent a3eaa1c6
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -43,8 +43,8 @@
#define A5XX_INT_UCHE_TRAP_INTR          25
#define A5XX_INT_DEBBUS_INTR_0           26
#define A5XX_INT_DEBBUS_INTR_1           27
#define A5XX_INT_GPMU_ERROR              28
#define A5XX_INT_GPMU_THERMAL            29
#define A5XX_INT_GPMU_VOLTAGE_DROOP      28
#define A5XX_INT_GPMU_FIRMWARE           29
#define A5XX_INT_ISDB_CPU_IRQ            30
#define A5XX_INT_ISDB_UNDER_DEBUG        31

@@ -777,6 +777,7 @@
#define A5XX_GPMU_PWR_COL_INTER_FRAME_HYST 0xA893
#define A5XX_GPMU_PWR_COL_BINNING_CTRL     0xA894
#define A5XX_GPMU_WFI_CONFIG               0xA8C1
#define A5XX_GPMU_RBBM_INTR_INFO           0xA8D6
#define A5XX_GPMU_CM3_SYSRESET             0xA8D8
#define A5XX_GPMU_GENERAL_0                0xA8E0
#define A5XX_GPMU_GENERAL_1                0xA8E1
+1 −0
Original line number Diff line number Diff line
@@ -337,6 +337,7 @@ struct adreno_device {
	uint32_t *lm_sequence;
	uint32_t lm_size;
	struct kgsl_memdesc preemption_counters;
	struct work_struct gpmu_work;
};

/**
+90 −8
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = {

#define PREEMPT_SMMU_RECORD(_field) \
		offsetof(struct a5xx_cp_smmu_info, _field)
static void a5xx_gpmu_reset(struct work_struct *work);

/**
 * Number of times to check if the regulator enabled before
@@ -344,6 +345,9 @@ static void a5xx_gpudev_init(struct adreno_device *adreno_dev)
				A510_VBIF_XIN_HALT_CTRL0_MASK;
	}

	if (adreno_is_a530(adreno_dev) && !adreno_is_a530v1(adreno_dev))
		INIT_WORK(&adreno_dev->gpmu_work, a5xx_gpmu_reset);

	/* Calculate SP local and private mem addresses */
	addr = ALIGN(ADRENO_UCHE_GMEM_BASE + adreno_dev->gmem_size, SZ_64K);
	adreno_dev->sp_local_gpuaddr = addr;
@@ -1232,7 +1236,7 @@ static void a5xx_lm_init(struct adreno_device *adreno_dev)
	kgsl_regwrite(device, A5XX_GPMU_TEMP_SENSOR_CONFIG, 0x1);

	kgsl_regwrite(device, A5XX_GPMU_GPMU_VOLTAGE,
			(0x80000000 | device->pwrctrl.default_pwrlevel));
			(0x80000000 | device->pwrctrl.active_pwrlevel));
	/* todo use the iddq fuse to correct this value at runtime */
	kgsl_regwrite(device, A5XX_GPMU_BASE_LEAKAGE, 0x00640002);
	/* default of 6A */
@@ -1348,6 +1352,55 @@ static void a5xx_enable_64bit(struct adreno_device *adreno_dev)
	kgsl_regwrite(device, A5XX_RBBM_SECVID_TSB_ADDR_MODE_CNTL, 0x1);
}

/*
 * a5xx_gpmu_reset() - Re-enable GPMU based power features and restart GPMU
 * @work: Pointer to the work struct for gpmu reset
 *
 * Load the GPMU microcode, set up any features such as hardware clock gating
 * or IFPC, and take the GPMU out of reset.
 */
static void a5xx_gpmu_reset(struct work_struct *work)
{
	struct adreno_device *adreno_dev = container_of(work,
			struct adreno_device, gpmu_work);
	struct kgsl_device *device = &adreno_dev->dev;

	if (test_bit(ADRENO_DEVICE_GPMU_INITIALIZED, &adreno_dev->priv))
		return;

	/*
	 * If GPMU has already experienced a restart or is in the process of it
	 * after the watchdog timeout, then there is no need to reset GPMU
	 * again.
	 */
	if (device->state != KGSL_STATE_NAP &&
		device->state != KGSL_STATE_AWARE &&
		device->state != KGSL_STATE_ACTIVE)
		return;

	mutex_lock(&device->mutex);

	if (device->state == KGSL_STATE_NAP)
		kgsl_pwrctrl_change_state(device, KGSL_STATE_AWARE);

	if (a5xx_regulator_enable(adreno_dev))
		goto out;

	/* Soft reset of the GPMU block */
	kgsl_regwrite(device, A5XX_RBBM_BLOCK_SW_RESET_CMD, BIT(16));

	a5xx_lm_init(adreno_dev);

	a5xx_enable_pc(adreno_dev);

	a5xx_gpmu_start(adreno_dev);

	a5xx_lm_enable(adreno_dev);

out:
	mutex_unlock(&device->mutex);
}

/*
 * a5xx_start() - Device start
 * @adreno_dev: Pointer to adreno device
@@ -2404,11 +2457,38 @@ void a5xx_err_callback(struct adreno_device *adreno_dev, int bit)
	case A5XX_INT_UCHE_TRAP_INTR:
		KGSL_DRV_CRIT_RATELIMIT(device, "UCHE: Trap interrupt\n");
		break;
	case A5XX_INT_GPMU_VOLTAGE_DROOP:
		KGSL_DRV_CRIT_RATELIMIT(device, "GPMU: Voltage droop\n");
		break;
	default:
		KGSL_DRV_CRIT_RATELIMIT(device, "Unknown interrupt %d\n", bit);
	}
}

static void a5xx_gpmu_int_callback(struct adreno_device *adreno_dev, int bit)
{
	struct kgsl_device *device = &adreno_dev->dev;
	unsigned int reg;

	kgsl_regread(device, A5XX_GPMU_RBBM_INTR_INFO, &reg);

	if (reg & BIT(31)) {
		if (test_and_clear_bit(ADRENO_DEVICE_GPMU_INITIALIZED,
					&adreno_dev->priv)) {
			/* Stop GPMU */
			kgsl_regwrite(device, A5XX_GPMU_CM3_SYSRESET, 1);

			queue_work(device->work_queue, &adreno_dev->gpmu_work);

			KGSL_DRV_CRIT_RATELIMIT(device,
						"GPMU: Watchdog bite\n");
		}
	} else
		KGSL_DRV_CRIT_RATELIMIT(device,
					"GPMU: Unknown interrupt 0x%08X\n",
					reg);
}

#define A5XX_INT_MASK \
	((1 << A5XX_INT_RBBM_AHB_ERROR) |		\
	 (1 << A5XX_INT_RBBM_TRANSFER_TIMEOUT) |		\
@@ -2422,9 +2502,11 @@ void a5xx_err_callback(struct adreno_device *adreno_dev, int bit)
	 (1 << A5XX_INT_CP_IB2) |			\
	 (1 << A5XX_INT_CP_RB) |			\
	 (1 << A5XX_INT_RBBM_ATB_BUS_OVERFLOW) |	\
	 (1 << A5XX_INT_UCHE_OOB_ACCESS)) |		\
	 (1 << A5XX_INT_UCHE_OOB_ACCESS) |		\
	 (1 << A5XX_INT_UCHE_TRAP_INTR) |		\
	 (1 << A5XX_INT_CP_SW)
	 (1 << A5XX_INT_CP_SW) |			\
	 (1 << A5XX_INT_GPMU_VOLTAGE_DROOP) |		\
	 (1 << A5XX_INT_GPMU_FIRMWARE))


static struct adreno_irq_funcs a5xx_irq_funcs[] = {
@@ -2464,10 +2546,10 @@ static struct adreno_irq_funcs a5xx_irq_funcs[] = {
	ADRENO_IRQ_CALLBACK(adreno_hang_int_callback),
	ADRENO_IRQ_CALLBACK(a5xx_err_callback), /* 24 - UCHE_OOB_ACCESS */
	ADRENO_IRQ_CALLBACK(a5xx_err_callback), /* 25 - UCHE_TRAP_INTR */
	ADRENO_IRQ_CALLBACK(NULL), /* 27 - DEBBUS_INTR_0 */
	ADRENO_IRQ_CALLBACK(NULL), /* 28 - DEBBUS_INTR_1 */
	ADRENO_IRQ_CALLBACK(NULL), /* 29 - GPMU_ERROR */
	ADRENO_IRQ_CALLBACK(NULL), /* 29 - GPMU_THERMAL */
	ADRENO_IRQ_CALLBACK(NULL), /* 26 - DEBBUS_INTR_0 */
	ADRENO_IRQ_CALLBACK(NULL), /* 27 - DEBBUS_INTR_1 */
	ADRENO_IRQ_CALLBACK(a5xx_err_callback), /* 28 - GPMU_VOLTAGE_DROOP */
	ADRENO_IRQ_CALLBACK(a5xx_gpmu_int_callback), /* 29 - GPMU_FIRMWARE */
	ADRENO_IRQ_CALLBACK(NULL), /* 30 - ISDB_CPU_IRQ */
	ADRENO_IRQ_CALLBACK(NULL), /* 31 - ISDB_UNDER_DEBUG */
};
+4 −4
Original line number Diff line number Diff line
@@ -669,10 +669,10 @@ TRACE_EVENT(kgsl_a5xx_irq_status,
				"DEBBUS_INTR_0" },
			{ 1 << A5XX_INT_DEBBUS_INTR_1,
				"DEBBUS_INTR_1" },
			{ 1 << A5XX_INT_GPMU_ERROR,
				"GPMU_ERROR" },
			{ 1 << A5XX_INT_GPMU_THERMAL,
				"GPMU_THERMAL" },
			{ 1 << A5XX_INT_GPMU_VOLTAGE_DROOP,
				"GPMU_VOLTAGE_DROOP" },
			{ 1 << A5XX_INT_GPMU_FIRMWARE,
				"GPMU_FIRMWARE" },
			{ 1 << A5XX_INT_ISDB_CPU_IRQ,
				"ISDB_CPU_IRQ" },
			{ 1 << A5XX_INT_ISDB_UNDER_DEBUG,