]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
SPARC64: Introduce IOMMU BYPASS method
authorTushar Dave <tushar.n.dave@oracle.com>
Fri, 21 Apr 2017 17:49:31 +0000 (10:49 -0700)
committerAllen Pais <allen.pais@oracle.com>
Tue, 11 Jul 2017 07:36:50 +0000 (13:06 +0530)
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 <chris.hyser@oracle.com>
Signed-off-by: Tushar Dave <tushar.n.dave@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/kernel/pci_sun4v.c
arch/sparc/kernel/pci_sun4v.h
arch/sparc/kernel/pci_sun4v_asm.S

index db968b72702eb60ea74ba14ab8cf6b2b5410c91b..0da3b11b98af66f61608b3a510355e27282092f0 100644 (file)
@@ -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)
index c87db0a89149d816cb70a12090b38a980e8f9d90..ea5e5290c20350c0b2b96e9a2ce46a543dfbf70f 100644 (file)
@@ -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,
index 55eb534e4613b4ba8a6be5486a35c7d6ea51ca34..5ba5b551dbde3520d920059b31537aac60ddd855 100644 (file)
@@ -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