resettable.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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. trace_resettable_reset_assert_begin(obj, type);
  47. assert(!enter_phase_in_progress);
  48. enter_phase_in_progress = true;
  49. resettable_phase_enter(obj, NULL, type);
  50. enter_phase_in_progress = false;
  51. resettable_phase_hold(obj, NULL, type);
  52. trace_resettable_reset_assert_end(obj);
  53. }
  54. void resettable_release_reset(Object *obj, ResetType type)
  55. {
  56. trace_resettable_reset_release_begin(obj, type);
  57. assert(!enter_phase_in_progress);
  58. exit_phase_in_progress += 1;
  59. resettable_phase_exit(obj, NULL, type);
  60. exit_phase_in_progress -= 1;
  61. trace_resettable_reset_release_end(obj);
  62. }
  63. bool resettable_is_in_reset(Object *obj)
  64. {
  65. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  66. ResettableState *s = rc->get_state(obj);
  67. return s->count > 0;
  68. }
  69. /**
  70. * resettable_child_foreach:
  71. * helper to avoid checking the existence of the method.
  72. */
  73. static void resettable_child_foreach(ResettableClass *rc, Object *obj,
  74. ResettableChildCallback cb,
  75. void *opaque, ResetType type)
  76. {
  77. if (rc->child_foreach) {
  78. rc->child_foreach(obj, cb, opaque, type);
  79. }
  80. }
  81. static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
  82. {
  83. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  84. ResettableState *s = rc->get_state(obj);
  85. const char *obj_typename = object_get_typename(obj);
  86. bool action_needed = false;
  87. /* exit phase has to finish properly before entering back in reset */
  88. assert(!s->exit_phase_in_progress);
  89. trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
  90. /* Only take action if we really enter reset for the 1st time. */
  91. /*
  92. * TODO: if adding more ResetType support, some additional checks
  93. * are probably needed here.
  94. */
  95. if (s->count++ == 0) {
  96. action_needed = true;
  97. }
  98. /*
  99. * We limit the count to an arbitrary "big" value. The value is big
  100. * enough not to be triggered normally.
  101. * The assert will stop an infinite loop if there is a cycle in the
  102. * reset tree. The loop goes through resettable_foreach_child below
  103. * which at some point will call us again.
  104. */
  105. assert(s->count <= 50);
  106. /*
  107. * handle the children even if action_needed is at false so that
  108. * child counts are incremented too
  109. */
  110. resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
  111. /* execute enter phase for the object if needed */
  112. if (action_needed) {
  113. trace_resettable_phase_enter_exec(obj, obj_typename, type,
  114. !!rc->phases.enter);
  115. if (rc->phases.enter) {
  116. rc->phases.enter(obj, type);
  117. }
  118. s->hold_phase_pending = true;
  119. }
  120. trace_resettable_phase_enter_end(obj, obj_typename, s->count);
  121. }
  122. static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
  123. {
  124. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  125. ResettableState *s = rc->get_state(obj);
  126. const char *obj_typename = object_get_typename(obj);
  127. /* exit phase has to finish properly before entering back in reset */
  128. assert(!s->exit_phase_in_progress);
  129. trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
  130. /* handle children first */
  131. resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
  132. /* exec hold phase */
  133. if (s->hold_phase_pending) {
  134. s->hold_phase_pending = false;
  135. trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
  136. if (rc->phases.hold) {
  137. rc->phases.hold(obj, type);
  138. }
  139. }
  140. trace_resettable_phase_hold_end(obj, obj_typename, s->count);
  141. }
  142. static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
  143. {
  144. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  145. ResettableState *s = rc->get_state(obj);
  146. const char *obj_typename = object_get_typename(obj);
  147. assert(!s->exit_phase_in_progress);
  148. trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
  149. /* exit_phase_in_progress ensures this phase is 'atomic' */
  150. s->exit_phase_in_progress = true;
  151. resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
  152. assert(s->count > 0);
  153. if (--s->count == 0) {
  154. trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
  155. if (rc->phases.exit) {
  156. rc->phases.exit(obj, type);
  157. }
  158. }
  159. s->exit_phase_in_progress = false;
  160. trace_resettable_phase_exit_end(obj, obj_typename, s->count);
  161. }
  162. /*
  163. * resettable_get_count:
  164. * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
  165. */
  166. static unsigned resettable_get_count(Object *obj)
  167. {
  168. if (obj) {
  169. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  170. return rc->get_state(obj)->count;
  171. }
  172. return 0;
  173. }
  174. void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
  175. {
  176. ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
  177. ResettableState *s = rc->get_state(obj);
  178. unsigned newp_count = resettable_get_count(newp);
  179. unsigned oldp_count = resettable_get_count(oldp);
  180. /*
  181. * Ensure we do not change parent when in enter or exit phase.
  182. * During these phases, the reset subtree being updated is partly in reset
  183. * and partly not in reset (it depends on the actual position in
  184. * resettable_child_foreach()s). We are not able to tell in which part is a
  185. * leaving or arriving device. Thus we cannot set the reset count of the
  186. * moving device to the proper value.
  187. */
  188. assert(!enter_phase_in_progress && !exit_phase_in_progress);
  189. trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
  190. /*
  191. * At most one of the two 'for' loops will be executed below
  192. * in order to cope with the difference between the two counts.
  193. */
  194. /* if newp is more reset than oldp */
  195. for (unsigned i = oldp_count; i < newp_count; i++) {
  196. resettable_assert_reset(obj, RESET_TYPE_COLD);
  197. }
  198. /*
  199. * if obj is leaving a bus under reset, we need to ensure
  200. * hold phase is not pending.
  201. */
  202. if (oldp_count && s->hold_phase_pending) {
  203. resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
  204. }
  205. /* if oldp is more reset than newp */
  206. for (unsigned i = newp_count; i < oldp_count; i++) {
  207. resettable_release_reset(obj, RESET_TYPE_COLD);
  208. }
  209. }
  210. void resettable_cold_reset_fn(void *opaque)
  211. {
  212. resettable_reset((Object *) opaque, RESET_TYPE_COLD);
  213. }
  214. void resettable_class_set_parent_phases(ResettableClass *rc,
  215. ResettableEnterPhase enter,
  216. ResettableHoldPhase hold,
  217. ResettableExitPhase exit,
  218. ResettablePhases *parent_phases)
  219. {
  220. *parent_phases = rc->phases;
  221. if (enter) {
  222. rc->phases.enter = enter;
  223. }
  224. if (hold) {
  225. rc->phases.hold = hold;
  226. }
  227. if (exit) {
  228. rc->phases.exit = exit;
  229. }
  230. }
  231. static const TypeInfo resettable_interface_info = {
  232. .name = TYPE_RESETTABLE_INTERFACE,
  233. .parent = TYPE_INTERFACE,
  234. .class_size = sizeof(ResettableClass),
  235. };
  236. static void reset_register_types(void)
  237. {
  238. type_register_static(&resettable_interface_info);
  239. }
  240. type_init(reset_register_types)