123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- /*
- * Resettable interface.
- *
- * Copyright (c) 2019 GreenSocs SAS
- *
- * Authors:
- * Damien Hedde
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
- #include "qemu/osdep.h"
- #include "qemu/module.h"
- #include "hw/resettable.h"
- #include "trace.h"
- /**
- * resettable_phase_enter/hold/exit:
- * Function executing a phase recursively in a resettable object and its
- * children.
- */
- static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
- static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
- static void resettable_phase_exit(Object *obj, void *opaque, ResetType type);
- /**
- * enter_phase_in_progress:
- * True if we are currently in reset enter phase.
- *
- * exit_phase_in_progress:
- * count the number of exit phase we are in.
- *
- * Note: These flags are only used to guarantee (using asserts) that the reset
- * API is used correctly. We can use global variables because we rely on the
- * iothread mutex to ensure only one reset operation is in a progress at a
- * given time.
- */
- static bool enter_phase_in_progress;
- static unsigned exit_phase_in_progress;
- void resettable_reset(Object *obj, ResetType type)
- {
- trace_resettable_reset(obj, type);
- resettable_assert_reset(obj, type);
- resettable_release_reset(obj, type);
- }
- void resettable_assert_reset(Object *obj, ResetType type)
- {
- trace_resettable_reset_assert_begin(obj, type);
- assert(!enter_phase_in_progress);
- enter_phase_in_progress = true;
- resettable_phase_enter(obj, NULL, type);
- enter_phase_in_progress = false;
- resettable_phase_hold(obj, NULL, type);
- trace_resettable_reset_assert_end(obj);
- }
- void resettable_release_reset(Object *obj, ResetType type)
- {
- trace_resettable_reset_release_begin(obj, type);
- assert(!enter_phase_in_progress);
- exit_phase_in_progress += 1;
- resettable_phase_exit(obj, NULL, type);
- exit_phase_in_progress -= 1;
- trace_resettable_reset_release_end(obj);
- }
- bool resettable_is_in_reset(Object *obj)
- {
- ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
- ResettableState *s = rc->get_state(obj);
- return s->count > 0;
- }
- /**
- * resettable_child_foreach:
- * helper to avoid checking the existence of the method.
- */
- static void resettable_child_foreach(ResettableClass *rc, Object *obj,
- ResettableChildCallback cb,
- void *opaque, ResetType type)
- {
- if (rc->child_foreach) {
- rc->child_foreach(obj, cb, opaque, type);
- }
- }
- static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
- {
- ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
- ResettableState *s = rc->get_state(obj);
- const char *obj_typename = object_get_typename(obj);
- bool action_needed = false;
- /* exit phase has to finish properly before entering back in reset */
- assert(!s->exit_phase_in_progress);
- trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
- /* Only take action if we really enter reset for the 1st time. */
- /*
- * TODO: if adding more ResetType support, some additional checks
- * are probably needed here.
- */
- if (s->count++ == 0) {
- action_needed = true;
- }
- /*
- * We limit the count to an arbitrary "big" value. The value is big
- * enough not to be triggered normally.
- * The assert will stop an infinite loop if there is a cycle in the
- * reset tree. The loop goes through resettable_foreach_child below
- * which at some point will call us again.
- */
- assert(s->count <= 50);
- /*
- * handle the children even if action_needed is at false so that
- * child counts are incremented too
- */
- resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
- /* execute enter phase for the object if needed */
- if (action_needed) {
- trace_resettable_phase_enter_exec(obj, obj_typename, type,
- !!rc->phases.enter);
- if (rc->phases.enter) {
- rc->phases.enter(obj, type);
- }
- s->hold_phase_pending = true;
- }
- trace_resettable_phase_enter_end(obj, obj_typename, s->count);
- }
- static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
- {
- ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
- ResettableState *s = rc->get_state(obj);
- const char *obj_typename = object_get_typename(obj);
- /* exit phase has to finish properly before entering back in reset */
- assert(!s->exit_phase_in_progress);
- trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
- /* handle children first */
- resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
- /* exec hold phase */
- if (s->hold_phase_pending) {
- s->hold_phase_pending = false;
- trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
- if (rc->phases.hold) {
- rc->phases.hold(obj, type);
- }
- }
- trace_resettable_phase_hold_end(obj, obj_typename, s->count);
- }
- static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
- {
- ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
- ResettableState *s = rc->get_state(obj);
- const char *obj_typename = object_get_typename(obj);
- assert(!s->exit_phase_in_progress);
- trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
- /* exit_phase_in_progress ensures this phase is 'atomic' */
- s->exit_phase_in_progress = true;
- resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
- assert(s->count > 0);
- if (--s->count == 0) {
- trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
- if (rc->phases.exit) {
- rc->phases.exit(obj, type);
- }
- }
- s->exit_phase_in_progress = false;
- trace_resettable_phase_exit_end(obj, obj_typename, s->count);
- }
- /*
- * resettable_get_count:
- * Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
- */
- static unsigned resettable_get_count(Object *obj)
- {
- if (obj) {
- ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
- return rc->get_state(obj)->count;
- }
- return 0;
- }
- void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
- {
- ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
- ResettableState *s = rc->get_state(obj);
- unsigned newp_count = resettable_get_count(newp);
- unsigned oldp_count = resettable_get_count(oldp);
- /*
- * Ensure we do not change parent when in enter or exit phase.
- * During these phases, the reset subtree being updated is partly in reset
- * and partly not in reset (it depends on the actual position in
- * resettable_child_foreach()s). We are not able to tell in which part is a
- * leaving or arriving device. Thus we cannot set the reset count of the
- * moving device to the proper value.
- */
- assert(!enter_phase_in_progress && !exit_phase_in_progress);
- trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
- /*
- * At most one of the two 'for' loops will be executed below
- * in order to cope with the difference between the two counts.
- */
- /* if newp is more reset than oldp */
- for (unsigned i = oldp_count; i < newp_count; i++) {
- resettable_assert_reset(obj, RESET_TYPE_COLD);
- }
- /*
- * if obj is leaving a bus under reset, we need to ensure
- * hold phase is not pending.
- */
- if (oldp_count && s->hold_phase_pending) {
- resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
- }
- /* if oldp is more reset than newp */
- for (unsigned i = newp_count; i < oldp_count; i++) {
- resettable_release_reset(obj, RESET_TYPE_COLD);
- }
- }
- void resettable_cold_reset_fn(void *opaque)
- {
- resettable_reset((Object *) opaque, RESET_TYPE_COLD);
- }
- void resettable_class_set_parent_phases(ResettableClass *rc,
- ResettableEnterPhase enter,
- ResettableHoldPhase hold,
- ResettableExitPhase exit,
- ResettablePhases *parent_phases)
- {
- *parent_phases = rc->phases;
- if (enter) {
- rc->phases.enter = enter;
- }
- if (hold) {
- rc->phases.hold = hold;
- }
- if (exit) {
- rc->phases.exit = exit;
- }
- }
- static const TypeInfo resettable_interface_info = {
- .name = TYPE_RESETTABLE_INTERFACE,
- .parent = TYPE_INTERFACE,
- .class_size = sizeof(ResettableClass),
- };
- static void reset_register_types(void)
- {
- type_register_static(&resettable_interface_info);
- }
- type_init(reset_register_types)
|