2 * Resettable interface.
4 * Copyright (c) 2019 GreenSocs SAS
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"
19 * resettable_phase_enter/hold/exit:
20 * Function executing a phase recursively in a resettable object and its
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
);
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
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 /* 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
);
56 enter_phase_in_progress
= true;
57 resettable_phase_enter(obj
, NULL
, type
);
58 enter_phase_in_progress
= false;
60 resettable_phase_hold(obj
, NULL
, type
);
62 trace_resettable_reset_assert_end(obj
);
65 void resettable_release_reset(Object
*obj
, ResetType type
)
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
);
72 exit_phase_in_progress
+= 1;
73 resettable_phase_exit(obj
, NULL
, type
);
74 exit_phase_in_progress
-= 1;
76 trace_resettable_reset_release_end(obj
);
79 bool resettable_is_in_reset(Object
*obj
)
81 ResettableClass
*rc
= RESETTABLE_GET_CLASS(obj
);
82 ResettableState
*s
= rc
->get_state(obj
);
88 * resettable_child_foreach:
89 * helper to avoid checking the existence of the method.
91 static void resettable_child_foreach(ResettableClass
*rc
, Object
*obj
,
92 ResettableChildCallback cb
,
93 void *opaque
, ResetType type
)
95 if (rc
->child_foreach
) {
96 rc
->child_foreach(obj
, cb
, opaque
, type
);
101 * resettable_get_tr_func:
102 * helper to fetch transitional reset callback if any.
104 static ResettableTrFunction
resettable_get_tr_func(ResettableClass
*rc
,
107 ResettableTrFunction tr_func
= NULL
;
108 if (rc
->get_transitional_function
) {
109 tr_func
= rc
->get_transitional_function(obj
);
114 static void resettable_phase_enter(Object
*obj
, void *opaque
, ResetType type
)
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;
121 /* exit phase has to finish properly before entering back in reset */
122 assert(!s
->exit_phase_in_progress
);
124 trace_resettable_phase_enter_begin(obj
, obj_typename
, s
->count
, type
);
126 /* Only take action if we really enter reset for the 1st time. */
128 * TODO: if adding more ResetType support, some additional checks
129 * are probably needed here.
131 if (s
->count
++ == 0) {
132 action_needed
= true;
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.
141 assert(s
->count
<= 50);
144 * handle the children even if action_needed is at false so that
145 * child counts are incremented too
147 resettable_child_foreach(rc
, obj
, resettable_phase_enter
, NULL
, type
);
149 /* execute enter phase for the object if needed */
151 trace_resettable_phase_enter_exec(obj
, obj_typename
, type
,
153 if (rc
->phases
.enter
&& !resettable_get_tr_func(rc
, obj
)) {
154 rc
->phases
.enter(obj
, type
);
156 s
->hold_phase_pending
= true;
158 trace_resettable_phase_enter_end(obj
, obj_typename
, s
->count
);
161 static void resettable_phase_hold(Object
*obj
, void *opaque
, ResetType type
)
163 ResettableClass
*rc
= RESETTABLE_GET_CLASS(obj
);
164 ResettableState
*s
= rc
->get_state(obj
);
165 const char *obj_typename
= object_get_typename(obj
);
167 /* exit phase has to finish properly before entering back in reset */
168 assert(!s
->exit_phase_in_progress
);
170 trace_resettable_phase_hold_begin(obj
, obj_typename
, s
->count
, type
);
172 /* handle children first */
173 resettable_child_foreach(rc
, obj
, resettable_phase_hold
, NULL
, type
);
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
);
181 trace_resettable_transitional_function(obj
, obj_typename
);
183 } else if (rc
->phases
.hold
) {
184 rc
->phases
.hold(obj
);
187 trace_resettable_phase_hold_end(obj
, obj_typename
, s
->count
);
190 static void resettable_phase_exit(Object
*obj
, void *opaque
, ResetType type
)
192 ResettableClass
*rc
= RESETTABLE_GET_CLASS(obj
);
193 ResettableState
*s
= rc
->get_state(obj
);
194 const char *obj_typename
= object_get_typename(obj
);
196 assert(!s
->exit_phase_in_progress
);
197 trace_resettable_phase_exit_begin(obj
, obj_typename
, s
->count
, type
);
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
);
203 assert(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
);
211 s
->exit_phase_in_progress
= false;
212 trace_resettable_phase_exit_end(obj
, obj_typename
, s
->count
);
216 * resettable_get_count:
217 * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
219 static unsigned resettable_get_count(Object
*obj
)
222 ResettableClass
*rc
= RESETTABLE_GET_CLASS(obj
);
223 return rc
->get_state(obj
)->count
;
228 void resettable_change_parent(Object
*obj
, Object
*newp
, Object
*oldp
)
230 ResettableClass
*rc
= RESETTABLE_GET_CLASS(obj
);
231 ResettableState
*s
= rc
->get_state(obj
);
232 unsigned newp_count
= resettable_get_count(newp
);
233 unsigned oldp_count
= resettable_get_count(oldp
);
236 * Ensure we do not change parent when in enter or exit phase.
237 * During these phases, the reset subtree being updated is partly in reset
238 * and partly not in reset (it depends on the actual position in
239 * resettable_child_foreach()s). We are not able to tell in which part is a
240 * leaving or arriving device. Thus we cannot set the reset count of the
241 * moving device to the proper value.
243 assert(!enter_phase_in_progress
&& !exit_phase_in_progress
);
244 trace_resettable_change_parent(obj
, oldp
, oldp_count
, newp
, newp_count
);
247 * At most one of the two 'for' loops will be executed below
248 * in order to cope with the difference between the two counts.
250 /* if newp is more reset than oldp */
251 for (unsigned i
= oldp_count
; i
< newp_count
; i
++) {
252 resettable_assert_reset(obj
, RESET_TYPE_COLD
);
255 * if obj is leaving a bus under reset, we need to ensure
256 * hold phase is not pending.
258 if (oldp_count
&& s
->hold_phase_pending
) {
259 resettable_phase_hold(obj
, NULL
, RESET_TYPE_COLD
);
261 /* if oldp is more reset than newp */
262 for (unsigned i
= newp_count
; i
< oldp_count
; i
++) {
263 resettable_release_reset(obj
, RESET_TYPE_COLD
);
267 void resettable_cold_reset_fn(void *opaque
)
269 resettable_reset((Object
*) opaque
, RESET_TYPE_COLD
);
272 void resettable_class_set_parent_phases(ResettableClass
*rc
,
273 ResettableEnterPhase enter
,
274 ResettableHoldPhase hold
,
275 ResettableExitPhase exit
,
276 ResettablePhases
*parent_phases
)
278 *parent_phases
= rc
->phases
;
280 rc
->phases
.enter
= enter
;
283 rc
->phases
.hold
= hold
;
286 rc
->phases
.exit
= exit
;
290 static const TypeInfo resettable_interface_info
= {
291 .name
= TYPE_RESETTABLE_INTERFACE
,
292 .parent
= TYPE_INTERFACE
,
293 .class_size
= sizeof(ResettableClass
),
296 static void reset_register_types(void)
298 type_register_static(&resettable_interface_info
);
301 type_init(reset_register_types
)