xref: /qemu/hw/core/resettable.c (revision 631f46d4)
1bc5a39bfSDamien Hedde /*
2bc5a39bfSDamien Hedde  * Resettable interface.
3bc5a39bfSDamien Hedde  *
4bc5a39bfSDamien Hedde  * Copyright (c) 2019 GreenSocs SAS
5bc5a39bfSDamien Hedde  *
6bc5a39bfSDamien Hedde  * Authors:
7bc5a39bfSDamien Hedde  *   Damien Hedde
8bc5a39bfSDamien Hedde  *
9bc5a39bfSDamien Hedde  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10bc5a39bfSDamien Hedde  * See the COPYING file in the top-level directory.
11bc5a39bfSDamien Hedde  */
12bc5a39bfSDamien Hedde 
13bc5a39bfSDamien Hedde #include "qemu/osdep.h"
14bc5a39bfSDamien Hedde #include "qemu/module.h"
15bc5a39bfSDamien Hedde #include "hw/resettable.h"
16bc5a39bfSDamien Hedde #include "trace.h"
17bc5a39bfSDamien Hedde 
18bc5a39bfSDamien Hedde /**
19bc5a39bfSDamien Hedde  * resettable_phase_enter/hold/exit:
20bc5a39bfSDamien Hedde  * Function executing a phase recursively in a resettable object and its
21bc5a39bfSDamien Hedde  * children.
22bc5a39bfSDamien Hedde  */
23bc5a39bfSDamien Hedde static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
24bc5a39bfSDamien Hedde static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
25bc5a39bfSDamien Hedde static void resettable_phase_exit(Object *obj, void *opaque, ResetType type);
26bc5a39bfSDamien Hedde 
27bc5a39bfSDamien Hedde /**
28bc5a39bfSDamien Hedde  * enter_phase_in_progress:
29bc5a39bfSDamien Hedde  * True if we are currently in reset enter phase.
30bc5a39bfSDamien Hedde  *
31614f731aSDamien Hedde  * exit_phase_in_progress:
32614f731aSDamien Hedde  * count the number of exit phase we are in.
33614f731aSDamien Hedde  *
34614f731aSDamien Hedde  * Note: These flags are only used to guarantee (using asserts) that the reset
35614f731aSDamien Hedde  * API is used correctly. We can use global variables because we rely on the
36bc5a39bfSDamien Hedde  * iothread mutex to ensure only one reset operation is in a progress at a
37bc5a39bfSDamien Hedde  * given time.
38bc5a39bfSDamien Hedde  */
39bc5a39bfSDamien Hedde static bool enter_phase_in_progress;
40614f731aSDamien Hedde static unsigned exit_phase_in_progress;
41bc5a39bfSDamien Hedde 
resettable_reset(Object * obj,ResetType type)42bc5a39bfSDamien Hedde void resettable_reset(Object *obj, ResetType type)
43bc5a39bfSDamien Hedde {
44bc5a39bfSDamien Hedde     trace_resettable_reset(obj, type);
45bc5a39bfSDamien Hedde     resettable_assert_reset(obj, type);
46bc5a39bfSDamien Hedde     resettable_release_reset(obj, type);
47bc5a39bfSDamien Hedde }
48bc5a39bfSDamien Hedde 
resettable_assert_reset(Object * obj,ResetType type)49bc5a39bfSDamien Hedde void resettable_assert_reset(Object *obj, ResetType type)
50bc5a39bfSDamien Hedde {
51bc5a39bfSDamien Hedde     trace_resettable_reset_assert_begin(obj, type);
52bc5a39bfSDamien Hedde     assert(!enter_phase_in_progress);
53bc5a39bfSDamien Hedde 
54bc5a39bfSDamien Hedde     enter_phase_in_progress = true;
55bc5a39bfSDamien Hedde     resettable_phase_enter(obj, NULL, type);
56bc5a39bfSDamien Hedde     enter_phase_in_progress = false;
57bc5a39bfSDamien Hedde 
58bc5a39bfSDamien Hedde     resettable_phase_hold(obj, NULL, type);
59bc5a39bfSDamien Hedde 
60bc5a39bfSDamien Hedde     trace_resettable_reset_assert_end(obj);
61bc5a39bfSDamien Hedde }
62bc5a39bfSDamien Hedde 
resettable_release_reset(Object * obj,ResetType type)63bc5a39bfSDamien Hedde void resettable_release_reset(Object *obj, ResetType type)
64bc5a39bfSDamien Hedde {
65bc5a39bfSDamien Hedde     trace_resettable_reset_release_begin(obj, type);
66bc5a39bfSDamien Hedde     assert(!enter_phase_in_progress);
67bc5a39bfSDamien Hedde 
68614f731aSDamien Hedde     exit_phase_in_progress += 1;
69bc5a39bfSDamien Hedde     resettable_phase_exit(obj, NULL, type);
70614f731aSDamien Hedde     exit_phase_in_progress -= 1;
71bc5a39bfSDamien Hedde 
72bc5a39bfSDamien Hedde     trace_resettable_reset_release_end(obj);
73bc5a39bfSDamien Hedde }
74bc5a39bfSDamien Hedde 
resettable_is_in_reset(Object * obj)75bc5a39bfSDamien Hedde bool resettable_is_in_reset(Object *obj)
76bc5a39bfSDamien Hedde {
77bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
78bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
79bc5a39bfSDamien Hedde 
80bc5a39bfSDamien Hedde     return s->count > 0;
81bc5a39bfSDamien Hedde }
82bc5a39bfSDamien Hedde 
83bc5a39bfSDamien Hedde /**
84bc5a39bfSDamien Hedde  * resettable_child_foreach:
85bc5a39bfSDamien Hedde  * helper to avoid checking the existence of the method.
86bc5a39bfSDamien Hedde  */
resettable_child_foreach(ResettableClass * rc,Object * obj,ResettableChildCallback cb,void * opaque,ResetType type)87bc5a39bfSDamien Hedde static void resettable_child_foreach(ResettableClass *rc, Object *obj,
88bc5a39bfSDamien Hedde                                      ResettableChildCallback cb,
89bc5a39bfSDamien Hedde                                      void *opaque, ResetType type)
90bc5a39bfSDamien Hedde {
91bc5a39bfSDamien Hedde     if (rc->child_foreach) {
92bc5a39bfSDamien Hedde         rc->child_foreach(obj, cb, opaque, type);
93bc5a39bfSDamien Hedde     }
94bc5a39bfSDamien Hedde }
95bc5a39bfSDamien Hedde 
96bc5a39bfSDamien Hedde /**
97bc5a39bfSDamien Hedde  * resettable_get_tr_func:
98bc5a39bfSDamien Hedde  * helper to fetch transitional reset callback if any.
99bc5a39bfSDamien Hedde  */
resettable_get_tr_func(ResettableClass * rc,Object * obj)100bc5a39bfSDamien Hedde static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
101bc5a39bfSDamien Hedde                                                    Object *obj)
102bc5a39bfSDamien Hedde {
103bc5a39bfSDamien Hedde     ResettableTrFunction tr_func = NULL;
104bc5a39bfSDamien Hedde     if (rc->get_transitional_function) {
105bc5a39bfSDamien Hedde         tr_func = rc->get_transitional_function(obj);
106bc5a39bfSDamien Hedde     }
107bc5a39bfSDamien Hedde     return tr_func;
108bc5a39bfSDamien Hedde }
109bc5a39bfSDamien Hedde 
resettable_phase_enter(Object * obj,void * opaque,ResetType type)110bc5a39bfSDamien Hedde static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
111bc5a39bfSDamien Hedde {
112bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
113bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
114bc5a39bfSDamien Hedde     const char *obj_typename = object_get_typename(obj);
115bc5a39bfSDamien Hedde     bool action_needed = false;
116bc5a39bfSDamien Hedde 
117bc5a39bfSDamien Hedde     /* exit phase has to finish properly before entering back in reset */
118bc5a39bfSDamien Hedde     assert(!s->exit_phase_in_progress);
119bc5a39bfSDamien Hedde 
120bc5a39bfSDamien Hedde     trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
121bc5a39bfSDamien Hedde 
122bc5a39bfSDamien Hedde     /* Only take action if we really enter reset for the 1st time. */
123bc5a39bfSDamien Hedde     /*
124bc5a39bfSDamien Hedde      * TODO: if adding more ResetType support, some additional checks
125bc5a39bfSDamien Hedde      * are probably needed here.
126bc5a39bfSDamien Hedde      */
127bc5a39bfSDamien Hedde     if (s->count++ == 0) {
128bc5a39bfSDamien Hedde         action_needed = true;
129bc5a39bfSDamien Hedde     }
130bc5a39bfSDamien Hedde     /*
131bc5a39bfSDamien Hedde      * We limit the count to an arbitrary "big" value. The value is big
132bc5a39bfSDamien Hedde      * enough not to be triggered normally.
133bc5a39bfSDamien Hedde      * The assert will stop an infinite loop if there is a cycle in the
134bc5a39bfSDamien Hedde      * reset tree. The loop goes through resettable_foreach_child below
135bc5a39bfSDamien Hedde      * which at some point will call us again.
136bc5a39bfSDamien Hedde      */
137bc5a39bfSDamien Hedde     assert(s->count <= 50);
138bc5a39bfSDamien Hedde 
139bc5a39bfSDamien Hedde     /*
140bc5a39bfSDamien Hedde      * handle the children even if action_needed is at false so that
141bc5a39bfSDamien Hedde      * child counts are incremented too
142bc5a39bfSDamien Hedde      */
143bc5a39bfSDamien Hedde     resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
144bc5a39bfSDamien Hedde 
145bc5a39bfSDamien Hedde     /* execute enter phase for the object if needed */
146bc5a39bfSDamien Hedde     if (action_needed) {
147bc5a39bfSDamien Hedde         trace_resettable_phase_enter_exec(obj, obj_typename, type,
148bc5a39bfSDamien Hedde                                           !!rc->phases.enter);
149bc5a39bfSDamien Hedde         if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
150bc5a39bfSDamien Hedde             rc->phases.enter(obj, type);
151bc5a39bfSDamien Hedde         }
152bc5a39bfSDamien Hedde         s->hold_phase_pending = true;
153bc5a39bfSDamien Hedde     }
154bc5a39bfSDamien Hedde     trace_resettable_phase_enter_end(obj, obj_typename, s->count);
155bc5a39bfSDamien Hedde }
156bc5a39bfSDamien Hedde 
resettable_phase_hold(Object * obj,void * opaque,ResetType type)157bc5a39bfSDamien Hedde static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
158bc5a39bfSDamien Hedde {
159bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
160bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
161bc5a39bfSDamien Hedde     const char *obj_typename = object_get_typename(obj);
162bc5a39bfSDamien Hedde 
163bc5a39bfSDamien Hedde     /* exit phase has to finish properly before entering back in reset */
164bc5a39bfSDamien Hedde     assert(!s->exit_phase_in_progress);
165bc5a39bfSDamien Hedde 
166bc5a39bfSDamien Hedde     trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
167bc5a39bfSDamien Hedde 
168bc5a39bfSDamien Hedde     /* handle children first */
169bc5a39bfSDamien Hedde     resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
170bc5a39bfSDamien Hedde 
171bc5a39bfSDamien Hedde     /* exec hold phase */
172bc5a39bfSDamien Hedde     if (s->hold_phase_pending) {
173bc5a39bfSDamien Hedde         s->hold_phase_pending = false;
174bc5a39bfSDamien Hedde         ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
175bc5a39bfSDamien Hedde         trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
176bc5a39bfSDamien Hedde         if (tr_func) {
177bc5a39bfSDamien Hedde             trace_resettable_transitional_function(obj, obj_typename);
178bc5a39bfSDamien Hedde             tr_func(obj);
179bc5a39bfSDamien Hedde         } else if (rc->phases.hold) {
180ad80e367SPeter Maydell             rc->phases.hold(obj, type);
181bc5a39bfSDamien Hedde         }
182bc5a39bfSDamien Hedde     }
183bc5a39bfSDamien Hedde     trace_resettable_phase_hold_end(obj, obj_typename, s->count);
184bc5a39bfSDamien Hedde }
185bc5a39bfSDamien Hedde 
resettable_phase_exit(Object * obj,void * opaque,ResetType type)186bc5a39bfSDamien Hedde static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
187bc5a39bfSDamien Hedde {
188bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
189bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
190bc5a39bfSDamien Hedde     const char *obj_typename = object_get_typename(obj);
191bc5a39bfSDamien Hedde 
192bc5a39bfSDamien Hedde     assert(!s->exit_phase_in_progress);
193bc5a39bfSDamien Hedde     trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
194bc5a39bfSDamien Hedde 
195bc5a39bfSDamien Hedde     /* exit_phase_in_progress ensures this phase is 'atomic' */
196bc5a39bfSDamien Hedde     s->exit_phase_in_progress = true;
197bc5a39bfSDamien Hedde     resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
198bc5a39bfSDamien Hedde 
199bc5a39bfSDamien Hedde     assert(s->count > 0);
200310616d3SDamien Hedde     if (--s->count == 0) {
201bc5a39bfSDamien Hedde         trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
202bc5a39bfSDamien Hedde         if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
203ad80e367SPeter Maydell             rc->phases.exit(obj, type);
204bc5a39bfSDamien Hedde         }
205bc5a39bfSDamien Hedde     }
206bc5a39bfSDamien Hedde     s->exit_phase_in_progress = false;
207bc5a39bfSDamien Hedde     trace_resettable_phase_exit_end(obj, obj_typename, s->count);
208bc5a39bfSDamien Hedde }
209bc5a39bfSDamien Hedde 
210614f731aSDamien Hedde /*
211614f731aSDamien Hedde  * resettable_get_count:
212614f731aSDamien Hedde  * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
213614f731aSDamien Hedde  */
resettable_get_count(Object * obj)214614f731aSDamien Hedde static unsigned resettable_get_count(Object *obj)
215614f731aSDamien Hedde {
216614f731aSDamien Hedde     if (obj) {
217614f731aSDamien Hedde         ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
218614f731aSDamien Hedde         return rc->get_state(obj)->count;
219614f731aSDamien Hedde     }
220614f731aSDamien Hedde     return 0;
221614f731aSDamien Hedde }
222614f731aSDamien Hedde 
resettable_change_parent(Object * obj,Object * newp,Object * oldp)223614f731aSDamien Hedde void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
224614f731aSDamien Hedde {
225614f731aSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
226614f731aSDamien Hedde     ResettableState *s = rc->get_state(obj);
227614f731aSDamien Hedde     unsigned newp_count = resettable_get_count(newp);
228614f731aSDamien Hedde     unsigned oldp_count = resettable_get_count(oldp);
229614f731aSDamien Hedde 
230614f731aSDamien Hedde     /*
231614f731aSDamien Hedde      * Ensure we do not change parent when in enter or exit phase.
232614f731aSDamien Hedde      * During these phases, the reset subtree being updated is partly in reset
233614f731aSDamien Hedde      * and partly not in reset (it depends on the actual position in
234614f731aSDamien Hedde      * resettable_child_foreach()s). We are not able to tell in which part is a
235614f731aSDamien Hedde      * leaving or arriving device. Thus we cannot set the reset count of the
236614f731aSDamien Hedde      * moving device to the proper value.
237614f731aSDamien Hedde      */
238614f731aSDamien Hedde     assert(!enter_phase_in_progress && !exit_phase_in_progress);
239614f731aSDamien Hedde     trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
240614f731aSDamien Hedde 
241614f731aSDamien Hedde     /*
242614f731aSDamien Hedde      * At most one of the two 'for' loops will be executed below
243614f731aSDamien Hedde      * in order to cope with the difference between the two counts.
244614f731aSDamien Hedde      */
245614f731aSDamien Hedde     /* if newp is more reset than oldp */
246614f731aSDamien Hedde     for (unsigned i = oldp_count; i < newp_count; i++) {
247614f731aSDamien Hedde         resettable_assert_reset(obj, RESET_TYPE_COLD);
248614f731aSDamien Hedde     }
249614f731aSDamien Hedde     /*
250614f731aSDamien Hedde      * if obj is leaving a bus under reset, we need to ensure
251614f731aSDamien Hedde      * hold phase is not pending.
252614f731aSDamien Hedde      */
253614f731aSDamien Hedde     if (oldp_count && s->hold_phase_pending) {
254614f731aSDamien Hedde         resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
255614f731aSDamien Hedde     }
256614f731aSDamien Hedde     /* if oldp is more reset than newp */
257614f731aSDamien Hedde     for (unsigned i = newp_count; i < oldp_count; i++) {
258614f731aSDamien Hedde         resettable_release_reset(obj, RESET_TYPE_COLD);
259614f731aSDamien Hedde     }
260614f731aSDamien Hedde }
261614f731aSDamien Hedde 
resettable_cold_reset_fn(void * opaque)262abb89dbfSDamien Hedde void resettable_cold_reset_fn(void *opaque)
263abb89dbfSDamien Hedde {
264abb89dbfSDamien Hedde     resettable_reset((Object *) opaque, RESET_TYPE_COLD);
265abb89dbfSDamien Hedde }
266abb89dbfSDamien Hedde 
resettable_class_set_parent_phases(ResettableClass * rc,ResettableEnterPhase enter,ResettableHoldPhase hold,ResettableExitPhase exit,ResettablePhases * parent_phases)267bc5a39bfSDamien Hedde void resettable_class_set_parent_phases(ResettableClass *rc,
268bc5a39bfSDamien Hedde                                         ResettableEnterPhase enter,
269bc5a39bfSDamien Hedde                                         ResettableHoldPhase hold,
270bc5a39bfSDamien Hedde                                         ResettableExitPhase exit,
271bc5a39bfSDamien Hedde                                         ResettablePhases *parent_phases)
272bc5a39bfSDamien Hedde {
273bc5a39bfSDamien Hedde     *parent_phases = rc->phases;
274bc5a39bfSDamien Hedde     if (enter) {
275bc5a39bfSDamien Hedde         rc->phases.enter = enter;
276bc5a39bfSDamien Hedde     }
277bc5a39bfSDamien Hedde     if (hold) {
278bc5a39bfSDamien Hedde         rc->phases.hold = hold;
279bc5a39bfSDamien Hedde     }
280bc5a39bfSDamien Hedde     if (exit) {
281bc5a39bfSDamien Hedde         rc->phases.exit = exit;
282bc5a39bfSDamien Hedde     }
283bc5a39bfSDamien Hedde }
284bc5a39bfSDamien Hedde 
285bc5a39bfSDamien Hedde static const TypeInfo resettable_interface_info = {
286bc5a39bfSDamien Hedde     .name       = TYPE_RESETTABLE_INTERFACE,
287bc5a39bfSDamien Hedde     .parent     = TYPE_INTERFACE,
288bc5a39bfSDamien Hedde     .class_size = sizeof(ResettableClass),
289bc5a39bfSDamien Hedde };
290bc5a39bfSDamien Hedde 
reset_register_types(void)291bc5a39bfSDamien Hedde static void reset_register_types(void)
292bc5a39bfSDamien Hedde {
293bc5a39bfSDamien Hedde     type_register_static(&resettable_interface_info);
294bc5a39bfSDamien Hedde }
295bc5a39bfSDamien Hedde 
296bc5a39bfSDamien Hedde type_init(reset_register_types)
297