Merge tag 'lsk-v3.10-15.09-android'
[firefly-linux-kernel-4.4.55.git] / drivers / iommu / rockchip-iovmm.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation.
5  */
6
7 #ifdef CONFIG_ROCKCHIP_IOMMU_DEBUG
8 #define DEBUG
9 #endif
10
11 #include <linux/kernel.h>
12 #include <linux/hardirq.h>
13 #include <linux/slab.h>
14 #include <linux/scatterlist.h>
15 #include <linux/err.h>
16
17 #include <linux/of.h>
18 #include <linux/of_platform.h>
19
20 #include "rockchip-iommu.h"
21
22 #define IOMMU_REGION_GUARD              (2<<PAGE_SHIFT)
23
24 static struct rk_vm_region *find_region(struct rk_iovmm *vmm, dma_addr_t iova)
25 {
26         struct rk_vm_region *region;
27
28         list_for_each_entry(region, &vmm->regions_list, node)
29                 if (region->start == iova)
30                         return region;
31
32         return NULL;
33 }
34
35 int rockchip_iovmm_invalidate_tlb(struct device *dev)
36 {
37         int ret = rockchip_iommu_tlb_invalidate_global(dev);
38
39         return ret;
40 }
41
42 void rockchip_iovmm_set_fault_handler(struct device *dev,
43                                        rockchip_iommu_fault_handler_t handler)
44 {
45         struct iommu_drvdata *data = dev_get_drvdata(dev->archdata.iommu);
46
47         data->fault_handler = handler;
48 }
49
50 int rockchip_iovmm_activate(struct device *dev)
51 {
52         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
53
54         return iommu_attach_device(vmm->domain, dev);
55 }
56
57 void rockchip_iovmm_deactivate(struct device *dev)
58 {
59         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
60
61         iommu_detach_device(vmm->domain, dev);
62 }
63
64 dma_addr_t rockchip_iovmm_map(struct device *dev,
65         struct scatterlist *sg, off_t offset, size_t size)
66 {
67         off_t start_off;
68         dma_addr_t addr, start = 0;
69         size_t mapped_size = 0;
70         struct rk_vm_region *region;
71         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
72         int order;
73         int ret;
74         
75         for (; sg_dma_len(sg) < offset; sg = sg_next(sg))
76                 offset -= sg_dma_len(sg);
77
78         start_off = offset_in_page(sg_phys(sg) + offset);
79         size = PAGE_ALIGN(size + start_off);
80
81         order = __fls(min_t(size_t, size, SZ_1M));
82
83         region = kmalloc(sizeof(*region), GFP_KERNEL);
84         if (!region) {
85                 ret = -ENOMEM;
86                 goto err_map_nomem;
87         }
88
89         start = (dma_addr_t)gen_pool_alloc(vmm->vmm_pool,
90                                            size+IOMMU_REGION_GUARD);
91         if (!start) {
92                 ret = -ENOMEM;
93                 goto err_map_noiomem;
94         }
95
96         pr_debug("%s: size = %zx\n", __func__, size);
97
98         addr = start;
99         do {
100                 phys_addr_t phys;
101                 size_t len;
102
103                 phys = sg_phys(sg);
104                 len = sg_dma_len(sg);
105
106                 /* if back to back sg entries are contiguous consolidate them */
107                 while (sg_next(sg) && sg_phys(sg) +
108                        sg_dma_len(sg) == sg_phys(sg_next(sg))) {
109                         len += sg_dma_len(sg_next(sg));
110                         sg = sg_next(sg);
111                 }
112
113                 if (offset > 0) {
114                         len -= offset;
115                         phys += offset;
116                         offset = 0;
117                 }
118
119                 if (offset_in_page(phys)) {
120                         len += offset_in_page(phys);
121                         phys = round_down(phys, PAGE_SIZE);
122                 }
123
124                 len = PAGE_ALIGN(len);
125
126                 if (len > (size - mapped_size))
127                         len = size - mapped_size;
128                 pr_debug("addr = %pad, phys = %pa, len = %zx\n", &addr, &phys, len);
129                 ret = iommu_map(vmm->domain, addr, phys, len, 0);
130                 if (ret)
131                         break;
132
133                 addr += len;
134                 mapped_size += len;
135         } while ((sg = sg_next(sg)) && (mapped_size < size));
136
137         BUG_ON(mapped_size > size);
138
139         if (mapped_size < size)
140                 goto err_map_map;
141
142         region->start = start + start_off;
143         region->size = size;
144
145         INIT_LIST_HEAD(&region->node);
146
147         spin_lock(&vmm->lock);
148
149         list_add(&region->node, &vmm->regions_list);
150
151         spin_unlock(&vmm->lock);
152
153         ret = rockchip_iommu_tlb_invalidate(dev);
154         if (ret) {
155                 spin_lock(&vmm->lock);
156                 list_del(&region->node);
157                 spin_unlock(&vmm->lock);
158                 goto err_map_map;
159         }
160         dev_dbg(dev->archdata.iommu, "IOVMM: Allocated VM region @ %p/%#X bytes.\n",
161         &region->start, region->size);
162
163         return region->start;
164
165 err_map_map:
166         iommu_unmap(vmm->domain, start, mapped_size);
167         gen_pool_free(vmm->vmm_pool, start, size);
168 err_map_noiomem:
169         kfree(region);
170 err_map_nomem:
171         dev_err(dev->archdata.iommu, "IOVMM: Failed to allocated VM region for %zx bytes.\n", size);
172         return (dma_addr_t)ret;
173 }
174
175 void rockchip_iovmm_unmap(struct device *dev, dma_addr_t iova)
176 {
177         struct rk_vm_region *region;
178         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
179         size_t unmapped_size;
180
181         /* This function must not be called in IRQ handlers */
182         BUG_ON(in_irq());
183
184         spin_lock(&vmm->lock);
185
186         region = find_region(vmm, iova);
187         if (WARN_ON(!region)) {
188                 spin_unlock(&vmm->lock);
189                 return;
190         }
191
192         list_del(&region->node);
193
194         spin_unlock(&vmm->lock);
195
196         region->start = round_down(region->start, PAGE_SIZE);
197
198         unmapped_size = iommu_unmap(vmm->domain,
199                                     region->start, region->size);
200         /*
201         rockchip_iommu_tlb_invalidate(dev);
202         */
203         gen_pool_free(vmm->vmm_pool, region->start,
204                       region->size+IOMMU_REGION_GUARD);
205
206         WARN_ON(unmapped_size != region->size);
207         
208         dev_dbg(dev->archdata.iommu, "IOVMM: Unmapped %zx bytes from %pad.\n",
209                 unmapped_size, &region->start);
210         
211         kfree(region);
212 }
213
214 int rockchip_iovmm_map_oto(struct device *dev, phys_addr_t phys, size_t size)
215 {
216         struct rk_vm_region *region;
217         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
218         int ret;
219
220         if (WARN_ON((phys + size) >= IOVA_START)) {
221                 dev_err(dev->archdata.iommu, "Unable to create one to one mapping for %zx @ %pa\n",
222                        size, &phys);
223                 return -EINVAL;
224         }
225
226         region = kmalloc(sizeof(*region), GFP_KERNEL);
227         if (!region)
228                 return -ENOMEM;
229
230         if (WARN_ON(phys & ~PAGE_MASK))
231                 phys = round_down(phys, PAGE_SIZE);
232
233
234         ret = iommu_map(vmm->domain, (dma_addr_t)phys, phys, size, 0);
235         if (ret < 0) {
236                 kfree(region);
237                 return ret;
238         }
239
240         region->start = (dma_addr_t)phys;
241         region->size = size;
242         INIT_LIST_HEAD(&region->node);
243
244         spin_lock(&vmm->lock);
245
246         list_add(&region->node, &vmm->regions_list);
247
248         spin_unlock(&vmm->lock);
249
250         ret = rockchip_iommu_tlb_invalidate(dev);
251         if (ret)
252                 return ret;
253
254         return 0;
255 }
256
257 void rockchip_iovmm_unmap_oto(struct device *dev, phys_addr_t phys)
258 {
259         struct rk_vm_region *region;
260         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
261         size_t unmapped_size;
262
263         /* This function must not be called in IRQ handlers */
264         BUG_ON(in_irq());
265
266         if (WARN_ON(phys & ~PAGE_MASK))
267                 phys = round_down(phys, PAGE_SIZE);
268
269         spin_lock(&vmm->lock);
270
271         region = find_region(vmm, (dma_addr_t)phys);
272         if (WARN_ON(!region)) {
273                 spin_unlock(&vmm->lock);
274                 return;
275         }
276
277         list_del(&region->node);
278
279         spin_unlock(&vmm->lock);
280
281         unmapped_size = iommu_unmap(vmm->domain, region->start, region->size);
282         WARN_ON(unmapped_size != region->size);
283         dev_dbg(dev->archdata.iommu, "IOVMM: Unmapped %zx bytes from %pad.\n",
284                unmapped_size, &region->start);
285
286         kfree(region);
287 }
288
289 int rockchip_init_iovmm(struct device *iommu, struct rk_iovmm *vmm)
290 {
291         int ret = 0;
292
293         vmm->vmm_pool = gen_pool_create(PAGE_SHIFT, -1);
294         if (!vmm->vmm_pool) {
295                 ret = -ENOMEM;
296                 goto err_setup_genalloc;
297         }
298
299         /* (1GB - 4KB) addr space from 0x10000000 */
300         ret = gen_pool_add(vmm->vmm_pool, IOVA_START, IOVM_SIZE, -1);
301         if (ret)
302                 goto err_setup_domain;
303
304         vmm->domain = iommu_domain_alloc(&platform_bus_type);
305         if (!vmm->domain) {
306                 ret = -ENOMEM;
307                 goto err_setup_domain;
308         }
309
310         spin_lock_init(&vmm->lock);
311
312         INIT_LIST_HEAD(&vmm->regions_list);
313
314         dev_info(iommu, "IOVMM: Created %#x B IOVMM from %#x.\n",
315                 IOVM_SIZE, IOVA_START);
316         return 0;
317 err_setup_domain:
318         gen_pool_destroy(vmm->vmm_pool);
319 err_setup_genalloc:
320         dev_err(iommu, "IOVMM: Failed to create IOVMM (%d)\n", ret);
321
322         return ret;
323 }