From 60c00665c3f3363c485a763a27bb607385760c26 Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Thu, 19 Sep 2019 15:38:36 +0530 Subject: [PATCH 1/2] iommu/arm-smmu: fix stale fault reporting in ecats operation If FSR is not cleared before starting ECATS operation, non-zero FSR cause successful ECATS operation to be reported as failed. Also, resume any faulting transcation before halting TBU. Change-Id: I46955d0a75660fbbee09468140301ab9b10a699e Signed-off-by: Prakash Gupta [isaacm@codeaurora.org: resolve merge conflicts] Signed-off-by: Isaac J. Manjarres --- drivers/iommu/arm-smmu.c | 102 ++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 43 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 614c023c6cf4..52ef346b3c23 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -4958,10 +4958,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")) { @@ -4976,47 +4976,44 @@ 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)) { + 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); + } + + 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!\n"); spin_unlock_irqrestore(&tbu->halt_lock, flags); return -ETIMEDOUT; } - /* - * 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, - (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); - 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; @@ -5146,6 +5143,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) @@ -5189,10 +5205,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); /* @@ -5200,13 +5218,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; } From dbee5229343b2832d1886a8f3646390bf8dc295f Mon Sep 17 00:00:00 2001 From: Prakash Gupta Date: Wed, 6 Nov 2019 13:29:47 +0530 Subject: [PATCH 2/2] iommu: arm-smmu: clear TBU halt request when halt times out TBU halt request is considered timed out after some wait. But the halt request is not cleared. This can result in halt request succeeding subsequently and driver not resuming the TBU. This can cause functional issue of TBU power collapse or TBU remaining halted. Clear the TBU halt request in case of timeout. Change-Id: I2225485cae8146917ea8cfdf3bf50742278ae628 Signed-off-by: Prakash Gupta [isaacm@codeaurora.org: Use DEBUG_SID_HALT_REQ] Signed-off-by: Isaac J. Manjarres --- drivers/iommu/arm-smmu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 52ef346b3c23..7540be31d29a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -5010,6 +5010,11 @@ static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu, (status & DEBUG_SR_HALT_ACK_VAL), 0, TBU_DBG_TIMEOUT_US)) { 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; }