support rockcihp 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         dev_dbg(dev, "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         dev_dbg(dev, "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         dev_dbg(dev, "IOVMM: Unmapped %#x bytes from %#x.\n",unmapped_size, region->start);
184
185         kfree(region);
186 }
187
188 int iovmm_map_oto(struct device *dev, phys_addr_t phys, size_t size)
189 {
190         struct rk_vm_region *region;
191         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
192         int ret;
193
194         if (WARN_ON((phys + size) >= IOVA_START)) 
195         {
196                 dev_err(dev,"Unable to create one to one mapping for %#x @ %#x\n",size, phys);
197                 return -EINVAL;
198         }
199
200         region = kmalloc(sizeof(*region), GFP_KERNEL);
201         if (!region)
202                 return -ENOMEM;
203
204         if (WARN_ON(phys & ~PAGE_MASK))
205                 phys = round_down(phys, PAGE_SIZE);
206
207
208         ret = iommu_map(vmm->domain, (dma_addr_t)phys, phys, size, 0);
209         if (ret < 0) 
210         {
211                 kfree(region);
212                 return ret;
213         }
214
215         region->start = (dma_addr_t)phys;
216         region->size = size;
217         INIT_LIST_HEAD(&region->node);
218
219         spin_lock(&vmm->lock);
220
221         list_add(&region->node, &vmm->regions_list);
222
223         spin_unlock(&vmm->lock);
224
225         return 0;
226 }
227
228 void iovmm_unmap_oto(struct device *dev, phys_addr_t phys)
229 {
230         struct rk_vm_region *region;
231         struct rk_iovmm *vmm = rockchip_get_iovmm(dev);
232         size_t unmapped_size;
233
234         /* This function must not be called in IRQ handlers */
235         BUG_ON(in_irq());
236
237         if (WARN_ON(phys & ~PAGE_MASK))
238                 phys = round_down(phys, PAGE_SIZE);
239
240         spin_lock(&vmm->lock);
241
242         region = find_region(vmm, (dma_addr_t)phys);
243         if (WARN_ON(!region)) 
244         {
245                 spin_unlock(&vmm->lock);
246                 return;
247         }
248
249         list_del(&region->node);
250
251         spin_unlock(&vmm->lock);
252
253         unmapped_size = iommu_unmap(vmm->domain, region->start, region->size);
254         rockchip_sysmmu_tlb_invalidate(dev);
255         WARN_ON(unmapped_size != region->size);
256         dev_dbg(dev, "IOVMM: Unmapped %#x bytes from %#x.\n",unmapped_size, region->start);
257
258         kfree(region);
259 }
260
261 int rockchip_init_iovmm(struct device *sysmmu, struct rk_iovmm *vmm)
262 {
263         int ret = 0;
264
265         vmm->vmm_pool = gen_pool_create(PAGE_SHIFT, -1);
266         if (!vmm->vmm_pool) 
267         {
268                 ret = -ENOMEM;
269                 goto err_setup_genalloc;
270         }
271
272         /* (1GB - 4KB) addr space from 0x10000000 */
273         ret = gen_pool_add(vmm->vmm_pool, IOVA_START, IOVM_SIZE, -1);
274         if (ret)
275                 goto err_setup_domain;
276
277         vmm->domain = iommu_domain_alloc(&platform_bus_type);
278         if (!vmm->domain) 
279         {
280                 ret = -ENOMEM;
281                 goto err_setup_domain;
282         }
283
284         spin_lock_init(&vmm->lock);
285
286         INIT_LIST_HEAD(&vmm->regions_list);
287         
288         pr_info("IOVMM: Created %#x B IOVMM from %#x.\n",IOVM_SIZE, IOVA_START);
289         dev_dbg(sysmmu, "IOVMM: Created %#x B IOVMM from %#x.\n",IOVM_SIZE, IOVA_START);
290         return 0;
291 err_setup_domain:
292         gen_pool_destroy(vmm->vmm_pool);
293 err_setup_genalloc:
294         dev_dbg(sysmmu, "IOVMM: Failed to create IOVMM (%d)\n", ret);
295
296         return ret;
297 }
298
299 /****
300 1,success : pointer to the device inside of platform device 
301 2,fail       : NULL
302 ****/
303 struct device *rockchip_get_sysmmu_device_by_compatible(const char *compt)
304 {
305         struct device_node *dn = NULL;
306         struct platform_device *pd = NULL;
307         struct device *ret = NULL ;
308
309 #if 0
310         dn = of_find_node_by_name(NULL,name);
311 #endif
312
313         dn = of_find_compatible_node(NULL,NULL,compt);
314         if(!dn)
315         {
316                 printk("can't find device node %s \r\n",compt);
317                 return NULL;
318         }
319         
320         pd = of_find_device_by_node(dn);
321         if(!pd)
322         {       
323                 printk("can't find platform device in device node %s \r\n",compt);
324                 return  NULL;
325         }
326         ret = &pd->dev;
327         
328         return ret;
329
330 }
331