Merge remote-tracking branch 'origin/develop-3.10' into develop-3.10-next
[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 static struct rk_vm_region *find_region(struct rk_iovmm *vmm, dma_addr_t iova)
23 {
24         struct rk_vm_region *region;
25
26         list_for_each_entry(region, &vmm->regions_list, node)
27                 if (region->start == iova)
28                         return region;
29
30         return NULL;
31 }
32
33 int iovmm_activate(struct device *dev)
34 {
35         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
36
37         return iommu_attach_device(vmm->domain, dev);
38 }
39
40 void iovmm_deactivate(struct device *dev)
41 {
42         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
43
44         iommu_detach_device(vmm->domain, dev);
45 }
46
47 dma_addr_t iovmm_map(struct device *dev,struct scatterlist *sg, off_t offset,size_t size)
48 {
49         off_t start_off;
50         dma_addr_t addr, start = 0;
51         size_t mapped_size = 0;
52         struct rk_vm_region *region;
53         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
54         int order;
55         int ret;
56         
57         for (; sg_dma_len(sg) < offset; sg = sg_next(sg))
58                 offset -= sg_dma_len(sg);
59
60         start_off = offset_in_page(sg_phys(sg) + offset);
61         size = PAGE_ALIGN(size + start_off);
62
63         order = __fls(min_t(size_t, size, SZ_1M));
64
65         region = kmalloc(sizeof(*region), GFP_KERNEL);
66         if (!region) 
67         {
68                 ret = -ENOMEM;
69                 goto err_map_nomem;
70         }
71         
72         start = (dma_addr_t)gen_pool_alloc(vmm->vmm_pool, size);
73         if (!start) 
74         {
75                 ret = -ENOMEM;
76                 goto err_map_noiomem;
77         }
78
79         addr = start;
80         do {
81                 phys_addr_t phys;
82                 size_t len;
83
84                 phys = sg_phys(sg);
85                 len = sg_dma_len(sg);
86
87                 /* if back to back sg entries are contiguous consolidate them */
88                 while (sg_next(sg) &&sg_phys(sg) + sg_dma_len(sg) == sg_phys(sg_next(sg))) 
89                 {
90                         len += sg_dma_len(sg_next(sg));
91                         sg = sg_next(sg);
92                 }
93
94                 if (offset > 0) 
95                 {
96                         len -= offset;
97                         phys += offset;
98                         offset = 0;
99                 }
100
101                 if (offset_in_page(phys))
102                 {
103                         len += offset_in_page(phys);
104                         phys = round_down(phys, PAGE_SIZE);
105                 }
106
107                 len = PAGE_ALIGN(len);
108
109                 if (len > (size - mapped_size))
110                         len = size - mapped_size;
111
112                 ret = iommu_map(vmm->domain, addr, phys, len, 0);
113                 if (ret)
114                         break;
115
116                 addr += len;
117                 mapped_size += len;
118         } while ((sg = sg_next(sg)) && (mapped_size < size));
119
120         BUG_ON(mapped_size > size);
121
122         if (mapped_size < size)
123                 goto err_map_map;
124
125         region->start = start + start_off;
126         region->size = size;
127
128         INIT_LIST_HEAD(&region->node);
129
130         spin_lock(&vmm->lock);
131
132         list_add(&region->node, &vmm->regions_list);
133
134         spin_unlock(&vmm->lock);
135
136         rockchip_sysmmu_tlb_invalidate(dev);
137         /*
138         pr_err("IOVMM: Allocated VM region @ %#x/%#X bytes.\n",region->start, region->size);
139         */
140         return region->start;
141
142 err_map_map:
143         iommu_unmap(vmm->domain, start, mapped_size);
144         gen_pool_free(vmm->vmm_pool, start, size);
145 err_map_noiomem:
146         kfree(region);
147 err_map_nomem:
148         pr_err("IOVMM: Failed to allocated VM region for %#x bytes.\n",size);
149         return (dma_addr_t)ret;
150 }
151
152 void iovmm_unmap(struct device *dev, dma_addr_t iova)
153 {
154         struct rk_vm_region *region;
155         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
156         size_t unmapped_size;
157
158         /* This function must not be called in IRQ handlers */
159         BUG_ON(in_irq());
160
161         spin_lock(&vmm->lock);
162
163         region = find_region(vmm, iova);
164         if (WARN_ON(!region)) 
165         {
166                 spin_unlock(&vmm->lock);
167                 return;
168         }
169
170         list_del(&region->node);
171
172         spin_unlock(&vmm->lock);
173
174         region->start = round_down(region->start, PAGE_SIZE);
175
176         unmapped_size = iommu_unmap(vmm->domain, region->start, region->size);
177         /*
178         rockchip_sysmmu_tlb_invalidate(dev);
179         */
180         gen_pool_free(vmm->vmm_pool, region->start, region->size);
181
182         WARN_ON(unmapped_size != region->size);
183         /*
184         pr_err("IOVMM: Unmapped %#x bytes from %#x.\n",unmapped_size, region->start);
185         */
186         kfree(region);
187 }
188
189 int iovmm_map_oto(struct device *dev, phys_addr_t phys, size_t size)
190 {
191         struct rk_vm_region *region;
192         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
193         int ret;
194
195         if (WARN_ON((phys + size) >= IOVA_START)) 
196         {
197                 pr_err("Unable to create one to one mapping for %#x @ %#x\n",size, phys);
198                 return -EINVAL;
199         }
200
201         region = kmalloc(sizeof(*region), GFP_KERNEL);
202         if (!region)
203                 return -ENOMEM;
204
205         if (WARN_ON(phys & ~PAGE_MASK))
206                 phys = round_down(phys, PAGE_SIZE);
207
208
209         ret = iommu_map(vmm->domain, (dma_addr_t)phys, phys, size, 0);
210         if (ret < 0) 
211         {
212                 kfree(region);
213                 return ret;
214         }
215
216         region->start = (dma_addr_t)phys;
217         region->size = size;
218         INIT_LIST_HEAD(&region->node);
219
220         spin_lock(&vmm->lock);
221
222         list_add(&region->node, &vmm->regions_list);
223
224         spin_unlock(&vmm->lock);
225         
226         rockchip_sysmmu_tlb_invalidate(dev);
227
228         return 0;
229 }
230
231 void iovmm_unmap_oto(struct device *dev, phys_addr_t phys)
232 {
233         struct rk_vm_region *region;
234         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
235         size_t unmapped_size;
236
237         /* This function must not be called in IRQ handlers */
238         BUG_ON(in_irq());
239
240         if (WARN_ON(phys & ~PAGE_MASK))
241                 phys = round_down(phys, PAGE_SIZE);
242
243         spin_lock(&vmm->lock);
244
245         region = find_region(vmm, (dma_addr_t)phys);
246         if (WARN_ON(!region)) 
247         {
248                 spin_unlock(&vmm->lock);
249                 return;
250         }
251
252         list_del(&region->node);
253
254         spin_unlock(&vmm->lock);
255
256         unmapped_size = iommu_unmap(vmm->domain, region->start, region->size);
257         rockchip_sysmmu_tlb_invalidate(dev);
258         WARN_ON(unmapped_size != region->size);
259         pr_err("IOVMM: Unmapped %#x bytes from %#x.\n",unmapped_size, region->start);
260
261         kfree(region);
262 }
263
264 int rockchip_init_iovmm(struct device *sysmmu, struct rk_iovmm *vmm)
265 {
266         int ret = 0;
267
268         vmm->vmm_pool = gen_pool_create(PAGE_SHIFT, -1);
269         if (!vmm->vmm_pool) 
270         {
271                 ret = -ENOMEM;
272                 goto err_setup_genalloc;
273         }
274
275         /* (1GB - 4KB) addr space from 0x10000000 */
276         ret = gen_pool_add(vmm->vmm_pool, IOVA_START, IOVM_SIZE, -1);
277         if (ret)
278                 goto err_setup_domain;
279
280         vmm->domain = iommu_domain_alloc(&platform_bus_type);
281         if (!vmm->domain) 
282         {
283                 ret = -ENOMEM;
284                 goto err_setup_domain;
285         }
286
287         spin_lock_init(&vmm->lock);
288
289         INIT_LIST_HEAD(&vmm->regions_list);
290         
291         pr_info("IOVMM: Created %#x B IOVMM from %#x.\n",IOVM_SIZE, IOVA_START);
292         return 0;
293 err_setup_domain:
294         gen_pool_destroy(vmm->vmm_pool);
295 err_setup_genalloc:
296         pr_err("IOVMM: Failed to create IOVMM (%d)\n", ret);
297
298         return ret;
299 }
300
301 /****
302 1,success : pointer to the device inside of platform device 
303 2,fail       : NULL
304 ****/
305 struct device *rockchip_get_sysmmu_device_by_compatible(const char *compt)
306 {
307         struct device_node *dn = NULL;
308         struct platform_device *pd = NULL;
309         struct device *ret = NULL ;
310
311 #if 0
312         dn = of_find_node_by_name(NULL,name);
313 #endif
314
315         dn = of_find_compatible_node(NULL,NULL,compt);
316         if(!dn)
317         {
318                 pr_err("can't find device node %s \r\n",compt);
319                 return NULL;
320         }
321         
322         pd = of_find_device_by_node(dn);
323         if(!pd)
324         {       
325                 pr_err("can't find platform device in device node %s \r\n",compt);
326                 return  NULL;
327         }
328         ret = &pd->dev;
329         
330         return ret;
331
332 }
333