1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/err.h>
14#include <linux/slab.h>
15#include <linux/vmalloc.h>
16#include <linux/device.h>
17#include <linux/scatterlist.h>
18
19#include <asm/cacheflush.h>
20#include <asm/mach/map.h>
21
22#include <plat/iommu.h>
23#include <plat/iovmm.h>
24
25#include "iopgtable.h"
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61static struct kmem_cache *iovm_area_cachep;
62
63
64static size_t sgtable_len(const struct sg_table *sgt)
65{
66 unsigned int i, total = 0;
67 struct scatterlist *sg;
68
69 if (!sgt)
70 return 0;
71
72 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
73 size_t bytes;
74
75 bytes = sg_dma_len(sg);
76
77 if (!iopgsz_ok(bytes)) {
78 pr_err("%s: sg[%d] not iommu pagesize(%x)\n",
79 __func__, i, bytes);
80 return 0;
81 }
82
83 total += bytes;
84 }
85
86 return total;
87}
88#define sgtable_ok(x) (!!sgtable_len(x))
89
90
91
92
93
94static unsigned int sgtable_nents(size_t bytes)
95{
96 int i;
97 unsigned int nr_entries;
98 const unsigned long pagesize[] = { SZ_16M, SZ_1M, SZ_64K, SZ_4K, };
99
100 if (!IS_ALIGNED(bytes, PAGE_SIZE)) {
101 pr_err("%s: wrong size %08x\n", __func__, bytes);
102 return 0;
103 }
104
105 nr_entries = 0;
106 for (i = 0; i < ARRAY_SIZE(pagesize); i++) {
107 if (bytes >= pagesize[i]) {
108 nr_entries += (bytes / pagesize[i]);
109 bytes %= pagesize[i];
110 }
111 }
112 BUG_ON(bytes);
113
114 return nr_entries;
115}
116
117
118static struct sg_table *sgtable_alloc(const size_t bytes, u32 flags)
119{
120 unsigned int nr_entries;
121 int err;
122 struct sg_table *sgt;
123
124 if (!bytes)
125 return ERR_PTR(-EINVAL);
126
127 if (!IS_ALIGNED(bytes, PAGE_SIZE))
128 return ERR_PTR(-EINVAL);
129
130
131 if ((flags & IOVMF_LINEAR) && (flags & IOVMF_DA_ANON)) {
132 nr_entries = sgtable_nents(bytes);
133 if (!nr_entries)
134 return ERR_PTR(-EINVAL);
135 } else
136 nr_entries = bytes / PAGE_SIZE;
137
138 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
139 if (!sgt)
140 return ERR_PTR(-ENOMEM);
141
142 err = sg_alloc_table(sgt, nr_entries, GFP_KERNEL);
143 if (err) {
144 kfree(sgt);
145 return ERR_PTR(err);
146 }
147
148 pr_debug("%s: sgt:%p(%d entries)\n", __func__, sgt, nr_entries);
149
150 return sgt;
151}
152
153
154static void sgtable_free(struct sg_table *sgt)
155{
156 if (!sgt)
157 return;
158
159 sg_free_table(sgt);
160 kfree(sgt);
161
162 pr_debug("%s: sgt:%p\n", __func__, sgt);
163}
164
165
166static void *vmap_sg(const struct sg_table *sgt)
167{
168 u32 va;
169 size_t total;
170 unsigned int i;
171 struct scatterlist *sg;
172 struct vm_struct *new;
173 const struct mem_type *mtype;
174
175 mtype = get_mem_type(MT_DEVICE);
176 if (!mtype)
177 return ERR_PTR(-EINVAL);
178
179 total = sgtable_len(sgt);
180 if (!total)
181 return ERR_PTR(-EINVAL);
182
183 new = __get_vm_area(total, VM_IOREMAP, VMALLOC_START, VMALLOC_END);
184 if (!new)
185 return ERR_PTR(-ENOMEM);
186 va = (u32)new->addr;
187
188 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
189 size_t bytes;
190 u32 pa;
191 int err;
192
193 pa = sg_phys(sg);
194 bytes = sg_dma_len(sg);
195
196 BUG_ON(bytes != PAGE_SIZE);
197
198 err = ioremap_page(va, pa, mtype);
199 if (err)
200 goto err_out;
201
202 va += bytes;
203 }
204
205 flush_cache_vmap((unsigned long)new->addr,
206 (unsigned long)(new->addr + total));
207 return new->addr;
208
209err_out:
210 WARN_ON(1);
211 vunmap(new->addr);
212 return ERR_PTR(-EAGAIN);
213}
214
215static inline void vunmap_sg(const void *va)
216{
217 vunmap(va);
218}
219
220static struct iovm_struct *__find_iovm_area(struct iommu *obj, const u32 da)
221{
222 struct iovm_struct *tmp;
223
224 list_for_each_entry(tmp, &obj->mmap, list) {
225 if ((da >= tmp->da_start) && (da < tmp->da_end)) {
226 size_t len;
227
228 len = tmp->da_end - tmp->da_start;
229
230 dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n",
231 __func__, tmp->da_start, da, tmp->da_end, len,
232 tmp->flags);
233
234 return tmp;
235 }
236 }
237
238 return NULL;
239}
240
241
242
243
244
245
246
247struct iovm_struct *find_iovm_area(struct iommu *obj, u32 da)
248{
249 struct iovm_struct *area;
250
251 mutex_lock(&obj->mmap_lock);
252 area = __find_iovm_area(obj, da);
253 mutex_unlock(&obj->mmap_lock);
254
255 return area;
256}
257EXPORT_SYMBOL_GPL(find_iovm_area);
258
259
260
261
262
263static struct iovm_struct *alloc_iovm_area(struct iommu *obj, u32 da,
264 size_t bytes, u32 flags)
265{
266 struct iovm_struct *new, *tmp;
267 u32 start, prev_end, alignement;
268
269 if (!obj || !bytes)
270 return ERR_PTR(-EINVAL);
271
272 start = da;
273 alignement = PAGE_SIZE;
274
275 if (flags & IOVMF_DA_ANON) {
276
277
278
279 start = PAGE_SIZE;
280 if (flags & IOVMF_LINEAR)
281 alignement = iopgsz_max(bytes);
282 start = roundup(start, alignement);
283 }
284
285 tmp = NULL;
286 if (list_empty(&obj->mmap))
287 goto found;
288
289 prev_end = 0;
290 list_for_each_entry(tmp, &obj->mmap, list) {
291
292 if (prev_end >= start)
293 break;
294
295 if (start + bytes < tmp->da_start)
296 goto found;
297
298 if (flags & IOVMF_DA_ANON)
299 start = roundup(tmp->da_end + 1, alignement);
300
301 prev_end = tmp->da_end;
302 }
303
304 if ((start > prev_end) && (ULONG_MAX - start >= bytes))
305 goto found;
306
307 dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n",
308 __func__, da, bytes, flags);
309
310 return ERR_PTR(-EINVAL);
311
312found:
313 new = kmem_cache_zalloc(iovm_area_cachep, GFP_KERNEL);
314 if (!new)
315 return ERR_PTR(-ENOMEM);
316
317 new->iommu = obj;
318 new->da_start = start;
319 new->da_end = start + bytes;
320 new->flags = flags;
321
322
323
324
325 if (tmp)
326 list_add_tail(&new->list, &tmp->list);
327 else
328 list_add(&new->list, &obj->mmap);
329
330 dev_dbg(obj->dev, "%s: found %08x-%08x-%08x(%x) %08x\n",
331 __func__, new->da_start, start, new->da_end, bytes, flags);
332
333 return new;
334}
335
336static void free_iovm_area(struct iommu *obj, struct iovm_struct *area)
337{
338 size_t bytes;
339
340 BUG_ON(!obj || !area);
341
342 bytes = area->da_end - area->da_start;
343
344 dev_dbg(obj->dev, "%s: %08x-%08x(%x) %08x\n",
345 __func__, area->da_start, area->da_end, bytes, area->flags);
346
347 list_del(&area->list);
348 kmem_cache_free(iovm_area_cachep, area);
349}
350
351
352
353
354
355
356
357
358
359void *da_to_va(struct iommu *obj, u32 da)
360{
361 void *va = NULL;
362 struct iovm_struct *area;
363
364 mutex_lock(&obj->mmap_lock);
365
366 area = __find_iovm_area(obj, da);
367 if (!area) {
368 dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da);
369 goto out;
370 }
371 va = area->va;
372out:
373 mutex_unlock(&obj->mmap_lock);
374
375 return va;
376}
377EXPORT_SYMBOL_GPL(da_to_va);
378
379static void sgtable_fill_vmalloc(struct sg_table *sgt, void *_va)
380{
381 unsigned int i;
382 struct scatterlist *sg;
383 void *va = _va;
384 void *va_end;
385
386 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
387 struct page *pg;
388 const size_t bytes = PAGE_SIZE;
389
390
391
392
393 pg = vmalloc_to_page(va);
394 BUG_ON(!pg);
395 sg_set_page(sg, pg, bytes, 0);
396
397 va += bytes;
398 }
399
400 va_end = _va + PAGE_SIZE * i;
401}
402
403static inline void sgtable_drain_vmalloc(struct sg_table *sgt)
404{
405
406
407
408
409 BUG_ON(!sgt);
410}
411
412static void sgtable_fill_kmalloc(struct sg_table *sgt, u32 pa, size_t len)
413{
414 unsigned int i;
415 struct scatterlist *sg;
416 void *va;
417
418 va = phys_to_virt(pa);
419
420 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
421 size_t bytes;
422
423 bytes = iopgsz_max(len);
424
425 BUG_ON(!iopgsz_ok(bytes));
426
427 sg_set_buf(sg, phys_to_virt(pa), bytes);
428
429
430
431 pa += bytes;
432 len -= bytes;
433 }
434 BUG_ON(len);
435}
436
437static inline void sgtable_drain_kmalloc(struct sg_table *sgt)
438{
439
440
441
442
443 BUG_ON(!sgt);
444}
445
446
447static int map_iovm_area(struct iommu *obj, struct iovm_struct *new,
448 const struct sg_table *sgt, u32 flags)
449{
450 int err;
451 unsigned int i, j;
452 struct scatterlist *sg;
453 u32 da = new->da_start;
454
455 if (!obj || !sgt)
456 return -EINVAL;
457
458 BUG_ON(!sgtable_ok(sgt));
459
460 for_each_sg(sgt->sgl, sg, sgt->nents, i) {
461 u32 pa;
462 int pgsz;
463 size_t bytes;
464 struct iotlb_entry e;
465
466 pa = sg_phys(sg);
467 bytes = sg_dma_len(sg);
468
469 flags &= ~IOVMF_PGSZ_MASK;
470 pgsz = bytes_to_iopgsz(bytes);
471 if (pgsz < 0)
472 goto err_out;
473 flags |= pgsz;
474
475 pr_debug("%s: [%d] %08x %08x(%x)\n", __func__,
476 i, da, pa, bytes);
477
478 iotlb_init_entry(&e, da, pa, flags);
479 err = iopgtable_store_entry(obj, &e);
480 if (err)
481 goto err_out;
482
483 da += bytes;
484 }
485 return 0;
486
487err_out:
488 da = new->da_start;
489
490 for_each_sg(sgt->sgl, sg, i, j) {
491 size_t bytes;
492
493 bytes = iopgtable_clear_entry(obj, da);
494
495 BUG_ON(!iopgsz_ok(bytes));
496
497 da += bytes;
498 }
499 return err;
500}
501
502
503static void unmap_iovm_area(struct iommu *obj, struct iovm_struct *area)
504{
505 u32 start;
506 size_t total = area->da_end - area->da_start;
507
508 BUG_ON((!total) || !IS_ALIGNED(total, PAGE_SIZE));
509
510 start = area->da_start;
511 while (total > 0) {
512 size_t bytes;
513
514 bytes = iopgtable_clear_entry(obj, start);
515 if (bytes == 0)
516 bytes = PAGE_SIZE;
517 else
518 dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n",
519 __func__, start, bytes, area->flags);
520
521 BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE));
522
523 total -= bytes;
524 start += bytes;
525 }
526 BUG_ON(total);
527}
528
529
530static struct sg_table *unmap_vm_area(struct iommu *obj, const u32 da,
531 void (*fn)(const void *), u32 flags)
532{
533 struct sg_table *sgt = NULL;
534 struct iovm_struct *area;
535
536 if (!IS_ALIGNED(da, PAGE_SIZE)) {
537 dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da);
538 return NULL;
539 }
540
541 mutex_lock(&obj->mmap_lock);
542
543 area = __find_iovm_area(obj, da);
544 if (!area) {
545 dev_dbg(obj->dev, "%s: no da area(%08x)\n", __func__, da);
546 goto out;
547 }
548
549 if ((area->flags & flags) != flags) {
550 dev_err(obj->dev, "%s: wrong flags(%08x)\n", __func__,
551 area->flags);
552 goto out;
553 }
554 sgt = (struct sg_table *)area->sgt;
555
556 unmap_iovm_area(obj, area);
557
558 fn(area->va);
559
560 dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", __func__,
561 area->da_start, da, area->da_end,
562 area->da_end - area->da_start, area->flags);
563
564 free_iovm_area(obj, area);
565out:
566 mutex_unlock(&obj->mmap_lock);
567
568 return sgt;
569}
570
571static u32 map_iommu_region(struct iommu *obj, u32 da,
572 const struct sg_table *sgt, void *va, size_t bytes, u32 flags)
573{
574 int err = -ENOMEM;
575 struct iovm_struct *new;
576
577 mutex_lock(&obj->mmap_lock);
578
579 new = alloc_iovm_area(obj, da, bytes, flags);
580 if (IS_ERR(new)) {
581 err = PTR_ERR(new);
582 goto err_alloc_iovma;
583 }
584 new->va = va;
585 new->sgt = sgt;
586
587 if (map_iovm_area(obj, new, sgt, new->flags))
588 goto err_map;
589
590 mutex_unlock(&obj->mmap_lock);
591
592 dev_dbg(obj->dev, "%s: da:%08x(%x) flags:%08x va:%p\n",
593 __func__, new->da_start, bytes, new->flags, va);
594
595 return new->da_start;
596
597err_map:
598 free_iovm_area(obj, new);
599err_alloc_iovma:
600 mutex_unlock(&obj->mmap_lock);
601 return err;
602}
603
604static inline u32 __iommu_vmap(struct iommu *obj, u32 da,
605 const struct sg_table *sgt, void *va, size_t bytes, u32 flags)
606{
607 return map_iommu_region(obj, da, sgt, va, bytes, flags);
608}
609
610
611
612
613
614
615
616
617
618
619u32 iommu_vmap(struct iommu *obj, u32 da, const struct sg_table *sgt,
620 u32 flags)
621{
622 size_t bytes;
623 void *va = NULL;
624
625 if (!obj || !obj->dev || !sgt)
626 return -EINVAL;
627
628 bytes = sgtable_len(sgt);
629 if (!bytes)
630 return -EINVAL;
631 bytes = PAGE_ALIGN(bytes);
632
633 if (flags & IOVMF_MMIO) {
634 va = vmap_sg(sgt);
635 if (IS_ERR(va))
636 return PTR_ERR(va);
637 }
638
639 flags &= IOVMF_HW_MASK;
640 flags |= IOVMF_DISCONT;
641 flags |= IOVMF_MMIO;
642 flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON);
643
644 da = __iommu_vmap(obj, da, sgt, va, bytes, flags);
645 if (IS_ERR_VALUE(da))
646 vunmap_sg(va);
647
648 return da;
649}
650EXPORT_SYMBOL_GPL(iommu_vmap);
651
652
653
654
655
656
657
658
659
660struct sg_table *iommu_vunmap(struct iommu *obj, u32 da)
661{
662 struct sg_table *sgt;
663
664
665
666
667 sgt = unmap_vm_area(obj, da, vunmap_sg, IOVMF_DISCONT | IOVMF_MMIO);
668 if (!sgt)
669 dev_dbg(obj->dev, "%s: No sgt\n", __func__);
670 return sgt;
671}
672EXPORT_SYMBOL_GPL(iommu_vunmap);
673
674
675
676
677
678
679
680
681
682
683
684u32 iommu_vmalloc(struct iommu *obj, u32 da, size_t bytes, u32 flags)
685{
686 void *va;
687 struct sg_table *sgt;
688
689 if (!obj || !obj->dev || !bytes)
690 return -EINVAL;
691
692 bytes = PAGE_ALIGN(bytes);
693
694 va = vmalloc(bytes);
695 if (!va)
696 return -ENOMEM;
697
698 sgt = sgtable_alloc(bytes, flags);
699 if (IS_ERR(sgt)) {
700 da = PTR_ERR(sgt);
701 goto err_sgt_alloc;
702 }
703 sgtable_fill_vmalloc(sgt, va);
704
705 flags &= IOVMF_HW_MASK;
706 flags |= IOVMF_DISCONT;
707 flags |= IOVMF_ALLOC;
708 flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON);
709
710 da = __iommu_vmap(obj, da, sgt, va, bytes, flags);
711 if (IS_ERR_VALUE(da))
712 goto err_iommu_vmap;
713
714 return da;
715
716err_iommu_vmap:
717 sgtable_drain_vmalloc(sgt);
718 sgtable_free(sgt);
719err_sgt_alloc:
720 vfree(va);
721 return da;
722}
723EXPORT_SYMBOL_GPL(iommu_vmalloc);
724
725
726
727
728
729
730
731
732
733void iommu_vfree(struct iommu *obj, const u32 da)
734{
735 struct sg_table *sgt;
736
737 sgt = unmap_vm_area(obj, da, vfree, IOVMF_DISCONT | IOVMF_ALLOC);
738 if (!sgt)
739 dev_dbg(obj->dev, "%s: No sgt\n", __func__);
740 sgtable_free(sgt);
741}
742EXPORT_SYMBOL_GPL(iommu_vfree);
743
744static u32 __iommu_kmap(struct iommu *obj, u32 da, u32 pa, void *va,
745 size_t bytes, u32 flags)
746{
747 struct sg_table *sgt;
748
749 sgt = sgtable_alloc(bytes, flags);
750 if (IS_ERR(sgt))
751 return PTR_ERR(sgt);
752
753 sgtable_fill_kmalloc(sgt, pa, bytes);
754
755 da = map_iommu_region(obj, da, sgt, va, bytes, flags);
756 if (IS_ERR_VALUE(da)) {
757 sgtable_drain_kmalloc(sgt);
758 sgtable_free(sgt);
759 }
760
761 return da;
762}
763
764
765
766
767
768
769
770
771
772
773
774u32 iommu_kmap(struct iommu *obj, u32 da, u32 pa, size_t bytes,
775 u32 flags)
776{
777 void *va;
778
779 if (!obj || !obj->dev || !bytes)
780 return -EINVAL;
781
782 bytes = PAGE_ALIGN(bytes);
783
784 va = ioremap(pa, bytes);
785 if (!va)
786 return -ENOMEM;
787
788 flags &= IOVMF_HW_MASK;
789 flags |= IOVMF_LINEAR;
790 flags |= IOVMF_MMIO;
791 flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON);
792
793 da = __iommu_kmap(obj, da, pa, va, bytes, flags);
794 if (IS_ERR_VALUE(da))
795 iounmap(va);
796
797 return da;
798}
799EXPORT_SYMBOL_GPL(iommu_kmap);
800
801
802
803
804
805
806
807
808
809void iommu_kunmap(struct iommu *obj, u32 da)
810{
811 struct sg_table *sgt;
812 typedef void (*func_t)(const void *);
813
814 sgt = unmap_vm_area(obj, da, (func_t)__iounmap,
815 IOVMF_LINEAR | IOVMF_MMIO);
816 if (!sgt)
817 dev_dbg(obj->dev, "%s: No sgt\n", __func__);
818 sgtable_free(sgt);
819}
820EXPORT_SYMBOL_GPL(iommu_kunmap);
821
822
823
824
825
826
827
828
829
830
831
832u32 iommu_kmalloc(struct iommu *obj, u32 da, size_t bytes, u32 flags)
833{
834 void *va;
835 u32 pa;
836
837 if (!obj || !obj->dev || !bytes)
838 return -EINVAL;
839
840 bytes = PAGE_ALIGN(bytes);
841
842 va = kmalloc(bytes, GFP_KERNEL | GFP_DMA);
843 if (!va)
844 return -ENOMEM;
845 pa = virt_to_phys(va);
846
847 flags &= IOVMF_HW_MASK;
848 flags |= IOVMF_LINEAR;
849 flags |= IOVMF_ALLOC;
850 flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON);
851
852 da = __iommu_kmap(obj, da, pa, va, bytes, flags);
853 if (IS_ERR_VALUE(da))
854 kfree(va);
855
856 return da;
857}
858EXPORT_SYMBOL_GPL(iommu_kmalloc);
859
860
861
862
863
864
865
866
867
868void iommu_kfree(struct iommu *obj, u32 da)
869{
870 struct sg_table *sgt;
871
872 sgt = unmap_vm_area(obj, da, kfree, IOVMF_LINEAR | IOVMF_ALLOC);
873 if (!sgt)
874 dev_dbg(obj->dev, "%s: No sgt\n", __func__);
875 sgtable_free(sgt);
876}
877EXPORT_SYMBOL_GPL(iommu_kfree);
878
879
880static int __init iovmm_init(void)
881{
882 const unsigned long flags = SLAB_HWCACHE_ALIGN;
883 struct kmem_cache *p;
884
885 p = kmem_cache_create("iovm_area_cache", sizeof(struct iovm_struct), 0,
886 flags, NULL);
887 if (!p)
888 return -ENOMEM;
889 iovm_area_cachep = p;
890
891 return 0;
892}
893module_init(iovmm_init);
894
895static void __exit iovmm_exit(void)
896{
897 kmem_cache_destroy(iovm_area_cachep);
898}
899module_exit(iovmm_exit);
900
901MODULE_DESCRIPTION("omap iommu: simple virtual address space management");
902MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>");
903MODULE_LICENSE("GPL v2");
904