hc
2023-11-20 3c9370f7b6bffd697c9907a7139e9df5b0d4b9df
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 * Copyright (C) 2004 - 2007  Paul Mundt
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/dma-noncoherent.h>
#include <linux/module.h>
#include <asm/cacheflush.h>
#include <asm/addrspace.h>
 
void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
       gfp_t gfp, unsigned long attrs)
{
   void *ret, *ret_nocache;
   int order = get_order(size);
 
   gfp |= __GFP_ZERO;
 
   ret = (void *)__get_free_pages(gfp, order);
   if (!ret)
       return NULL;
 
   /*
    * Pages from the page allocator may have data present in
    * cache. So flush the cache before using uncached memory.
    */
   arch_sync_dma_for_device(dev, virt_to_phys(ret), size,
           DMA_BIDIRECTIONAL);
 
   ret_nocache = (void __force *)ioremap_nocache(virt_to_phys(ret), size);
   if (!ret_nocache) {
       free_pages((unsigned long)ret, order);
       return NULL;
   }
 
   split_page(pfn_to_page(virt_to_phys(ret) >> PAGE_SHIFT), order);
 
   *dma_handle = virt_to_phys(ret);
   if (!WARN_ON(!dev))
       *dma_handle -= PFN_PHYS(dev->dma_pfn_offset);
 
   return ret_nocache;
}
 
void arch_dma_free(struct device *dev, size_t size, void *vaddr,
       dma_addr_t dma_handle, unsigned long attrs)
{
   int order = get_order(size);
   unsigned long pfn = (dma_handle >> PAGE_SHIFT);
   int k;
 
   if (!WARN_ON(!dev))
       pfn += dev->dma_pfn_offset;
 
   for (k = 0; k < (1 << order); k++)
       __free_pages(pfn_to_page(pfn + k), 0);
 
   iounmap(vaddr);
}
 
void arch_sync_dma_for_device(struct device *dev, phys_addr_t paddr,
       size_t size, enum dma_data_direction dir)
{
   void *addr = sh_cacheop_vaddr(phys_to_virt(paddr));
 
   switch (dir) {
   case DMA_FROM_DEVICE:        /* invalidate only */
       __flush_invalidate_region(addr, size);
       break;
   case DMA_TO_DEVICE:        /* writeback only */
       __flush_wback_region(addr, size);
       break;
   case DMA_BIDIRECTIONAL:        /* writeback and invalidate */
       __flush_purge_region(addr, size);
       break;
   default:
       BUG();
   }
}