summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/a6xx_gmu.c')
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.c209
1 files changed, 155 insertions, 54 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 7923129363b0..8c4900444b2c 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. */
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/of_platform.h>
@@ -202,9 +203,10 @@ int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu)
static int a6xx_gmu_start(struct a6xx_gmu *gmu)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ u32 mask, reset_val, val;
int ret;
- u32 val;
- u32 mask, reset_val;
val = gmu_read(gmu, REG_A6XX_GMU_CM3_DTCM_START + 0xff8);
if (val <= 0x20010004) {
@@ -220,7 +222,11 @@ static int a6xx_gmu_start(struct a6xx_gmu *gmu)
/* Set the log wptr index
* note: downstream saves the value in poweroff and restores it here
*/
- gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_RESP, 0);
+ if (adreno_is_a7xx(adreno_gpu))
+ gmu_write(gmu, REG_A6XX_GMU_GENERAL_9, 0);
+ else
+ gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_RESP, 0);
+
gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 0);
@@ -513,6 +519,7 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct platform_device *pdev = to_platform_device(gmu->dev);
void __iomem *pdcptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc");
+ u32 seqmem0_drv0_reg = REG_A6XX_RSCC_SEQ_MEM_0_DRV0;
void __iomem *seqptr = NULL;
uint32_t pdc_address_offset;
bool pdc_in_aop = false;
@@ -520,7 +527,9 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
if (IS_ERR(pdcptr))
goto err;
- if (adreno_is_a650(adreno_gpu) || adreno_is_a660_family(adreno_gpu))
+ if (adreno_is_a650(adreno_gpu) ||
+ adreno_is_a660_family(adreno_gpu) ||
+ adreno_is_a7xx(adreno_gpu))
pdc_in_aop = true;
else if (adreno_is_a618(adreno_gpu) || adreno_is_a640_family(adreno_gpu))
pdc_address_offset = 0x30090;
@@ -544,20 +553,26 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR, 0);
gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 2, 0);
gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + 2, 0);
- gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 4, 0x80000000);
+ gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_DATA + 4,
+ adreno_is_a740_family(adreno_gpu) ? 0x80000021 : 0x80000000);
gmu_write_rscc(gmu, REG_A6XX_RSCC_HIDDEN_TCS_CMD0_ADDR + 4, 0);
gmu_write_rscc(gmu, REG_A6XX_RSCC_OVERRIDE_START_ADDR, 0);
gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_SEQ_START_ADDR, 0x4520);
gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_MATCH_VALUE_LO, 0x4510);
gmu_write_rscc(gmu, REG_A6XX_RSCC_PDC_MATCH_VALUE_HI, 0x4514);
+ /* The second spin of A7xx GPUs messed with some register offsets.. */
+ if (adreno_is_a740_family(adreno_gpu))
+ seqmem0_drv0_reg = REG_A7XX_RSCC_SEQ_MEM_0_DRV0_A740;
+
/* Load RSC sequencer uCode for sleep and wakeup */
- if (adreno_is_a650_family(adreno_gpu)) {
- gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0, 0xeaaae5a0);
- gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 1, 0xe1a1ebab);
- gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 2, 0xa2e0a581);
- gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 3, 0xecac82e2);
- gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 4, 0x0020edad);
+ if (adreno_is_a650_family(adreno_gpu) ||
+ adreno_is_a7xx(adreno_gpu)) {
+ gmu_write_rscc(gmu, seqmem0_drv0_reg, 0xeaaae5a0);
+ gmu_write_rscc(gmu, seqmem0_drv0_reg + 1, 0xe1a1ebab);
+ gmu_write_rscc(gmu, seqmem0_drv0_reg + 2, 0xa2e0a581);
+ gmu_write_rscc(gmu, seqmem0_drv0_reg + 3, 0xecac82e2);
+ gmu_write_rscc(gmu, seqmem0_drv0_reg + 4, 0x0020edad);
} else {
gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0, 0xa7a506a0);
gmu_write_rscc(gmu, REG_A6XX_RSCC_SEQ_MEM_0_DRV0 + 1, 0xa1e6a6e7);
@@ -637,11 +652,18 @@ err:
/* Set up the idle state for the GMU */
static void a6xx_gmu_power_config(struct a6xx_gmu *gmu)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+
/* Disable GMU WB/RB buffer */
gmu_write(gmu, REG_A6XX_GMU_SYS_BUS_CONFIG, 0x1);
gmu_write(gmu, REG_A6XX_GMU_ICACHE_CONFIG, 0x1);
gmu_write(gmu, REG_A6XX_GMU_DCACHE_CONFIG, 0x1);
+ /* A7xx knows better by default! */
+ if (adreno_is_a7xx(adreno_gpu))
+ return;
+
gmu_write(gmu, REG_A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0x9c40400);
switch (gmu->idle_level) {
@@ -698,7 +720,7 @@ static int a6xx_gmu_fw_load(struct a6xx_gmu *gmu)
u32 itcm_base = 0x00000000;
u32 dtcm_base = 0x00040000;
- if (adreno_is_a650_family(adreno_gpu))
+ if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu))
dtcm_base = 0x10004000;
if (gmu->legacy) {
@@ -747,14 +769,22 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
{
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ u32 fence_range_lower, fence_range_upper;
+ u32 chipid, chipid_min = 0;
int ret;
- u32 chipid;
- if (adreno_is_a650_family(adreno_gpu)) {
+ /* Vote veto for FAL10 */
+ if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu)) {
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, 1);
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FAL_INTF, 1);
}
+ /* Turn on TCM (Tightly Coupled Memory) retention */
+ if (adreno_is_a7xx(adreno_gpu))
+ a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1);
+ else
+ gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1);
+
if (state == GMU_WARM_BOOT) {
ret = a6xx_rpmh_start(gmu);
if (ret)
@@ -764,9 +794,6 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
"GMU firmware is not loaded\n"))
return -ENOENT;
- /* Turn on register retention */
- gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1);
-
ret = a6xx_rpmh_start(gmu);
if (ret)
return ret;
@@ -776,6 +803,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
return ret;
}
+ /* Clear init result to make sure we are getting a fresh value */
gmu_write(gmu, REG_A6XX_GMU_CM3_FW_INIT_RESULT, 0);
gmu_write(gmu, REG_A6XX_GMU_CM3_BOOT_CONFIG, 0x02);
@@ -783,8 +811,18 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_ADDR, gmu->hfi.iova);
gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_INFO, 1);
+ if (adreno_is_a7xx(adreno_gpu)) {
+ fence_range_upper = 0x32;
+ fence_range_lower = 0x8a0;
+ } else {
+ fence_range_upper = 0xa;
+ fence_range_lower = 0xa0;
+ }
+
gmu_write(gmu, REG_A6XX_GMU_AHB_FENCE_RANGE_0,
- (1 << 31) | (0xa << 18) | (0xa0));
+ BIT(31) |
+ FIELD_PREP(GENMASK(30, 18), fence_range_upper) |
+ FIELD_PREP(GENMASK(17, 0), fence_range_lower));
/*
* Snapshots toggle the NMI bit which will result in a jump to the NMI
@@ -792,21 +830,49 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
*/
gmu_write(gmu, REG_A6XX_GMU_CM3_CFG, 0x4052);
- /*
- * Note that the GMU has a slightly different layout for
- * chip_id, for whatever reason, so a bit of massaging
- * is needed. The upper 16b are the same, but minor and
- * patchid are packed in four bits each with the lower
- * 8b unused:
- */
- chipid = adreno_gpu->chip_id & 0xffff0000;
- chipid |= (adreno_gpu->chip_id << 4) & 0xf000; /* minor */
- chipid |= (adreno_gpu->chip_id << 8) & 0x0f00; /* patchid */
+ /* NOTE: A730 may also fall in this if-condition with a future GMU fw update. */
+ if (adreno_is_a7xx(adreno_gpu) && !adreno_is_a730(adreno_gpu)) {
+ /* A7xx GPUs have obfuscated chip IDs. Use constant maj = 7 */
+ chipid = FIELD_PREP(GENMASK(31, 24), 0x7);
+
+ /*
+ * The min part has a 1-1 mapping for each GPU SKU.
+ * This chipid that the GMU expects corresponds to the "GENX_Y_Z" naming,
+ * where X = major, Y = minor, Z = patchlevel, e.g. GEN7_2_1 for prod A740.
+ */
+ if (adreno_is_a740(adreno_gpu))
+ chipid_min = 2;
+ else
+ return -EINVAL;
+
+ chipid |= FIELD_PREP(GENMASK(23, 16), chipid_min);
- gmu_write(gmu, REG_A6XX_GMU_HFI_SFR_ADDR, chipid);
+ /* Get the patchid (which may vary) from the device tree */
+ chipid |= FIELD_PREP(GENMASK(15, 8), adreno_patchid(adreno_gpu));
+ } else {
+ /*
+ * Note that the GMU has a slightly different layout for
+ * chip_id, for whatever reason, so a bit of massaging
+ * is needed. The upper 16b are the same, but minor and
+ * patchid are packed in four bits each with the lower
+ * 8b unused:
+ */
+ chipid = adreno_gpu->chip_id & 0xffff0000;
+ chipid |= (adreno_gpu->chip_id << 4) & 0xf000; /* minor */
+ chipid |= (adreno_gpu->chip_id << 8) & 0x0f00; /* patchid */
+ }
- gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_MSG,
- gmu->log.iova | (gmu->log.size / SZ_4K - 1));
+ if (adreno_is_a7xx(adreno_gpu)) {
+ gmu_write(gmu, REG_A6XX_GMU_GENERAL_10, chipid);
+ gmu_write(gmu, REG_A6XX_GMU_GENERAL_8,
+ (gmu->log.iova & GENMASK(31, 12)) |
+ ((gmu->log.size / SZ_4K - 1) & GENMASK(7, 0)));
+ } else {
+ gmu_write(gmu, REG_A6XX_GMU_HFI_SFR_ADDR, chipid);
+
+ gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_MSG,
+ gmu->log.iova | (gmu->log.size / SZ_4K - 1));
+ }
/* Set up the lowest idle level on the GMU */
a6xx_gmu_power_config(gmu);
@@ -857,17 +923,23 @@ static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu)
static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu)
{
- u32 val;
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ u32 val, seqmem_off = 0;
+
+ /* The second spin of A7xx GPUs messed with some register offsets.. */
+ if (adreno_is_a740_family(adreno_gpu))
+ seqmem_off = 4;
/* Make sure there are no outstanding RPMh votes */
- gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS0_DRV0_STATUS, val,
- (val & 1), 100, 10000);
- gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS1_DRV0_STATUS, val,
- (val & 1), 100, 10000);
- gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS2_DRV0_STATUS, val,
- (val & 1), 100, 10000);
- gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS, val,
- (val & 1), 100, 1000);
+ gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS0_DRV0_STATUS + seqmem_off,
+ val, (val & 1), 100, 10000);
+ gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS1_DRV0_STATUS + seqmem_off,
+ val, (val & 1), 100, 10000);
+ gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS2_DRV0_STATUS + seqmem_off,
+ val, (val & 1), 100, 10000);
+ gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS + seqmem_off,
+ val, (val & 1), 100, 1000);
}
/* Force the GMU off in case it isn't responsive */
@@ -950,6 +1022,14 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
gmu->hung = false;
+ /* Notify AOSS about the ACD state (unimplemented for now => disable it) */
+ if (!IS_ERR(gmu->qmp)) {
+ ret = qmp_send(gmu->qmp, "{class: gpu, res: acd, val: %d}",
+ 0 /* Hardcode ACD to be disabled for now */);
+ if (ret)
+ dev_err(gmu->dev, "failed to send GPU ACD state\n");
+ }
+
/* Turn on the resources */
pm_runtime_get_sync(gmu->dev);
@@ -963,7 +1043,8 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
/* Use a known rate to bring up the GMU */
clk_set_rate(gmu->core_clk, 200000000);
- clk_set_rate(gmu->hub_clk, 150000000);
+ clk_set_rate(gmu->hub_clk, adreno_is_a740_family(adreno_gpu) ?
+ 200000000 : 150000000);
ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks);
if (ret) {
pm_runtime_put(gmu->gxpd);
@@ -980,15 +1061,19 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
enable_irq(gmu->gmu_irq);
/* Check to see if we are doing a cold or warm boot */
- status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ?
- GMU_WARM_BOOT : GMU_COLD_BOOT;
-
- /*
- * Warm boot path does not work on newer GPUs
- * Presumably this is because icache/dcache regions must be restored
- */
- if (!gmu->legacy)
+ if (adreno_is_a7xx(adreno_gpu)) {
+ status = a6xx_llc_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ?
+ GMU_WARM_BOOT : GMU_COLD_BOOT;
+ } else if (gmu->legacy) {
+ status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ?
+ GMU_WARM_BOOT : GMU_COLD_BOOT;
+ } else {
+ /*
+ * Warm boot path does not work on newer A6xx GPUs
+ * Presumably this is because icache/dcache regions must be restored
+ */
status = GMU_COLD_BOOT;
+ }
ret = a6xx_gmu_fw_start(gmu, status);
if (ret)
@@ -1473,6 +1558,9 @@ void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu)
dev_pm_domain_detach(gmu->gxpd, false);
}
+ if (!IS_ERR_OR_NULL(gmu->qmp))
+ qmp_put(gmu->qmp);
+
iounmap(gmu->mmio);
if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "rscc"))
iounmap(gmu->rscc);
@@ -1569,6 +1657,7 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
struct platform_device *pdev = of_find_device_by_node(node);
+ struct device_link *link;
int ret;
if (!pdev)
@@ -1600,7 +1689,8 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
* are otherwise unused by a660.
*/
gmu->dummy.size = SZ_4K;
- if (adreno_is_a660_family(adreno_gpu)) {
+ if (adreno_is_a660_family(adreno_gpu) ||
+ adreno_is_a7xx(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_4K * 7,
0x60400000, "debug");
if (ret)
@@ -1616,7 +1706,8 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
goto err_memory;
/* Note that a650 family also includes a660 family: */
- if (adreno_is_a650_family(adreno_gpu)) {
+ if (adreno_is_a650_family(adreno_gpu) ||
+ adreno_is_a7xx(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->icache,
SZ_16M - SZ_16K, 0x04000, "icache");
if (ret)
@@ -1664,7 +1755,8 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
goto err_memory;
}
- if (adreno_is_a650_family(adreno_gpu)) {
+ if (adreno_is_a650_family(adreno_gpu) ||
+ adreno_is_a7xx(adreno_gpu)) {
gmu->rscc = a6xx_gmu_get_mmio(pdev, "rscc");
if (IS_ERR(gmu->rscc)) {
ret = -ENODEV;
@@ -1689,12 +1781,18 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
goto err_mmio;
}
- if (!device_link_add(gmu->dev, gmu->cxpd,
- DL_FLAG_PM_RUNTIME)) {
+ link = device_link_add(gmu->dev, gmu->cxpd, DL_FLAG_PM_RUNTIME);
+ if (!link) {
ret = -ENODEV;
goto detach_cxpd;
}
+ gmu->qmp = qmp_get(gmu->dev);
+ if (IS_ERR(gmu->qmp) && adreno_is_a7xx(adreno_gpu)) {
+ ret = PTR_ERR(gmu->qmp);
+ goto remove_device_link;
+ }
+
init_completion(&gmu->pd_gate);
complete_all(&gmu->pd_gate);
gmu->pd_nb.notifier_call = cxpd_notifier_cb;
@@ -1718,6 +1816,9 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
return 0;
+remove_device_link:
+ device_link_del(link);
+
detach_cxpd:
dev_pm_domain_detach(gmu->cxpd, false);