devres.c 5.4 KB

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