diff --git a/include/monitor/qdev.h b/include/monitor/qdev.h
index 1d57bf6577..dc05b70146 100644
--- a/include/monitor/qdev.h
+++ b/include/monitor/qdev.h
@@ -6,6 +6,7 @@
 void hmp_info_qtree(Monitor *mon, const QDict *qdict);
 void hmp_info_qdm(Monitor *mon, const QDict *qdict);
 void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp);
+void qmp_connect_gpios(QDict *qdict, QObject **ret_data, Error **errp);
 
 int qdev_device_help(QemuOpts *opts);
 DeviceState *qdev_device_add(QemuOpts *opts, Error **errp);
@@ -36,4 +37,6 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
  */
 const char *qdev_set_id(DeviceState *dev, char *id, Error **errp);
 
+int qdev_connect_gpios(QemuOpts *opts, Error **errp);
+
 #endif
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 5b4397eeb8..66c5f5129e 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -106,6 +106,7 @@ extern QemuOptsList qemu_drive_opts;
 extern QemuOptsList bdrv_runtime_opts;
 extern QemuOptsList qemu_chardev_opts;
 extern QemuOptsList qemu_device_opts;
+extern QemuOptsList qemu_connect_gpios_opts;
 extern QemuOptsList qemu_netdev_opts;
 extern QemuOptsList qemu_nic_opts;
 extern QemuOptsList qemu_net_opts;
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
index f84a0dc523..3333598a5b 100644
--- a/monitor/qmp-cmds.c
+++ b/monitor/qmp-cmds.c
@@ -203,6 +203,9 @@ static void __attribute__((__constructor__)) monitor_init_qmp_commands(void)
     qmp_register_command(&qmp_commands, "device_add",
                          qmp_device_add, 0, 0);
 
+    qmp_register_command(&qmp_commands, "connect_gpios",
+                         qmp_connect_gpios, 0, 0);
+
     QTAILQ_INIT(&qmp_cap_negotiation_commands);
     qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
                          qmp_marshal_qmp_capabilities,
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 44994ea0e1..16d417610a 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -735,6 +735,68 @@ err_del_dev:
     return NULL;
 }
 
+static DeviceState *qdev_from_opt(QemuOpts *opts, const char *name,
+                                  Error **errp)
+{
+    Object *obj;
+    const char *path = qemu_opt_get(opts, name);
+
+    if (!path) {
+        error_setg(errp, QERR_MISSING_PARAMETER, name);
+        return NULL;
+    }
+
+    obj = object_resolve_path(path, NULL);
+    if (!obj) {
+        error_setg(errp, "Could not resolve path: %s", path);
+        return NULL;
+    }
+
+    if (!object_dynamic_cast(obj, TYPE_DEVICE)) {
+        error_setg(errp, "%s is not a device", path);
+        return NULL;
+    }
+
+    return DEVICE(obj);
+}
+
+int qdev_connect_gpios(QemuOpts *opts, Error **errp)
+{
+    qemu_irq in_irq;
+    const char *in_gpio_name, *out_gpio_name;
+    int in_gpio_idx = 0, out_gpio_idx = 0;
+    DeviceState *dev;
+
+    dev = qdev_from_opt(opts, "in-dev-path", errp);
+    if (!dev) {
+        return -1;
+    }
+
+    in_gpio_name = qemu_opt_get(opts, "in-gpio-name");
+    out_gpio_name = qemu_opt_get(opts, "out-gpio-name");
+    in_gpio_idx = qemu_opt_get_number(opts, "out-gpio-index", 0);
+    out_gpio_idx = qemu_opt_get_number(opts, "out-gpio-index", 0);
+
+    if (in_gpio_name) {
+        in_irq = qdev_get_gpio_in_named(dev, in_gpio_name, in_gpio_idx);
+    } else {
+        in_irq = qdev_get_gpio_in(dev, in_gpio_idx);
+    }
+
+    dev = qdev_from_opt(opts, "out-dev-path", errp);
+    if (!dev) {
+        return -1;
+    }
+
+    if (out_gpio_name) {
+        qdev_connect_gpio_out_named(dev, out_gpio_name, out_gpio_idx, in_irq);
+    } else {
+        qdev_connect_gpio_out(dev, out_gpio_idx, in_irq);
+    }
+
+    return 0;
+}
+
 /* Takes ownership of @opts on success */
 DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
 {
@@ -885,6 +947,20 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
     object_unref(OBJECT(dev));
 }
 
