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