1
2
3
4
5
6
7#include <linux/spinlock.h>
8#include <linux/dma-mapping.h>
9#include <linux/err.h>
10#include <linux/genalloc.h>
11#include <linux/io.h>
12#include <linux/mm.h>
13#include <linux/scatterlist.h>
14#include <linux/slab.h>
15#include <linux/vmalloc.h>
16#include "ion.h"
17
18#define ION_CARVEOUT_ALLOCATE_FAIL -1
19
20struct ion_carveout_heap {
21 struct ion_heap heap;
22 struct gen_pool *pool;
23 phys_addr_t base;
24};
25
26static phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
27 unsigned long size)
28{
29 struct ion_carveout_heap *carveout_heap =
30 container_of(heap, struct ion_carveout_heap, heap);
31 unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);
32
33 if (!offset)
34 return ION_CARVEOUT_ALLOCATE_FAIL;
35
36 return offset;
37}
38
39static void ion_carveout_free(struct ion_heap *heap, phys_addr_t addr,
40 unsigned long size)
41{
42 struct ion_carveout_heap *carveout_heap =
43 container_of(heap, struct ion_carveout_heap, heap);
44
45 if (addr == ION_CARVEOUT_ALLOCATE_FAIL)
46 return;
47 gen_pool_free(carveout_heap->pool, addr, size);
48}
49
50static int ion_carveout_heap_allocate(struct ion_heap *heap,
51 struct ion_buffer *buffer,
52 unsigned long size,
53 unsigned long flags)
54{
55 struct sg_table *table;
56 phys_addr_t paddr;
57 int ret;
58
59 table = kmalloc(sizeof(*table), GFP_KERNEL);
60 if (!table)
61 return -ENOMEM;
62 ret = sg_alloc_table(table, 1, GFP_KERNEL);
63 if (ret)
64 goto err_free;
65
66 paddr = ion_carveout_allocate(heap, size);
67 if (paddr == ION_CARVEOUT_ALLOCATE_FAIL) {
68 ret = -ENOMEM;
69 goto err_free_table;
70 }
71
72 sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
73 buffer->sg_table = table;
74
75 return 0;
76
77err_free_table:
78 sg_free_table(table);
79err_free:
80 kfree(table);
81 return ret;
82}
83
84static void ion_carveout_heap_free(struct ion_buffer *buffer)
85{
86 struct ion_heap *heap = buffer->heap;
87 struct sg_table *table = buffer->sg_table;
88 struct page *page = sg_page(table->sgl);
89 phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
90
91 ion_heap_buffer_zero(buffer);
92
93 ion_carveout_free(heap, paddr, buffer->size);
94 sg_free_table(table);
95 kfree(table);
96}
97
98static struct ion_heap_ops carveout_heap_ops = {
99 .allocate = ion_carveout_heap_allocate,
100 .free = ion_carveout_heap_free,
101 .map_user = ion_heap_map_user,
102 .map_kernel = ion_heap_map_kernel,
103 .unmap_kernel = ion_heap_unmap_kernel,
104};
105
106struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
107{
108 struct ion_carveout_heap *carveout_heap;
109 int ret;
110
111 struct page *page;
112 size_t size;
113
114 page = pfn_to_page(PFN_DOWN(heap_data->base));
115 size = heap_data->size;
116
117 ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
118 if (ret)
119 return ERR_PTR(ret);
120
121 carveout_heap = kzalloc(sizeof(*carveout_heap), GFP_KERNEL);
122 if (!carveout_heap)
123 return ERR_PTR(-ENOMEM);
124
125 carveout_heap->pool = gen_pool_create(PAGE_SHIFT, -1);
126 if (!carveout_heap->pool) {
127 kfree(carveout_heap);
128 return ERR_PTR(-ENOMEM);
129 }
130 carveout_heap->base = heap_data->base;
131 gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,
132 -1);
133 carveout_heap->heap.ops = &carveout_heap_ops;
134 carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
135 carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
136
137 return &carveout_heap->heap;
138}
139