• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * cmm.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * The Communication(Shared) Memory Management(CMM) module provides
7  * shared memory management services for DSP/BIOS Bridge data streaming
8  * and messaging.
9  *
10  * Multiple shared memory segments can be registered with CMM.
11  * Each registered SM segment is represented by a SM "allocator" that
12  * describes a block of physically contiguous shared memory used for
13  * future allocations by CMM.
14  *
15  * Memory is coalesced back to the appropriate heap when a buffer is
16  * freed.
17  *
18  * Notes:
19  *   Va: Virtual address.
20  *   Pa: Physical or kernel system address.
21  *
22  * Copyright (C) 2005-2006 Texas Instruments, Inc.
23  *
24  * This package is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License version 2 as
26  * published by the Free Software Foundation.
27  *
28  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31  */
32 #include <linux/types.h>
33 #include <linux/list.h>
34 
35 /*  ----------------------------------- DSP/BIOS Bridge */
36 #include <dspbridge/dbdefs.h>
37 
38 /*  ----------------------------------- OS Adaptation Layer */
39 #include <dspbridge/sync.h>
40 
41 /*  ----------------------------------- Platform Manager */
42 #include <dspbridge/dev.h>
43 #include <dspbridge/proc.h>
44 
45 /*  ----------------------------------- This */
46 #include <dspbridge/cmm.h>
47 
48 /*  ----------------------------------- Defines, Data Structures, Typedefs */
49 #define NEXT_PA(pnode)   (pnode->pa + pnode->size)
50 
51 /* Other bus/platform translations */
52 #define DSPPA2GPPPA(base, x, y)  ((x)+(y))
53 #define GPPPA2DSPPA(base, x, y)  ((x)-(y))
54 
55 /*
56  *  Allocators define a block of contiguous memory used for future allocations.
57  *
58  *      sma - shared memory allocator.
59  *      vma - virtual memory allocator.(not used).
60  */
61 struct cmm_allocator {		/* sma */
62 	unsigned int shm_base;	/* Start of physical SM block */
63 	u32 sm_size;		/* Size of SM block in bytes */
64 	unsigned int vm_base;	/* Start of VM block. (Dev driver
65 					 * context for 'sma') */
66 	u32 dsp_phys_addr_offset;	/* DSP PA to GPP PA offset for this
67 					 * SM space */
68 	s8 c_factor;		/* DSPPa to GPPPa Conversion Factor */
69 	unsigned int dsp_base;	/* DSP virt base byte address */
70 	u32 dsp_size;	/* DSP seg size in bytes */
71 	struct cmm_object *cmm_mgr;	/* back ref to parent mgr */
72 	/* node list of available memory */
73 	struct list_head free_list;
74 	/* node list of memory in use */
75 	struct list_head in_use_list;
76 };
77 
78 struct cmm_xlator {		/* Pa<->Va translator object */
79 	/* CMM object this translator associated */
80 	struct cmm_object *cmm_mgr;
81 	/*
82 	 *  Client process virtual base address that corresponds to phys SM
83 	 *  base address for translator's seg_id.
84 	 *  Only 1 segment ID currently supported.
85 	 */
86 	unsigned int virt_base;	/* virtual base address */
87 	u32 virt_size;		/* size of virt space in bytes */
88 	u32 seg_id;		/* Segment Id */
89 };
90 
91 /* CMM Mgr */
92 struct cmm_object {
93 	/*
94 	 * Cmm Lock is used to serialize access mem manager for multi-threads.
95 	 */
96 	struct mutex cmm_lock;	/* Lock to access cmm mgr */
97 	struct list_head node_free_list;	/* Free list of memory nodes */
98 	u32 min_block_size;	/* Min SM block; default 16 bytes */
99 	u32 page_size;	/* Memory Page size (1k/4k) */
100 	/* GPP SM segment ptrs */
101 	struct cmm_allocator *pa_gppsm_seg_tab[CMM_MAXGPPSEGS];
102 };
103 
104 /* Default CMM Mgr attributes */
105 static struct cmm_mgrattrs cmm_dfltmgrattrs = {
106 	/* min_block_size, min block size(bytes) allocated by cmm mgr */
107 	16
108 };
109 
110 /* Default allocation attributes */
111 static struct cmm_attrs cmm_dfltalctattrs = {
112 	1		/* seg_id, default segment Id for allocator */
113 };
114 
115 /* Address translator default attrs */
116 static struct cmm_xlatorattrs cmm_dfltxlatorattrs = {
117 	/* seg_id, does not have to match cmm_dfltalctattrs ul_seg_id */
118 	1,
119 	0,			/* dsp_bufs */
120 	0,			/* dsp_buf_size */
121 	NULL,			/* vm_base */
122 	0,			/* vm_size */
123 };
124 
125 /* SM node representing a block of memory. */
126 struct cmm_mnode {
127 	struct list_head link;	/* must be 1st element */
128 	u32 pa;		/* Phys addr */
129 	u32 va;			/* Virtual address in device process context */
130 	u32 size;		/* SM block size in bytes */
131 	u32 client_proc;	/* Process that allocated this mem block */
132 };
133 
134 /*  ----------------------------------- Function Prototypes */
135 static void add_to_free_list(struct cmm_allocator *allocator,
136 			     struct cmm_mnode *pnode);
137 static struct cmm_allocator *get_allocator(struct cmm_object *cmm_mgr_obj,
138 					   u32 ul_seg_id);
139 static struct cmm_mnode *get_free_block(struct cmm_allocator *allocator,
140 					u32 usize);
141 static struct cmm_mnode *get_node(struct cmm_object *cmm_mgr_obj, u32 dw_pa,
142 				  u32 dw_va, u32 ul_size);
143 /* get available slot for new allocator */
144 static s32 get_slot(struct cmm_object *cmm_mgr_obj);
145 static void un_register_gppsm_seg(struct cmm_allocator *psma);
146 
147 /*
148  *  ======== cmm_calloc_buf ========
149  *  Purpose:
150  *      Allocate a SM buffer, zero contents, and return the physical address
151  *      and optional driver context virtual address(pp_buf_va).
152  *
153  *      The freelist is sorted in increasing size order. Get the first
154  *      block that satifies the request and sort the remaining back on
155  *      the freelist; if large enough. The kept block is placed on the
156  *      inUseList.
157  */
cmm_calloc_buf(struct cmm_object * hcmm_mgr,u32 usize,struct cmm_attrs * pattrs,void ** pp_buf_va)158 void *cmm_calloc_buf(struct cmm_object *hcmm_mgr, u32 usize,
159 		     struct cmm_attrs *pattrs, void **pp_buf_va)
160 {
161 	struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
162 	void *buf_pa = NULL;
163 	struct cmm_mnode *pnode = NULL;
164 	struct cmm_mnode *new_node = NULL;
165 	struct cmm_allocator *allocator = NULL;
166 	u32 delta_size;
167 	u8 *pbyte = NULL;
168 	s32 cnt;
169 
170 	if (pattrs == NULL)
171 		pattrs = &cmm_dfltalctattrs;
172 
173 	if (pp_buf_va != NULL)
174 		*pp_buf_va = NULL;
175 
176 	if (cmm_mgr_obj && (usize != 0)) {
177 		if (pattrs->seg_id > 0) {
178 			/* SegId > 0 is SM */
179 			/* get the allocator object for this segment id */
180 			allocator =
181 			    get_allocator(cmm_mgr_obj, pattrs->seg_id);
182 			/* keep block size a multiple of min_block_size */
183 			usize =
184 			    ((usize - 1) & ~(cmm_mgr_obj->min_block_size -
185 					     1))
186 			    + cmm_mgr_obj->min_block_size;
187 			mutex_lock(&cmm_mgr_obj->cmm_lock);
188 			pnode = get_free_block(allocator, usize);
189 		}
190 		if (pnode) {
191 			delta_size = (pnode->size - usize);
192 			if (delta_size >= cmm_mgr_obj->min_block_size) {
193 				/* create a new block with the leftovers and
194 				 * add to freelist */
195 				new_node =
196 				    get_node(cmm_mgr_obj, pnode->pa + usize,
197 					     pnode->va + usize,
198 					     (u32) delta_size);
199 				/* leftovers go free */
200 				add_to_free_list(allocator, new_node);
201 				/* adjust our node's size */
202 				pnode->size = usize;
203 			}
204 			/* Tag node with client process requesting allocation
205 			 * We'll need to free up a process's alloc'd SM if the
206 			 * client process goes away.
207 			 */
208 			/* Return TGID instead of process handle */
209 			pnode->client_proc = current->tgid;
210 
211 			/* put our node on InUse list */
212 			list_add_tail(&pnode->link, &allocator->in_use_list);
213 			buf_pa = (void *)pnode->pa;	/* physical address */
214 			/* clear mem */
215 			pbyte = (u8 *) pnode->va;
216 			for (cnt = 0; cnt < (s32) usize; cnt++, pbyte++)
217 				*pbyte = 0;
218 
219 			if (pp_buf_va != NULL) {
220 				/* Virtual address */
221 				*pp_buf_va = (void *)pnode->va;
222 			}
223 		}
224 		mutex_unlock(&cmm_mgr_obj->cmm_lock);
225 	}
226 	return buf_pa;
227 }
228 
229 /*
230  *  ======== cmm_create ========
231  *  Purpose:
232  *      Create a communication memory manager object.
233  */
cmm_create(struct cmm_object ** ph_cmm_mgr,struct dev_object * hdev_obj,const struct cmm_mgrattrs * mgr_attrts)234 int cmm_create(struct cmm_object **ph_cmm_mgr,
235 		      struct dev_object *hdev_obj,
236 		      const struct cmm_mgrattrs *mgr_attrts)
237 {
238 	struct cmm_object *cmm_obj = NULL;
239 	int status = 0;
240 
241 	*ph_cmm_mgr = NULL;
242 	/* create, zero, and tag a cmm mgr object */
243 	cmm_obj = kzalloc(sizeof(struct cmm_object), GFP_KERNEL);
244 	if (!cmm_obj)
245 		return -ENOMEM;
246 
247 	if (mgr_attrts == NULL)
248 		mgr_attrts = &cmm_dfltmgrattrs;	/* set defaults */
249 
250 	/* save away smallest block allocation for this cmm mgr */
251 	cmm_obj->min_block_size = mgr_attrts->min_block_size;
252 	cmm_obj->page_size = PAGE_SIZE;
253 
254 	/* create node free list */
255 	INIT_LIST_HEAD(&cmm_obj->node_free_list);
256 	mutex_init(&cmm_obj->cmm_lock);
257 	*ph_cmm_mgr = cmm_obj;
258 
259 	return status;
260 }
261 
262 /*
263  *  ======== cmm_destroy ========
264  *  Purpose:
265  *      Release the communication memory manager resources.
266  */
cmm_destroy(struct cmm_object * hcmm_mgr,bool force)267 int cmm_destroy(struct cmm_object *hcmm_mgr, bool force)
268 {
269 	struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
270 	struct cmm_info temp_info;
271 	int status = 0;
272 	s32 slot_seg;
273 	struct cmm_mnode *node, *tmp;
274 
275 	if (!hcmm_mgr) {
276 		status = -EFAULT;
277 		return status;
278 	}
279 	mutex_lock(&cmm_mgr_obj->cmm_lock);
280 	/* If not force then fail if outstanding allocations exist */
281 	if (!force) {
282 		/* Check for outstanding memory allocations */
283 		status = cmm_get_info(hcmm_mgr, &temp_info);
284 		if (!status) {
285 			if (temp_info.total_in_use_cnt > 0) {
286 				/* outstanding allocations */
287 				status = -EPERM;
288 			}
289 		}
290 	}
291 	if (!status) {
292 		/* UnRegister SM allocator */
293 		for (slot_seg = 0; slot_seg < CMM_MAXGPPSEGS; slot_seg++) {
294 			if (cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] != NULL) {
295 				un_register_gppsm_seg
296 				    (cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg]);
297 				/* Set slot to NULL for future reuse */
298 				cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] = NULL;
299 			}
300 		}
301 	}
302 	list_for_each_entry_safe(node, tmp, &cmm_mgr_obj->node_free_list,
303 			link) {
304 		list_del(&node->link);
305 		kfree(node);
306 	}
307 	mutex_unlock(&cmm_mgr_obj->cmm_lock);
308 	if (!status) {
309 		/* delete CS & cmm mgr object */
310 		mutex_destroy(&cmm_mgr_obj->cmm_lock);
311 		kfree(cmm_mgr_obj);
312 	}
313 	return status;
314 }
315 
316 /*
317  *  ======== cmm_free_buf ========
318  *  Purpose:
319  *      Free the given buffer.
320  */
cmm_free_buf(struct cmm_object * hcmm_mgr,void * buf_pa,u32 ul_seg_id)321 int cmm_free_buf(struct cmm_object *hcmm_mgr, void *buf_pa, u32 ul_seg_id)
322 {
323 	struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
324 	int status = -EFAULT;
325 	struct cmm_mnode *curr, *tmp;
326 	struct cmm_allocator *allocator;
327 	struct cmm_attrs *pattrs;
328 
329 	if (ul_seg_id == 0) {
330 		pattrs = &cmm_dfltalctattrs;
331 		ul_seg_id = pattrs->seg_id;
332 	}
333 	if (!hcmm_mgr || !(ul_seg_id > 0)) {
334 		status = -EFAULT;
335 		return status;
336 	}
337 
338 	allocator = get_allocator(cmm_mgr_obj, ul_seg_id);
339 	if (!allocator)
340 		return status;
341 
342 	mutex_lock(&cmm_mgr_obj->cmm_lock);
343 	list_for_each_entry_safe(curr, tmp, &allocator->in_use_list, link) {
344 		if (curr->pa == (u32) buf_pa) {
345 			list_del(&curr->link);
346 			add_to_free_list(allocator, curr);
347 			status = 0;
348 			break;
349 		}
350 	}
351 	mutex_unlock(&cmm_mgr_obj->cmm_lock);
352 
353 	return status;
354 }
355 
356 /*
357  *  ======== cmm_get_handle ========
358  *  Purpose:
359  *      Return the communication memory manager object for this device.
360  *      This is typically called from the client process.
361  */
cmm_get_handle(void * hprocessor,struct cmm_object ** ph_cmm_mgr)362 int cmm_get_handle(void *hprocessor, struct cmm_object ** ph_cmm_mgr)
363 {
364 	int status = 0;
365 	struct dev_object *hdev_obj;
366 
367 	if (hprocessor != NULL)
368 		status = proc_get_dev_object(hprocessor, &hdev_obj);
369 	else
370 		hdev_obj = dev_get_first();	/* default */
371 
372 	if (!status)
373 		status = dev_get_cmm_mgr(hdev_obj, ph_cmm_mgr);
374 
375 	return status;
376 }
377 
378 /*
379  *  ======== cmm_get_info ========
380  *  Purpose:
381  *      Return the current memory utilization information.
382  */
cmm_get_info(struct cmm_object * hcmm_mgr,struct cmm_info * cmm_info_obj)383 int cmm_get_info(struct cmm_object *hcmm_mgr,
384 			struct cmm_info *cmm_info_obj)
385 {
386 	struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
387 	u32 ul_seg;
388 	int status = 0;
389 	struct cmm_allocator *altr;
390 	struct cmm_mnode *curr;
391 
392 	if (!hcmm_mgr) {
393 		status = -EFAULT;
394 		return status;
395 	}
396 	mutex_lock(&cmm_mgr_obj->cmm_lock);
397 	cmm_info_obj->num_gppsm_segs = 0;	/* # of SM segments */
398 	/* Total # of outstanding alloc */
399 	cmm_info_obj->total_in_use_cnt = 0;
400 	/* min block size */
401 	cmm_info_obj->min_block_size = cmm_mgr_obj->min_block_size;
402 	/* check SM memory segments */
403 	for (ul_seg = 1; ul_seg <= CMM_MAXGPPSEGS; ul_seg++) {
404 		/* get the allocator object for this segment id */
405 		altr = get_allocator(cmm_mgr_obj, ul_seg);
406 		if (!altr)
407 			continue;
408 		cmm_info_obj->num_gppsm_segs++;
409 		cmm_info_obj->seg_info[ul_seg - 1].seg_base_pa =
410 			altr->shm_base - altr->dsp_size;
411 		cmm_info_obj->seg_info[ul_seg - 1].total_seg_size =
412 			altr->dsp_size + altr->sm_size;
413 		cmm_info_obj->seg_info[ul_seg - 1].gpp_base_pa =
414 			altr->shm_base;
415 		cmm_info_obj->seg_info[ul_seg - 1].gpp_size =
416 			altr->sm_size;
417 		cmm_info_obj->seg_info[ul_seg - 1].dsp_base_va =
418 			altr->dsp_base;
419 		cmm_info_obj->seg_info[ul_seg - 1].dsp_size =
420 			altr->dsp_size;
421 		cmm_info_obj->seg_info[ul_seg - 1].seg_base_va =
422 			altr->vm_base - altr->dsp_size;
423 		cmm_info_obj->seg_info[ul_seg - 1].in_use_cnt = 0;
424 
425 		list_for_each_entry(curr, &altr->in_use_list, link) {
426 			cmm_info_obj->total_in_use_cnt++;
427 			cmm_info_obj->seg_info[ul_seg - 1].in_use_cnt++;
428 		}
429 	}
430 	mutex_unlock(&cmm_mgr_obj->cmm_lock);
431 	return status;
432 }
433 
434 /*
435  *  ======== cmm_register_gppsm_seg ========
436  *  Purpose:
437  *      Register a block of SM with the CMM to be used for later GPP SM
438  *      allocations.
439  */
cmm_register_gppsm_seg(struct cmm_object * hcmm_mgr,u32 dw_gpp_base_pa,u32 ul_size,u32 dsp_addr_offset,s8 c_factor,u32 dw_dsp_base,u32 ul_dsp_size,u32 * sgmt_id,u32 gpp_base_va)440 int cmm_register_gppsm_seg(struct cmm_object *hcmm_mgr,
441 				  u32 dw_gpp_base_pa, u32 ul_size,
442 				  u32 dsp_addr_offset, s8 c_factor,
443 				  u32 dw_dsp_base, u32 ul_dsp_size,
444 				  u32 *sgmt_id, u32 gpp_base_va)
445 {
446 	struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
447 	struct cmm_allocator *psma = NULL;
448 	int status = 0;
449 	struct cmm_mnode *new_node;
450 	s32 slot_seg;
451 
452 	dev_dbg(bridge, "%s: dw_gpp_base_pa %x ul_size %x dsp_addr_offset %x "
453 			"dw_dsp_base %x ul_dsp_size %x gpp_base_va %x\n",
454 			__func__, dw_gpp_base_pa, ul_size, dsp_addr_offset,
455 			dw_dsp_base, ul_dsp_size, gpp_base_va);
456 
457 	if (!hcmm_mgr)
458 		return -EFAULT;
459 
460 	/* make sure we have room for another allocator */
461 	mutex_lock(&cmm_mgr_obj->cmm_lock);
462 
463 	slot_seg = get_slot(cmm_mgr_obj);
464 	if (slot_seg < 0) {
465 		status = -EPERM;
466 		goto func_end;
467 	}
468 
469 	/* Check if input ul_size is big enough to alloc at least one block */
470 	if (ul_size < cmm_mgr_obj->min_block_size) {
471 		status = -EINVAL;
472 		goto func_end;
473 	}
474 
475 	/* create, zero, and tag an SM allocator object */
476 	psma = kzalloc(sizeof(struct cmm_allocator), GFP_KERNEL);
477 	if (!psma) {
478 		status = -ENOMEM;
479 		goto func_end;
480 	}
481 
482 	psma->cmm_mgr = hcmm_mgr;	/* ref to parent */
483 	psma->shm_base = dw_gpp_base_pa;	/* SM Base phys */
484 	psma->sm_size = ul_size;	/* SM segment size in bytes */
485 	psma->vm_base = gpp_base_va;
486 	psma->dsp_phys_addr_offset = dsp_addr_offset;
487 	psma->c_factor = c_factor;
488 	psma->dsp_base = dw_dsp_base;
489 	psma->dsp_size = ul_dsp_size;
490 	if (psma->vm_base == 0) {
491 		status = -EPERM;
492 		goto func_end;
493 	}
494 	/* return the actual segment identifier */
495 	*sgmt_id = (u32) slot_seg + 1;
496 
497 	INIT_LIST_HEAD(&psma->free_list);
498 	INIT_LIST_HEAD(&psma->in_use_list);
499 
500 	/* Get a mem node for this hunk-o-memory */
501 	new_node = get_node(cmm_mgr_obj, dw_gpp_base_pa,
502 			psma->vm_base, ul_size);
503 	/* Place node on the SM allocator's free list */
504 	if (new_node) {
505 		list_add_tail(&new_node->link, &psma->free_list);
506 	} else {
507 		status = -ENOMEM;
508 		goto func_end;
509 	}
510 	/* make entry */
511 	cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] = psma;
512 
513 func_end:
514 	/* Cleanup allocator */
515 	if (status && psma)
516 		un_register_gppsm_seg(psma);
517 	mutex_unlock(&cmm_mgr_obj->cmm_lock);
518 
519 	return status;
520 }
521 
522 /*
523  *  ======== cmm_un_register_gppsm_seg ========
524  *  Purpose:
525  *      UnRegister GPP SM segments with the CMM.
526  */
cmm_un_register_gppsm_seg(struct cmm_object * hcmm_mgr,u32 ul_seg_id)527 int cmm_un_register_gppsm_seg(struct cmm_object *hcmm_mgr,
528 				     u32 ul_seg_id)
529 {
530 	struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
531 	int status = 0;
532 	struct cmm_allocator *psma;
533 	u32 ul_id = ul_seg_id;
534 
535 	if (!hcmm_mgr)
536 		return -EFAULT;
537 
538 	if (ul_seg_id == CMM_ALLSEGMENTS)
539 		ul_id = 1;
540 
541 	if ((ul_id <= 0) || (ul_id > CMM_MAXGPPSEGS))
542 		return -EINVAL;
543 
544 	/*
545 	 * FIXME: CMM_MAXGPPSEGS == 1. why use a while cycle? Seems to me like
546 	 * the ul_seg_id is not needed here. It must be always 1.
547 	 */
548 	while (ul_id <= CMM_MAXGPPSEGS) {
549 		mutex_lock(&cmm_mgr_obj->cmm_lock);
550 		/* slot = seg_id-1 */
551 		psma = cmm_mgr_obj->pa_gppsm_seg_tab[ul_id - 1];
552 		if (psma != NULL) {
553 			un_register_gppsm_seg(psma);
554 			/* Set alctr ptr to NULL for future reuse */
555 			cmm_mgr_obj->pa_gppsm_seg_tab[ul_id - 1] = NULL;
556 		} else if (ul_seg_id != CMM_ALLSEGMENTS) {
557 			status = -EPERM;
558 		}
559 		mutex_unlock(&cmm_mgr_obj->cmm_lock);
560 		if (ul_seg_id != CMM_ALLSEGMENTS)
561 			break;
562 
563 		ul_id++;
564 	}	/* end while */
565 	return status;
566 }
567 
568 /*
569  *  ======== un_register_gppsm_seg ========
570  *  Purpose:
571  *      UnRegister the SM allocator by freeing all its resources and
572  *      nulling cmm mgr table entry.
573  *  Note:
574  *      This routine is always called within cmm lock crit sect.
575  */
un_register_gppsm_seg(struct cmm_allocator * psma)576 static void un_register_gppsm_seg(struct cmm_allocator *psma)
577 {
578 	struct cmm_mnode *curr, *tmp;
579 
580 	/* free nodes on free list */
581 	list_for_each_entry_safe(curr, tmp, &psma->free_list, link) {
582 		list_del(&curr->link);
583 		kfree(curr);
584 	}
585 
586 	/* free nodes on InUse list */
587 	list_for_each_entry_safe(curr, tmp, &psma->in_use_list, link) {
588 		list_del(&curr->link);
589 		kfree(curr);
590 	}
591 
592 	if ((void *)psma->vm_base != NULL)
593 		MEM_UNMAP_LINEAR_ADDRESS((void *)psma->vm_base);
594 
595 	/* Free allocator itself */
596 	kfree(psma);
597 }
598 
599 /*
600  *  ======== get_slot ========
601  *  Purpose:
602  *      An available slot # is returned. Returns negative on failure.
603  */
get_slot(struct cmm_object * cmm_mgr_obj)604 static s32 get_slot(struct cmm_object *cmm_mgr_obj)
605 {
606 	s32 slot_seg = -1;	/* neg on failure */
607 	/* get first available slot in cmm mgr SMSegTab[] */
608 	for (slot_seg = 0; slot_seg < CMM_MAXGPPSEGS; slot_seg++) {
609 		if (cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] == NULL)
610 			break;
611 
612 	}
613 	if (slot_seg == CMM_MAXGPPSEGS)
614 		slot_seg = -1;	/* failed */
615 
616 	return slot_seg;
617 }
618 
619 /*
620  *  ======== get_node ========
621  *  Purpose:
622  *      Get a memory node from freelist or create a new one.
623  */
get_node(struct cmm_object * cmm_mgr_obj,u32 dw_pa,u32 dw_va,u32 ul_size)624 static struct cmm_mnode *get_node(struct cmm_object *cmm_mgr_obj, u32 dw_pa,
625 				  u32 dw_va, u32 ul_size)
626 {
627 	struct cmm_mnode *pnode;
628 
629 	/* Check cmm mgr's node freelist */
630 	if (list_empty(&cmm_mgr_obj->node_free_list)) {
631 		pnode = kzalloc(sizeof(struct cmm_mnode), GFP_KERNEL);
632 		if (!pnode)
633 			return NULL;
634 	} else {
635 		/* surely a valid element */
636 		pnode = list_first_entry(&cmm_mgr_obj->node_free_list,
637 				struct cmm_mnode, link);
638 		list_del_init(&pnode->link);
639 	}
640 
641 	pnode->pa = dw_pa;
642 	pnode->va = dw_va;
643 	pnode->size = ul_size;
644 
645 	return pnode;
646 }
647 
648 /*
649  *  ======== delete_node ========
650  *  Purpose:
651  *      Put a memory node on the cmm nodelist for later use.
652  *      Doesn't actually delete the node. Heap thrashing friendly.
653  */
delete_node(struct cmm_object * cmm_mgr_obj,struct cmm_mnode * pnode)654 static void delete_node(struct cmm_object *cmm_mgr_obj, struct cmm_mnode *pnode)
655 {
656 	list_add_tail(&pnode->link, &cmm_mgr_obj->node_free_list);
657 }
658 
659 /*
660  * ====== get_free_block ========
661  *  Purpose:
662  *      Scan the free block list and return the first block that satisfies
663  *      the size.
664  */
get_free_block(struct cmm_allocator * allocator,u32 usize)665 static struct cmm_mnode *get_free_block(struct cmm_allocator *allocator,
666 					u32 usize)
667 {
668 	struct cmm_mnode *node, *tmp;
669 
670 	if (!allocator)
671 		return NULL;
672 
673 	list_for_each_entry_safe(node, tmp, &allocator->free_list, link) {
674 		if (usize <= node->size) {
675 			list_del(&node->link);
676 			return node;
677 		}
678 	}
679 
680 	return NULL;
681 }
682 
683 /*
684  *  ======== add_to_free_list ========
685  *  Purpose:
686  *      Coalesce node into the freelist in ascending size order.
687  */
add_to_free_list(struct cmm_allocator * allocator,struct cmm_mnode * node)688 static void add_to_free_list(struct cmm_allocator *allocator,
689 			     struct cmm_mnode *node)
690 {
691 	struct cmm_mnode *curr;
692 
693 	if (!node) {
694 		pr_err("%s: failed - node is NULL\n", __func__);
695 		return;
696 	}
697 
698 	list_for_each_entry(curr, &allocator->free_list, link) {
699 		if (NEXT_PA(curr) == node->pa) {
700 			curr->size += node->size;
701 			delete_node(allocator->cmm_mgr, node);
702 			return;
703 		}
704 		if (curr->pa == NEXT_PA(node)) {
705 			curr->pa = node->pa;
706 			curr->va = node->va;
707 			curr->size += node->size;
708 			delete_node(allocator->cmm_mgr, node);
709 			return;
710 		}
711 	}
712 	list_for_each_entry(curr, &allocator->free_list, link) {
713 		if (curr->size >= node->size) {
714 			list_add_tail(&node->link, &curr->link);
715 			return;
716 		}
717 	}
718 	list_add_tail(&node->link, &allocator->free_list);
719 }
720 
721 /*
722  * ======== get_allocator ========
723  *  Purpose:
724  *      Return the allocator for the given SM Segid.
725  *      SegIds:  1,2,3..max.
726  */
get_allocator(struct cmm_object * cmm_mgr_obj,u32 ul_seg_id)727 static struct cmm_allocator *get_allocator(struct cmm_object *cmm_mgr_obj,
728 					   u32 ul_seg_id)
729 {
730 	return cmm_mgr_obj->pa_gppsm_seg_tab[ul_seg_id - 1];
731 }
732 
733 /*
734  *  The CMM_Xlator[xxx] routines below are used by Node and Stream
735  *  to perform SM address translation to the client process address space.
736  *  A "translator" object is created by a node/stream for each SM seg used.
737  */
738 
739 /*
740  *  ======== cmm_xlator_create ========
741  *  Purpose:
742  *      Create an address translator object.
743  */
cmm_xlator_create(struct cmm_xlatorobject ** xlator,struct cmm_object * hcmm_mgr,struct cmm_xlatorattrs * xlator_attrs)744 int cmm_xlator_create(struct cmm_xlatorobject **xlator,
745 			     struct cmm_object *hcmm_mgr,
746 			     struct cmm_xlatorattrs *xlator_attrs)
747 {
748 	struct cmm_xlator *xlator_object = NULL;
749 	int status = 0;
750 
751 	*xlator = NULL;
752 	if (xlator_attrs == NULL)
753 		xlator_attrs = &cmm_dfltxlatorattrs;	/* set defaults */
754 
755 	xlator_object = kzalloc(sizeof(struct cmm_xlator), GFP_KERNEL);
756 	if (xlator_object != NULL) {
757 		xlator_object->cmm_mgr = hcmm_mgr;	/* ref back to CMM */
758 		/* SM seg_id */
759 		xlator_object->seg_id = xlator_attrs->seg_id;
760 	} else {
761 		status = -ENOMEM;
762 	}
763 	if (!status)
764 		*xlator = (struct cmm_xlatorobject *)xlator_object;
765 
766 	return status;
767 }
768 
769 /*
770  *  ======== cmm_xlator_alloc_buf ========
771  */
cmm_xlator_alloc_buf(struct cmm_xlatorobject * xlator,void * va_buf,u32 pa_size)772 void *cmm_xlator_alloc_buf(struct cmm_xlatorobject *xlator, void *va_buf,
773 			   u32 pa_size)
774 {
775 	struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
776 	void *pbuf = NULL;
777 	void *tmp_va_buff;
778 	struct cmm_attrs attrs;
779 
780 	if (xlator_obj) {
781 		attrs.seg_id = xlator_obj->seg_id;
782 		__raw_writel(0, va_buf);
783 		/* Alloc SM */
784 		pbuf =
785 		    cmm_calloc_buf(xlator_obj->cmm_mgr, pa_size, &attrs, NULL);
786 		if (pbuf) {
787 			/* convert to translator(node/strm) process Virtual
788 			 * address */
789 			 tmp_va_buff = cmm_xlator_translate(xlator,
790 							 pbuf, CMM_PA2VA);
791 			__raw_writel((u32)tmp_va_buff, va_buf);
792 		}
793 	}
794 	return pbuf;
795 }
796 
797 /*
798  *  ======== cmm_xlator_free_buf ========
799  *  Purpose:
800  *      Free the given SM buffer and descriptor.
801  *      Does not free virtual memory.
802  */
cmm_xlator_free_buf(struct cmm_xlatorobject * xlator,void * buf_va)803 int cmm_xlator_free_buf(struct cmm_xlatorobject *xlator, void *buf_va)
804 {
805 	struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
806 	int status = -EPERM;
807 	void *buf_pa = NULL;
808 
809 	if (xlator_obj) {
810 		/* convert Va to Pa so we can free it. */
811 		buf_pa = cmm_xlator_translate(xlator, buf_va, CMM_VA2PA);
812 		if (buf_pa) {
813 			status = cmm_free_buf(xlator_obj->cmm_mgr, buf_pa,
814 					      xlator_obj->seg_id);
815 			if (status) {
816 				/* Uh oh, this shouldn't happen. Descriptor
817 				 * gone! */
818 				pr_err("%s, line %d: Assertion failed\n",
819 				       __FILE__, __LINE__);
820 			}
821 		}
822 	}
823 	return status;
824 }
825 
826 /*
827  *  ======== cmm_xlator_info ========
828  *  Purpose:
829  *      Set/Get translator info.
830  */
cmm_xlator_info(struct cmm_xlatorobject * xlator,u8 ** paddr,u32 ul_size,u32 segm_id,bool set_info)831 int cmm_xlator_info(struct cmm_xlatorobject *xlator, u8 ** paddr,
832 			   u32 ul_size, u32 segm_id, bool set_info)
833 {
834 	struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
835 	int status = 0;
836 
837 	if (xlator_obj) {
838 		if (set_info) {
839 			/* set translators virtual address range */
840 			xlator_obj->virt_base = (u32) *paddr;
841 			xlator_obj->virt_size = ul_size;
842 		} else {	/* return virt base address */
843 			*paddr = (u8 *) xlator_obj->virt_base;
844 		}
845 	} else {
846 		status = -EFAULT;
847 	}
848 	return status;
849 }
850 
851 /*
852  *  ======== cmm_xlator_translate ========
853  */
cmm_xlator_translate(struct cmm_xlatorobject * xlator,void * paddr,enum cmm_xlatetype xtype)854 void *cmm_xlator_translate(struct cmm_xlatorobject *xlator, void *paddr,
855 			   enum cmm_xlatetype xtype)
856 {
857 	u32 dw_addr_xlate = 0;
858 	struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
859 	struct cmm_object *cmm_mgr_obj = NULL;
860 	struct cmm_allocator *allocator = NULL;
861 	u32 dw_offset = 0;
862 
863 	if (!xlator_obj)
864 		goto loop_cont;
865 
866 	cmm_mgr_obj = (struct cmm_object *)xlator_obj->cmm_mgr;
867 	/* get this translator's default SM allocator */
868 	allocator = cmm_mgr_obj->pa_gppsm_seg_tab[xlator_obj->seg_id - 1];
869 	if (!allocator)
870 		goto loop_cont;
871 
872 	if ((xtype == CMM_VA2DSPPA) || (xtype == CMM_VA2PA) ||
873 	    (xtype == CMM_PA2VA)) {
874 		if (xtype == CMM_PA2VA) {
875 			/* Gpp Va = Va Base + offset */
876 			dw_offset = (u8 *) paddr - (u8 *) (allocator->shm_base -
877 							   allocator->
878 							   dsp_size);
879 			dw_addr_xlate = xlator_obj->virt_base + dw_offset;
880 			/* Check if translated Va base is in range */
881 			if ((dw_addr_xlate < xlator_obj->virt_base) ||
882 			    (dw_addr_xlate >=
883 			     (xlator_obj->virt_base +
884 			      xlator_obj->virt_size))) {
885 				dw_addr_xlate = 0;	/* bad address */
886 			}
887 		} else {
888 			/* Gpp PA =  Gpp Base + offset */
889 			dw_offset =
890 			    (u8 *) paddr - (u8 *) xlator_obj->virt_base;
891 			dw_addr_xlate =
892 			    allocator->shm_base - allocator->dsp_size +
893 			    dw_offset;
894 		}
895 	} else {
896 		dw_addr_xlate = (u32) paddr;
897 	}
898 	/*Now convert address to proper target physical address if needed */
899 	if ((xtype == CMM_VA2DSPPA) || (xtype == CMM_PA2DSPPA)) {
900 		/* Got Gpp Pa now, convert to DSP Pa */
901 		dw_addr_xlate =
902 		    GPPPA2DSPPA((allocator->shm_base - allocator->dsp_size),
903 				dw_addr_xlate,
904 				allocator->dsp_phys_addr_offset *
905 				allocator->c_factor);
906 	} else if (xtype == CMM_DSPPA2PA) {
907 		/* Got DSP Pa, convert to GPP Pa */
908 		dw_addr_xlate =
909 		    DSPPA2GPPPA(allocator->shm_base - allocator->dsp_size,
910 				dw_addr_xlate,
911 				allocator->dsp_phys_addr_offset *
912 				allocator->c_factor);
913 	}
914 loop_cont:
915 	return (void *)dw_addr_xlate;
916 }
917