blob: bb32fca7f36658ca89e4c622dda1a23ed12161c2 [file] [log] [blame]
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/slab.h>
14#include <mach/ocmem_priv.h>
15
Naveen Ramarajcc4ec152012-05-14 09:55:29 -070016static DEFINE_MUTEX(ocmem_eviction_lock);
17static DECLARE_BITMAP(evicted, OCMEM_CLIENT_MAX);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -070018
19static struct ocmem_handle *generate_handle(void)
20{
21 struct ocmem_handle *handle = NULL;
22
23 handle = kzalloc(sizeof(struct ocmem_handle), GFP_KERNEL);
24 if (!handle) {
25 pr_err("ocmem: Unable to generate buffer handle\n");
26 return NULL;
27 }
28 mutex_init(&handle->handle_mutex);
29 return handle;
30}
31
32static int free_handle(struct ocmem_handle *handle)
33{
34 if (!handle)
35 return -EINVAL;
36
37 mutex_destroy(&handle->handle_mutex);
38 kfree(handle);
39 handle = NULL;
40 return 0;
41}
42
43static int __ocmem_free(int id, struct ocmem_buf *buf)
44{
45 int ret = 0;
46 struct ocmem_handle *handle = buffer_to_handle(buf);
47
48 if (!handle)
49 return -EINVAL;
50
51 mutex_lock(&handle->handle_mutex);
52 ret = process_free(id, handle);
53 mutex_unlock(&handle->handle_mutex);
54
55 if (ret)
56 return -EINVAL;
57
58 free_handle(handle);
59 return 0;
60}
61
Naveen Ramarajcc4ec152012-05-14 09:55:29 -070062static int __ocmem_shrink(int id, struct ocmem_buf *buf, unsigned long len)
63{
64 int ret = 0;
65 struct ocmem_handle *handle = buffer_to_handle(buf);
66
67 if (!handle)
68 return -EINVAL;
69
70 mutex_lock(&handle->handle_mutex);
71 ret = process_shrink(id, handle, len);
72 mutex_unlock(&handle->handle_mutex);
73
74 if (ret)
75 return -EINVAL;
76
77 return 0;
78}
79
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -070080static struct ocmem_buf *__ocmem_allocate_range(int id, unsigned long min,
81 unsigned long max, unsigned long step, bool block, bool wait)
82{
83 struct ocmem_handle *handle = NULL;
84 int ret = 0;
85
86 handle = generate_handle();
87 if (!handle) {
88 pr_err("ocmem: Unable to generate handle\n");
89 return NULL;
90 }
91
92 mutex_lock(&handle->handle_mutex);
93 ret = process_allocate(id, handle, min, max, step, block, wait);
94 mutex_unlock(&handle->handle_mutex);
95 if (ret) {
96 pr_err("ocmem allocation failed\n");
97 free_handle(handle);
98 return NULL;
99 } else
100 return handle_to_buffer(handle);
101}
102
103struct ocmem_buf *ocmem_allocate(int client_id, unsigned long size)
104{
105 bool can_block = false;
106 bool can_wait = true;
107
108 if (!check_id(client_id)) {
109 pr_err("ocmem: Invalid client id: %d\n", client_id);
110 return NULL;
111 }
112
113 if (size < OCMEM_MIN_ALLOC) {
114 pr_err("ocmem: requested size %lx must be at least %x\n",
115 size, OCMEM_MIN_ALLOC);
116 return NULL;
117 }
118
119 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
120 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
121 OCMEM_MIN_ALIGN);
122 return NULL;
123 }
124
125 return __ocmem_allocate_range(client_id, size, size,
126 size, can_block, can_wait);
127}
128
129struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size)
130{
131 bool can_block = false;
132 bool can_wait = false;
133
134 if (!check_id(client_id)) {
135 pr_err("ocmem: Invalid client id: %d\n", client_id);
136 return NULL;
137 }
138
139 if (size < OCMEM_MIN_ALLOC) {
140 pr_err("ocmem: requested size %lx must be at least %x\n",
141 size, OCMEM_MIN_ALLOC);
142 return NULL;
143 }
144
145 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
146 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
147 OCMEM_MIN_ALIGN);
148 return NULL;
149 }
150 return __ocmem_allocate_range(client_id, size, size,
151 size, can_block, can_wait);
152}
153
154struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min,
155 unsigned long goal, unsigned long step)
156{
157 bool can_block = true;
158 bool can_wait = false;
159
160 if (!check_id(client_id)) {
161 pr_err("ocmem: Invalid client id: %d\n", client_id);
162 return NULL;
163 }
164
165 /* Asynchronous API requires notifier registration */
166 if (!check_notifier(client_id)) {
167 pr_err("ocmem: No notifier registered for client %d\n",
168 client_id);
169 return NULL;
170 }
171
172 if (min < OCMEM_MIN_ALLOC) {
173 pr_err("ocmem: requested min size %lx must be at least %x\n",
174 min, OCMEM_MIN_ALLOC);
175 return NULL;
176 }
177
178 if (!IS_ALIGNED(min | goal | step, OCMEM_MIN_ALIGN)) {
179 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
180 OCMEM_MIN_ALIGN);
181 return NULL;
182 }
183
184 return __ocmem_allocate_range(client_id, min, goal,
185 step, can_block, can_wait);
186}
187
188struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size)
189{
190 bool can_block = true;
191 bool can_wait = false;
192
193 if (!check_id(client_id)) {
194 pr_err("ocmem: Invalid client id: %d\n", client_id);
195 return NULL;
196 }
197
198 /* Asynchronous API requires notifier registration */
199 if (!check_notifier(client_id)) {
200 pr_err("ocmem: No notifier registered for client %d\n",
201 client_id);
202 return NULL;
203 }
204
205 if (size < OCMEM_MIN_ALLOC) {
206 pr_err("ocmem: requested size %lx must be at least %x\n",
207 size, OCMEM_MIN_ALLOC);
208 return NULL;
209 }
210
211 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
212 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
213 OCMEM_MIN_ALIGN);
214 return NULL;
215 }
216
217 return __ocmem_allocate_range(client_id, 0, size, size,
218 can_block, can_wait);
219
220}
221
222int ocmem_free(int client_id, struct ocmem_buf *buffer)
223{
224 if (!check_id(client_id)) {
225 pr_err("ocmem: Invalid client id: %d\n", client_id);
226 return -EINVAL;
227 }
228
229 if (!buffer) {
230 pr_err("ocmem: Invalid buffer\n");
231 return -EINVAL;
232 }
233
234 return __ocmem_free(client_id, buffer);
235}
236
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700237int ocmem_shrink(int client_id, struct ocmem_buf *buffer, unsigned long len)
238{
239 if (!buffer)
240 return -EINVAL;
241 if (len >= buffer->len)
242 return -EINVAL;
243 return __ocmem_shrink(client_id, buffer, len);
244}
245
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700246int pre_validate_chunk_list(struct ocmem_map_list *list)
247{
248 int i = 0;
249 struct ocmem_chunk *chunks;
250
251 if (!list)
252 return -EINVAL;
253
254 if (list->num_chunks > OCMEM_MAX_CHUNKS || list->num_chunks == 0)
255 return -EINVAL;
256
257 chunks = list->chunks;
258
259 if (!chunks)
260 return -EINVAL;
261
262 for (i = 0; i < list->num_chunks; i++) {
263 if (!chunks[i].ddr_paddr ||
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700264 chunks[i].size < MIN_CHUNK_SIZE ||
265 !IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
266 pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
267 i, chunks[i].ddr_paddr, chunks[i].size);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700268 return -EINVAL;
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700269 }
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700270 }
271 return 0;
272}
273
274int ocmem_map(int client_id, struct ocmem_buf *buffer,
275 struct ocmem_map_list *list)
276{
277 int ret = 0;
278 struct ocmem_handle *handle = NULL;
279
280 if (!check_id(client_id)) {
281 pr_err("ocmem: Invalid client id: %d\n", client_id);
282 return -EINVAL;
283 }
284
285 /* Asynchronous API requires notifier registration */
286 if (!check_notifier(client_id)) {
287 pr_err("ocmem: No notifier registered for client %d\n",
288 client_id);
289 return -EINVAL;
290 }
291
292 if (!buffer) {
293 pr_err("ocmem: Invalid buffer\n");
294 return -EINVAL;
295 }
296
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700297 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700298 return -EINVAL;
299
300 handle = buffer_to_handle(buffer);
301
302 if (!handle)
303 return -EINVAL;
304
305 mutex_lock(&handle->handle_mutex);
306 ret = process_xfer(client_id, handle, list, TO_OCMEM);
307 mutex_unlock(&handle->handle_mutex);
308 return ret;
309}
310
311int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
312 struct ocmem_map_list *list)
313{
314
315 int ret = 0;
316 struct ocmem_handle *handle = NULL;
317
318 if (!check_id(client_id)) {
319 pr_err("ocmem: Invalid client id: %d\n", client_id);
320 return -EINVAL;
321 }
322
323 /* Asynchronous API requires notifier registration */
324 if (!check_notifier(client_id)) {
325 pr_err("ocmem: No notifier registered for client %d\n",
326 client_id);
327 return -EINVAL;
328 }
329
330 if (!buffer) {
331 pr_err("ocmem: Invalid buffer\n");
332 return -EINVAL;
333 }
334
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700335 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700336 return -EINVAL;
337
338 handle = buffer_to_handle(buffer);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700339 mutex_lock(&handle->handle_mutex);
340 ret = process_xfer(client_id, handle, list, TO_DDR);
341 mutex_unlock(&handle->handle_mutex);
342 return ret;
343}
344
345unsigned long get_max_quota(int client_id)
346{
347 if (!check_id(client_id)) {
348 pr_err("ocmem: Invalid client id: %d\n", client_id);
349 return 0x0;
350 }
351 return process_quota(client_id);
352}
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700353
354/* Synchronous eviction/restore calls */
355/* Only a single eviction or restoration is allowed */
356/* Evictions/Restorations cannot be concurrent with other maps */
357int ocmem_evict(int client_id)
358{
359 int ret = 0;
360
361 if (!check_id(client_id)) {
362 pr_err("ocmem: Invalid client id: %d\n", client_id);
363 return -EINVAL;
364 }
365
366 mutex_lock(&ocmem_eviction_lock);
367 if (test_bit(client_id, evicted)) {
368 pr_err("ocmem: Previous eviction was not restored by %d\n",
369 client_id);
370 mutex_unlock(&ocmem_eviction_lock);
371 return -EINVAL;
372 }
373
374 ret = process_evict(client_id);
375 if (ret == 0)
376 set_bit(client_id, evicted);
377
378 mutex_unlock(&ocmem_eviction_lock);
379 return ret;
380}
381
382int ocmem_restore(int client_id)
383{
384 int ret = 0;
385
386 if (!check_id(client_id)) {
387 pr_err("ocmem: Invalid client id: %d\n", client_id);
388 return -EINVAL;
389 }
390
391 mutex_lock(&ocmem_eviction_lock);
392 if (!test_bit(client_id, evicted)) {
393 pr_err("ocmem: No previous eviction by %d\n", client_id);
394 mutex_unlock(&ocmem_eviction_lock);
395 return -EINVAL;
396 }
397 ret = process_restore(client_id);
398 clear_bit(client_id, evicted);
399 mutex_unlock(&ocmem_eviction_lock);
400 return ret;
401}