diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index a799c83815..97007454a8 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -1,6 +1,7 @@
 # core qdev-related obj files, also used by *-user:
 common-obj-y += qdev.o qdev-properties.o
 common-obj-y += bus.o reset.o
+common-obj-y += resettable.o
 common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o
 common-obj-$(CONFIG_SOFTMMU) += fw-path-provider.o
 # irq.o needed for qdev GPIO handling:
diff --git a/hw/core/resettable.c b/hw/core/resettable.c
new file mode 100644
index 0000000000..59954dac05
--- /dev/null
+++ b/hw/core/resettable.c
@@ -0,0 +1,121 @@
+/*
+ * 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"
+
+#define RESETTABLE_MAX_COUNT 50
+
+#define RESETTABLE_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ResettableClass, (obj), TYPE_RESETTABLE)
+
+static void resettable_init_phase(Object *obj, bool cold);
+
+static void resettable_cold_init_phase(Object *obj)
+{
+    resettable_init_phase(obj, true);
+}
+
+static void resettable_warm_init_phase(Object *obj)
+{
+    resettable_init_phase(obj, false);
+}
+
+static void resettable_init_phase(Object *obj, bool cold)
+{
+    void (*func)(Object *);
+    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
+    uint32_t count;
+
+    count = rc->increment_count(obj);
+    /* this assert is triggered by an eventual reset loop */
+    assert(count <= RESETTABLE_MAX_COUNT);
+
+    func = cold ? resettable_cold_init_phase : resettable_warm_init_phase;
+    rc->foreach_child(obj, func);
+
+    if (rc->phases.init) {
+        rc->phases.init(obj, cold);
+    }
+}
+
+static void resettable_hold_phase(Object *obj)
+{
+    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
+
+    rc->foreach_child(obj, resettable_hold_phase);
+
+    if (rc->phases.hold) {
+        rc->phases.hold(obj);
+    }
+}
+
+static void resettable_exit_phase(Object *obj)
+{
+    ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
+
+    rc->foreach_child(obj, resettable_exit_phase);
+
+    assert(rc->get_count(obj) > 0);
+    if (rc->decrement_count(obj) == 0 && rc->phases.exit) {
+        rc->phases.exit(obj);
+    }
+}
+
+void resettable_assert_reset(Object *obj, bool cold)
+{
+    resettable_init_phase(obj, cold);
+    resettable_hold_phase(obj);
+}
+
+void resettable_deassert_reset(Object *obj)
+{
+    resettable_exit_phase(obj);
+}
+
+void resettable_reset(Object *obj, bool cold)
+{
+    resettable_assert_reset(obj, cold);
+    resettable_deassert_reset(obj);
+}
+
+void resettable_class_set_parent_reset_phases(ResettableClass *rc,
+                                              ResettableInitPhase init,
+                                              ResettableHoldPhase hold,
+                                              ResettableExitPhase exit,
+                                              ResettablePhases *parent_phases)
+{
+    *parent_phases = rc->phases;
+    if (init) {
+        rc->phases.init = init;
+    }
+    if (hold) {
+        rc->phases.hold = hold;
+    }
+    if (exit) {
+        rc->phases.exit = exit;
+    }
+}
+
+static const TypeInfo resettable_interface_info = {
+    .name       = TYPE_RESETTABLE,
+    .parent     = TYPE_INTERFACE,
+    .class_size = sizeof(ResettableClass),
+};
+
+static void reset_register_types(void)
+{
+    type_register_static(&resettable_interface_info);
+}
+
+type_init(reset_register_types)
diff --git a/include/hw/resettable.h b/include/hw/resettable.h
new file mode 100644
index 0000000000..39522b9b51
--- /dev/null
+++ b/include/hw/resettable.h
@@ -0,0 +1,104 @@
+#ifndef HW_RESETTABLE_H
+#define HW_RESETTABLE_H
+
+#include "qom/object.h"
+
+#define TYPE_RESETTABLE "resettable"
+
+#define RESETTABLE_CLASS(class) \
+    OBJECT_CLASS_CHECK(ResettableClass, (class), TYPE_RESETTABLE)
+
+/*
+ * ResettableClass:
+ * Interface for resettable objects.
+ *
+ * The reset operation is divided in several phases each represented by a
+ * method.
+ *
+ * Each Ressetable must maintain a reset counter in its state, 3 methods allows
+ * to interact with it.
+ *
+ * @phases.init: should reset local state only. Takes a bool @cold argument
+ * specifying whether the reset is cold or warm. It must not do side-effect
+ * on others objects.
+ *
+ * @phases.hold: side-effects action on others objects due to staying in a
+ * resetting state.
+ *
+ * @phases.exit: leave the reset state, may do side-effects action on others
+ * objects.
+ *
+ * @get_count: Get the current reset count
+ * @increment_count: Increment the reset count, returns the new count
+ * @decrement_count: decrement the reset count, returns the new count
+ *
+ * @foreach_child: Executes a given function on every Resettable child.
+ * A child is not a QOM child, but a child a reset meaning.
+ */
+typedef void (*ResettableInitPhase)(Object *obj, bool cold);
+typedef void (*ResettableHoldPhase)(Object *obj);
+typedef void (*ResettableExitPhase)(Object *obj);
+typedef uint32_t (*ResettableGetCount)(Object *obj);
+typedef uint32_t (*ResettableIncrementCount)(Object *obj);
+typedef uint32_t (*ResettableDecrementCount)(Object *obj);
+typedef void (*ResettableForeachChild)(Object *obj, void (*visitor)(Object *));
+typedef struct ResettableClass {
+    InterfaceClass parent_class;
+
+    struct ResettablePhases {
+        ResettableInitPhase init;
+        ResettableHoldPhase hold;
+        ResettableExitPhase exit;
+    } phases;
+
+    ResettableGetCount get_count;
+    ResettableIncrementCount increment_count;
+    ResettableDecrementCount decrement_count;
+    ResettableForeachChild foreach_child;
+} ResettableClass;
+typedef struct ResettablePhases ResettablePhases;
+
+/**
+ * resettable_assert_reset:
+ * Increments the reset count and executes the init and hold phases.
+ * Each time resettable_assert_reset is called, resettable_deassert_reset
+ * must eventually be called once.
+ * It will also impact reset children.
+ *
+ * @obj object to reset, must implement Resettable interface.
+ * @cold boolean indicating the type of reset (cold or warm)
+ */
+void resettable_assert_reset(Object *obj, bool cold);
+
+/**
+ * resettable_deassert_reset:
+ * Decrements the reset count by one and executes the exit phase if it hits
+ * zero.
+ * It will also impact reset children.
+ *
+ * @obj object to reset, must implement Resettable interface.
+ */
+void resettable_deassert_reset(Object *obj);
+
+/**
+ * resettable_reset:
+ * Calling this function is equivalent to call @assert_reset then
+ * @deassert_reset.
+ */
+void resettable_reset(Object *obj, bool cold);
+
+/**
+ * resettable_class_set_parent_reset_phases:
+ *
+ * Save @rc current reset phases into @parent_phases and override @rc phases
+ * by the given new methods (@init, @hold and @exit).
+ * Each phase is overriden only if the new one is not NULL allowing to
+ * override a subset of phases.
+ */
+void resettable_class_set_parent_reset_phases(ResettableClass *rc,
+                                              ResettableInitPhase init,
+                                              ResettableHoldPhase hold,
+                                              ResettableExitPhase exit,
+                                              ResettablePhases *parent_phases);
+
+#endif
