devres.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
  3. *
  4. * Based on the original work in Linux by
  5. * Copyright (c) 2006 SUSE Linux Products GmbH
  6. * Copyright (c) 2006 Tejun Heo <teheo@suse.de>
  7. *
  8. * SPDX-License-Identifier: GPL-2.0+
  9. */
  10. #include <common.h>
  11. #include <linux/compat.h>
  12. #include <linux/kernel.h>
  13. #include <linux/list.h>
  14. #include <dm/device.h>
  15. #include <dm/root.h>
  16. #include <dm/util.h>
  17. /**
  18. * struct devres - Bookkeeping info for managed device resource
  19. * @entry: List to associate this structure with a device
  20. * @release: Callback invoked when this resource is released
  21. * @probe: Flag to show when this resource was allocated
  22. (true = probe, false = bind)
  23. * @name: Name of release function
  24. * @size: Size of resource data
  25. * @data: Resource data
  26. */
  27. struct devres {
  28. struct list_head entry;
  29. dr_release_t release;
  30. bool probe;
  31. #ifdef CONFIG_DEBUG_DEVRES
  32. const char *name;
  33. size_t size;
  34. #endif
  35. unsigned long long data[];
  36. };
  37. #ifdef CONFIG_DEBUG_DEVRES
  38. static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
  39. {
  40. dr->name = name;
  41. dr->size = size;
  42. }
  43. static void devres_log(struct udevice *dev, struct devres *dr,
  44. const char *op)
  45. {
  46. printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
  47. dev->name, op, dr, dr->name, (unsigned long)dr->size);
  48. }
  49. #else /* CONFIG_DEBUG_DEVRES */
  50. #define set_node_dbginfo(dr, n, s) do {} while (0)
  51. #define devres_log(dev, dr, op) do {} while (0)
  52. #endif
  53. #if CONFIG_DEBUG_DEVRES
  54. void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
  55. const char *name)
  56. #else
  57. void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
  58. #endif
  59. {
  60. size_t tot_size = sizeof(struct devres) + size;
  61. struct devres *dr;
  62. dr = kmalloc(tot_size, gfp);
  63. if (unlikely(!dr))
  64. return NULL;
  65. INIT_LIST_HEAD(&dr->entry);
  66. dr->release = release;
  67. set_node_dbginfo(dr, name, size);
  68. return dr->data;
  69. }
  70. void devres_free(void *res)
  71. {
  72. if (res) {
  73. struct devres *dr = container_of(res, struct devres, data);
  74. BUG_ON(!list_empty(&dr->entry));
  75. kfree(dr);
  76. }
  77. }
  78. void devres_add(struct udevice *dev, void *res)
  79. {
  80. struct devres *dr = container_of(res, struct devres, data);
  81. devres_log(dev, dr, "ADD");
  82. BUG_ON(!list_empty(&dr->entry));
  83. dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
  84. list_add_tail(&dr->entry, &dev->devres_head);
  85. }
  86. void *devres_find(struct udevice *dev, dr_release_t release,
  87. dr_match_t match, void *match_data)
  88. {
  89. struct devres *dr;
  90. list_for_each_entry_reverse(dr, &dev->devres_head, entry) {
  91. if (dr->release != release)
  92. continue;
  93. if (match && !match(dev, dr->data, match_data))
  94. continue;
  95. return dr->data;
  96. }
  97. return NULL;
  98. }
  99. void *devres_get(struct udevice *dev, void *new_res,
  100. dr_match_t match, void *match_data)
  101. {
  102. struct devres *new_dr = container_of(new_res, struct devres, data);
  103. void *res;
  104. res = devres_find(dev, new_dr->release, match, match_data);
  105. if (!res) {
  106. devres_add(dev, new_res);
  107. res = new_res;
  108. new_res = NULL;
  109. }
  110. devres_free(new_res);
  111. return res;
  112. }
  113. void *devres_remove(struct udevice *dev, dr_release_t release,
  114. dr_match_t match, void *match_data)
  115. {
  116. void *res;
  117. res = devres_find(dev, release, match, match_data);
  118. if (res) {
  119. struct devres *dr = container_of(res, struct devres, data);
  120. list_del_init(&dr->entry);
  121. devres_log(dev, dr, "REM");
  122. }
  123. return res;
  124. }
  125. int devres_destroy(struct udevice *dev, dr_release_t release,
  126. dr_match_t match, void *match_data)
  127. {
  128. void *res;
  129. res = devres_remove(dev, release, match, match_data);
  130. if (unlikely(!res))
  131. return -ENOENT;
  132. devres_free(res);
  133. return 0;
  134. }
  135. int devres_release(struct udevice *dev, dr_release_t release,
  136. dr_match_t match, void *match_data)
  137. {
  138. void *res;
  139. res = devres_remove(dev, release, match, match_data);
  140. if (unlikely(!res))
  141. return -ENOENT;
  142. (*release)(dev, res);
  143. devres_free(res);
  144. return 0;
  145. }
  146. static void release_nodes(struct udevice *dev, struct list_head *head,
  147. bool probe_only)
  148. {
  149. struct devres *dr, *tmp;
  150. list_for_each_entry_safe_reverse(dr, tmp, head, entry) {
  151. if (probe_only && !dr->probe)
  152. break;
  153. devres_log(dev, dr, "REL");
  154. dr->release(dev, dr->data);
  155. list_del(&dr->entry);
  156. kfree(dr);
  157. }
  158. }
  159. void devres_release_probe(struct udevice *dev)
  160. {
  161. release_nodes(dev, &dev->devres_head, true);
  162. }
  163. void devres_release_all(struct udevice *dev)
  164. {
  165. release_nodes(dev, &dev->devres_head, false);
  166. }
  167. #ifdef CONFIG_DEBUG_DEVRES
  168. static void dump_resources(struct udevice *dev, int depth)
  169. {
  170. struct devres *dr;
  171. struct udevice *child;
  172. printf("- %s\n", dev->name);
  173. list_for_each_entry(dr, &dev->devres_head, entry)
  174. printf(" %p (%lu byte) %s %s\n", dr,
  175. (unsigned long)dr->size, dr->name,
  176. dr->probe ? "PROBE" : "BIND");
  177. list_for_each_entry(child, &dev->child_head, sibling_node)
  178. dump_resources(child, depth + 1);
  179. }
  180. void dm_dump_devres(void)
  181. {
  182. struct udevice *root;
  183. root = dm_root();
  184. if (root)
  185. dump_resources(root, 0);
  186. }
  187. #endif
  188. /*
  189. * Managed kmalloc/kfree
  190. */
  191. static void devm_kmalloc_release(struct udevice *dev, void *res)
  192. {
  193. /* noop */
  194. }
  195. static int devm_kmalloc_match(struct udevice *dev, void *res, void *data)
  196. {
  197. return res == data;
  198. }
  199. void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
  200. {
  201. void *data;
  202. data = _devres_alloc(devm_kmalloc_release, size, gfp);
  203. if (unlikely(!data))
  204. return NULL;
  205. devres_add(dev, data);
  206. return data;
  207. }
  208. void devm_kfree(struct udevice *dev, void *p)
  209. {
  210. int rc;
  211. rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
  212. WARN_ON(rc);
  213. }