blob: f14a777b978fcb7d2bdb26c8dc11e76ef76531ee [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -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 Ramaraj160a3ca2012-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 Ramaraj160a3ca2012-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 Ramaraj160a3ca2012-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 Ramaraj160a3ca2012-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 Ramaraj160a3ca2012-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 Ramaraj160a3ca2012-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 Ramarajcc4ec152012-05-14 09:55:29 -0700307 chunks[i].size < MIN_CHUNK_SIZE ||
308 !IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
309 pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
310 i, chunks[i].ddr_paddr, chunks[i].size);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700311 return -EINVAL;
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700312 }
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700313 }
314 return 0;
315}
316
317int ocmem_map(int client_id, struct ocmem_buf *buffer,
318 struct ocmem_map_list *list)
319{
320 int ret = 0;
321 struct ocmem_handle *handle = NULL;
322
323 if (!check_id(client_id)) {
324 pr_err("ocmem: Invalid client id: %d\n", client_id);
325 return -EINVAL;
326 }
327
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700328 if (!zone_active(client_id)) {
329 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
330 get_name(client_id), client_id);
331 return -EINVAL;
332 }
333
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700334 /* Asynchronous API requires notifier registration */
335 if (!check_notifier(client_id)) {
336 pr_err("ocmem: No notifier registered for client %d\n",
337 client_id);
338 return -EINVAL;
339 }
340
341 if (!buffer) {
342 pr_err("ocmem: Invalid buffer\n");
343 return -EINVAL;
344 }
345
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700346 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700347 return -EINVAL;
348
349 handle = buffer_to_handle(buffer);
350
351 if (!handle)
352 return -EINVAL;
353
354 mutex_lock(&handle->handle_mutex);
355 ret = process_xfer(client_id, handle, list, TO_OCMEM);
356 mutex_unlock(&handle->handle_mutex);
357 return ret;
358}
Naveen Ramaraj160a3ca2012-09-06 23:16:48 -0700359EXPORT_SYMBOL(ocmem_map);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700360
361int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
362 struct ocmem_map_list *list)
363{
364
365 int ret = 0;
366 struct ocmem_handle *handle = NULL;
367
368 if (!check_id(client_id)) {
369 pr_err("ocmem: Invalid client id: %d\n", client_id);
370 return -EINVAL;
371 }
372
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700373 if (!zone_active(client_id)) {
374 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
375 get_name(client_id), client_id);
376 return -EINVAL;
377 }
378
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700379 /* Asynchronous API requires notifier registration */
380 if (!check_notifier(client_id)) {
381 pr_err("ocmem: No notifier registered for client %d\n",
382 client_id);
383 return -EINVAL;
384 }
385
386 if (!buffer) {
387 pr_err("ocmem: Invalid buffer\n");
388 return -EINVAL;
389 }
390
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700391 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700392 return -EINVAL;
393
394 handle = buffer_to_handle(buffer);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700395 mutex_lock(&handle->handle_mutex);
396 ret = process_xfer(client_id, handle, list, TO_DDR);
397 mutex_unlock(&handle->handle_mutex);
398 return ret;
399}
Naveen Ramaraj160a3ca2012-09-06 23:16:48 -0700400EXPORT_SYMBOL(ocmem_unmap);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700401
402unsigned long get_max_quota(int client_id)
403{
404 if (!check_id(client_id)) {
405 pr_err("ocmem: Invalid client id: %d\n", client_id);
406 return 0x0;
407 }
408 return process_quota(client_id);
409}
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700410
411/* Synchronous eviction/restore calls */
412/* Only a single eviction or restoration is allowed */
413/* Evictions/Restorations cannot be concurrent with other maps */
414int ocmem_evict(int client_id)
415{
416 int ret = 0;
417
418 if (!check_id(client_id)) {
419 pr_err("ocmem: Invalid client id: %d\n", client_id);
420 return -EINVAL;
421 }
422
423 mutex_lock(&ocmem_eviction_lock);
424 if (test_bit(client_id, evicted)) {
425 pr_err("ocmem: Previous eviction was not restored by %d\n",
426 client_id);
427 mutex_unlock(&ocmem_eviction_lock);
428 return -EINVAL;
429 }
430
431 ret = process_evict(client_id);
432 if (ret == 0)
433 set_bit(client_id, evicted);
434
435 mutex_unlock(&ocmem_eviction_lock);
436 return ret;
437}
Naveen Ramaraj160a3ca2012-09-06 23:16:48 -0700438EXPORT_SYMBOL(ocmem_evict);
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700439
440int ocmem_restore(int client_id)
441{
442 int ret = 0;
443
444 if (!check_id(client_id)) {
445 pr_err("ocmem: Invalid client id: %d\n", client_id);
446 return -EINVAL;
447 }
448
449 mutex_lock(&ocmem_eviction_lock);
450 if (!test_bit(client_id, evicted)) {
451 pr_err("ocmem: No previous eviction by %d\n", client_id);
452 mutex_unlock(&ocmem_eviction_lock);
453 return -EINVAL;
454 }
455 ret = process_restore(client_id);
456 clear_bit(client_id, evicted);
457 mutex_unlock(&ocmem_eviction_lock);
458 return ret;
459}
Naveen Ramaraj160a3ca2012-09-06 23:16:48 -0700460EXPORT_SYMBOL(ocmem_restore);
Naveen Ramaraj99b07562012-05-28 20:57:09 -0700461
462/* Wrappers until power control is transitioned to clients */
463enum ocmem_power_state ocmem_get_power_state(int client_id,
464 struct ocmem_buf *buffer)
465{
466 return 0;
467}
468
469int ocmem_set_power_state(int client_id, struct ocmem_buf *buffer,
470 enum ocmem_power_state new_state)
471{
472 return 0;
473}
474
475struct ocmem_vectors *ocmem_get_vectors(int client_id,
476 struct ocmem_buf *buffer)
477{
478 return NULL;
479}