msm: kgsl: Simplify GBIF halt sequence for stall-on-fault case

Currently during stall-on-fault recovery, we keep resuming SMMU and
check if GBIF halt succeeded. This is done to recover from a case where
a GPU block continuously initiated faulty transactions, which would keep
stalling SMMU. If we set CFCFG to 0 as part of handling the first fault,
all these transactions will be terminated and will not stall SMMU. This
simplifies the GBIF halt sequence and avoids resuming every time.

Change-Id: Iada83d6a08e5e0a4f6241b17d1b178ab680f1364
Signed-off-by: Puranam V G Tejaswi <pvgtejas@codeaurora.org>
This commit is contained in:
Puranam V G Tejaswi 2020-11-11 22:28:43 +05:30
commit ed12df75f6
4 changed files with 54 additions and 89 deletions

View file

@ -1264,6 +1264,29 @@ void a6xx_gx_cpr_toggle(struct kgsl_device *device)
wmb();
}
/* This is only defined for non-GMU and non-RGMU targets */
static int a6xx_clear_pending_transactions(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
int ret;
if (adreno_is_a619_holi(adreno_dev)) {
kgsl_regwrite(device, A6XX_RBBM_GPR0_CNTL, 0x1e0);
ret = adreno_wait_for_halt_ack(device,
A6XX_RBBM_VBIF_GX_RESET_STATUS, 0xf0);
} else {
kgsl_regwrite(device, A6XX_RBBM_GBIF_HALT,
A6XX_GBIF_GX_HALT_MASK);
ret = adreno_wait_for_halt_ack(device, A6XX_RBBM_GBIF_HALT_ACK,
A6XX_GBIF_GX_HALT_MASK);
}
if (ret)
return ret;
return a6xx_halt_gbif(adreno_dev);
}
/**
* a6xx_reset() - Helper function to reset the GPU
* @device: Pointer to the KGSL device structure for the GPU
@ -1277,19 +1300,9 @@ static int a6xx_reset(struct kgsl_device *device)
int ret;
unsigned long flags = device->pwrctrl.ctrl_flags;
/*
* Stall on fault needs GBIF halt sequences for robust recovery.
* Because in a worst-case scenario, if any of the GPU blocks is
* generating a stream of un-ending faulting transactions, SMMU will
* process those transactions when we try to resume it and enter
* stall-on-fault mode again. GBIF halt sequences make sure that all
* GPU transactions are halted at GBIF which ensures that SMMU
* can resume safely.
*/
a6xx_do_gbif_halt(adreno_dev, A6XX_RBBM_GBIF_HALT,
A6XX_RBBM_GBIF_HALT_ACK, A6XX_GBIF_GX_HALT_MASK, "GX");
a6xx_do_gbif_halt(adreno_dev, A6XX_GBIF_HALT, A6XX_GBIF_HALT_ACK,
A6XX_GBIF_ARB_HALT_MASK, "CX");
ret = a6xx_clear_pending_transactions(adreno_dev);
if (ret)
return ret;
/* Clear ctrl_flags to ensure clocks and regulators are turned off */
device->pwrctrl.ctrl_flags = 0;
@ -2441,63 +2454,6 @@ u64 a6xx_read_alwayson(struct adreno_device *adreno_dev)
return (((u64) hi) << 32) | lo;
}
void a6xx_do_gbif_halt(struct adreno_device *adreno_dev,
u32 halt_reg, u32 ack_reg, u32 mask, const char *client)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
unsigned long t;
u32 val;
kgsl_regwrite(device, halt_reg, mask);
t = jiffies + msecs_to_jiffies(100);
do {
kgsl_regread(device, ack_reg, &val);
if ((val & mask) == mask)
return;
/*
* If we are attempting GBIF halt in case of stall-on-fault
* then the halt sequence will not complete as long as SMMU
* is stalled.
*/
kgsl_mmu_pagefault_resume(&device->mmu);
usleep_range(10, 100);
} while (!time_after(jiffies, t));
/* Check one last time */
kgsl_mmu_pagefault_resume(&device->mmu);
kgsl_regread(device, ack_reg, &val);
if ((val & mask) == mask)
return;
dev_err(device->dev, "%s GBIF Halt ack timed out\n", client);
}
/* This is only defined for non-GMU and non-RGMU targets */
static int a6xx_clear_pending_transactions(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
int ret;
if (adreno_is_a619_holi(adreno_dev)) {
kgsl_regwrite(device, A6XX_RBBM_GPR0_CNTL, 0x1e0);
ret = adreno_wait_for_halt_ack(device,
A6XX_RBBM_VBIF_GX_RESET_STATUS, 0xf0);
} else {
kgsl_regwrite(device, A6XX_RBBM_GBIF_HALT,
A6XX_GBIF_GX_HALT_MASK);
ret = adreno_wait_for_halt_ack(device, A6XX_RBBM_GBIF_HALT_ACK,
A6XX_GBIF_GX_HALT_MASK);
}
if (ret)
return ret;
return a6xx_halt_gbif(adreno_dev);
}
const struct adreno_gpudev adreno_a6xx_gpudev = {
.reg_offsets = a6xx_register_offsets,
.probe = a6xx_probe,

View file

@ -317,17 +317,6 @@ int a6xx_gmu_sptprac_enable(struct adreno_device *adreno_dev);
void a6xx_gmu_sptprac_disable(struct adreno_device *adreno_dev);
bool a6xx_gmu_sptprac_is_on(struct adreno_device *adreno_dev);
/**
* a6xx_do_gbif_halt - halt gbif traffic and wait for ack
* @adreno_dev: An Adreno GPU handle
* @halt_reg: reg to trigger gbif halt
* @ack_reg: status register to check for ack
* @mask: mask for ack
* @client: client name - "GX" or "CX"
*/
void a6xx_do_gbif_halt(struct adreno_device *adreno_dev,
u32 halt_reg, u32 ack_reg, u32 mask, const char *client);
/**
* a6xx_read_alwayson - Read the current always on clock value
* @adreno_dev: An Adreno GPU handle

View file

@ -1617,15 +1617,15 @@ static void a6xx_gmu_pwrctrl_suspend(struct adreno_device *adreno_dev)
if (adreno_has_gbif(adreno_dev)) {
/* Halt GX traffic */
if (a6xx_gmu_gx_is_on(device))
a6xx_do_gbif_halt(adreno_dev, A6XX_RBBM_GBIF_HALT,
A6XX_RBBM_GBIF_HALT_ACK,
A6XX_GBIF_GX_HALT_MASK,
"GX");
if (a6xx_gmu_gx_is_on(device)) {
kgsl_regwrite(device, A6XX_RBBM_GBIF_HALT,
A6XX_GBIF_GX_HALT_MASK);
adreno_wait_for_halt_ack(device,
A6XX_RBBM_GBIF_HALT_ACK,
A6XX_GBIF_GX_HALT_MASK);
}
/* Halt CX traffic */
a6xx_do_gbif_halt(adreno_dev, A6XX_GBIF_HALT, A6XX_GBIF_HALT_ACK,
A6XX_GBIF_ARB_HALT_MASK, "CX");
a6xx_halt_gbif(adreno_dev);
}
if (a6xx_gmu_gx_is_on(device))

View file

@ -1747,6 +1747,26 @@ static void kgsl_iommu_pagefault_resume(struct kgsl_mmu *mmu)
struct kgsl_iommu_context *ctx = &iommu->user_context;
if (ctx->default_pt != NULL && ctx->stalled_on_fault) {
u32 sctlr_val = KGSL_IOMMU_GET_CTX_REG(ctx,
KGSL_IOMMU_CTX_SCTLR);
/*
* As part of recovery, GBIF halt sequence should be performed.
* In a worst case scenario, if any GPU block is generating a
* stream of un-ending faulting transactions, SMMU would enter
* stall-on-fault mode again after resuming and not let GBIF
* halt succeed. In order to avoid that situation and terminate
* those faulty transactions, set CFCFG and HUPCF to 0.
*/
sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_CFCFG_SHIFT);
sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR, sctlr_val);
/*
* Make sure the above register write is not reordered across
* the barrier as we use writel_relaxed to write it.
*/
wmb();
/*
* This will only clear fault bits in FSR. FSR.SS will still
* be set. Writing to RESUME (below) is the only way to clear