xref: /qemu/hw/core/resettable.c (revision ad80e367)
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 
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 
49bc5a39bfSDamien Hedde void resettable_assert_reset(Object *obj, ResetType type)
50bc5a39bfSDamien Hedde {
51bc5a39bfSDamien Hedde     /* TODO: change this assert when adding support for other reset types */
52bc5a39bfSDamien Hedde     assert(type == RESET_TYPE_COLD);
53bc5a39bfSDamien Hedde     trace_resettable_reset_assert_begin(obj, type);
54bc5a39bfSDamien Hedde     assert(!enter_phase_in_progress);
55bc5a39bfSDamien Hedde 
56bc5a39bfSDamien Hedde     enter_phase_in_progress = true;
57bc5a39bfSDamien Hedde     resettable_phase_enter(obj, NULL, type);
58bc5a39bfSDamien Hedde     enter_phase_in_progress = false;
59bc5a39bfSDamien Hedde 
60bc5a39bfSDamien Hedde     resettable_phase_hold(obj, NULL, type);
61bc5a39bfSDamien Hedde 
62bc5a39bfSDamien Hedde     trace_resettable_reset_assert_end(obj);
63bc5a39bfSDamien Hedde }
64bc5a39bfSDamien Hedde 
65bc5a39bfSDamien Hedde void resettable_release_reset(Object *obj, ResetType type)
66bc5a39bfSDamien Hedde {
67bc5a39bfSDamien Hedde     /* TODO: change this assert when adding support for other reset types */
68bc5a39bfSDamien Hedde     assert(type == RESET_TYPE_COLD);
69bc5a39bfSDamien Hedde     trace_resettable_reset_release_begin(obj, type);
70bc5a39bfSDamien Hedde     assert(!enter_phase_in_progress);
71bc5a39bfSDamien Hedde 
72614f731aSDamien Hedde     exit_phase_in_progress += 1;
73bc5a39bfSDamien Hedde     resettable_phase_exit(obj, NULL, type);
74614f731aSDamien Hedde     exit_phase_in_progress -= 1;
75bc5a39bfSDamien Hedde 
76bc5a39bfSDamien Hedde     trace_resettable_reset_release_end(obj);
77bc5a39bfSDamien Hedde }
78bc5a39bfSDamien Hedde 
79bc5a39bfSDamien Hedde bool resettable_is_in_reset(Object *obj)
80bc5a39bfSDamien Hedde {
81bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
82bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
83bc5a39bfSDamien Hedde 
84bc5a39bfSDamien Hedde     return s->count > 0;
85bc5a39bfSDamien Hedde }
86bc5a39bfSDamien Hedde 
87bc5a39bfSDamien Hedde /**
88bc5a39bfSDamien Hedde  * resettable_child_foreach:
89bc5a39bfSDamien Hedde  * helper to avoid checking the existence of the method.
90bc5a39bfSDamien Hedde  */
91bc5a39bfSDamien Hedde static void resettable_child_foreach(ResettableClass *rc, Object *obj,
92bc5a39bfSDamien Hedde                                      ResettableChildCallback cb,
93bc5a39bfSDamien Hedde                                      void *opaque, ResetType type)
94bc5a39bfSDamien Hedde {
95bc5a39bfSDamien Hedde     if (rc->child_foreach) {
96bc5a39bfSDamien Hedde         rc->child_foreach(obj, cb, opaque, type);
97bc5a39bfSDamien Hedde     }
98bc5a39bfSDamien Hedde }
99bc5a39bfSDamien Hedde 
100bc5a39bfSDamien Hedde /**
101bc5a39bfSDamien Hedde  * resettable_get_tr_func:
102bc5a39bfSDamien Hedde  * helper to fetch transitional reset callback if any.
103bc5a39bfSDamien Hedde  */
104bc5a39bfSDamien Hedde static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
105bc5a39bfSDamien Hedde                                                    Object *obj)
106bc5a39bfSDamien Hedde {
107bc5a39bfSDamien Hedde     ResettableTrFunction tr_func = NULL;
108bc5a39bfSDamien Hedde     if (rc->get_transitional_function) {
109bc5a39bfSDamien Hedde         tr_func = rc->get_transitional_function(obj);
110bc5a39bfSDamien Hedde     }
111bc5a39bfSDamien Hedde     return tr_func;
112bc5a39bfSDamien Hedde }
113bc5a39bfSDamien Hedde 
114bc5a39bfSDamien Hedde static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
115bc5a39bfSDamien Hedde {
116bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
117bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
118bc5a39bfSDamien Hedde     const char *obj_typename = object_get_typename(obj);
119bc5a39bfSDamien Hedde     bool action_needed = false;
120bc5a39bfSDamien Hedde 
121bc5a39bfSDamien Hedde     /* exit phase has to finish properly before entering back in reset */
122bc5a39bfSDamien Hedde     assert(!s->exit_phase_in_progress);
123bc5a39bfSDamien Hedde 
124bc5a39bfSDamien Hedde     trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
125bc5a39bfSDamien Hedde 
126bc5a39bfSDamien Hedde     /* Only take action if we really enter reset for the 1st time. */
127bc5a39bfSDamien Hedde     /*
128bc5a39bfSDamien Hedde      * TODO: if adding more ResetType support, some additional checks
129bc5a39bfSDamien Hedde      * are probably needed here.
130bc5a39bfSDamien Hedde      */
131bc5a39bfSDamien Hedde     if (s->count++ == 0) {
132bc5a39bfSDamien Hedde         action_needed = true;
133bc5a39bfSDamien Hedde     }
134bc5a39bfSDamien Hedde     /*
135bc5a39bfSDamien Hedde      * We limit the count to an arbitrary "big" value. The value is big
136bc5a39bfSDamien Hedde      * enough not to be triggered normally.
137bc5a39bfSDamien Hedde      * The assert will stop an infinite loop if there is a cycle in the
138bc5a39bfSDamien Hedde      * reset tree. The loop goes through resettable_foreach_child below
139bc5a39bfSDamien Hedde      * which at some point will call us again.
140bc5a39bfSDamien Hedde      */
141bc5a39bfSDamien Hedde     assert(s->count <= 50);
142bc5a39bfSDamien Hedde 
143bc5a39bfSDamien Hedde     /*
144bc5a39bfSDamien Hedde      * handle the children even if action_needed is at false so that
145bc5a39bfSDamien Hedde      * child counts are incremented too
146bc5a39bfSDamien Hedde      */
147bc5a39bfSDamien Hedde     resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
148bc5a39bfSDamien Hedde 
149bc5a39bfSDamien Hedde     /* execute enter phase for the object if needed */
150bc5a39bfSDamien Hedde     if (action_needed) {
151bc5a39bfSDamien Hedde         trace_resettable_phase_enter_exec(obj, obj_typename, type,
152bc5a39bfSDamien Hedde                                           !!rc->phases.enter);
153bc5a39bfSDamien Hedde         if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
154bc5a39bfSDamien Hedde             rc->phases.enter(obj, type);
155bc5a39bfSDamien Hedde         }
156bc5a39bfSDamien Hedde         s->hold_phase_pending = true;
157bc5a39bfSDamien Hedde     }
158bc5a39bfSDamien Hedde     trace_resettable_phase_enter_end(obj, obj_typename, s->count);
159bc5a39bfSDamien Hedde }
160bc5a39bfSDamien Hedde 
161bc5a39bfSDamien Hedde static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
162bc5a39bfSDamien Hedde {
163bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
164bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
165bc5a39bfSDamien Hedde     const char *obj_typename = object_get_typename(obj);
166bc5a39bfSDamien Hedde 
167bc5a39bfSDamien Hedde     /* exit phase has to finish properly before entering back in reset */
168bc5a39bfSDamien Hedde     assert(!s->exit_phase_in_progress);
169bc5a39bfSDamien Hedde 
170bc5a39bfSDamien Hedde     trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
171bc5a39bfSDamien Hedde 
172bc5a39bfSDamien Hedde     /* handle children first */
173bc5a39bfSDamien Hedde     resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
174bc5a39bfSDamien Hedde 
175bc5a39bfSDamien Hedde     /* exec hold phase */
176bc5a39bfSDamien Hedde     if (s->hold_phase_pending) {
177bc5a39bfSDamien Hedde         s->hold_phase_pending = false;
178bc5a39bfSDamien Hedde         ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
179bc5a39bfSDamien Hedde         trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
180bc5a39bfSDamien Hedde         if (tr_func) {
181bc5a39bfSDamien Hedde             trace_resettable_transitional_function(obj, obj_typename);
182bc5a39bfSDamien Hedde             tr_func(obj);
183bc5a39bfSDamien Hedde         } else if (rc->phases.hold) {
184ad80e367SPeter Maydell             rc->phases.hold(obj, type);
185bc5a39bfSDamien Hedde         }
186bc5a39bfSDamien Hedde     }
187bc5a39bfSDamien Hedde     trace_resettable_phase_hold_end(obj, obj_typename, s->count);
188bc5a39bfSDamien Hedde }
189bc5a39bfSDamien Hedde 
190bc5a39bfSDamien Hedde static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
191bc5a39bfSDamien Hedde {
192bc5a39bfSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
193bc5a39bfSDamien Hedde     ResettableState *s = rc->get_state(obj);
194bc5a39bfSDamien Hedde     const char *obj_typename = object_get_typename(obj);
195bc5a39bfSDamien Hedde 
196bc5a39bfSDamien Hedde     assert(!s->exit_phase_in_progress);
197bc5a39bfSDamien Hedde     trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
198bc5a39bfSDamien Hedde 
199bc5a39bfSDamien Hedde     /* exit_phase_in_progress ensures this phase is 'atomic' */
200bc5a39bfSDamien Hedde     s->exit_phase_in_progress = true;
201bc5a39bfSDamien Hedde     resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
202bc5a39bfSDamien Hedde 
203bc5a39bfSDamien Hedde     assert(s->count > 0);
204310616d3SDamien Hedde     if (--s->count == 0) {
205bc5a39bfSDamien Hedde         trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
206bc5a39bfSDamien Hedde         if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
207ad80e367SPeter Maydell             rc->phases.exit(obj, type);
208bc5a39bfSDamien Hedde         }
209bc5a39bfSDamien Hedde     }
210bc5a39bfSDamien Hedde     s->exit_phase_in_progress = false;
211bc5a39bfSDamien Hedde     trace_resettable_phase_exit_end(obj, obj_typename, s->count);
212bc5a39bfSDamien Hedde }
213bc5a39bfSDamien Hedde 
214614f731aSDamien Hedde /*
215614f731aSDamien Hedde  * resettable_get_count:
216614f731aSDamien Hedde  * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
217614f731aSDamien Hedde  */
218614f731aSDamien Hedde static unsigned resettable_get_count(Object *obj)
219614f731aSDamien Hedde {
220614f731aSDamien Hedde     if (obj) {
221614f731aSDamien Hedde         ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
222614f731aSDamien Hedde         return rc->get_state(obj)->count;
223614f731aSDamien Hedde     }
224614f731aSDamien Hedde     return 0;
225614f731aSDamien Hedde }
226614f731aSDamien Hedde 
227614f731aSDamien Hedde void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
228614f731aSDamien Hedde {
229614f731aSDamien Hedde     ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
230614f731aSDamien Hedde     ResettableState *s = rc->get_state(obj);
231614f731aSDamien Hedde     unsigned newp_count = resettable_get_count(newp);
232614f731aSDamien Hedde     unsigned oldp_count = resettable_get_count(oldp);
233614f731aSDamien Hedde 
234614f731aSDamien Hedde     /*
235614f731aSDamien Hedde      * Ensure we do not change parent when in enter or exit phase.
236614f731aSDamien Hedde      * During these phases, the reset subtree being updated is partly in reset
237614f731aSDamien Hedde      * and partly not in reset (it depends on the actual position in
238614f731aSDamien Hedde      * resettable_child_foreach()s). We are not able to tell in which part is a
239614f731aSDamien Hedde      * leaving or arriving device. Thus we cannot set the reset count of the
240614f731aSDamien Hedde      * moving device to the proper value.
241614f731aSDamien Hedde      */
242614f731aSDamien Hedde     assert(!enter_phase_in_progress && !exit_phase_in_progress);
243614f731aSDamien Hedde     trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
244614f731aSDamien Hedde 
245614f731aSDamien Hedde     /*
246614f731aSDamien Hedde      * At most one of the two 'for' loops will be executed below
247614f731aSDamien Hedde      * in order to cope with the difference between the two counts.
248614f731aSDamien Hedde      */
249614f731aSDamien Hedde     /* if newp is more reset than oldp */
250614f731aSDamien Hedde     for (unsigned i = oldp_count; i < newp_count; i++) {
251614f731aSDamien Hedde         resettable_assert_reset(obj, RESET_TYPE_COLD);
252614f731aSDamien Hedde     }
253614f731aSDamien Hedde     /*
254614f731aSDamien Hedde      * if obj is leaving a bus under reset, we need to ensure
255614f731aSDamien Hedde      * hold phase is not pending.
256614f731aSDamien Hedde      */
257614f731aSDamien Hedde     if (oldp_count && s->hold_phase_pending) {
258614f731aSDamien Hedde         resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
259614f731aSDamien Hedde     }
260614f731aSDamien Hedde     /* if oldp is more reset than newp */
261614f731aSDamien Hedde     for (unsigned i = newp_count; i < oldp_count; i++) {
262614f731aSDamien Hedde         resettable_release_reset(obj, RESET_TYPE_COLD);
263614f731aSDamien Hedde     }
264614f731aSDamien Hedde }
265614f731aSDamien Hedde 
266abb89dbfSDamien Hedde void resettable_cold_reset_fn(void *opaque)
267abb89dbfSDamien Hedde {
268abb89dbfSDamien Hedde     resettable_reset((Object *) opaque, RESET_TYPE_COLD);
269abb89dbfSDamien Hedde }
270abb89dbfSDamien Hedde 
271bc5a39bfSDamien Hedde void resettable_class_set_parent_phases(ResettableClass *rc,
272bc5a39bfSDamien Hedde                                         ResettableEnterPhase enter,
273bc5a39bfSDamien Hedde                                         ResettableHoldPhase hold,
274bc5a39bfSDamien Hedde                                         ResettableExitPhase exit,
275bc5a39bfSDamien Hedde                                         ResettablePhases *parent_phases)
276bc5a39bfSDamien Hedde {
277bc5a39bfSDamien Hedde     *parent_phases = rc->phases;
278bc5a39bfSDamien Hedde     if (enter) {
279bc5a39bfSDamien Hedde         rc->phases.enter = enter;
280bc5a39bfSDamien Hedde     }
281bc5a39bfSDamien Hedde     if (hold) {
282bc5a39bfSDamien Hedde         rc->phases.hold = hold;
283bc5a39bfSDamien Hedde     }
284bc5a39bfSDamien Hedde     if (exit) {
285bc5a39bfSDamien Hedde         rc->phases.exit = exit;
286bc5a39bfSDamien Hedde     }
287bc5a39bfSDamien Hedde }
288bc5a39bfSDamien Hedde 
289bc5a39bfSDamien Hedde static const TypeInfo resettable_interface_info = {
290bc5a39bfSDamien Hedde     .name       = TYPE_RESETTABLE_INTERFACE,
291bc5a39bfSDamien Hedde     .parent     = TYPE_INTERFACE,
292bc5a39bfSDamien Hedde     .class_size = sizeof(ResettableClass),
293bc5a39bfSDamien Hedde };
294bc5a39bfSDamien Hedde 
295bc5a39bfSDamien Hedde static void reset_register_types(void)
296bc5a39bfSDamien Hedde {
297bc5a39bfSDamien Hedde     type_register_static(&resettable_interface_info);
298bc5a39bfSDamien Hedde }
299bc5a39bfSDamien Hedde 
300bc5a39bfSDamien Hedde type_init(reset_register_types)
301