// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. */
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <soc/qcom/cmd-db.h>
#include "a6xx_gpu.h"
#include "a6xx_gmu.xml.h"
static void a6xx_gmu_fault(struct a6xx_gmu *gmu)
{
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base;
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
/* FIXME: add a banner here */
gmu->hung = true;
/* Turn off the hangcheck timer while we are resetting */
del_timer(&gpu->hangcheck_timer);
/* Queue the GPU handler because we need to treat this as a recovery */
queue_work(priv->wq, &gpu->recover_work);
}
static irqreturn_t a6xx_gmu_irq(int irq, void *data)
{
struct a6xx_gmu *gmu = data;
u32 status;
status = gmu_read(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_STATUS);
gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, status);
if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE) {
dev_err_ratelimited(gmu->dev, "GMU watchdog expired\n");
a6xx_gmu_fault(gmu);
}
if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR)
dev_err_ratelimited(gmu->dev, "GMU AHB bus error\n");
if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_FENCE_ERR)
dev_err_ratelimited(gmu->dev, "GMU fence error: 0x%x\n",
gmu_read(gmu, REG_A6XX_GMU_AHB_FENCE_STATUS));
return IRQ_HANDLED;
}
static irqreturn_t a6xx_hfi_irq(int irq, void *data)
{
struct a6xx_gmu *gmu = data;
u32 status;
status = gmu_read(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO);
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, status);
if (status & A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) {
dev_err_ratelimited(gmu->dev, "GMU firmware fault\n");
a6xx_gmu_fault(gmu);
}
return IRQ_HANDLED;
}
bool a6xx_gmu_sptprac_is_on(struct a6xx_gmu *gmu)
{
u32 val;
/* This can be called from gpu state code so make sure GMU is valid */
if (IS_ERR_OR_NULL(gmu->mmio))
return false;
val = gmu_read(gmu, REG_A6XX_GMU_SPTPRAC_PWR_CLK_STATUS);
return !(val &
(A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_SPTPRAC_GDSC_POWER_OFF |
A6XX_GMU_SPTPRAC_PWR_CLK_STATUS_SP_CLOCK_OFF));
}
/* Check to see if the GX rail is still powered */
bool a6xx_gmu_gx_is_on(struct a6xx_gmu *gm