From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-pf0-f196.google.com ([209.85.192.196]:34945 "EHLO mail-pf0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753190AbcITMDF (ORCPT ); Tue, 20 Sep 2016 08:03:05 -0400 Received: by mail-pf0-f196.google.com with SMTP id 6so895104pfl.2 for ; Tue, 20 Sep 2016 05:03:04 -0700 (PDT) From: Magnus Damm To: linux-renesas-soc@vger.kernel.org Cc: Magnus Damm Date: Tue, 20 Sep 2016 20:54:33 +0900 Message-Id: <20160920115433.14772.84308.sendpatchset@little-apple> Subject: [PATCH/RFC] iommu/ipmmu-vmsa: IPMMU SYS-DMAC iova mapping workaround Sender: linux-renesas-soc-owner@vger.kernel.org List-ID: From: Magnus Damm Here's some prototype code that works around the lack of software support for mapping I/O devices to the SYS-DMAC hardware via the DMA Engine framework when using IOMMU. The code itself is one big layering violation that goes through the DT and unconditionally maps I/O devices using DMACs via the IPMMU device instance into iova space with a 1:1 mapping. This very short term prototype will for instance automatically make the SCIF serial port function with the IPMMU hardware in case the SYS-DMAC is hooked up to the IPMMU device. Not to be confused with the more long term solution to allow the DMA Engine framework to map I/O device memory dynamically. Not-Yet-Signed-off-by: Magnus Damm --- Applies on top of: renesas-drivers-2016-09-13-v4.8-rc6 drivers/iommu/ipmmu-vmsa.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) --- 0001/drivers/iommu/ipmmu-vmsa.c +++ work/drivers/iommu/ipmmu-vmsa.c 2016-09-20 20:03:37.620607110 +0900 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -625,6 +626,78 @@ static void ipmmu_domain_free(struct iom kfree(domain); } +static void ipmmu_workaround_map(struct iommu_domain *io_domain, + struct device *dma_dev, struct resource *res) +{ + phys_addr_t phys_addr; + + dev_info(dma_dev, "map %pr\n", res); + + phys_addr = iommu_iova_to_phys(io_domain, res->start); + if (phys_addr) + return; + + iommu_map(io_domain, res->start, res->start, + ALIGN(resource_size(res), SZ_4K), + IOMMU_READ | IOMMU_WRITE); +} + +static void ipmmu_workaround_dt(struct iommu_domain *io_domain, + struct device *dev, + void (*match)(struct iommu_domain *io_domain, + struct device *dma_dev, + struct resource *res)) +{ + struct device_node *np = NULL; + struct of_phandle_args dma_spec; + struct resource r; + int i, cnt; + bool found; + + /* Locate I/O devices using the DMAC and map their registers */ + while ((np = of_find_all_nodes(np))) { + if (!of_find_property(np, "dmas", NULL)) + continue; + + cnt = of_property_count_strings(np, "dma-names"); + if (cnt < 0) + continue; + + found = false; + for (i = 0; i < cnt; i++) { + if (of_parse_phandle_with_args(np, "dmas", + "#dma-cells", i, + &dma_spec)) + continue; + + if (dma_spec.np == dev->of_node) + found = true; + + of_node_put(dma_spec.np); + } + + if (!found) + continue; + + i = 0; + while (!of_address_to_resource(np, i, &r)) { + match(io_domain, dev, &r); + i++; + } + } +} + +static void ipmmu_workaround(struct iommu_domain *io_domain, + struct device *dev) +{ + /* only apply workaround for DMA controllers */ + if (!strstr(dev_name(dev), "dma-controller")) + return; + + dev_info(dev, "Adding iommu workaround to map I/O devices for DMACs\n"); + ipmmu_workaround_dt(io_domain, dev, ipmmu_workaround_map); +} + static int ipmmu_attach_device(struct iommu_domain *io_domain, struct device *dev) { @@ -678,6 +751,8 @@ static int ipmmu_attach_device(struct io if (ret < 0) return ret; + ipmmu_workaround(io_domain, dev); + for (i = 0; i < archdata->num_utlbs; ++i) ipmmu_utlb_enable(domain, archdata->utlbs[i]);