diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 318be3f9f8bb..96a213db2d84 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -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; }