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