blob: ef3cbaeb0e71706fabba9e3ed0cc093026f2386e [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2012, 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
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
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700113 if (!zone_active(client_id)) {
114 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
115 get_name(client_id), client_id);
116 return NULL;
117 }
118
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700119 if (size < OCMEM_MIN_ALLOC) {
120 pr_err("ocmem: requested size %lx must be at least %x\n",
121 size, OCMEM_MIN_ALLOC);
122 return NULL;
123 }
124
125 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
126 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
127 OCMEM_MIN_ALIGN);
128 return NULL;
129 }
130
131 return __ocmem_allocate_range(client_id, size, size,
132 size, can_block, can_wait);
133}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700134EXPORT_SYMBOL(ocmem_allocate);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700135
136struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size)
137{
138 bool can_block = false;
139 bool can_wait = false;
140
141 if (!check_id(client_id)) {
142 pr_err("ocmem: Invalid client id: %d\n", client_id);
143 return NULL;
144 }
145
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700146 if (!zone_active(client_id)) {
147 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
148 get_name(client_id), client_id);
149 return NULL;
150 }
151
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700152 if (size < OCMEM_MIN_ALLOC) {
153 pr_err("ocmem: requested size %lx must be at least %x\n",
154 size, OCMEM_MIN_ALLOC);
155 return NULL;
156 }
157
158 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
159 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
160 OCMEM_MIN_ALIGN);
161 return NULL;
162 }
163 return __ocmem_allocate_range(client_id, size, size,
164 size, can_block, can_wait);
165}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700166EXPORT_SYMBOL(ocmem_allocate_nowait);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700167
168struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min,
169 unsigned long goal, unsigned long step)
170{
171 bool can_block = true;
172 bool can_wait = false;
173
174 if (!check_id(client_id)) {
175 pr_err("ocmem: Invalid client id: %d\n", client_id);
176 return NULL;
177 }
178
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700179 if (!zone_active(client_id)) {
180 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
181 get_name(client_id), client_id);
182 return NULL;
183 }
184
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700185 /* Asynchronous API requires notifier registration */
186 if (!check_notifier(client_id)) {
187 pr_err("ocmem: No notifier registered for client %d\n",
188 client_id);
189 return NULL;
190 }
191
192 if (min < OCMEM_MIN_ALLOC) {
193 pr_err("ocmem: requested min size %lx must be at least %x\n",
194 min, OCMEM_MIN_ALLOC);
195 return NULL;
196 }
197
198 if (!IS_ALIGNED(min | goal | step, OCMEM_MIN_ALIGN)) {
199 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
200 OCMEM_MIN_ALIGN);
201 return NULL;
202 }
203
204 return __ocmem_allocate_range(client_id, min, goal,
205 step, can_block, can_wait);
206}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700207EXPORT_SYMBOL(ocmem_allocate_range);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700208
209struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size)
210{
211 bool can_block = true;
212 bool can_wait = false;
213
214 if (!check_id(client_id)) {
215 pr_err("ocmem: Invalid client id: %d\n", client_id);
216 return NULL;
217 }
218
219 /* Asynchronous API requires notifier registration */
220 if (!check_notifier(client_id)) {
221 pr_err("ocmem: No notifier registered for client %d\n",
222 client_id);
223 return NULL;
224 }
225
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700226 if (!zone_active(client_id)) {
227 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
228 get_name(client_id), client_id);
229 return NULL;
230 }
231
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700232 if (size < OCMEM_MIN_ALLOC) {
233 pr_err("ocmem: requested size %lx must be at least %x\n",
234 size, OCMEM_MIN_ALLOC);
235 return NULL;
236 }
237
238 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
239 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
240 OCMEM_MIN_ALIGN);
241 return NULL;
242 }
243
244 return __ocmem_allocate_range(client_id, 0, size, size,
245 can_block, can_wait);
246
247}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700248EXPORT_SYMBOL(ocmem_allocate_nb);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700249
250int ocmem_free(int client_id, struct ocmem_buf *buffer)
251{
252 if (!check_id(client_id)) {
253 pr_err("ocmem: Invalid client id: %d\n", client_id);
254 return -EINVAL;
255 }
256
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700257 if (!zone_active(client_id)) {
258 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
259 get_name(client_id), client_id);
260 return -EINVAL;
261 }
262
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700263 if (!buffer) {
264 pr_err("ocmem: Invalid buffer\n");
265 return -EINVAL;
266 }
267
268 return __ocmem_free(client_id, buffer);
269}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700270EXPORT_SYMBOL(ocmem_free);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700271
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700272int ocmem_shrink(int client_id, struct ocmem_buf *buffer, unsigned long len)
273{
274 if (!buffer)
275 return -EINVAL;
276 if (len >= buffer->len)
277 return -EINVAL;
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700278
279 if (!zone_active(client_id)) {
280 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
281 get_name(client_id), client_id);
282 return -EINVAL;
283 }
284
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700285 return __ocmem_shrink(client_id, buffer, len);
286}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700287EXPORT_SYMBOL(ocmem_shrink);
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700288
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700289int pre_validate_chunk_list(struct ocmem_map_list *list)
290{
291 int i = 0;
292 struct ocmem_chunk *chunks;
293
294 if (!list)
295 return -EINVAL;
296
297 if (list->num_chunks > OCMEM_MAX_CHUNKS || list->num_chunks == 0)
298 return -EINVAL;
299
300 chunks = list->chunks;
301
302 if (!chunks)
303 return -EINVAL;
304
305 for (i = 0; i < list->num_chunks; i++) {
306 if (!chunks[i].ddr_paddr ||
Naveen Ramaraj66522592012-11-19 11:33:09 -0800307 !IS_ALIGNED(chunks[i].ddr_paddr, MIN_CHUNK_SIZE) ||
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700308 chunks[i].size < MIN_CHUNK_SIZE ||
309 !IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
310 pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
311 i, chunks[i].ddr_paddr, chunks[i].size);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700312 return -EINVAL;
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700313 }
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700314 }
315 return 0;
316}
317
318int ocmem_map(int client_id, struct ocmem_buf *buffer,
319 struct ocmem_map_list *list)
320{
321 int ret = 0;
322 struct ocmem_handle *handle = NULL;
323
324 if (!check_id(client_id)) {
325 pr_err("ocmem: Invalid client id: %d\n", client_id);
326 return -EINVAL;
327 }
328
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700329 if (!zone_active(client_id)) {
330 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
331 get_name(client_id), client_id);
332 return -EINVAL;
333 }
334
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700335 /* Asynchronous API requires notifier registration */
336 if (!check_notifier(client_id)) {
337 pr_err("ocmem: No notifier registered for client %d\n",
338 client_id);
339 return -EINVAL;
340 }
341
342 if (!buffer) {
343 pr_err("ocmem: Invalid buffer\n");
344 return -EINVAL;
345 }
346
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700347 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700348 return -EINVAL;
349
350 handle = buffer_to_handle(buffer);
351
352 if (!handle)
353 return -EINVAL;
354
355 mutex_lock(&handle->handle_mutex);
356 ret = process_xfer(client_id, handle, list, TO_OCMEM);
357 mutex_unlock(&handle->handle_mutex);
358 return ret;
359}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700360EXPORT_SYMBOL(ocmem_map);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700361
362int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
363 struct ocmem_map_list *list)
364{
365
366 int ret = 0;
367 struct ocmem_handle *handle = NULL;
368
369 if (!check_id(client_id)) {
370 pr_err("ocmem: Invalid client id: %d\n", client_id);
371 return -EINVAL;
372 }
373
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700374 if (!zone_active(client_id)) {
375 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
376 get_name(client_id), client_id);
377 return -EINVAL;
378 }
379
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700380 /* Asynchronous API requires notifier registration */
381 if (!check_notifier(client_id)) {
382 pr_err("ocmem: No notifier registered for client %d\n",
383 client_id);
384 return -EINVAL;
385 }
386
387 if (!buffer) {
388 pr_err("ocmem: Invalid buffer\n");
389 return -EINVAL;
390 }
391
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700392 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700393 return -EINVAL;
394
395 handle = buffer_to_handle(buffer);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700396 mutex_lock(&handle->handle_mutex);
397 ret = process_xfer(client_id, handle, list, TO_DDR);
398 mutex_unlock(&handle->handle_mutex);
399 return ret;
400}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700401EXPORT_SYMBOL(ocmem_unmap);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700402
Naveen Ramaraj55ed8902012-09-26 13:18:06 -0700403int ocmem_dump(int client_id, struct ocmem_buf *buffer,
404 unsigned long dst_phys_addr)
405{
406 int ret = 0;
407 struct ocmem_handle *handle = NULL;
408
409 if (!check_id(client_id)) {
410 pr_err("ocmem: Invalid client id: %d\n", client_id);
411 return -EINVAL;
412 }
413
414 if (!zone_active(client_id)) {
415 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
416 get_name(client_id), client_id);
417 return -EINVAL;
418 }
419
420 if (!buffer) {
421 pr_err("ocmem: Invalid buffer\n");
422 return -EINVAL;
423 }
424
425 handle = buffer_to_handle(buffer);
426 mutex_lock(&handle->handle_mutex);
427 ret = process_dump(client_id, handle, dst_phys_addr);
428 mutex_unlock(&handle->handle_mutex);
429 return ret;
430}
431EXPORT_SYMBOL(ocmem_dump);
432
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700433unsigned long get_max_quota(int client_id)
434{
435 if (!check_id(client_id)) {
436 pr_err("ocmem: Invalid client id: %d\n", client_id);
437 return 0x0;
438 }
439 return process_quota(client_id);
440}
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700441
442/* Synchronous eviction/restore calls */
443/* Only a single eviction or restoration is allowed */
444/* Evictions/Restorations cannot be concurrent with other maps */
445int ocmem_evict(int client_id)
446{
447 int ret = 0;
448
449 if (!check_id(client_id)) {
450 pr_err("ocmem: Invalid client id: %d\n", client_id);
451 return -EINVAL;
452 }
453
454 mutex_lock(&ocmem_eviction_lock);
455 if (test_bit(client_id, evicted)) {
456 pr_err("ocmem: Previous eviction was not restored by %d\n",
457 client_id);
458 mutex_unlock(&ocmem_eviction_lock);
459 return -EINVAL;
460 }
461
462 ret = process_evict(client_id);
463 if (ret == 0)
464 set_bit(client_id, evicted);
465
466 mutex_unlock(&ocmem_eviction_lock);
467 return ret;
468}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700469EXPORT_SYMBOL(ocmem_evict);
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700470
471int ocmem_restore(int client_id)
472{
473 int ret = 0;
474
475 if (!check_id(client_id)) {
476 pr_err("ocmem: Invalid client id: %d\n", client_id);
477 return -EINVAL;
478 }
479
480 mutex_lock(&ocmem_eviction_lock);
481 if (!test_bit(client_id, evicted)) {
482 pr_err("ocmem: No previous eviction by %d\n", client_id);
483 mutex_unlock(&ocmem_eviction_lock);
484 return -EINVAL;
485 }
486 ret = process_restore(client_id);
487 clear_bit(client_id, evicted);
488 mutex_unlock(&ocmem_eviction_lock);
489 return ret;
490}
Naveen Ramarajfdfb0cc2012-09-06 23:16:48 -0700491EXPORT_SYMBOL(ocmem_restore);
Naveen Ramaraj99b07562012-05-28 20:57:09 -0700492
493/* Wrappers until power control is transitioned to clients */
494enum ocmem_power_state ocmem_get_power_state(int client_id,
495 struct ocmem_buf *buffer)
496{
497 return 0;
498}
499
500int ocmem_set_power_state(int client_id, struct ocmem_buf *buffer,
501 enum ocmem_power_state new_state)
502{
503 return 0;
504}
505
506struct ocmem_vectors *ocmem_get_vectors(int client_id,
507 struct ocmem_buf *buffer)
508{
509 return NULL;
510}