2
0

resettable.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * Resettable interface.
  3. *
  4. * Copyright (c) 2019 GreenSocs SAS
  5. *
  6. * Authors:
  7. * Damien Hedde
  8. *
  9. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10. * See the COPYING file in the top-level directory.
  11. */
  12. #include "qemu/osdep.h"
  13. #include "qemu/module.h"
  14. #include "hw/resettable.h"
  15. #include "trace.h"
  16. /**
  17. * resettable_phase_enter/hold/exit:
  18. * Function executing a phase recursively in a resettable object and its
  19. * children.
  20. */
  21. static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
  22. static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
  23. static void resettable_phase_exit(Object *obj, void *opaque, ResetType type);
  24. /**
  25. * enter_phase_in_progress:
  26. * True if we are currently in reset enter phase.
  27. *
  28. * exit_phase_in_progress:
  29. * count the number of exit phase we are in.
  30. *
  31. * Note: These flags are only used to guarantee (using asserts) that the reset
  32. * API is used correctly. We can use global variables because we rely on the
  33. * iothread mutex to ensure only one reset operation is in a progress at a
  34. * given time.
  35. */
  36. static bool enter_phase_in_progress;
  37. static unsigned exit_phase_in_progress;
  38. void resettable_reset(Object *obj, ResetType type)
  39. {
  40. trace_resettable_reset(obj, type);
  41. resettable_assert_reset(obj, type);
  42. resettable_release_reset(obj, type);
  43. }
  44. void resettable_assert_reset(Object *obj, ResetType type)
  45. {
  46. /* TODO: change this assert when adding support for other reset types */
  47. assert(type == RESET_TYPE_COLD);
  48. trace_resettable_reset_assert_begin(obj, type);
  49. assert(!enter_phase_in_progress);
  50. enter_phase_in_progress = true;
  51. resettable_phase_enter(obj, NULL, type);
  52. enter_phase_in_progress = false;
  53. resettable_phase_hold(obj, NULL, type);
  54. trace_resettable_reset_assert_end(obj);
  55. }
  56. void resettable_release_reset(Object *obj, ResetType type)
  57. {
  58. /* TODO: change this assert when adding support for other reset types */
  59. assert(type == RESET_TYPE_COLD);
  60. trace_resettable_reset_release_begin(obj, type);
  61. assert(!enter_phase_in_progress);
  62. exit_phase_in_progress += 1;
  63. resettable_phase_exit(obj, NULL, type);
  64. exit_phase_in_progress -= 1;
  65. trace_resettable_reset_release_end(obj);
  66. }
  67. bool resettable_is_in_reset(Object *obj)
  68. {
  69. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  70. ResettableState *s = rc->get_state(obj);
  71. return s->count > 0;
  72. }
  73. /**
  74. * resettable_child_foreach:
  75. * helper to avoid checking the existence of the method.
  76. */
  77. static void resettable_child_foreach(ResettableClass *rc, Object *obj,
  78. ResettableChildCallback cb,
  79. void *opaque, ResetType type)
  80. {
  81. if (rc->child_foreach) {
  82. rc->child_foreach(obj, cb, opaque, type);
  83. }
  84. }
  85. /**
  86. * resettable_get_tr_func:
  87. * helper to fetch transitional reset callback if any.
  88. */
  89. static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
  90. Object *obj)
  91. {
  92. ResettableTrFunction tr_func = NULL;
  93. if (rc->get_transitional_function) {
  94. tr_func = rc->get_transitional_function(obj);
  95. }
  96. return tr_func;
  97. }
  98. static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
  99. {
  100. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  101. ResettableState *s = rc->get_state(obj);
  102. const char *obj_typename = object_get_typename(obj);
  103. bool action_needed = false;
  104. /* exit phase has to finish properly before entering back in reset */
  105. assert(!s->exit_phase_in_progress);
  106. trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
  107. /* Only take action if we really enter reset for the 1st time. */
  108. /*
  109. * TODO: if adding more ResetType support, some additional checks
  110. * are probably needed here.
  111. */
  112. if (s->count++ == 0) {
  113. action_needed = true;
  114. }
  115. /*
  116. * We limit the count to an arbitrary "big" value. The value is big
  117. * enough not to be triggered normally.
  118. * The assert will stop an infinite loop if there is a cycle in the
  119. * reset tree. The loop goes through resettable_foreach_child below
  120. * which at some point will call us again.
  121. */
  122. assert(s->count <= 50);
  123. /*
  124. * handle the children even if action_needed is at false so that
  125. * child counts are incremented too
  126. */
  127. resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
  128. /* execute enter phase for the object if needed */
  129. if (action_needed) {
  130. trace_resettable_phase_enter_exec(obj, obj_typename, type,
  131. !!rc->phases.enter);
  132. if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
  133. rc->phases.enter(obj, type);
  134. }
  135. s->hold_phase_pending = true;
  136. }
  137. trace_resettable_phase_enter_end(obj, obj_typename, s->count);
  138. }
  139. static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
  140. {
  141. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  142. ResettableState *s = rc->get_state(obj);
  143. const char *obj_typename = object_get_typename(obj);
  144. /* exit phase has to finish properly before entering back in reset */
  145. assert(!s->exit_phase_in_progress);
  146. trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
  147. /* handle children first */
  148. resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
  149. /* exec hold phase */
  150. if (s->hold_phase_pending) {
  151. s->hold_phase_pending = false;
  152. ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
  153. trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
  154. if (tr_func) {
  155. trace_resettable_transitional_function(obj, obj_typename);
  156. tr_func(obj);
  157. } else if (rc->phases.hold) {
  158. rc->phases.hold(obj);
  159. }
  160. }
  161. trace_resettable_phase_hold_end(obj, obj_typename, s->count);
  162. }
  163. static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
  164. {
  165. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  166. ResettableState *s = rc->get_state(obj);
  167. const char *obj_typename = object_get_typename(obj);
  168. assert(!s->exit_phase_in_progress);
  169. trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
  170. /* exit_phase_in_progress ensures this phase is 'atomic' */
  171. s->exit_phase_in_progress = true;
  172. resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
  173. assert(s->count > 0);
  174. if (s->count == 1) {
  175. trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
  176. if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
  177. rc->phases.exit(obj);
  178. }
  179. s->count = 0;
  180. }
  181. s->exit_phase_in_progress = false;
  182. trace_resettable_phase_exit_end(obj, obj_typename, s->count);
  183. }
  184. /*
  185. * resettable_get_count:
  186. * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
  187. */
  188. static unsigned resettable_get_count(Object *obj)
  189. {
  190. if (obj) {
  191. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  192. return rc->get_state(obj)->count;
  193. }
  194. return 0;
  195. }
  196. void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
  197. {
  198. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  199. ResettableState *s = rc->get_state(obj);
  200. unsigned newp_count = resettable_get_count(newp);
  201. unsigned oldp_count = resettable_get_count(oldp);
  202. /*
  203. * Ensure we do not change parent when in enter or exit phase.
  204. * During these phases, the reset subtree being updated is partly in reset
  205. * and partly not in reset (it depends on the actual position in
  206. * resettable_child_foreach()s). We are not able to tell in which part is a
  207. * leaving or arriving device. Thus we cannot set the reset count of the
  208. * moving device to the proper value.
  209. */
  210. assert(!enter_phase_in_progress && !exit_phase_in_progress);
  211. trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
  212. /*
  213. * At most one of the two 'for' loops will be executed below
  214. * in order to cope with the difference between the two counts.
  215. */
  216. /* if newp is more reset than oldp */
  217. for (unsigned i = oldp_count; i < newp_count; i++) {
  218. resettable_assert_reset(obj, RESET_TYPE_COLD);
  219. }
  220. /*
  221. * if obj is leaving a bus under reset, we need to ensure
  222. * hold phase is not pending.
  223. */
  224. if (oldp_count && s->hold_phase_pending) {
  225. resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
  226. }
  227. /* if oldp is more reset than newp */
  228. for (unsigned i = newp_count; i < oldp_count; i++) {
  229. resettable_release_reset(obj, RESET_TYPE_COLD);
  230. }
  231. }
  232. void resettable_cold_reset_fn(void *opaque)
  233. {
  234. resettable_reset((Object *) opaque, RESET_TYPE_COLD);
  235. }
  236. void resettable_class_set_parent_phases(ResettableClass *rc,
  237. ResettableEnterPhase enter,
  238. ResettableHoldPhase hold,
  239. ResettableExitPhase exit,
  240. ResettablePhases *parent_phases)
  241. {
  242. *parent_phases = rc->phases;
  243. if (enter) {
  244. rc->phases.enter = enter;
  245. }
  246. if (hold) {
  247. rc->phases.hold = hold;
  248. }
  249. if (exit) {
  250. rc->phases.exit = exit;
  251. }
  252. }
  253. static const TypeInfo resettable_interface_info = {
  254. .name = TYPE_RESETTABLE_INTERFACE,
  255. .parent = TYPE_INTERFACE,
  256. .class_size = sizeof(ResettableClass),
  257. };
  258. static void reset_register_types(void)
  259. {
  260. type_register_static(&resettable_interface_info);
  261. }
  262. type_init(reset_register_types)