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