From patchwork Wed Jun 15 12:04:42 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shunqian Zheng X-Patchwork-Id: 9178301 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 3C19F604DB for ; Wed, 15 Jun 2016 12:08:50 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2A21E27BFC for ; Wed, 15 Jun 2016 12:08:50 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1CCB427D4A; Wed, 15 Jun 2016 12:08:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 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.wl.linuxfoundation.org (Postfix) with ESMTPS id 258B727BFC for ; Wed, 15 Jun 2016 12:08:48 +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 1bD9cl-0003cc-3W; Wed, 15 Jun 2016 12:08:47 +0000 Received: from mail-pf0-f196.google.com ([209.85.192.196]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1bD9cF-0002wS-0s; Wed, 15 Jun 2016 12:08:18 +0000 Received: by mail-pf0-f196.google.com with SMTP id 62so1664158pfd.3; Wed, 15 Jun 2016 05:07:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=L4TswmHKjFBZVUcfCDiTArBFNucqkoc97/dFp6SEdTs=; b=BTQ7GgiBBC3yIzHMQ6yKeou81iY38YWI9/4nWivjzizlaJcl/I5+yDRGG7j5neumQf LCnL93OnRdmo+OYs4jAUJtzfEL927IQ+mijle2uKdIIUBLumdbUx6TNST6H2mdtUNkvY Is1oKf9CXVWkQ47pVTTL/nV3nJDEkEkYR6CbiEe0C1cBC7zGE6NoyZJJ78UsH+a614la Y5Zvhfa6jfD9bztxgUUHApEUbj2q6YEUKokCmRrCpubx1BWwR0E9phxIu+WV2ax0MSD8 NAaxeoe1jlnabXmrrCyKVElBm15s/30+Xst5OlSyYhGxs1CzX2eQbEwQAH4yRwN9pG9u TKIA== X-Gm-Message-State: ALyK8tI0tD27TS2aMj+lGAm/4IYc867e/5IMFOEmYdR9mNW8z3DNfRJ4Ma0CLq40IkhxNQ== X-Received: by 10.98.9.68 with SMTP id e65mr3546833pfd.121.1465992474029; Wed, 15 Jun 2016 05:07:54 -0700 (PDT) Received: from SHUNQIAN-W530.example.org ([103.29.142.67]) by smtp.gmail.com with ESMTPSA id e9sm52724619pfg.2.2016.06.15.05.07.50 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 15 Jun 2016 05:07:53 -0700 (PDT) From: Shunqian Zheng To: joro@8bytes.org, heiko@sntech.de, robh+dt@kernel.org, mark.rutland@arm.com, linux@armlinux.org.uk, mark.yao@rock-chips.com, airlied@linux.ie, tfiga@google.com, xxm@rock-chips.com Subject: [PATCH v3 3/6] iommu/rockchip: support virtual iommu slave device Date: Wed, 15 Jun 2016 20:04:42 +0800 Message-Id: <1465992285-16187-4-git-send-email-zhengsq@rock-chips.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1465992285-16187-1-git-send-email-zhengsq@rock-chips.com> References: <1465992285-16187-1-git-send-email-zhengsq@rock-chips.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20160615_050815_268978_623B3DA7 X-CRM114-Status: GOOD ( 18.62 ) X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, iommu@lists.linux-foundation.org, Shunqian Zheng , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+patchwork-linux-rockchip=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP An virtual master device like DRM need to attach to iommu domain to share the mapping with VOPs(the one with actual iommu slaves). DRM attaches to iommu and allocates buffers before VOPs enabled, which means there may have not real iommu devices can be used to do dma mapping. This patch creates a iommu when virtual master(group is NULL) attaching, so it can use this iommu even the real iommus disabled. Changes of V3: - Instead of registering virtual iommu in DTS, this patch creates a iommu when attaching. Signed-off-by: Shunqian Zheng Suggested-by: Tomasz Figa --- drivers/iommu/rockchip-iommu.c | 133 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 122 insertions(+), 11 deletions(-) diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 3c16ec3..82ecc99 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,14 @@ struct rk_iommu { struct iommu_domain *domain; /* domain to which iommu is attached */ }; +/* A virtual iommu registered without resource or + * interrupts when DRM attaching. + */ +static inline bool rk_iommu_is_virtual(struct rk_iommu *iommu) +{ + return iommu->num_mmu == 0; +} + static inline void rk_table_flush(u32 *va, unsigned int count) { phys_addr_t pa_start = virt_to_phys(va); @@ -780,6 +789,85 @@ static struct rk_iommu *rk_iommu_from_dev(struct device *dev) return rk_iommu; } +static struct rk_iommu *rk_iommu_add_virtual_iommu(struct device *dev, + struct rk_iommu_domain *rk_domain) +{ + struct rk_iommu *iommu; + struct iommu_group *group; + struct platform_device *pdev; + struct device *iommu_dev; + int ret; + + pdev = platform_device_register_simple("rk_iommu", PLATFORM_DEVID_AUTO, + NULL, 0); + if (IS_ERR(pdev)) { + dev_err(dev, "Failed to register simple rk_iommu device\n"); + return NULL; + } + + iommu_dev = &pdev->dev; + iommu_dev->dma_parms = devm_kzalloc(iommu_dev, + sizeof(*iommu_dev->dma_parms), GFP_KERNEL); + if (!iommu_dev->dma_parms) + goto err_unreg_pdev; + + /* Set dma_ops for virtual iommu, otherwise it would be dummy_dma_ops */ + arch_setup_dma_ops(iommu_dev, 0, DMA_BIT_MASK(32), NULL, false); + + dma_set_max_seg_size(iommu_dev, DMA_BIT_MASK(32)); + dma_coerce_mask_and_coherent(iommu_dev, DMA_BIT_MASK(32)); + + iommu = devm_kzalloc(iommu_dev, sizeof(*iommu), GFP_KERNEL); + if (!iommu) + goto err_unreg_pdev; + + iommu->dev = iommu_dev; + iommu->num_mmu = 0; + platform_set_drvdata(pdev, iommu); + + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(iommu_dev, "Failed to allocate IOMMU group\n"); + goto err_unreg_pdev; + } + + ret = iommu_group_add_device(group, dev); + if (ret) { + dev_err(iommu_dev, "Failed to add device to group\n"); + goto err_put_group; + } + + iommu_group_set_iommudata(group, iommu_dev, NULL); + + ret = iommu_attach_group(&rk_domain->domain, group); + if (ret) { + dev_err(iommu_dev, "Failed to attach group\n"); + goto err_remove_device; + } + + iommu_group_put(group); + + return iommu; + +err_remove_device: + iommu_group_remove_device(dev); +err_put_group: + iommu_group_put(group); +err_unreg_pdev: + platform_device_unregister(pdev); + + return NULL; +} + +static void rk_iommu_remove_virtual_iommu(struct device *dev) +{ + struct rk_iommu *iommu = rk_iommu_from_dev(dev); + struct platform_device *pdev = to_platform_device(iommu->dev); + + iommu_group_remove_device(dev); + platform_device_unregister(pdev); +} + static int rk_iommu_attach_device(struct iommu_domain *domain, struct device *dev) { @@ -789,13 +877,20 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, int ret, i; phys_addr_t dte_addr; - /* - * Allow 'virtual devices' (e.g., drm) to attach to domain. - * Such a device does not belong to an iommu group. - */ iommu = rk_iommu_from_dev(dev); - if (!iommu) + + /* Register iommu for virtual master(like DRM) */ + if (!iommu) { + iommu = rk_iommu_add_virtual_iommu(dev, rk_domain); + if (!iommu) + return -ENOMEM; + } + + iommu->domain = domain; + if (rk_iommu_is_virtual(iommu)) { + dev_dbg(dev, "Attach virtual device to iommu domain\n"); return 0; + } ret = rk_iommu_enable_stall(iommu); if (ret) @@ -805,7 +900,6 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, if (ret) return ret; - iommu->domain = domain; ret = devm_request_irq(iommu->dev, iommu->irq, rk_iommu_irq, IRQF_SHARED, dev_name(dev), iommu); @@ -842,10 +936,14 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, unsigned long flags; int i; - /* Allow 'virtual devices' (eg drm) to detach from domain */ iommu = rk_iommu_from_dev(dev); - if (!iommu) + + if (rk_iommu_is_virtual(iommu)) { + rk_iommu_remove_virtual_iommu(dev); + iommu->domain = NULL; + dev_dbg(dev, "Master with virtual iommu detached\n"); return; + } spin_lock_irqsave(&rk_domain->iommus_lock, flags); list_del_init(&iommu->node); @@ -861,7 +959,6 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, rk_iommu_disable_stall(iommu); devm_free_irq(iommu->dev, iommu->irq, iommu); - iommu->domain = NULL; dev_dbg(dev, "Detached from iommu domain\n"); @@ -878,6 +975,9 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type) if (!rk_domain) return NULL; + if (iommu_get_dma_cookie(&rk_domain->domain)) + goto err_dt; + /* * rk32xx iommus use a 2 level pagetable. * Each level1 (dt) and level2 (pt) table has 1024 4-byte entries. @@ -917,6 +1017,9 @@ static void rk_iommu_domain_free(struct iommu_domain *domain) } free_page((unsigned long)rk_domain->dt); + + iommu_put_dma_cookie(&rk_domain->domain); + kfree(rk_domain); } @@ -1034,8 +1137,15 @@ static int rk_iommu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rk_iommu *iommu; struct resource *res; + int num_res = pdev->num_resources; int i; + /* This is iommu register in rk_iommu_add_virtual_iommu() + * when virtual device attaching. + */ + if (num_res == 0) + return 0; + iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL); if (!iommu) return -ENOMEM; @@ -1043,12 +1153,13 @@ static int rk_iommu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, iommu); iommu->dev = dev; iommu->num_mmu = 0; - iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * iommu->num_mmu, + + iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * num_res, GFP_KERNEL); if (!iommu->bases) return -ENOMEM; - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < num_res; i++) { res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (!res) continue;