blob: 16dd8b8f38810cdb9ce0cc6c5facfaf52550c0a3 [file] [log] [blame]
Naveen Ramaraj89738952013-02-13 15:24:57 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -07002 *
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
Naveen Ramaraj89738952013-02-13 15:24:57 -080055 if (ret) {
56 pr_err("ocmem: Free failed for client %s\n", get_name(id));
57 return ret;
58 }
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -070059 free_handle(handle);
60 return 0;
61}
62
Naveen Ramarajcc4ec152012-05-14 09:55:29 -070063static int __ocmem_shrink(int id, struct ocmem_buf *buf, unsigned long len)
64{
65 int ret = 0;
66 struct ocmem_handle *handle = buffer_to_handle(buf);
67
68 if (!handle)
69 return -EINVAL;
70
71 mutex_lock(&handle->handle_mutex);
72 ret = process_shrink(id, handle, len);
73 mutex_unlock(&handle->handle_mutex);
74
75 if (ret)
76 return -EINVAL;
77
78 return 0;
79}
80
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -070081static struct ocmem_buf *__ocmem_allocate_range(int id, unsigned long min,
82 unsigned long max, unsigned long step, bool block, bool wait)
83{
84 struct ocmem_handle *handle = NULL;
85 int ret = 0;
86
87 handle = generate_handle();
88 if (!handle) {
89 pr_err("ocmem: Unable to generate handle\n");
90 return NULL;
91 }
92
93 mutex_lock(&handle->handle_mutex);
94 ret = process_allocate(id, handle, min, max, step, block, wait);
95 mutex_unlock(&handle->handle_mutex);
96 if (ret) {
97 pr_err("ocmem allocation failed\n");
98 free_handle(handle);
99 return NULL;
100 } else
101 return handle_to_buffer(handle);
102}
103
104struct ocmem_buf *ocmem_allocate(int client_id, unsigned long size)
105{
106 bool can_block = false;
107 bool can_wait = true;
108
109 if (!check_id(client_id)) {
110 pr_err("ocmem: Invalid client id: %d\n", client_id);
111 return NULL;
112 }
113
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700114 if (!zone_active(client_id)) {
115 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
116 get_name(client_id), client_id);
117 return NULL;
118 }
119
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700120 if (size < OCMEM_MIN_ALLOC) {
121 pr_err("ocmem: requested size %lx must be at least %x\n",
122 size, OCMEM_MIN_ALLOC);
123 return NULL;
124 }
125
126 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
127 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
128 OCMEM_MIN_ALIGN);
129 return NULL;
130 }
131
132 return __ocmem_allocate_range(client_id, size, size,
133 size, can_block, can_wait);
134}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700135EXPORT_SYMBOL(ocmem_allocate);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700136
137struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size)
138{
139 bool can_block = false;
140 bool can_wait = false;
141
142 if (!check_id(client_id)) {
143 pr_err("ocmem: Invalid client id: %d\n", client_id);
144 return NULL;
145 }
146
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700147 if (!zone_active(client_id)) {
148 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
149 get_name(client_id), client_id);
150 return NULL;
151 }
152
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700153 if (size < OCMEM_MIN_ALLOC) {
154 pr_err("ocmem: requested size %lx must be at least %x\n",
155 size, OCMEM_MIN_ALLOC);
156 return NULL;
157 }
158
159 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
160 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
161 OCMEM_MIN_ALIGN);
162 return NULL;
163 }
164 return __ocmem_allocate_range(client_id, size, size,
165 size, can_block, can_wait);
166}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700167EXPORT_SYMBOL(ocmem_allocate_nowait);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700168
169struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min,
170 unsigned long goal, unsigned long step)
171{
172 bool can_block = true;
173 bool can_wait = false;
174
175 if (!check_id(client_id)) {
176 pr_err("ocmem: Invalid client id: %d\n", client_id);
177 return NULL;
178 }
179
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700180 if (!zone_active(client_id)) {
181 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
182 get_name(client_id), client_id);
183 return NULL;
184 }
185
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700186 /* Asynchronous API requires notifier registration */
187 if (!check_notifier(client_id)) {
188 pr_err("ocmem: No notifier registered for client %d\n",
189 client_id);
190 return NULL;
191 }
192
193 if (min < OCMEM_MIN_ALLOC) {
194 pr_err("ocmem: requested min size %lx must be at least %x\n",
195 min, OCMEM_MIN_ALLOC);
196 return NULL;
197 }
198
199 if (!IS_ALIGNED(min | goal | step, OCMEM_MIN_ALIGN)) {
200 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
201 OCMEM_MIN_ALIGN);
202 return NULL;
203 }
204
205 return __ocmem_allocate_range(client_id, min, goal,
206 step, can_block, can_wait);
207}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700208EXPORT_SYMBOL(ocmem_allocate_range);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700209
210struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size)
211{
212 bool can_block = true;
213 bool can_wait = false;
214
215 if (!check_id(client_id)) {
216 pr_err("ocmem: Invalid client id: %d\n", client_id);
217 return NULL;
218 }
219
220 /* Asynchronous API requires notifier registration */
221 if (!check_notifier(client_id)) {
222 pr_err("ocmem: No notifier registered for client %d\n",
223 client_id);
224 return NULL;
225 }
226
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700227 if (!zone_active(client_id)) {
228 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
229 get_name(client_id), client_id);
230 return NULL;
231 }
232
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700233 if (size < OCMEM_MIN_ALLOC) {
234 pr_err("ocmem: requested size %lx must be at least %x\n",
235 size, OCMEM_MIN_ALLOC);
236 return NULL;
237 }
238
239 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
240 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
241 OCMEM_MIN_ALIGN);
242 return NULL;
243 }
244
245 return __ocmem_allocate_range(client_id, 0, size, size,
246 can_block, can_wait);
247
248}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700249EXPORT_SYMBOL(ocmem_allocate_nb);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700250
251int ocmem_free(int client_id, struct ocmem_buf *buffer)
252{
253 if (!check_id(client_id)) {
254 pr_err("ocmem: Invalid client id: %d\n", client_id);
255 return -EINVAL;
256 }
257
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700258 if (!zone_active(client_id)) {
259 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
260 get_name(client_id), client_id);
261 return -EINVAL;
262 }
263
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700264 if (!buffer) {
265 pr_err("ocmem: Invalid buffer\n");
266 return -EINVAL;
267 }
268
269 return __ocmem_free(client_id, buffer);
270}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700271EXPORT_SYMBOL(ocmem_free);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700272
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700273int ocmem_shrink(int client_id, struct ocmem_buf *buffer, unsigned long len)
274{
275 if (!buffer)
276 return -EINVAL;
277 if (len >= buffer->len)
278 return -EINVAL;
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700279
280 if (!zone_active(client_id)) {
281 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
282 get_name(client_id), client_id);
283 return -EINVAL;
284 }
285
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700286 return __ocmem_shrink(client_id, buffer, len);
287}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700288EXPORT_SYMBOL(ocmem_shrink);
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700289
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700290int pre_validate_chunk_list(struct ocmem_map_list *list)
291{
292 int i = 0;
293 struct ocmem_chunk *chunks;
294
295 if (!list)
296 return -EINVAL;
297
298 if (list->num_chunks > OCMEM_MAX_CHUNKS || list->num_chunks == 0)
299 return -EINVAL;
300
301 chunks = list->chunks;
302
303 if (!chunks)
304 return -EINVAL;
305
306 for (i = 0; i < list->num_chunks; i++) {
307 if (!chunks[i].ddr_paddr ||
Naveen Ramaraj66522592012-11-19 11:33:09 -0800308 !IS_ALIGNED(chunks[i].ddr_paddr, MIN_CHUNK_SIZE) ||
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700309 chunks[i].size < MIN_CHUNK_SIZE ||
310 !IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
311 pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
312 i, chunks[i].ddr_paddr, chunks[i].size);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700313 return -EINVAL;
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700314 }
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700315 }
316 return 0;
317}
318
319int ocmem_map(int client_id, struct ocmem_buf *buffer,
320 struct ocmem_map_list *list)
321{
322 int ret = 0;
323 struct ocmem_handle *handle = NULL;
324
325 if (!check_id(client_id)) {
326 pr_err("ocmem: Invalid client id: %d\n", client_id);
327 return -EINVAL;
328 }
329
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700330 if (!zone_active(client_id)) {
331 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
332 get_name(client_id), client_id);
333 return -EINVAL;
334 }
335
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700336 /* Asynchronous API requires notifier registration */
337 if (!check_notifier(client_id)) {
338 pr_err("ocmem: No notifier registered for client %d\n",
339 client_id);
340 return -EINVAL;
341 }
342
343 if (!buffer) {
344 pr_err("ocmem: Invalid buffer\n");
345 return -EINVAL;
346 }
347
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700348 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700349 return -EINVAL;
350
351 handle = buffer_to_handle(buffer);
352
353 if (!handle)
354 return -EINVAL;
355
356 mutex_lock(&handle->handle_mutex);
357 ret = process_xfer(client_id, handle, list, TO_OCMEM);
358 mutex_unlock(&handle->handle_mutex);
359 return ret;
360}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700361EXPORT_SYMBOL(ocmem_map);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700362
363int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
364 struct ocmem_map_list *list)
365{
366
367 int ret = 0;
368 struct ocmem_handle *handle = NULL;
369
370 if (!check_id(client_id)) {
371 pr_err("ocmem: Invalid client id: %d\n", client_id);
372 return -EINVAL;
373 }
374
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700375 if (!zone_active(client_id)) {
376 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
377 get_name(client_id), client_id);
378 return -EINVAL;
379 }
380
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700381 /* Asynchronous API requires notifier registration */
382 if (!check_notifier(client_id)) {
383 pr_err("ocmem: No notifier registered for client %d\n",
384 client_id);
385 return -EINVAL;
386 }
387
388 if (!buffer) {
389 pr_err("ocmem: Invalid buffer\n");
390 return -EINVAL;
391 }
392
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700393 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700394 return -EINVAL;
395
396 handle = buffer_to_handle(buffer);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700397 mutex_lock(&handle->handle_mutex);
398 ret = process_xfer(client_id, handle, list, TO_DDR);
399 mutex_unlock(&handle->handle_mutex);
400 return ret;
401}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700402EXPORT_SYMBOL(ocmem_unmap);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700403
Neeti Desaidad1d8e2013-01-09 19:42:06 -0800404int ocmem_drop(int client_id, struct ocmem_buf *buffer,
405 struct ocmem_map_list *list)
406{
407 int ret = 0;
408 struct ocmem_handle *handle = NULL;
409
410 if (!check_id(client_id)) {
411 pr_err("ocmem: Invalid client id: %d\n", client_id);
412 return -EINVAL;
413 }
414
415 if (!zone_active(client_id)) {
416 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
417 get_name(client_id), client_id);
418 return -EINVAL;
419 }
420
421 if (!buffer) {
422 pr_err("ocmem: Invalid buffer\n");
423 return -EINVAL;
424 }
425
426 if (pre_validate_chunk_list(list) != 0)
427 return -EINVAL;
428
429 handle = buffer_to_handle(buffer);
430 mutex_lock(&handle->handle_mutex);
431 ret = process_drop(client_id, handle, list);
432 mutex_unlock(&handle->handle_mutex);
433 return ret;
434}
435EXPORT_SYMBOL(ocmem_drop);
436
437
Naveen Ramaraj55ed8902012-09-26 13:18:06 -0700438int ocmem_dump(int client_id, struct ocmem_buf *buffer,
439 unsigned long dst_phys_addr)
440{
441 int ret = 0;
442 struct ocmem_handle *handle = NULL;
443
444 if (!check_id(client_id)) {
445 pr_err("ocmem: Invalid client id: %d\n", client_id);
446 return -EINVAL;
447 }
448
449 if (!zone_active(client_id)) {
450 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
451 get_name(client_id), client_id);
452 return -EINVAL;
453 }
454
455 if (!buffer) {
456 pr_err("ocmem: Invalid buffer\n");
457 return -EINVAL;
458 }
459
460 handle = buffer_to_handle(buffer);
461 mutex_lock(&handle->handle_mutex);
462 ret = process_dump(client_id, handle, dst_phys_addr);
463 mutex_unlock(&handle->handle_mutex);
464 return ret;
465}
466EXPORT_SYMBOL(ocmem_dump);
467
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700468unsigned long get_max_quota(int client_id)
469{
470 if (!check_id(client_id)) {
471 pr_err("ocmem: Invalid client id: %d\n", client_id);
472 return 0x0;
473 }
474 return process_quota(client_id);
475}
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700476
477/* Synchronous eviction/restore calls */
478/* Only a single eviction or restoration is allowed */
479/* Evictions/Restorations cannot be concurrent with other maps */
480int ocmem_evict(int client_id)
481{
482 int ret = 0;
483
484 if (!check_id(client_id)) {
485 pr_err("ocmem: Invalid client id: %d\n", client_id);
486 return -EINVAL;
487 }
488
489 mutex_lock(&ocmem_eviction_lock);
490 if (test_bit(client_id, evicted)) {
491 pr_err("ocmem: Previous eviction was not restored by %d\n",
492 client_id);
493 mutex_unlock(&ocmem_eviction_lock);
494 return -EINVAL;
495 }
496
497 ret = process_evict(client_id);
498 if (ret == 0)
499 set_bit(client_id, evicted);
500
501 mutex_unlock(&ocmem_eviction_lock);
502 return ret;
503}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700504EXPORT_SYMBOL(ocmem_evict);
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700505
506int ocmem_restore(int client_id)
507{
508 int ret = 0;
509
510 if (!check_id(client_id)) {
511 pr_err("ocmem: Invalid client id: %d\n", client_id);
512 return -EINVAL;
513 }
514
515 mutex_lock(&ocmem_eviction_lock);
516 if (!test_bit(client_id, evicted)) {
517 pr_err("ocmem: No previous eviction by %d\n", client_id);
518 mutex_unlock(&ocmem_eviction_lock);
519 return -EINVAL;
520 }
521 ret = process_restore(client_id);
522 clear_bit(client_id, evicted);
523 mutex_unlock(&ocmem_eviction_lock);
524 return ret;
525}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700526EXPORT_SYMBOL(ocmem_restore);
Naveen Ramaraj99b07562012-05-28 20:57:09 -0700527
528/* Wrappers until power control is transitioned to clients */
529enum ocmem_power_state ocmem_get_power_state(int client_id,
530 struct ocmem_buf *buffer)
531{
532 return 0;
533}
534
535int ocmem_set_power_state(int client_id, struct ocmem_buf *buffer,
536 enum ocmem_power_state new_state)
537{
538 return 0;
539}
540
541struct ocmem_vectors *ocmem_get_vectors(int client_id,
542 struct ocmem_buf *buffer)
543{
544 return NULL;
545}