]> git.proxmox.com Git - mirror_qemu.git/blame - hw/core/resettable.c
hw/core: create Resettable QOM interface
[mirror_qemu.git] / hw / core / resettable.c
CommitLineData
bc5a39bf
DH
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 */
23static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
24static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
25static 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 * Note: This flag is only used to guarantee (using asserts) that the reset
32 * API is used correctly. We can use a global variable because we rely on the
33 * iothread mutex to ensure only one reset operation is in a progress at a
34 * given time.
35 */
36static bool enter_phase_in_progress;
37
38void resettable_reset(Object *obj, ResetType type)
39{
40 trace_resettable_reset(obj, type);
41 resettable_assert_reset(obj, type);
42 resettable_release_reset(obj, type);
43}
44
45void resettable_assert_reset(Object *obj, ResetType type)
46{
47 /* TODO: change this assert when adding support for other reset types */
48 assert(type == RESET_TYPE_COLD);
49 trace_resettable_reset_assert_begin(obj, type);
50 assert(!enter_phase_in_progress);
51
52 enter_phase_in_progress = true;
53 resettable_phase_enter(obj, NULL, type);
54 enter_phase_in_progress = false;
55
56 resettable_phase_hold(obj, NULL, type);
57
58 trace_resettable_reset_assert_end(obj);
59}
60
61void resettable_release_reset(Object *obj, ResetType type)
62{
63 /* TODO: change this assert when adding support for other reset types */
64 assert(type == RESET_TYPE_COLD);
65 trace_resettable_reset_release_begin(obj, type);
66 assert(!enter_phase_in_progress);
67
68 resettable_phase_exit(obj, NULL, type);
69
70 trace_resettable_reset_release_end(obj);
71}
72
73bool resettable_is_in_reset(Object *obj)
74{
75 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
76 ResettableState *s = rc->get_state(obj);
77
78 return s->count > 0;
79}
80
81/**
82 * resettable_child_foreach:
83 * helper to avoid checking the existence of the method.
84 */
85static void resettable_child_foreach(ResettableClass *rc, Object *obj,
86 ResettableChildCallback cb,
87 void *opaque, ResetType type)
88{
89 if (rc->child_foreach) {
90 rc->child_foreach(obj, cb, opaque, type);
91 }
92}
93
94/**
95 * resettable_get_tr_func:
96 * helper to fetch transitional reset callback if any.
97 */
98static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
99 Object *obj)
100{
101 ResettableTrFunction tr_func = NULL;
102 if (rc->get_transitional_function) {
103 tr_func = rc->get_transitional_function(obj);
104 }
105 return tr_func;
106}
107
108static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
109{
110 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
111 ResettableState *s = rc->get_state(obj);
112 const char *obj_typename = object_get_typename(obj);
113 bool action_needed = false;
114
115 /* exit phase has to finish properly before entering back in reset */
116 assert(!s->exit_phase_in_progress);
117
118 trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
119
120 /* Only take action if we really enter reset for the 1st time. */
121 /*
122 * TODO: if adding more ResetType support, some additional checks
123 * are probably needed here.
124 */
125 if (s->count++ == 0) {
126 action_needed = true;
127 }
128 /*
129 * We limit the count to an arbitrary "big" value. The value is big
130 * enough not to be triggered normally.
131 * The assert will stop an infinite loop if there is a cycle in the
132 * reset tree. The loop goes through resettable_foreach_child below
133 * which at some point will call us again.
134 */
135 assert(s->count <= 50);
136
137 /*
138 * handle the children even if action_needed is at false so that
139 * child counts are incremented too
140 */
141 resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
142
143 /* execute enter phase for the object if needed */
144 if (action_needed) {
145 trace_resettable_phase_enter_exec(obj, obj_typename, type,
146 !!rc->phases.enter);
147 if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
148 rc->phases.enter(obj, type);
149 }
150 s->hold_phase_pending = true;
151 }
152 trace_resettable_phase_enter_end(obj, obj_typename, s->count);
153}
154
155static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
156{
157 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
158 ResettableState *s = rc->get_state(obj);
159 const char *obj_typename = object_get_typename(obj);
160
161 /* exit phase has to finish properly before entering back in reset */
162 assert(!s->exit_phase_in_progress);
163
164 trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
165
166 /* handle children first */
167 resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
168
169 /* exec hold phase */
170 if (s->hold_phase_pending) {
171 s->hold_phase_pending = false;
172 ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
173 trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
174 if (tr_func) {
175 trace_resettable_transitional_function(obj, obj_typename);
176 tr_func(obj);
177 } else if (rc->phases.hold) {
178 rc->phases.hold(obj);
179 }
180 }
181 trace_resettable_phase_hold_end(obj, obj_typename, s->count);
182}
183
184static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
185{
186 ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
187 ResettableState *s = rc->get_state(obj);
188 const char *obj_typename = object_get_typename(obj);
189
190 assert(!s->exit_phase_in_progress);
191 trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
192
193 /* exit_phase_in_progress ensures this phase is 'atomic' */
194 s->exit_phase_in_progress = true;
195 resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
196
197 assert(s->count > 0);
198 if (s->count == 1) {
199 trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
200 if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
201 rc->phases.exit(obj);
202 }
203 s->count = 0;
204 }
205 s->exit_phase_in_progress = false;
206 trace_resettable_phase_exit_end(obj, obj_typename, s->count);
207}
208
209void resettable_class_set_parent_phases(ResettableClass *rc,
210 ResettableEnterPhase enter,
211 ResettableHoldPhase hold,
212 ResettableExitPhase exit,
213 ResettablePhases *parent_phases)
214{
215 *parent_phases = rc->phases;
216 if (enter) {
217 rc->phases.enter = enter;
218 }
219 if (hold) {
220 rc->phases.hold = hold;
221 }
222 if (exit) {
223 rc->phases.exit = exit;
224 }
225}
226
227static const TypeInfo resettable_interface_info = {
228 .name = TYPE_RESETTABLE_INTERFACE,
229 .parent = TYPE_INTERFACE,
230 .class_size = sizeof(ResettableClass),
231};
232
233static void reset_register_types(void)
234{
235 type_register_static(&resettable_interface_info);
236}
237
238type_init(reset_register_types)