Merge remote-tracking branch 'qemu-project/master'
[qemu/ar7.git] / hw / core / resettable.c
blob6dd3e3dc487eb4180ec29003c64ae5150599794f
1 /*
2 * Resettable interface.
4 * Copyright (c) 2019 GreenSocs SAS
6 * Authors:
7 * Damien Hedde
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.
13 #include "qemu/osdep.h"
14 #include "qemu/module.h"
15 #include "hw/resettable.h"
16 #include "trace.h"
18 /**
19 * resettable_phase_enter/hold/exit:
20 * Function executing a phase recursively in a resettable object and its
21 * children.
23 static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
24 static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
25 static void resettable_phase_exit(Object *obj, void *opaque, ResetType type);
27 /**
28 * enter_phase_in_progress:
29 * True if we are currently in reset enter phase.
31 * exit_phase_in_progress:
32 * count the number of exit phase we are in.
34 * Note: These flags are only used to guarantee (using asserts) that the reset
35 * API is used correctly. We can use global variables because we rely on the
36 * iothread mutex to ensure only one reset operation is in a progress at a
37 * given time.
39 static bool enter_phase_in_progress;
40 static unsigned exit_phase_in_progress;
42 void resettable_reset(Object *obj, ResetType type)
44 trace_resettable_reset(obj, type);
45 resettable_assert_reset(obj, type);
46 resettable_release_reset(obj, type);
49 void resettable_assert_reset(Object *obj, ResetType type)
51 trace_resettable_reset_assert_begin(obj, type);
52 assert(!enter_phase_in_progress);
54 enter_phase_in_progress = true;
55 resettable_phase_enter(obj, NULL, type);
56 enter_phase_in_progress = false;
58 resettable_phase_hold(obj, NULL, type);
60 trace_resettable_reset_assert_end(obj);
63 void resettable_release_reset(Object *obj, ResetType type)
65 trace_resettable_reset_release_begin(obj, type);
66 assert(!enter_phase_in_progress);
68 exit_phase_in_progress += 1;
69 resettable_phase_exit(obj, NULL, type);
70 exit_phase_in_progress -= 1;
72 trace_resettable_reset_release_end(obj);
75 bool resettable_is_in_reset(Object *obj)
77 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
78 ResettableState *s = rc->get_state(obj);
80 return s->count > 0;
83 /**
84 * resettable_child_foreach:
85 * helper to avoid checking the existence of the method.
87 static void resettable_child_foreach(ResettableClass *rc, Object *obj,
88 ResettableChildCallback cb,
89 void *opaque, ResetType type)
91 if (rc->child_foreach) {
92 rc->child_foreach(obj, cb, opaque, type);
96 /**
97 * resettable_get_tr_func:
98 * helper to fetch transitional reset callback if any.
100 static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
101 Object *obj)
103 ResettableTrFunction tr_func = NULL;
104 if (rc->get_transitional_function) {
105 tr_func = rc->get_transitional_function(obj);
107 return tr_func;
110 static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
112 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
113 ResettableState *s = rc->get_state(obj);
114 const char *obj_typename = object_get_typename(obj);
115 bool action_needed = false;
117 /* exit phase has to finish properly before entering back in reset */
118 assert(!s->exit_phase_in_progress);
120 trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
122 /* Only take action if we really enter reset for the 1st time. */
124 * TODO: if adding more ResetType support, some additional checks
125 * are probably needed here.
127 if (s->count++ == 0) {
128 action_needed = true;
131 * We limit the count to an arbitrary "big" value. The value is big
132 * enough not to be triggered normally.
133 * The assert will stop an infinite loop if there is a cycle in the
134 * reset tree. The loop goes through resettable_foreach_child below
135 * which at some point will call us again.
137 assert(s->count <= 50);
140 * handle the children even if action_needed is at false so that
141 * child counts are incremented too
143 resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
145 /* execute enter phase for the object if needed */
146 if (action_needed) {
147 trace_resettable_phase_enter_exec(obj, obj_typename, type,
148 !!rc->phases.enter);
149 if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
150 rc->phases.enter(obj, type);
152 s->hold_phase_pending = true;
154 trace_resettable_phase_enter_end(obj, obj_typename, s->count);
157 static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
159 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
160 ResettableState *s = rc->get_state(obj);
161 const char *obj_typename = object_get_typename(obj);
163 /* exit phase has to finish properly before entering back in reset */
164 assert(!s->exit_phase_in_progress);
166 trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
168 /* handle children first */
169 resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
171 /* exec hold phase */
172 if (s->hold_phase_pending) {
173 s->hold_phase_pending = false;
174 ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
175 trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
176 if (tr_func) {
177 trace_resettable_transitional_function(obj, obj_typename);
178 tr_func(obj);
179 } else if (rc->phases.hold) {
180 rc->phases.hold(obj, type);
183 trace_resettable_phase_hold_end(obj, obj_typename, s->count);
186 static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
188 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
189 ResettableState *s = rc->get_state(obj);
190 const char *obj_typename = object_get_typename(obj);
192 assert(!s->exit_phase_in_progress);
193 trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
195 /* exit_phase_in_progress ensures this phase is 'atomic' */
196 s->exit_phase_in_progress = true;
197 resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
199 assert(s->count > 0);
200 if (--s->count == 0) {
201 trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
202 if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
203 rc->phases.exit(obj, type);
206 s->exit_phase_in_progress = false;
207 trace_resettable_phase_exit_end(obj, obj_typename, s->count);
211 * resettable_get_count:
212 * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
214 static unsigned resettable_get_count(Object *obj)
216 if (obj) {
217 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
218 return rc->get_state(obj)->count;
220 return 0;
223 void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
225 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
226 ResettableState *s = rc->get_state(obj);
227 unsigned newp_count = resettable_get_count(newp);
228 unsigned oldp_count = resettable_get_count(oldp);
231 * Ensure we do not change parent when in enter or exit phase.
232 * During these phases, the reset subtree being updated is partly in reset
233 * and partly not in reset (it depends on the actual position in
234 * resettable_child_foreach()s). We are not able to tell in which part is a
235 * leaving or arriving device. Thus we cannot set the reset count of the
236 * moving device to the proper value.
238 assert(!enter_phase_in_progress && !exit_phase_in_progress);
239 trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
242 * At most one of the two 'for' loops will be executed below
243 * in order to cope with the difference between the two counts.
245 /* if newp is more reset than oldp */
246 for (unsigned i = oldp_count; i < newp_count; i++) {
247 resettable_assert_reset(obj, RESET_TYPE_COLD);
250 * if obj is leaving a bus under reset, we need to ensure
251 * hold phase is not pending.
253 if (oldp_count && s->hold_phase_pending) {
254 resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
256 /* if oldp is more reset than newp */
257 for (unsigned i = newp_count; i < oldp_count; i++) {
258 resettable_release_reset(obj, RESET_TYPE_COLD);
262 void resettable_cold_reset_fn(void *opaque)
264 resettable_reset((Object *) opaque, RESET_TYPE_COLD);
267 void resettable_class_set_parent_phases(ResettableClass *rc,
268 ResettableEnterPhase enter,
269 ResettableHoldPhase hold,
270 ResettableExitPhase exit,
271 ResettablePhases *parent_phases)
273 *parent_phases = rc->phases;
274 if (enter) {
275 rc->phases.enter = enter;
277 if (hold) {
278 rc->phases.hold = hold;
280 if (exit) {
281 rc->phases.exit = exit;
285 static const TypeInfo resettable_interface_info = {
286 .name = TYPE_RESETTABLE_INTERFACE,
287 .parent = TYPE_INTERFACE,
288 .class_size = sizeof(ResettableClass),
291 static void reset_register_types(void)
293 type_register_static(&resettable_interface_info);
296 type_init(reset_register_types)