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 */ 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) 279