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 */ 61struct 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 78struct 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 */ 92struct 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 */ 105static 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 */ 111static struct cmm_attrs cmm_dfltalctattrs = { 112 1 /* seg_id, default segment Id for allocator */ 113}; 114 115/* Address translator default attrs */ 116static 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. */ 126struct 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 */ 135static void add_to_free_list(struct cmm_allocator *allocator, 136 struct cmm_mnode *pnode); 137static struct cmm_allocator *get_allocator(struct cmm_object *cmm_mgr_obj, 138 u32 ul_seg_id); 139static struct cmm_mnode *get_free_block(struct cmm_allocator *allocator, 140 u32 usize); 141static 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 */ 144static s32 get_slot(struct cmm_object *cmm_mgr_obj); 145static 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 */ 158void *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 */ 234int 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 */ 267int 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 */ 321int 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 */ 362int 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 */ 383int 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 */ 440int 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 513func_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 */ 527int 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 */ 576static 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 */ 604static 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 */ 624static 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 */ 654static 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 */ 665static 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 */ 688static 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 */ 727static 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 */ 744int 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 */ 772void *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 */ 803int 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 */ 831int 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 */ 854void *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 } 914loop_cont: 915 return (void *)dw_addr_xlate; 916} 917