From: Tushar Dave Date: Fri, 21 Apr 2017 17:49:31 +0000 (-0700) Subject: SPARC64: Introduce IOMMU BYPASS method X-Git-Tag: v4.1.12-106.0.20170720_1900~80 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=6b8533ad05451fc27cf39cc890a6ce6c4dc2d5e9;p=users%2Fjedix%2Flinux-maple.git SPARC64: Introduce IOMMU BYPASS method This change adds IOMMU BYPASS HV API that will allow PCIe devices to bypass IOMMU during DMA map/unmap. Orabug: 25573557 Reviewed-by: chris hyser Signed-off-by: Tushar Dave Signed-off-by: Allen Pais --- diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c index db968b72702e..0da3b11b98af 100644 --- a/arch/sparc/kernel/pci_sun4v.c +++ b/arch/sparc/kernel/pci_sun4v.c @@ -361,6 +361,40 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu, free_pages((unsigned long)cpu, order); } +static dma_addr_t dma_4v_map_page_bypass(struct device *dev, struct page *page, + unsigned long offset, size_t sz, + enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + struct pci_pbm_info *pbm; + unsigned long devhandle; + unsigned long ra; + unsigned long prot; + unsigned long dma_addr; + + if (unlikely(direction == DMA_NONE)) + goto bad; + + prot = HV_PCI_MAP_ATTR_READ; + if (direction != DMA_TO_DEVICE) + prot |= HV_PCI_MAP_ATTR_WRITE; + + /* VPCI maj=2, min=[0,1] or greater supports relax ordering */ + if (dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs) && vpci_major >= 2) + prot |= HV_PCI_MAP_ATTR_RELAXED_ORDER; + + pbm = dev->archdata.host_controller; + devhandle = pbm->devhandle; + ra = __pa(page_address(page) + offset); + if (pci_sun4v_iommu_getbypass(devhandle, ra, prot, &dma_addr)) + goto bad; + + return dma_addr; + +bad: + return DMA_ERROR_CODE; +} + static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page, unsigned long offset, size_t sz, enum dma_data_direction direction, @@ -484,6 +518,45 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr, iommu_tbl_range_free(tbl, bus_addr, npages, IOMMU_ERROR_CODE); } +static int dma_4v_map_sg_bypass(struct device *dev, struct scatterlist *sglist, + int nelems, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + struct pci_pbm_info *pbm; + unsigned long devhandle; + unsigned long ra; + unsigned long prot; + unsigned long dma_addr; + struct scatterlist *s; + int i; + + if (unlikely(direction == DMA_NONE)) + goto bad; + + prot = HV_PCI_MAP_ATTR_READ; + if (direction != DMA_TO_DEVICE) + prot |= HV_PCI_MAP_ATTR_WRITE; + + /* VPCI maj=2, min=[0,1] or greater supports relax ordering */ + if (dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs) && vpci_major >= 2) + prot |= HV_PCI_MAP_ATTR_RELAXED_ORDER; + + pbm = dev->archdata.host_controller; + devhandle = pbm->devhandle; + for_each_sg(sglist, s, nelems, i) { + ra = (unsigned long)SG_ENT_PHYS_ADDRESS(s); + if (pci_sun4v_iommu_getbypass(devhandle, ra, prot, &dma_addr)) + goto bad; + s->dma_address = dma_addr; + s->dma_length = s->length; + } + + return nelems; + +bad: + return 0; +} + static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist, int nelems, enum dma_data_direction direction, struct dma_attrs *attrs) diff --git a/arch/sparc/kernel/pci_sun4v.h b/arch/sparc/kernel/pci_sun4v.h index c87db0a89149..ea5e5290c203 100644 --- a/arch/sparc/kernel/pci_sun4v.h +++ b/arch/sparc/kernel/pci_sun4v.h @@ -18,6 +18,10 @@ unsigned long pci_sun4v_iommu_getmap(unsigned long devhandle, unsigned long tsbid, unsigned long *io_attributes, unsigned long *real_address); +unsigned long pci_sun4v_iommu_getbypass(unsigned long dev_hdl, + unsigned long ra, + unsigned long attr, + unsigned long *io_addr_p); unsigned long pci_sun4v_config_get(unsigned long devhandle, unsigned long pci_device, unsigned long config_offset, diff --git a/arch/sparc/kernel/pci_sun4v_asm.S b/arch/sparc/kernel/pci_sun4v_asm.S index 55eb534e4613..5ba5b551dbde 100644 --- a/arch/sparc/kernel/pci_sun4v_asm.S +++ b/arch/sparc/kernel/pci_sun4v_asm.S @@ -56,6 +56,22 @@ ENTRY(pci_sun4v_iommu_getmap) mov %o0, %o0 ENDPROC(pci_sun4v_iommu_getmap) + /* %o0: devhandle + * %o1: r_addr + * %o2: io_attributes + * %o3: &io_addr + * + * returns: %o0: status + */ +ENTRY(pci_sun4v_iommu_getbypass) + mov HV_FAST_PCI_IOMMU_GETBYPASS, %o5 + ta HV_FAST_TRAP + brz,a %o0, 1f + stx %o1, [%o3] +1: retl + nop +ENDPROC(pci_sun4v_iommu_getbypass) + /* %o0: devhandle * %o1: pci_device * %o2: pci_config_offset