tcm.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright (C) 2010 Imagination Technologies Ltd.
  3. */
  4. #include <linux/init.h>
  5. #include <linux/kernel.h>
  6. #include <linux/spinlock.h>
  7. #include <linux/stddef.h>
  8. #include <linux/genalloc.h>
  9. #include <linux/string.h>
  10. #include <linux/list.h>
  11. #include <linux/slab.h>
  12. #include <asm/page.h>
  13. #include <asm/tcm.h>
  14. struct tcm_pool {
  15. struct list_head list;
  16. unsigned int tag;
  17. unsigned long start;
  18. unsigned long end;
  19. struct gen_pool *pool;
  20. };
  21. static LIST_HEAD(pool_list);
  22. static struct tcm_pool *find_pool(unsigned int tag)
  23. {
  24. struct list_head *lh;
  25. struct tcm_pool *pool;
  26. list_for_each(lh, &pool_list) {
  27. pool = list_entry(lh, struct tcm_pool, list);
  28. if (pool->tag == tag)
  29. return pool;
  30. }
  31. return NULL;
  32. }
  33. /**
  34. * tcm_alloc - allocate memory from a TCM pool
  35. * @tag: tag of the pool to allocate memory from
  36. * @len: number of bytes to be allocated
  37. *
  38. * Allocate the requested number of bytes from the pool matching
  39. * the specified tag. Returns the address of the allocated memory
  40. * or zero on failure.
  41. */
  42. unsigned long tcm_alloc(unsigned int tag, size_t len)
  43. {
  44. unsigned long vaddr;
  45. struct tcm_pool *pool;
  46. pool = find_pool(tag);
  47. if (!pool)
  48. return 0;
  49. vaddr = gen_pool_alloc(pool->pool, len);
  50. if (!vaddr)
  51. return 0;
  52. return vaddr;
  53. }
  54. /**
  55. * tcm_free - free a block of memory to a TCM pool
  56. * @tag: tag of the pool to free memory to
  57. * @addr: address of the memory to be freed
  58. * @len: number of bytes to be freed
  59. *
  60. * Free the requested number of bytes at a specific address to the
  61. * pool matching the specified tag.
  62. */
  63. void tcm_free(unsigned int tag, unsigned long addr, size_t len)
  64. {
  65. struct tcm_pool *pool;
  66. pool = find_pool(tag);
  67. if (!pool)
  68. return;
  69. gen_pool_free(pool->pool, addr, len);
  70. }
  71. /**
  72. * tcm_lookup_tag - find the tag matching an address
  73. * @p: memory address to lookup the tag for
  74. *
  75. * Find the tag of the tcm memory region that contains the
  76. * specified address. Returns %TCM_INVALID_TAG if no such
  77. * memory region could be found.
  78. */
  79. unsigned int tcm_lookup_tag(unsigned long p)
  80. {
  81. struct list_head *lh;
  82. struct tcm_pool *pool;
  83. unsigned long addr = (unsigned long) p;
  84. list_for_each(lh, &pool_list) {
  85. pool = list_entry(lh, struct tcm_pool, list);
  86. if (addr >= pool->start && addr < pool->end)
  87. return pool->tag;
  88. }
  89. return TCM_INVALID_TAG;
  90. }
  91. /**
  92. * tcm_add_region - add a memory region to TCM pool list
  93. * @reg: descriptor of region to be added
  94. *
  95. * Add a region of memory to the TCM pool list. Returns 0 on success.
  96. */
  97. int __init tcm_add_region(struct tcm_region *reg)
  98. {
  99. struct tcm_pool *pool;
  100. pool = kmalloc(sizeof(*pool), GFP_KERNEL);
  101. if (!pool) {
  102. pr_err("Failed to alloc memory for TCM pool!\n");
  103. return -ENOMEM;
  104. }
  105. pool->tag = reg->tag;
  106. pool->start = reg->res.start;
  107. pool->end = reg->res.end;
  108. /*
  109. * 2^3 = 8 bytes granularity to allow for 64bit access alignment.
  110. * -1 = NUMA node specifier.
  111. */
  112. pool->pool = gen_pool_create(3, -1);
  113. if (!pool->pool) {
  114. pr_err("Failed to create TCM pool!\n");
  115. kfree(pool);
  116. return -ENOMEM;
  117. }
  118. if (gen_pool_add(pool->pool, reg->res.start,
  119. reg->res.end - reg->res.start + 1, -1)) {
  120. pr_err("Failed to add memory to TCM pool!\n");
  121. return -ENOMEM;
  122. }
  123. pr_info("Added %s TCM pool (%08x bytes @ %08x)\n",
  124. reg->res.name, reg->res.end - reg->res.start + 1,
  125. reg->res.start);
  126. list_add_tail(&pool->list, &pool_list);
  127. return 0;
  128. }