Branch data Line data Source code
1 : : /*
2 : : * Coherent per-device memory handling.
3 : : * Borrowed from i386
4 : : */
5 : : #include <linux/slab.h>
6 : : #include <linux/kernel.h>
7 : : #include <linux/module.h>
8 : : #include <linux/dma-mapping.h>
9 : :
10 : : struct dma_coherent_mem {
11 : : void *virt_base;
12 : : dma_addr_t device_base;
13 : : phys_addr_t pfn_base;
14 : : int size;
15 : : int flags;
16 : : unsigned long *bitmap;
17 : : };
18 : :
19 : 0 : int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
20 : : dma_addr_t device_addr, size_t size, int flags)
21 : : {
22 : : void __iomem *mem_base = NULL;
23 : 0 : int pages = size >> PAGE_SHIFT;
24 : 0 : int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
25 : :
26 [ # # ]: 0 : if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0)
27 : : goto out;
28 [ # # ]: 0 : if (!size)
29 : : goto out;
30 [ # # ]: 0 : if (dev->dma_mem)
31 : : goto out;
32 : :
33 : : /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */
34 : :
35 : 0 : mem_base = ioremap(bus_addr, size);
36 [ # # ]: 0 : if (!mem_base)
37 : : goto out;
38 : :
39 : 0 : dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
40 [ # # ]: 0 : if (!dev->dma_mem)
41 : : goto out;
42 : 0 : dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
43 [ # # ]: 0 : if (!dev->dma_mem->bitmap)
44 : : goto free1_out;
45 : :
46 : 0 : dev->dma_mem->virt_base = mem_base;
47 : 0 : dev->dma_mem->device_base = device_addr;
48 : 0 : dev->dma_mem->pfn_base = PFN_DOWN(bus_addr);
49 : 0 : dev->dma_mem->size = pages;
50 : 0 : dev->dma_mem->flags = flags;
51 : :
52 [ # # ]: 0 : if (flags & DMA_MEMORY_MAP)
53 : : return DMA_MEMORY_MAP;
54 : :
55 : 0 : return DMA_MEMORY_IO;
56 : :
57 : : free1_out:
58 : 0 : kfree(dev->dma_mem);
59 : : out:
60 [ # # ]: 0 : if (mem_base)
61 : 0 : iounmap(mem_base);
62 : : return 0;
63 : : }
64 : : EXPORT_SYMBOL(dma_declare_coherent_memory);
65 : :
66 : 0 : void dma_release_declared_memory(struct device *dev)
67 : : {
68 : 0 : struct dma_coherent_mem *mem = dev->dma_mem;
69 : :
70 [ # # ]: 0 : if (!mem)
71 : 0 : return;
72 : 0 : dev->dma_mem = NULL;
73 : 0 : iounmap(mem->virt_base);
74 : 0 : kfree(mem->bitmap);
75 : 0 : kfree(mem);
76 : : }
77 : : EXPORT_SYMBOL(dma_release_declared_memory);
78 : :
79 : 0 : void *dma_mark_declared_memory_occupied(struct device *dev,
80 : : dma_addr_t device_addr, size_t size)
81 : : {
82 : 0 : struct dma_coherent_mem *mem = dev->dma_mem;
83 : : int pos, err;
84 : :
85 : 0 : size += device_addr & ~PAGE_MASK;
86 : :
87 [ # # ]: 0 : if (!mem)
88 : : return ERR_PTR(-EINVAL);
89 : :
90 : 0 : pos = (device_addr - mem->device_base) >> PAGE_SHIFT;
91 [ # # ][ # # ]: 0 : err = bitmap_allocate_region(mem->bitmap, pos, get_order(size));
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
92 [ # # ]: 0 : if (err != 0)
93 : 0 : return ERR_PTR(err);
94 : 0 : return mem->virt_base + (pos << PAGE_SHIFT);
95 : : }
96 : : EXPORT_SYMBOL(dma_mark_declared_memory_occupied);
97 : :
98 : : /**
99 : : * dma_alloc_from_coherent() - try to allocate memory from the per-device coherent area
100 : : *
101 : : * @dev: device from which we allocate memory
102 : : * @size: size of requested memory area
103 : : * @dma_handle: This will be filled with the correct dma handle
104 : : * @ret: This pointer will be filled with the virtual address
105 : : * to allocated area.
106 : : *
107 : : * This function should be only called from per-arch dma_alloc_coherent()
108 : : * to support allocation from per-device coherent memory pools.
109 : : *
110 : : * Returns 0 if dma_alloc_coherent should continue with allocating from
111 : : * generic memory areas, or !0 if dma_alloc_coherent should return @ret.
112 : : */
113 : 0 : int dma_alloc_from_coherent(struct device *dev, ssize_t size,
114 : : dma_addr_t *dma_handle, void **ret)
115 : : {
116 : : struct dma_coherent_mem *mem;
117 [ # # ][ # # ]: 0 : int order = get_order(size);
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
118 : : int pageno;
119 : :
120 [ # # ]: 0 : if (!dev)
121 : : return 0;
122 : 0 : mem = dev->dma_mem;
123 [ # # ]: 0 : if (!mem)
124 : : return 0;
125 : :
126 : 0 : *ret = NULL;
127 : :
128 [ # # ]: 0 : if (unlikely(size > (mem->size << PAGE_SHIFT)))
129 : : goto err;
130 : :
131 : 0 : pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
132 [ # # ]: 0 : if (unlikely(pageno < 0))
133 : : goto err;
134 : :
135 : : /*
136 : : * Memory was found in the per-device area.
137 : : */
138 : 0 : *dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
139 : 0 : *ret = mem->virt_base + (pageno << PAGE_SHIFT);
140 [ # # ]: 0 : memset(*ret, 0, size);
141 : :
142 : : return 1;
143 : :
144 : : err:
145 : : /*
146 : : * In the case where the allocation can not be satisfied from the
147 : : * per-device area, try to fall back to generic memory if the
148 : : * constraints allow it.
149 : : */
150 : 0 : return mem->flags & DMA_MEMORY_EXCLUSIVE;
151 : : }
152 : : EXPORT_SYMBOL(dma_alloc_from_coherent);
153 : :
154 : : /**
155 : : * dma_release_from_coherent() - try to free the memory allocated from per-device coherent memory pool
156 : : * @dev: device from which the memory was allocated
157 : : * @order: the order of pages allocated
158 : : * @vaddr: virtual address of allocated pages
159 : : *
160 : : * This checks whether the memory was allocated from the per-device
161 : : * coherent memory pool and if so, releases that memory.
162 : : *
163 : : * Returns 1 if we correctly released the memory, or 0 if
164 : : * dma_release_coherent() should proceed with releasing memory from
165 : : * generic pools.
166 : : */
167 : 0 : int dma_release_from_coherent(struct device *dev, int order, void *vaddr)
168 : : {
169 [ # # ]: 0 : struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
170 : :
171 [ # # ][ # # ]: 0 : if (mem && vaddr >= mem->virt_base && vaddr <
[ # # ]
172 : 0 : (mem->virt_base + (mem->size << PAGE_SHIFT))) {
173 : 0 : int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
174 : :
175 : 0 : bitmap_release_region(mem->bitmap, page, order);
176 : 0 : return 1;
177 : : }
178 : : return 0;
179 : : }
180 : : EXPORT_SYMBOL(dma_release_from_coherent);
181 : :
182 : : /**
183 : : * dma_mmap_from_coherent() - try to mmap the memory allocated from
184 : : * per-device coherent memory pool to userspace
185 : : * @dev: device from which the memory was allocated
186 : : * @vma: vm_area for the userspace memory
187 : : * @vaddr: cpu address returned by dma_alloc_from_coherent
188 : : * @size: size of the memory buffer allocated by dma_alloc_from_coherent
189 : : * @ret: result from remap_pfn_range()
190 : : *
191 : : * This checks whether the memory was allocated from the per-device
192 : : * coherent memory pool and if so, maps that memory to the provided vma.
193 : : *
194 : : * Returns 1 if we correctly mapped the memory, or 0 if the caller should
195 : : * proceed with mapping memory from generic pools.
196 : : */
197 : 0 : int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma,
198 : : void *vaddr, size_t size, int *ret)
199 : : {
200 [ # # ]: 0 : struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
201 : :
202 [ # # ][ # # ]: 0 : if (mem && vaddr >= mem->virt_base && vaddr + size <=
[ # # ]
203 : 0 : (mem->virt_base + (mem->size << PAGE_SHIFT))) {
204 : 0 : unsigned long off = vma->vm_pgoff;
205 : 0 : int start = (vaddr - mem->virt_base) >> PAGE_SHIFT;
206 : 0 : int user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
207 : 0 : int count = size >> PAGE_SHIFT;
208 : :
209 : 0 : *ret = -ENXIO;
210 [ # # ][ # # ]: 0 : if (off < count && user_count <= count - off) {
211 : 0 : unsigned pfn = mem->pfn_base + start + off;
212 : 0 : *ret = remap_pfn_range(vma, vma->vm_start, pfn,
213 : 0 : user_count << PAGE_SHIFT,
214 : : vma->vm_page_prot);
215 : : }
216 : : return 1;
217 : : }
218 : : return 0;
219 : : }
220 : : EXPORT_SYMBOL(dma_mmap_from_coherent);
|