1 /*
2 * dmm.c
3 *
4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5 *
6 * The Dynamic Memory Manager (DMM) module manages the DSP Virtual address
7 * space that can be directly mapped to any MPU buffer or memory region
8 *
9 * Notes:
10 * Region: Generic memory entitiy having a start address and a size
11 * Chunk: Reserved region
12 *
13 * Copyright (C) 2005-2006 Texas Instruments, Inc.
14 *
15 * This package is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License version 2 as
17 * published by the Free Software Foundation.
18 *
19 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 */
23 #include <linux/types.h>
24
25 /* ----------------------------------- Host OS */
26 #include <dspbridge/host_os.h>
27
28 /* ----------------------------------- DSP/BIOS Bridge */
29 #include <dspbridge/dbdefs.h>
30
31 /* ----------------------------------- OS Adaptation Layer */
32 #include <dspbridge/sync.h>
33
34 /* ----------------------------------- Platform Manager */
35 #include <dspbridge/dev.h>
36 #include <dspbridge/proc.h>
37
38 /* ----------------------------------- This */
39 #include <dspbridge/dmm.h>
40
41 /* ----------------------------------- Defines, Data Structures, Typedefs */
42 #define DMM_ADDR_VIRTUAL(a) \
43 (((struct map_page *)(a) - virtual_mapping_table) * PG_SIZE4K +\
44 dyn_mem_map_beg)
45 #define DMM_ADDR_TO_INDEX(a) (((a) - dyn_mem_map_beg) / PG_SIZE4K)
46
47 /* DMM Mgr */
48 struct dmm_object {
49 /* Dmm Lock is used to serialize access mem manager for
50 * multi-threads. */
51 spinlock_t dmm_lock; /* Lock to access dmm mgr */
52 };
53
54 struct map_page {
55 u32 region_size:15;
56 u32 mapped_size:15;
57 u32 reserved:1;
58 u32 mapped:1;
59 };
60
61 /* Create the free list */
62 static struct map_page *virtual_mapping_table;
63 static u32 free_region; /* The index of free region */
64 static u32 free_size;
65 static u32 dyn_mem_map_beg; /* The Beginning of dynamic memory mapping */
66 static u32 table_size; /* The size of virt and phys pages tables */
67
68 /* ----------------------------------- Function Prototypes */
69 static struct map_page *get_region(u32 addr);
70 static struct map_page *get_free_region(u32 len);
71 static struct map_page *get_mapped_region(u32 addrs);
72
73 /* ======== dmm_create_tables ========
74 * Purpose:
75 * Create table to hold the information of physical address
76 * the buffer pages that is passed by the user, and the table
77 * to hold the information of the virtual memory that is reserved
78 * for DSP.
79 */
dmm_create_tables(struct dmm_object * dmm_mgr,u32 addr,u32 size)80 int dmm_create_tables(struct dmm_object *dmm_mgr, u32 addr, u32 size)
81 {
82 struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
83 int status = 0;
84
85 status = dmm_delete_tables(dmm_obj);
86 if (!status) {
87 dyn_mem_map_beg = addr;
88 table_size = PG_ALIGN_HIGH(size, PG_SIZE4K) / PG_SIZE4K;
89 /* Create the free list */
90 virtual_mapping_table = __vmalloc(table_size *
91 sizeof(struct map_page), GFP_KERNEL |
92 __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
93 if (virtual_mapping_table == NULL)
94 status = -ENOMEM;
95 else {
96 /* On successful allocation,
97 * all entries are zero ('free') */
98 free_region = 0;
99 free_size = table_size * PG_SIZE4K;
100 virtual_mapping_table[0].region_size = table_size;
101 }
102 }
103
104 if (status)
105 pr_err("%s: failure, status 0x%x\n", __func__, status);
106
107 return status;
108 }
109
110 /*
111 * ======== dmm_create ========
112 * Purpose:
113 * Create a dynamic memory manager object.
114 */
dmm_create(struct dmm_object ** dmm_manager,struct dev_object * hdev_obj,const struct dmm_mgrattrs * mgr_attrts)115 int dmm_create(struct dmm_object **dmm_manager,
116 struct dev_object *hdev_obj,
117 const struct dmm_mgrattrs *mgr_attrts)
118 {
119 struct dmm_object *dmm_obj = NULL;
120 int status = 0;
121
122 *dmm_manager = NULL;
123 /* create, zero, and tag a cmm mgr object */
124 dmm_obj = kzalloc(sizeof(struct dmm_object), GFP_KERNEL);
125 if (dmm_obj != NULL) {
126 spin_lock_init(&dmm_obj->dmm_lock);
127 *dmm_manager = dmm_obj;
128 } else {
129 status = -ENOMEM;
130 }
131
132 return status;
133 }
134
135 /*
136 * ======== dmm_destroy ========
137 * Purpose:
138 * Release the communication memory manager resources.
139 */
dmm_destroy(struct dmm_object * dmm_mgr)140 int dmm_destroy(struct dmm_object *dmm_mgr)
141 {
142 struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
143 int status = 0;
144
145 if (dmm_mgr) {
146 status = dmm_delete_tables(dmm_obj);
147 if (!status)
148 kfree(dmm_obj);
149 } else
150 status = -EFAULT;
151
152 return status;
153 }
154
155 /*
156 * ======== dmm_delete_tables ========
157 * Purpose:
158 * Delete DMM Tables.
159 */
dmm_delete_tables(struct dmm_object * dmm_mgr)160 int dmm_delete_tables(struct dmm_object *dmm_mgr)
161 {
162 int status = 0;
163
164 /* Delete all DMM tables */
165 if (dmm_mgr)
166 vfree(virtual_mapping_table);
167 else
168 status = -EFAULT;
169 return status;
170 }
171
172 /*
173 * ======== dmm_get_handle ========
174 * Purpose:
175 * Return the dynamic memory manager object for this device.
176 * This is typically called from the client process.
177 */
dmm_get_handle(void * hprocessor,struct dmm_object ** dmm_manager)178 int dmm_get_handle(void *hprocessor, struct dmm_object **dmm_manager)
179 {
180 int status = 0;
181 struct dev_object *hdev_obj;
182
183 if (hprocessor != NULL)
184 status = proc_get_dev_object(hprocessor, &hdev_obj);
185 else
186 hdev_obj = dev_get_first(); /* default */
187
188 if (!status)
189 status = dev_get_dmm_mgr(hdev_obj, dmm_manager);
190
191 return status;
192 }
193
194 /*
195 * ======== dmm_map_memory ========
196 * Purpose:
197 * Add a mapping block to the reserved chunk. DMM assumes that this block
198 * will be mapped in the DSP/IVA's address space. DMM returns an error if a
199 * mapping overlaps another one. This function stores the info that will be
200 * required later while unmapping the block.
201 */
dmm_map_memory(struct dmm_object * dmm_mgr,u32 addr,u32 size)202 int dmm_map_memory(struct dmm_object *dmm_mgr, u32 addr, u32 size)
203 {
204 struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
205 struct map_page *chunk;
206 int status = 0;
207
208 spin_lock(&dmm_obj->dmm_lock);
209 /* Find the Reserved memory chunk containing the DSP block to
210 * be mapped */
211 chunk = (struct map_page *)get_region(addr);
212 if (chunk != NULL) {
213 /* Mark the region 'mapped', leave the 'reserved' info as-is */
214 chunk->mapped = true;
215 chunk->mapped_size = (size / PG_SIZE4K);
216 } else
217 status = -ENOENT;
218 spin_unlock(&dmm_obj->dmm_lock);
219
220 dev_dbg(bridge, "%s dmm_mgr %p, addr %x, size %x\n\tstatus %x, "
221 "chunk %p", __func__, dmm_mgr, addr, size, status, chunk);
222
223 return status;
224 }
225
226 /*
227 * ======== dmm_reserve_memory ========
228 * Purpose:
229 * Reserve a chunk of virtually contiguous DSP/IVA address space.
230 */
dmm_reserve_memory(struct dmm_object * dmm_mgr,u32 size,u32 * prsv_addr)231 int dmm_reserve_memory(struct dmm_object *dmm_mgr, u32 size,
232 u32 *prsv_addr)
233 {
234 int status = 0;
235 struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
236 struct map_page *node;
237 u32 rsv_addr = 0;
238 u32 rsv_size = 0;
239
240 spin_lock(&dmm_obj->dmm_lock);
241
242 /* Try to get a DSP chunk from the free list */
243 node = get_free_region(size);
244 if (node != NULL) {
245 /* DSP chunk of given size is available. */
246 rsv_addr = DMM_ADDR_VIRTUAL(node);
247 /* Calculate the number entries to use */
248 rsv_size = size / PG_SIZE4K;
249 if (rsv_size < node->region_size) {
250 /* Mark remainder of free region */
251 node[rsv_size].mapped = false;
252 node[rsv_size].reserved = false;
253 node[rsv_size].region_size =
254 node->region_size - rsv_size;
255 node[rsv_size].mapped_size = 0;
256 }
257 /* get_region will return first fit chunk. But we only use what
258 is requested. */
259 node->mapped = false;
260 node->reserved = true;
261 node->region_size = rsv_size;
262 node->mapped_size = 0;
263 /* Return the chunk's starting address */
264 *prsv_addr = rsv_addr;
265 } else
266 /*dSP chunk of given size is not available */
267 status = -ENOMEM;
268
269 spin_unlock(&dmm_obj->dmm_lock);
270
271 dev_dbg(bridge, "%s dmm_mgr %p, size %x, prsv_addr %p\n\tstatus %x, "
272 "rsv_addr %x, rsv_size %x\n", __func__, dmm_mgr, size,
273 prsv_addr, status, rsv_addr, rsv_size);
274
275 return status;
276 }
277
278 /*
279 * ======== dmm_un_map_memory ========
280 * Purpose:
281 * Remove the mapped block from the reserved chunk.
282 */
dmm_un_map_memory(struct dmm_object * dmm_mgr,u32 addr,u32 * psize)283 int dmm_un_map_memory(struct dmm_object *dmm_mgr, u32 addr, u32 *psize)
284 {
285 struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
286 struct map_page *chunk;
287 int status = 0;
288
289 spin_lock(&dmm_obj->dmm_lock);
290 chunk = get_mapped_region(addr);
291 if (chunk == NULL)
292 status = -ENOENT;
293
294 if (!status) {
295 /* Unmap the region */
296 *psize = chunk->mapped_size * PG_SIZE4K;
297 chunk->mapped = false;
298 chunk->mapped_size = 0;
299 }
300 spin_unlock(&dmm_obj->dmm_lock);
301
302 dev_dbg(bridge, "%s: dmm_mgr %p, addr %x, psize %p\n\tstatus %x, "
303 "chunk %p\n", __func__, dmm_mgr, addr, psize, status, chunk);
304
305 return status;
306 }
307
308 /*
309 * ======== dmm_un_reserve_memory ========
310 * Purpose:
311 * Free a chunk of reserved DSP/IVA address space.
312 */
dmm_un_reserve_memory(struct dmm_object * dmm_mgr,u32 rsv_addr)313 int dmm_un_reserve_memory(struct dmm_object *dmm_mgr, u32 rsv_addr)
314 {
315 struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
316 struct map_page *chunk;
317 u32 i;
318 int status = 0;
319 u32 chunk_size;
320
321 spin_lock(&dmm_obj->dmm_lock);
322
323 /* Find the chunk containing the reserved address */
324 chunk = get_mapped_region(rsv_addr);
325 if (chunk == NULL)
326 status = -ENOENT;
327
328 if (!status) {
329 /* Free all the mapped pages for this reserved region */
330 i = 0;
331 while (i < chunk->region_size) {
332 if (chunk[i].mapped) {
333 /* Remove mapping from the page tables. */
334 chunk_size = chunk[i].mapped_size;
335 /* Clear the mapping flags */
336 chunk[i].mapped = false;
337 chunk[i].mapped_size = 0;
338 i += chunk_size;
339 } else
340 i++;
341 }
342 /* Clear the flags (mark the region 'free') */
343 chunk->reserved = false;
344 /* NOTE: We do NOT coalesce free regions here.
345 * Free regions are coalesced in get_region(), as it traverses
346 *the whole mapping table
347 */
348 }
349 spin_unlock(&dmm_obj->dmm_lock);
350
351 dev_dbg(bridge, "%s: dmm_mgr %p, rsv_addr %x\n\tstatus %x chunk %p",
352 __func__, dmm_mgr, rsv_addr, status, chunk);
353
354 return status;
355 }
356
357 /*
358 * ======== get_region ========
359 * Purpose:
360 * Returns a region containing the specified memory region
361 */
get_region(u32 addr)362 static struct map_page *get_region(u32 addr)
363 {
364 struct map_page *curr_region = NULL;
365 u32 i = 0;
366
367 if (virtual_mapping_table != NULL) {
368 /* find page mapped by this address */
369 i = DMM_ADDR_TO_INDEX(addr);
370 if (i < table_size)
371 curr_region = virtual_mapping_table + i;
372 }
373
374 dev_dbg(bridge, "%s: curr_region %p, free_region %d, free_size %d\n",
375 __func__, curr_region, free_region, free_size);
376 return curr_region;
377 }
378
379 /*
380 * ======== get_free_region ========
381 * Purpose:
382 * Returns the requested free region
383 */
get_free_region(u32 len)384 static struct map_page *get_free_region(u32 len)
385 {
386 struct map_page *curr_region = NULL;
387 u32 i = 0;
388 u32 region_size = 0;
389 u32 next_i = 0;
390
391 if (virtual_mapping_table == NULL)
392 return curr_region;
393 if (len > free_size) {
394 /* Find the largest free region
395 * (coalesce during the traversal) */
396 while (i < table_size) {
397 region_size = virtual_mapping_table[i].region_size;
398 next_i = i + region_size;
399 if (virtual_mapping_table[i].reserved == false) {
400 /* Coalesce, if possible */
401 if (next_i < table_size &&
402 virtual_mapping_table[next_i].reserved
403 == false) {
404 virtual_mapping_table[i].region_size +=
405 virtual_mapping_table
406 [next_i].region_size;
407 continue;
408 }
409 region_size *= PG_SIZE4K;
410 if (region_size > free_size) {
411 free_region = i;
412 free_size = region_size;
413 }
414 }
415 i = next_i;
416 }
417 }
418 if (len <= free_size) {
419 curr_region = virtual_mapping_table + free_region;
420 free_region += (len / PG_SIZE4K);
421 free_size -= len;
422 }
423 return curr_region;
424 }
425
426 /*
427 * ======== get_mapped_region ========
428 * Purpose:
429 * Returns the requestedmapped region
430 */
get_mapped_region(u32 addrs)431 static struct map_page *get_mapped_region(u32 addrs)
432 {
433 u32 i = 0;
434 struct map_page *curr_region = NULL;
435
436 if (virtual_mapping_table == NULL)
437 return curr_region;
438
439 i = DMM_ADDR_TO_INDEX(addrs);
440 if (i < table_size && (virtual_mapping_table[i].mapped ||
441 virtual_mapping_table[i].reserved))
442 curr_region = virtual_mapping_table + i;
443 return curr_region;
444 }
445
446 #ifdef DSP_DMM_DEBUG
dmm_mem_map_dump(struct dmm_object * dmm_mgr)447 u32 dmm_mem_map_dump(struct dmm_object *dmm_mgr)
448 {
449 struct map_page *curr_node = NULL;
450 u32 i;
451 u32 freemem = 0;
452 u32 bigsize = 0;
453
454 spin_lock(&dmm_mgr->dmm_lock);
455
456 if (virtual_mapping_table != NULL) {
457 for (i = 0; i < table_size; i +=
458 virtual_mapping_table[i].region_size) {
459 curr_node = virtual_mapping_table + i;
460 if (curr_node->reserved) {
461 /*printk("RESERVED size = 0x%x, "
462 "Map size = 0x%x\n",
463 (curr_node->region_size * PG_SIZE4K),
464 (curr_node->mapped == false) ? 0 :
465 (curr_node->mapped_size * PG_SIZE4K));
466 */
467 } else {
468 /* printk("UNRESERVED size = 0x%x\n",
469 (curr_node->region_size * PG_SIZE4K));
470 */
471 freemem += (curr_node->region_size * PG_SIZE4K);
472 if (curr_node->region_size > bigsize)
473 bigsize = curr_node->region_size;
474 }
475 }
476 }
477 spin_unlock(&dmm_mgr->dmm_lock);
478 printk(KERN_INFO "Total DSP VA FREE memory = %d Mbytes\n",
479 freemem / (1024 * 1024));
480 printk(KERN_INFO "Total DSP VA USED memory= %d Mbytes \n",
481 (((table_size * PG_SIZE4K) - freemem)) / (1024 * 1024));
482 printk(KERN_INFO "DSP VA - Biggest FREE block = %d Mbytes \n\n",
483 (bigsize * PG_SIZE4K / (1024 * 1024)));
484
485 return 0;
486 }
487 #endif
488