From patchwork Mon Feb 29 13:46:20 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robin Murphy X-Patchwork-Id: 8454621 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 E65BB9F52D for ; Mon, 29 Feb 2016 13:53:47 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BF44120220 for ; Mon, 29 Feb 2016 13:53:46 +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 8597620160 for ; Mon, 29 Feb 2016 13:53:45 +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 1aaOEi-0005bv-JG; Mon, 29 Feb 2016 13:51:44 +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 1aaOAi-0000l7-SM for linux-arm-kernel@lists.infradead.org; Mon, 29 Feb 2016 13:47:39 +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 2705A5EF; Mon, 29 Feb 2016 05:46:19 -0800 (PST) Received: from e104324-lin.cambridge.arm.com (e104324-lin.cambridge.arm.com [10.1.205.42]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 4D8433F213; Mon, 29 Feb 2016 05:47:12 -0800 (PST) From: Robin Murphy To: iommu@lists.linux-foundation.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH 11/12] iommu/arm-smmu: Generic IOMMU DT bindings support Date: Mon, 29 Feb 2016 13:46:20 +0000 Message-Id: X-Mailer: git-send-email 2.7.2.333.g70bd996.dirty In-Reply-To: References: X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160229_054737_065089_CAAB7437 X-CRM114-Status: GOOD ( 19.19 ) 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: Thomas.Lendacky@amd.com, anup.patel@broadcom.com, thunder.leizhen@huawei.com, will.deacon@arm.com, stuart.yoder@nxp.com, Suravee.Suthikulpanit@amd.com, tchalamarla@caviumnetworks.com 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, 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 Implement an of_xlate callback and the appropriate registration so that we can configure masters via generic DT bindings. Initially, we have the equivalent level of functionality with respect to groups, stream ID limits, etc. as for the old bindings, but the door is now open for further improvements. Since of_iommmu_configure() is not yet clever enough to enforce the necessary probe ordering dependencies, employ the same explicit device creation tactic as the Exynos IOMMU driver to ensure our actual SMMU instance is up and running in time to handle of_xlate calls. Signed-off-by: Robin Murphy --- drivers/iommu/arm-smmu.c | 144 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 106 insertions(+), 38 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 91b0a1b..8fcf27a 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -421,6 +423,9 @@ static struct arm_smmu_master *find_smmu_master(struct device_node *dev_node) { struct arm_smmu_master *master; + if (!dev_node) + return NULL; + read_lock(&arm_smmu_masters_lock); list_for_each_entry(master, &arm_smmu_masters, list) if (master->of_node == dev_node) @@ -1011,13 +1016,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, if (ret) return ret; - /* - * FIXME: This won't be needed once we have IOMMU-backed DMA ops - * for all devices behind the SMMU. - */ - if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA) - return 0; - for (i = 0; i < cfg->num_streamids; ++i) { u32 idx = cfg->streamids[i].s2cr_idx - 1; u32 s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV | @@ -1206,57 +1204,79 @@ static bool arm_smmu_capable(enum iommu_cap cap) } } +static int arm_smmu_add_dev_streamid(struct arm_smmu_device *smmu, + struct device *dev, u16 sid) +{ + struct arm_smmu_master_cfg *cfg = dev->archdata.iommu; + int i; + + if (!cfg) { + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->smmu = smmu; + dev->archdata.iommu = cfg; + } + + if (cfg->num_streamids >= MAX_MASTER_STREAMIDS) + return -ENOSPC; + + /* Avoid duplicate SIDs, as this can lead to SMR conflicts */ + for (i = 0; i < cfg->num_streamids; ++i) + if (cfg->streamids[i].id == sid) { + dev_warn(dev, "Stream ID 0x%hx repeated; ignoring\n", + sid); + return 0; + } + + cfg->streamids[cfg->num_streamids++].id = sid; + + return 0; +} + static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) { *((u16 *)data) = alias; return 0; /* Continue walking */ } -static int arm_smmu_init_pci_device(struct arm_smmu_device *smmu, - struct pci_dev *pdev) +static int arm_smmu_init_legacy_master(struct device *dev) { - struct arm_smmu_master_cfg *cfg; + struct arm_smmu_master *master; + struct device_node *np = dev_get_dev_node(dev); u16 sid; - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; - /* - * Assume Stream ID == Requester ID for now. - * We need a way to describe the ID mappings in FDT. - */ - pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); + master = find_smmu_master(np); + if (!master) + return -ENODEV; - cfg->streamids[0].id = sid; - cfg->num_streamids = 1; + if (!dev_is_pci(dev)) { + dev->archdata.iommu = &master->cfg; + return 0; + } - cfg->smmu = smmu; - pdev->dev.archdata.iommu = cfg; - - return 0; + /* Legacy bindings assume Stream ID == Requester ID */ + pci_for_each_dma_alias(to_pci_dev(dev), __arm_smmu_get_pci_sid, &sid); + return arm_smmu_add_dev_streamid(master->cfg.smmu, dev, sid); } static int arm_smmu_add_device(struct device *dev) { struct iommu_group *group; - struct arm_smmu_master *master; - if (dev->archdata.iommu) - return -EEXIST; + if (!dev->archdata.iommu) { + int ret = arm_smmu_init_legacy_master(dev); - master = find_smmu_master(dev_get_dev_node(dev)); - if (!master) - return -ENODEV; - - if (dev_is_pci(dev)) { - int ret = arm_smmu_init_pci_device(master->cfg.smmu, - to_pci_dev(dev)); if (ret) return ret; - } else { - dev->archdata.iommu = &master->cfg; } + /* + * For now, assume that the default group allocators suffice. + * We might have to do some preparatory work here to properly + * handle multiple devices sharing stream IDs. + */ group = iommu_group_get_for_dev(dev); if (IS_ERR(group)) return PTR_ERR(group); @@ -1268,7 +1288,7 @@ static int arm_smmu_add_device(struct device *dev) static void arm_smmu_remove_device(struct device *dev) { iommu_group_remove_device(dev); - if (dev_is_pci(dev)) + if (!find_smmu_master(dev->of_node)) kfree(dev->archdata.iommu); } @@ -1381,6 +1401,20 @@ out_unlock: return ret; } +static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) +{ + struct arm_smmu_device *smmu; + struct platform_device *smmu_pdev; + + smmu_pdev = of_find_device_by_node(args->np); + if (!smmu_pdev) + return -ENODEV; + + smmu = platform_get_drvdata(smmu_pdev); + + return arm_smmu_add_dev_streamid(smmu, dev, args->args[0]); +} + static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc, @@ -1395,6 +1429,7 @@ static struct iommu_ops arm_smmu_ops = { .device_group = arm_smmu_device_group, .domain_get_attr = arm_smmu_domain_get_attr, .domain_set_attr = arm_smmu_domain_set_attr, + .of_xlate = arm_smmu_of_xlate, .pgsize_bitmap = -1UL, /* Restricted during device attach */ }; @@ -1731,6 +1766,9 @@ static int arm_smmu_probe_mmu_masters(struct arm_smmu_device *smmu) struct of_phandle_args masterspec; int err, i = 0; + dev_notice(smmu->dev, + "Deprecated \"mmu-masters\" property found; update DT to \"iommus\" property if possible\n"); + while (!of_parse_phandle_with_args(smmu->dev->of_node, "mmu-masters", "#stream-id-cells", i, &masterspec)) { @@ -1838,7 +1876,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, smmu); - arm_smmu_probe_mmu_masters(smmu); + /* Check first to avoid of_parse_phandle_with_args complaining */ + if (of_property_read_bool(dev->of_node, "mmu-masters")) + arm_smmu_probe_mmu_masters(smmu); arm_smmu_device_reset(smmu); return 0; @@ -1881,8 +1921,11 @@ static struct platform_driver arm_smmu_driver = { static int __init arm_smmu_init(void) { struct device_node *np; + static bool done; int ret; + if (done) + return 0; /* * Play nice with systems that don't have an ARM SMMU by checking that * an ARM SMMU exists in the system before proceeding with the driver @@ -1912,6 +1955,7 @@ static int __init arm_smmu_init(void) bus_set_iommu(&pci_bus_type, &arm_smmu_ops); #endif + done = true; return 0; } @@ -1923,6 +1967,30 @@ static void __exit arm_smmu_exit(void) subsys_initcall(arm_smmu_init); module_exit(arm_smmu_exit); +static int __init arm_smmu_of_init(struct device_node *np) +{ + struct arm_smmu_device *smmu; + struct platform_device *pdev; + int ret = arm_smmu_init(); + + if (ret) + return ret; + + pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root); + if (!pdev) + return -ENODEV; + + smmu = platform_get_drvdata(pdev); + of_iommu_set_ops(np, &arm_smmu_ops); + + return 0; +} +IOMMU_OF_DECLARE(arm_smmuv1, "arm,smmu-v1", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_smmuv2, "arm,smmu-v2", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_mmu400, "arm,mmu-400", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_mmu401, "arm,mmu-401", arm_smmu_of_init); +IOMMU_OF_DECLARE(arm_mmu500, "arm,mmu-500", arm_smmu_of_init); + MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations"); MODULE_AUTHOR("Will Deacon "); MODULE_LICENSE("GPL v2");