blob: 2604d4729001c7ead0ff9e5233aaab2a1c92560a [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
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}
134
135struct ocmem_buf *ocmem_allocate_nowait(int client_id, unsigned long size)
136{
137 bool can_block = false;
138 bool can_wait = false;
139
140 if (!check_id(client_id)) {
141 pr_err("ocmem: Invalid client id: %d\n", client_id);
142 return NULL;
143 }
144
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700145 if (!zone_active(client_id)) {
146 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
147 get_name(client_id), client_id);
148 return NULL;
149 }
150
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700151 if (size < OCMEM_MIN_ALLOC) {
152 pr_err("ocmem: requested size %lx must be at least %x\n",
153 size, OCMEM_MIN_ALLOC);
154 return NULL;
155 }
156
157 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
158 pr_err("ocmem: Invalid alignment, size must be %x aligned\n",
159 OCMEM_MIN_ALIGN);
160 return NULL;
161 }
162 return __ocmem_allocate_range(client_id, size, size,
163 size, can_block, can_wait);
164}
165
166struct ocmem_buf *ocmem_allocate_range(int client_id, unsigned long min,
167 unsigned long goal, unsigned long step)
168{
169 bool can_block = true;
170 bool can_wait = false;
171
172 if (!check_id(client_id)) {
173 pr_err("ocmem: Invalid client id: %d\n", client_id);
174 return NULL;
175 }
176
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700177 if (!zone_active(client_id)) {
178 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
179 get_name(client_id), client_id);
180 return NULL;
181 }
182
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700183 /* Asynchronous API requires notifier registration */
184 if (!check_notifier(client_id)) {
185 pr_err("ocmem: No notifier registered for client %d\n",
186 client_id);
187 return NULL;
188 }
189
190 if (min < OCMEM_MIN_ALLOC) {
191 pr_err("ocmem: requested min size %lx must be at least %x\n",
192 min, OCMEM_MIN_ALLOC);
193 return NULL;
194 }
195
196 if (!IS_ALIGNED(min | goal | step, OCMEM_MIN_ALIGN)) {
197 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
198 OCMEM_MIN_ALIGN);
199 return NULL;
200 }
201
202 return __ocmem_allocate_range(client_id, min, goal,
203 step, can_block, can_wait);
204}
205
206struct ocmem_buf *ocmem_allocate_nb(int client_id, unsigned long size)
207{
208 bool can_block = true;
209 bool can_wait = false;
210
211 if (!check_id(client_id)) {
212 pr_err("ocmem: Invalid client id: %d\n", client_id);
213 return NULL;
214 }
215
216 /* Asynchronous API requires notifier registration */
217 if (!check_notifier(client_id)) {
218 pr_err("ocmem: No notifier registered for client %d\n",
219 client_id);
220 return NULL;
221 }
222
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700223 if (!zone_active(client_id)) {
224 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
225 get_name(client_id), client_id);
226 return NULL;
227 }
228
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700229 if (size < OCMEM_MIN_ALLOC) {
230 pr_err("ocmem: requested size %lx must be at least %x\n",
231 size, OCMEM_MIN_ALLOC);
232 return NULL;
233 }
234
235 if (!IS_ALIGNED(size, OCMEM_MIN_ALIGN)) {
236 pr_err("ocmem: Invalid alignment, args must be %x aligned\n",
237 OCMEM_MIN_ALIGN);
238 return NULL;
239 }
240
241 return __ocmem_allocate_range(client_id, 0, size, size,
242 can_block, can_wait);
243
244}
245
246int ocmem_free(int client_id, struct ocmem_buf *buffer)
247{
248 if (!check_id(client_id)) {
249 pr_err("ocmem: Invalid client id: %d\n", client_id);
250 return -EINVAL;
251 }
252
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700253 if (!zone_active(client_id)) {
254 pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n",
255 get_name(client_id), client_id);
256 return -EINVAL;
257 }
258
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700259 if (!buffer) {
260 pr_err("ocmem: Invalid buffer\n");
261 return -EINVAL;
262 }
263
264 return __ocmem_free(client_id, buffer);
265}
266
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700267int ocmem_shrink(int client_id, struct ocmem_buf *buffer, unsigned long len)
268{
269 if (!buffer)
270 return -EINVAL;
271 if (len >= buffer->len)
272 return -EINVAL;
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700273
274 if (!zone_active(client_id)) {
275 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
276 get_name(client_id), client_id);
277 return -EINVAL;
278 }
279
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700280 return __ocmem_shrink(client_id, buffer, len);
281}
282
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700283int pre_validate_chunk_list(struct ocmem_map_list *list)
284{
285 int i = 0;
286 struct ocmem_chunk *chunks;
287
288 if (!list)
289 return -EINVAL;
290
291 if (list->num_chunks > OCMEM_MAX_CHUNKS || list->num_chunks == 0)
292 return -EINVAL;
293
294 chunks = list->chunks;
295
296 if (!chunks)
297 return -EINVAL;
298
299 for (i = 0; i < list->num_chunks; i++) {
300 if (!chunks[i].ddr_paddr ||
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700301 chunks[i].size < MIN_CHUNK_SIZE ||
302 !IS_ALIGNED(chunks[i].size, MIN_CHUNK_SIZE)) {
303 pr_err("Invalid ocmem chunk at index %d (p: %lx, size %lx)\n",
304 i, chunks[i].ddr_paddr, chunks[i].size);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700305 return -EINVAL;
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700306 }
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700307 }
308 return 0;
309}
310
311int ocmem_map(int client_id, struct ocmem_buf *buffer,
312 struct ocmem_map_list *list)
313{
314 int ret = 0;
315 struct ocmem_handle *handle = NULL;
316
317 if (!check_id(client_id)) {
318 pr_err("ocmem: Invalid client id: %d\n", client_id);
319 return -EINVAL;
320 }
321
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700322 if (!zone_active(client_id)) {
323 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
324 get_name(client_id), client_id);
325 return -EINVAL;
326 }
327
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700328 /* Asynchronous API requires notifier registration */
329 if (!check_notifier(client_id)) {
330 pr_err("ocmem: No notifier registered for client %d\n",
331 client_id);
332 return -EINVAL;
333 }
334
335 if (!buffer) {
336 pr_err("ocmem: Invalid buffer\n");
337 return -EINVAL;
338 }
339
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700340 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700341 return -EINVAL;
342
343 handle = buffer_to_handle(buffer);
344
345 if (!handle)
346 return -EINVAL;
347
348 mutex_lock(&handle->handle_mutex);
349 ret = process_xfer(client_id, handle, list, TO_OCMEM);
350 mutex_unlock(&handle->handle_mutex);
351 return ret;
352}
353
354int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
355 struct ocmem_map_list *list)
356{
357
358 int ret = 0;
359 struct ocmem_handle *handle = NULL;
360
361 if (!check_id(client_id)) {
362 pr_err("ocmem: Invalid client id: %d\n", client_id);
363 return -EINVAL;
364 }
365
Naveen Ramaraj4d5e3542012-08-12 21:55:49 -0700366 if (!zone_active(client_id)) {
367 pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
368 get_name(client_id), client_id);
369 return -EINVAL;
370 }
371
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700372 /* Asynchronous API requires notifier registration */
373 if (!check_notifier(client_id)) {
374 pr_err("ocmem: No notifier registered for client %d\n",
375 client_id);
376 return -EINVAL;
377 }
378
379 if (!buffer) {
380 pr_err("ocmem: Invalid buffer\n");
381 return -EINVAL;
382 }
383
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700384 if (pre_validate_chunk_list(list) != 0)
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700385 return -EINVAL;
386
387 handle = buffer_to_handle(buffer);
Naveen Ramaraj0f5e7ab2012-04-24 19:10:23 -0700388 mutex_lock(&handle->handle_mutex);
389 ret = process_xfer(client_id, handle, list, TO_DDR);
390 mutex_unlock(&handle->handle_mutex);
391 return ret;
392}
393
394unsigned long get_max_quota(int client_id)
395{
396 if (!check_id(client_id)) {
397 pr_err("ocmem: Invalid client id: %d\n", client_id);
398 return 0x0;
399 }
400 return process_quota(client_id);
401}
Naveen Ramarajcc4ec152012-05-14 09:55:29 -0700402
403/* Synchronous eviction/restore calls */
404/* Only a single eviction or restoration is allowed */
405/* Evictions/Restorations cannot be concurrent with other maps */
406int ocmem_evict(int client_id)
407{
408 int ret = 0;
409
410 if (!check_id(client_id)) {
411 pr_err("ocmem: Invalid client id: %d\n", client_id);
412 return -EINVAL;
413 }
414
415 mutex_lock(&ocmem_eviction_lock);
416 if (test_bit(client_id, evicted)) {
417 pr_err("ocmem: Previous eviction was not restored by %d\n",
418 client_id);
419 mutex_unlock(&ocmem_eviction_lock);
420 return -EINVAL;
421 }
422
423 ret = process_evict(client_id);
424 if (ret == 0)
425 set_bit(client_id, evicted);
426
427 mutex_unlock(&ocmem_eviction_lock);
428 return ret;
429}
430
431int ocmem_restore(int client_id)
432{
433 int ret = 0;
434
435 if (!check_id(client_id)) {
436 pr_err("ocmem: Invalid client id: %d\n", client_id);
437 return -EINVAL;
438 }
439
440 mutex_lock(&ocmem_eviction_lock);
441 if (!test_bit(client_id, evicted)) {
442 pr_err("ocmem: No previous eviction by %d\n", client_id);
443 mutex_unlock(&ocmem_eviction_lock);
444 return -EINVAL;
445 }
446 ret = process_restore(client_id);
447 clear_bit(client_id, evicted);
448 mutex_unlock(&ocmem_eviction_lock);
449 return ret;
450}
Naveen Ramaraj99b07562012-05-28 20:57:09 -0700451
452/* Wrappers until power control is transitioned to clients */
453enum ocmem_power_state ocmem_get_power_state(int client_id,
454 struct ocmem_buf *buffer)
455{
456 return 0;
457}
458
459int ocmem_set_power_state(int client_id, struct ocmem_buf *buffer,
460 enum ocmem_power_state new_state)
461{
462 return 0;
463}
464
465struct ocmem_vectors *ocmem_get_vectors(int client_id,
466 struct ocmem_buf *buffer)
467{
468 return NULL;
469}