diff --git a/hw/core/qdev-vmstate.c b/hw/core/qdev-vmstate.c
index 24f8465c61..72f84c6cee 100644
--- a/hw/core/qdev-vmstate.c
+++ b/hw/core/qdev-vmstate.c
@@ -24,10 +24,23 @@ static int device_vmstate_reset_post_load(void *opaque, int version_id)
 {
     DeviceState *dev = (DeviceState *) opaque;
     BusState *bus;
+    uint32_t io_count = 0;
+
     QLIST_FOREACH(bus, &dev->child_bus, sibling) {
         bus->resetting = dev->resetting;
         bus->reset_is_cold = dev->reset_is_cold;
     }
+
+    if (dev->cold_reset_input.state) {
+        io_count += 1;
+    }
+    if (dev->warm_reset_input.state) {
+        io_count += 1;
+    }
+    /* ensure resetting count is coherent with io states */
+    if (dev->resetting < io_count) {
+        return -1;
+    }
     return 0;
 }
 
@@ -40,6 +53,8 @@ const struct VMStateDescription device_vmstate_reset = {
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(resetting, DeviceState),
         VMSTATE_BOOL(reset_is_cold, DeviceState),
+        VMSTATE_BOOL(cold_reset_input.state, DeviceState),
+        VMSTATE_BOOL(warm_reset_input.state, DeviceState),
         VMSTATE_END_OF_LIST()
     },
 };
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 88387d3743..11a4de55ea 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -450,6 +450,67 @@ void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
     qdev_init_gpio_in_named(dev, handler, NULL, n);
 }
 
+static DeviceResetInputState *device_get_reset_input_state(DeviceState *dev,
+                                                            bool cold)
+{
+    return cold ? &dev->cold_reset_input : &dev->warm_reset_input;
+}
+
+static void device_reset_handler(DeviceState *dev, bool cold, bool level)
+{
+    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
+
+    if (dris->type == DEVICE_RESET_ACTIVE_LOW) {
+        level = !level;
+    }
+
+    if (dris->state == level) {
+        /* io state has not changed */
+        return;
+    }
+
+    dris->state = level;
+
+    if (level) {
+        resettable_assert_reset(OBJECT(dev), cold);
+    } else {
+        resettable_deassert_reset(OBJECT(dev));
+    }
+}
+
+static void device_cold_reset_handler(void *opaque, int n, int level)
+{
+    device_reset_handler((DeviceState *) opaque, true, level);
+}
+
+static void device_warm_reset_handler(void *opaque, int n, int level)
+{
+    device_reset_handler((DeviceState *) opaque, false, level);
+}
+
+void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
+                                   bool cold, DeviceResetActiveType type)
+{
+    DeviceResetInputState *dris = device_get_reset_input_state(dev, cold);
+    qemu_irq_handler handler;
+
+    switch (type) {
+    case DEVICE_RESET_ACTIVE_LOW:
+    case DEVICE_RESET_ACTIVE_HIGH:
+        break;
+    default:
+        assert(false);
+        break;
+    }
+
+    assert(!dris->exists);
+    dris->exists = true;
+    dris->type = type;
+
+    handler = cold ? device_cold_reset_handler : device_warm_reset_handler;
+    qdev_init_gpio_in_named(dev, handler, name, 1);
+}
+
 void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
                               const char *name, int n)
 {
@@ -1007,6 +1068,10 @@ static void device_initfn(Object *obj)
     dev->instance_id_alias = -1;
     dev->realized = false;
     dev->resetting = 0;
+    dev->cold_reset_input.exists = false;
+    dev->cold_reset_input.state = false;
+    dev->warm_reset_input.exists = false;
+    dev->warm_reset_input.state = false;
 
     object_property_add_bool(obj, "realized",
                              device_get_realized, device_set_realized, NULL);
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 926d4bbcb1..f724ddc8f4 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -136,6 +136,23 @@ struct NamedGPIOList {
     QLIST_ENTRY(NamedGPIOList) node;
 };
 
+typedef enum DeviceResetActiveType {
+    DEVICE_RESET_ACTIVE_LOW,
+    DEVICE_RESET_ACTIVE_HIGH,
+} DeviceResetActiveType;
+
+/**
+ * DeviceResetInputState:
+ * @exists: tell if io exists
+ * @type: tell whether the io active low or high
+ * @state: true if reset is currently active
+ */
+typedef struct DeviceResetInputState {
+    bool exists;
+    DeviceResetActiveType type;
+    bool state;
+} DeviceResetInputState;
+
 /**
  * DeviceState:
  * @realized: Indicates whether the device has been fully constructed.
@@ -143,6 +160,8 @@ struct NamedGPIOList {
  * used to count how many times reset has been initiated on the device.
  * @reset_is_cold: If the device is under reset, indicates whether it is cold
  * or warm.
+ * @cold_reset_input: state data for cold reset io
+ * @warm_reset_input: state data for warm reset io
  *
  * This structure should not be accessed directly.  We declare it here
  * so that it can be embedded in individual device state structures.
@@ -167,6 +186,8 @@ struct DeviceState {
     uint32_t resetting;
     bool reset_is_cold;
     bool reset_hold_needed;
+    DeviceResetInputState cold_reset_input;
+    DeviceResetInputState warm_reset_input;
 };
 
 struct DeviceListener {
@@ -372,6 +393,42 @@ static inline void qdev_init_gpio_in_named(DeviceState *dev,
 void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
                      const char *name);
 
+/**
+ * qdev_init_reset_gpio_in_named:
+ * Create a gpio controlling the warm or cold reset of the device.
+ *
+ * @cold: specify whether it triggers cold or warm reset
+ * @type: what kind of reset io it is
+ *
+ * Note: the io is considered created in its inactive state. No reset
+ * is started by this function.
+ */
+void qdev_init_reset_gpio_in_named(DeviceState *dev, const char *name,
+                                   bool cold, DeviceResetActiveType type);
+
+/**
+ * qdev_init_warm_reset_gpio:
+ * Create the input to control the device warm reset.
+ */
+static inline void qdev_init_warm_reset_gpio(DeviceState *dev,
+                                             const char *name,
+                                             DeviceResetActiveType type)
+{
+    qdev_init_reset_gpio_in_named(dev, name, false, type);
+}
+
+/**
+ * qdev_init_cold_reset_gpio:
+ * Create the input to control the device cold reset.
+ * It can also be used as a power gate control.
+ */
+static inline void qdev_init_cold_reset_gpio(DeviceState *dev,
+                                             const char *name,
+                                             DeviceResetActiveType type)
+{
+    qdev_init_reset_gpio_in_named(dev, name, true, type);
+}
+
 BusState *qdev_get_parent_bus(DeviceState *dev);
 
 /*** BUS API. ***/
