GNU Linux-libre 4.19.268-gnu1
[releases.git] / drivers / staging / android / ion / ion_carveout_heap.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * drivers/staging/android/ion/ion_carveout_heap.c
4  *
5  * Copyright (C) 2011 Google, Inc.
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
20 struct ion_carveout_heap {
21         struct ion_heap heap;
22         struct gen_pool *pool;
23         phys_addr_t base;
24 };
25
26 static 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
39 static 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
50 static 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
77 err_free_table:
78         sg_free_table(table);
79 err_free:
80         kfree(table);
81         return ret;
82 }
83
84 static 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
98 static 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
106 struct 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 }