Merge "iommu: arm-smmu: clear TBU halt request when halt times out"

This commit is contained in:
qctecmdr 2019-12-14 04:32:38 -08:00 • committed by Gerrit - the friendly Code Review server
commit 916dbcdb30

View file

@ -5012,10 +5012,10 @@ static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu,
struct arm_smmu_domain *smmu_domain)
{
unsigned long flags;
u32 halt, fsr, sctlr_orig, sctlr, status;
void __iomem *base;
struct arm_smmu_device *smmu = smmu_domain->smmu;
int idx = smmu_domain->cfg.cbndx;
u32 halt, fsr, status;
void __iomem *tbu_base;
if (of_property_read_bool(tbu->dev->of_node,
"qcom,opt-out-tbu-halting")) {
@ -5030,47 +5030,49 @@ static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu,
return 0;
}
base = tbu->base;
halt = readl_relaxed(base + DEBUG_SID_HALT_REG);
tbu_base = tbu->base;
halt = readl_relaxed(tbu_base + DEBUG_SID_HALT_REG);
halt |= DEBUG_SID_HALT_REQ;
writel_relaxed(halt, base + DEBUG_SID_HALT_REG);
if (!readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, status,
(status & DEBUG_SR_HALT_ACK_VAL),
0, TBU_DBG_TIMEOUT_US))
goto out;
writel_relaxed(halt, tbu_base + DEBUG_SID_HALT_REG);
fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
if (!(fsr & FSR_FAULT)) {
dev_err(tbu->dev, "Couldn't halt TBU!\n");
spin_unlock_irqrestore(&tbu->halt_lock, flags);
return -ETIMEDOUT;
if ((fsr & FSR_FAULT) && (fsr & FSR_SS)) {
u32 sctlr_orig, sctlr;
/*
* We are in a fault; Our request to halt the bus will not
* complete until transactions in front of us (such as the fault
* itself) have completed. Disable iommu faults and terminate
* any existing transactions.
*/
sctlr_orig = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_SCTLR);
sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
/*
* Barrier required to ensure that the FSR is cleared
* before resuming SMMU operation
*/
wmb();
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
RESUME_TERMINATE);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr_orig);
}
/*
* We are in a fault; Our request to halt the bus will not complete
* until transactions in front of us (such as the fault itself) have
* completed. Disable iommu faults and terminate any existing
* transactions.
*/
sctlr_orig = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_SCTLR);
sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME, RESUME_TERMINATE);
if (readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, status,
if (readl_poll_timeout_atomic(tbu_base + DEBUG_SR_HALT_ACK_REG, status,
(status & DEBUG_SR_HALT_ACK_VAL),
0, TBU_DBG_TIMEOUT_US)) {
dev_err(tbu->dev, "Couldn't halt TBU from fault context!\n");
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr_orig);
dev_err(tbu->dev, "Couldn't halt TBU!\n");
halt = readl_relaxed(tbu_base + DEBUG_SID_HALT_REG);
halt &= ~DEBUG_SID_HALT_REQ;
writel_relaxed(halt, tbu_base + DEBUG_SID_HALT_REG);
spin_unlock_irqrestore(&tbu->halt_lock, flags);
return -ETIMEDOUT;
}
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr_orig);
out:
tbu->halt_count = 1;
spin_unlock_irqrestore(&tbu->halt_lock, flags);
return 0;
@ -5200,6 +5202,25 @@ static phys_addr_t qsmmuv500_iova_to_phys(
sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE);
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr);
fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
if (fsr & FSR_FAULT) {
/* Clear pending interrupts */
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
/*
* Barrier required to ensure that the FSR is cleared
* before resuming SMMU operation.
*/
wmb();
/*
* TBU halt takes care of resuming any stalled transcation.
* Kept it here for completeness sake.
*/
if (fsr & FSR_SS)
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
RESUME_TERMINATE);
}
/* Only one concurrent atos operation */
ret = qsmmuv500_ecats_lock(smmu_domain, tbu, &flags);
if (ret)
@ -5243,10 +5264,12 @@ redo:
val = readq_relaxed(tbu->base + DEBUG_PAR_REG);
fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
if (fsr & FSR_FAULT) {
if (val & DEBUG_PAR_FAULT_VAL) {
dev_err(tbu->dev, "ECATS generated a fault interrupt! FSR = %llx, SID=0x%x\n",
fsr, sid);
fsr, sid);
dev_err(tbu->dev, "ECATS translation failed! PAR = %llx\n",
val);
/* Clear pending interrupts */
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
/*
@ -5254,13 +5277,11 @@ redo:
* before resuming SMMU operation.
*/
wmb();
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
RESUME_TERMINATE);
/* Check if ECATS translation failed */
if (val & DEBUG_PAR_FAULT_VAL)
dev_err(tbu->dev, "ECATS translation failed! PAR = %llx\n",
val);
if (fsr & FSR_SS)
arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
RESUME_TERMINATE);
ret = -EINVAL;
}