From patchwork Tue Oct 6 18:15:26 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marc Zyngier X-Patchwork-Id: 7338961 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 870779F6CD for ; Tue, 6 Oct 2015 18:18:20 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 97315206FD for ; Tue, 6 Oct 2015 18:18:19 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 2961A20718 for ; Tue, 6 Oct 2015 18:18:18 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZjWmK-0004yw-QO; Tue, 06 Oct 2015 18:15:56 +0000 Received: from foss.arm.com ([217.140.101.70]) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZjWmH-0004vO-TF for linux-arm-kernel@lists.infradead.org; Tue, 06 Oct 2015 18:15:54 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0687549; Tue, 6 Oct 2015 11:15:32 -0700 (PDT) Received: from approximate.cambridge.arm.com (approximate.cambridge.arm.com [10.1.209.125]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 34DAA3F487; Tue, 6 Oct 2015 11:15:31 -0700 (PDT) From: Marc Zyngier To: Will Deacon Subject: [PATCH v2] iommu/arm-smmu: Add support for MSI on SMMUv3 Date: Tue, 6 Oct 2015 19:15:26 +0100 Message-Id: <1444155326-28379-1-git-send-email-marc.zyngier@arm.com> X-Mailer: git-send-email 2.1.4 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151006_111553_984840_34812AD2 X-CRM114-Status: GOOD ( 14.65 ) X-Spam-Score: -6.9 (------) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: iommu@lists.linux-foundation.org, linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Despite being a platform device, the SMMUv3 is capable of signaling interrupts using MSIs. Hook it into the platform MSI framework and enjoy faults being reported in a new and exciting way. Signed-off-by: Marc Zyngier --- Now rebased on top of Will's iommu/devel branch, which leads to a slightly different patch. drivers/iommu/arm-smmu-v3.c | 82 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c index 5b11b77..3ccc8ed 100644 --- a/drivers/iommu/arm-smmu-v3.c +++ b/drivers/iommu/arm-smmu-v3.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2176,6 +2177,63 @@ static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val, 1, ARM_SMMU_POLL_TIMEOUT_US); } +static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) +{ + struct device *dev = msi_desc_to_dev(desc); + struct arm_smmu_device *smmu = dev_get_drvdata(dev); + phys_addr_t cfg0_offset, cfg1_offset, cfg2_offset; + phys_addr_t doorbell; + + switch (desc->platform.msi_index) { + case 0: /* EVTQ */ + cfg0_offset = ARM_SMMU_EVTQ_IRQ_CFG0; + cfg1_offset = ARM_SMMU_EVTQ_IRQ_CFG1; + cfg2_offset = ARM_SMMU_EVTQ_IRQ_CFG2; + break; + case 1: /* GERROR */ + cfg0_offset = ARM_SMMU_GERROR_IRQ_CFG0; + cfg1_offset = ARM_SMMU_GERROR_IRQ_CFG1; + cfg2_offset = ARM_SMMU_GERROR_IRQ_CFG2; + break; + case 2: /* PRIQ */ + cfg0_offset = ARM_SMMU_PRIQ_IRQ_CFG0; + cfg1_offset = ARM_SMMU_PRIQ_IRQ_CFG1; + cfg2_offset = ARM_SMMU_PRIQ_IRQ_CFG2; + break; + default: /* Unknown */ + return; + } + + doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo; + doorbell &= MSI_CFG0_ADDR_MASK << MSI_CFG0_ADDR_SHIFT; + + writeq_relaxed(doorbell, smmu->base + cfg0_offset); + writew_relaxed(msg->data, smmu->base + cfg1_offset); + writew_relaxed(MSI_CFG2_MEMATTR_DEVICE_nGnRE, + smmu->base + cfg2_offset); +} + +static void arm_smmu_msi_override_irqs(struct arm_smmu_device *smmu) +{ + struct msi_desc *desc; + + for_each_msi_entry(desc, smmu->dev) { + switch (desc->platform.msi_index) { + case 0: /* EVTQ */ + smmu->evtq.q.irq = desc->irq; + break; + case 1: /* GERROR */ + smmu->gerr_irq = desc->irq; + break; + case 2: /* PRIQ */ + smmu->priq.q.irq = desc->irq; + break; + default: /* Unknown */ + continue; + } + } +} + static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) { int ret, irq; @@ -2192,6 +2250,23 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) /* Clear the MSI address regs */ writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0); writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0); + if (smmu->features & ARM_SMMU_FEAT_PRI) + writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0); + + /* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */ + if (smmu->features & ARM_SMMU_FEAT_MSI) { + int nvecs = 2; + + if (smmu->features & ARM_SMMU_FEAT_PRI) + nvecs++; + + ret = platform_msi_domain_alloc_irqs(smmu->dev, nvecs, + arm_smmu_write_msi_msg); + if (ret) + dev_warn(smmu->dev, "failed to allocate MSIs\n"); + else + arm_smmu_msi_override_irqs(smmu); + } /* Request wired interrupt lines */ irq = smmu->evtq.q.irq; @@ -2222,8 +2297,6 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) } if (smmu->features & ARM_SMMU_FEAT_PRI) { - writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0); - irq = smmu->priq.q.irq; if (irq) { ret = devm_request_threaded_irq(smmu->dev, irq, @@ -2602,13 +2675,14 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) if (ret) return ret; + /* Record our private device structure */ + platform_set_drvdata(pdev, smmu); + /* Reset the device */ ret = arm_smmu_device_reset(smmu); if (ret) goto out_free_structures; - /* Record our private device structure */ - platform_set_drvdata(pdev, smmu); return 0; out_free_structures: