ARC: dma: non-coherent pages need V-P mapping if in HIGHMEM
authorVineet Gupta <vgupta@synopsys.com>
Mon, 14 Mar 2016 10:04:36 +0000 (15:34 +0530)
committerVineet Gupta <vgupta@synopsys.com>
Sat, 19 Mar 2016 09:04:08 +0000 (14:34 +0530)
Previously a non-coherent page (hardware IOC or simply driver needs)
could be handled by cpu with paddr alone (kvaddr used to be needed for
coherent mappings to enforce uncached semantics via a MMU mapping).

Now however such a page might still require a V-P mapping if it was in
physical address space > 32bits due to PAE40, which the CPU can't access
directly with a paddr

So decouple decision of kvaddr allocation from type of alloc request
(coh/non-coh)

Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
arch/arc/mm/dma.c

index 6cf1d924209a842f365b03c9ab6fc875c2b5b244..4b428a4493002ef57b11d010571929feea1b7f63 100644 (file)
@@ -28,23 +28,18 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
        struct page *page;
        phys_addr_t paddr;
        void *kvaddr;
+       int need_coh = 1, need_kvaddr = 0;
 
        page = alloc_pages(gfp, order);
        if (!page)
                return NULL;
 
-       /* This is linear addr (0x8000_0000 based) */
-       paddr = page_to_phys(page);
-
-       /* For now bus address is exactly same as paddr */
-       *dma_handle = paddr;
-
        /*
         * IOC relies on all data (even coherent DMA data) being in cache
         * Thus allocate normal cached memory
         *
         * The gains with IOC are two pronged:
-        *   -For streaming data, elides needs for cache maintenance, saving
+        *   -For streaming data, elides need for cache maintenance, saving
         *    cycles in flush code, and bus bandwidth as all the lines of a
         *    buffer need to be flushed out to memory
         *   -For coherent data, Read/Write to buffers terminate early in cache
@@ -52,13 +47,31 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
         */
        if ((is_isa_arcv2() && ioc_exists) ||
            dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
-               return paddr;
+               need_coh = 0;
+
+       /*
+        * - A coherent buffer needs MMU mapping to enforce non-cachability
+        * - A highmem page needs a virtual handle (hence MMU mapping)
+        *   independent of cachability
+        */
+       if (PageHighMem(page) || need_coh)
+               need_kvaddr = 1;
+
+       /* This is linear addr (0x8000_0000 based) */
+       paddr = page_to_phys(page);
+
+       /* For now bus address is exactly same as paddr */
+       *dma_handle = paddr;
 
        /* This is kernel Virtual address (0x7000_0000 based) */
-       kvaddr = ioremap_nocache((unsigned long)paddr, size);
-       if (kvaddr == NULL) {
-               __free_pages(page, order);
-               return NULL;
+       if (need_kvaddr) {
+               kvaddr = ioremap_nocache((unsigned long)paddr, size);
+               if (kvaddr == NULL) {
+                       __free_pages(page, order);
+                       return NULL;
+               }
+       } else {
+               kvaddr = (void *)paddr;
        }
 
        /*
@@ -71,7 +84,8 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
         * Currently flush_cache_vmap nukes the L1 cache completely which
         * will be optimized as a separate commit
         */
-       dma_cache_wback_inv((unsigned long)paddr, size);
+       if (need_coh)
+               dma_cache_wback_inv((unsigned long)paddr, size);
 
        return kvaddr;
 }
@@ -80,9 +94,12 @@ static void arc_dma_free(struct device *dev, size_t size, void *vaddr,
                dma_addr_t dma_handle, struct dma_attrs *attrs)
 {
        struct page *page = virt_to_page(dma_handle);
+       int is_non_coh = 1;
+
+       is_non_coh = dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs) ||
+                       (is_isa_arcv2() && ioc_exists);
 
-       if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs) &&
-           !(is_isa_arcv2() && ioc_exists))
+       if (PageHighMem(page) || !is_non_coh)
                iounmap((void __force __iomem *)vaddr);
 
        __free_pages(page, get_order(size));