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