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