1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2014 The Linux Foundation
4  */
5 #include <linux/dma-map-ops.h>
6 #include <linux/slab.h>
7 #include <linux/vmalloc.h>
8 
dma_common_find_pages(void * cpu_addr)9 struct page **dma_common_find_pages(void *cpu_addr)
10 {
11 	struct vm_struct *area = find_vm_area(cpu_addr);
12 
13 	if (!area || area->flags != VM_DMA_COHERENT)
14 		return NULL;
15 	return area->pages;
16 }
17 
18 /*
19  * Remaps an array of PAGE_SIZE pages into another vm_area.
20  * Cannot be used in non-sleeping contexts
21  */
dma_common_pages_remap(struct page ** pages,size_t size,pgprot_t prot,const void * caller)22 void *dma_common_pages_remap(struct page **pages, size_t size,
23 			 pgprot_t prot, const void *caller)
24 {
25 	void *vaddr;
26 
27 	vaddr = vmap(pages, PAGE_ALIGN(size) >> PAGE_SHIFT,
28 		     VM_DMA_COHERENT, prot);
29 	if (vaddr)
30 		find_vm_area(vaddr)->pages = pages;
31 	return vaddr;
32 }
33 
34 /*
35  * Remaps an allocated contiguous region into another vm_area.
36  * Cannot be used in non-sleeping contexts
37  */
dma_common_contiguous_remap(struct page * page,size_t size,pgprot_t prot,const void * caller)38 void *dma_common_contiguous_remap(struct page *page, size_t size,
39 			pgprot_t prot, const void *caller)
40 {
41 	int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
42 	struct page **pages;
43 	void *vaddr;
44 	int i;
45 
46 	pages = kmalloc_array(count, sizeof(struct page *), GFP_KERNEL);
47 	if (!pages)
48 		return NULL;
49 	for (i = 0; i < count; i++)
50 		pages[i] = nth_page(page, i);
51 	vaddr = vmap(pages, count, VM_DMA_COHERENT, prot);
52 	kfree(pages);
53 
54 	return vaddr;
55 }
56 
57 /*
58  * Unmaps a range previously mapped by dma_common_*_remap
59  */
dma_common_free_remap(void * cpu_addr,size_t size)60 void dma_common_free_remap(void *cpu_addr, size_t size)
61 {
62 	struct vm_struct *area = find_vm_area(cpu_addr);
63 
64 	if (!area || area->flags != VM_DMA_COHERENT) {
65 		WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
66 		return;
67 	}
68 
69 	vunmap(cpu_addr);
70 }
71