+void qmp_connect_gpios(QDict *qdict, QObject **ret_data, Error **errp)
+{
+    QemuOpts *opts;
+
+    opts = qemu_opts_from_qdict(qemu_find_opts("connect-gpios"), qdict, errp);
+    if (!opts) {
+        return;
+    }
+
+    qdev_connect_gpios(opts, errp);
+
+    qemu_opts_del(opts);
+}
+
 static DeviceState *find_device_state(const char *id, Error **errp)
 {
     Object *obj = object_resolve_path_at(qdev_get_peripheral(), id);
@@ -1102,6 +1178,33 @@ QemuOptsList qemu_device_opts = {
     },
 };
 
+QemuOptsList qemu_connect_gpios_opts = {
+    .name = "connect-gpios",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_connect_gpios_opts.head),
+    .desc = {
+        {
+            .name = "in-dev-path",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "in-gpio-name",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "in-gpio-index",
+            .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "out-dev-path",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "out-gpio-name",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "out-gpio-index",
+            .type = QEMU_OPT_NUMBER,
+        },
+        { /* end of list */ }
+    },
+};
+
 QemuOptsList qemu_global_opts = {
     .name = "global",
     .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
diff --git a/system/vl.c b/system/vl.c
index fe547ca47c..c36562fbeb 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -1222,6 +1222,19 @@ static int device_init_func(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
+static int connect_gpios_func(void *opaque, QemuOpts *opts, Error **errp)
+{
+    int err;
+
+    err = qdev_connect_gpios(opts, errp);
+    if (err != 0 && *errp) {
+        error_report_err(*errp);
+        return err;
+    }
+
+    return 0;
+}
+
 static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
     Error *local_err = NULL;
@@ -2665,6 +2678,10 @@ static void qemu_create_cli_devices(void)
         object_unref(OBJECT(dev));
         loc_pop(&opt->loc);
     }
+
+    qemu_opts_foreach(qemu_find_opts("connect-gpios"),
+                      connect_gpios_func, NULL, &error_fatal);
+
     rom_reset_order_override();
 }
 
@@ -2765,6 +2782,7 @@ void qemu_init(int argc, char **argv)
     qemu_add_drive_opts(&bdrv_runtime_opts);
     qemu_add_opts(&qemu_chardev_opts);
     qemu_add_opts(&qemu_device_opts);
+    qemu_add_opts(&qemu_connect_gpios_opts);
     qemu_add_opts(&qemu_netdev_opts);
     qemu_add_opts(&qemu_nic_opts);
     qemu_add_opts(&qemu_net_opts);
@@ -3373,6 +3391,12 @@ void qemu_init(int argc, char **argv)
                     }
                 }
                 break;
+            case QEMU_OPTION_connect_gpios:
+                if (!qemu_opts_parse_noisily(qemu_find_opts("connect-gpios"),
+                                             optarg, false)) {
+                    exit(1);
+                }
+                break;
             case QEMU_OPTION_smp:
                 machine_parse_property_opt(qemu_find_opts("smp-opts"),
                                            "smp", optarg);
diff --git a/qemu-options.hx b/qemu-options.hx
index 20a1ce0d43..011aa66569 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1187,6 +1187,37 @@ SRST
 
 ERST
 
+DEF("connect-gpios", HAS_ARG, QEMU_OPTION_connect_gpios,
+    "-connect-gpios in-dev-path=path,out-dev-path=path[,in-gpio-name=name][,out-gpio-name=name][,in-gpio-index=index][,out-gpio-index=index]\n"
+    "                connect an input and an output gpio.\n",
+    QEMU_ARCH_ALL)
+SRST
+``-connect-gpios in-dev-path=path,out-dev-path=path[,in-gpio-name=name][,out-gpio-name=name][,in-gpio-index=index][,out-gpio-index=index]``
+    Connect an input and an output gpio.
+
+    ``in-dev-path``, ``out-dev-path`` required, the device canonical
+        object path (e.g. /machine/peripheral-anon/device[0],
+        /machine/iotkit/cluster0/armv7m0) for the devices that should
+        have their in <-> out GPIOs connected
+
+    ``in-gpio-name``, ``out-gpio-name`` optional, the name of the GPIO list;
+      if not specified, the unnamed GPIO list is used
+
+    ``in-gpio-index``, ``out-gpio-index`` optional, the index in the GPIO list
+      that identifies the GPIO to be used; if not specified 0 (the first
+      GPIO in the list) is used
+
+    Examples:
+
+    .. parsed-literal::
+
+        # add the tmp105 sensor and connects its irq line to the CPU
+        qemu-system-arm \\
+         --machine mps2-an505 \\
+         --device tmp105,bus=/versatile_i2c/i2c,address=0x50 \\
+         --connect-gpios out-dev-path=/machine/peripheral-anon/device[0],in-dev-path=/machine/iotkit/cluster0/armv7m0,in-gpio-index=100
+ERST
+
 DEF("name", HAS_ARG, QEMU_OPTION_name,
     "-name string1[,process=string2][,debug-threads=on|off]\n"
     "                set the name of the guest\n"
