diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 717fcbdae4715db1..2b5f68fedd40bea0 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -16,3 +16,5 @@ common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \
 endif
 
 common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
+
+common-obj-$(CONFIG_GPIO) += gpiodev.o
diff --git a/backends/gpiodev.c b/backends/gpiodev.c
new file mode 100644
index 0000000000000000..8d90e150f5472463
--- /dev/null
+++ b/backends/gpiodev.c
@@ -0,0 +1,183 @@
+/*
+ * QEMU GPIO Backend
+ *
+ * Copyright (C) 2018 Glider bvba
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <errno.h>
+#include <gpiod.h>
+
+#include "qemu/osdep.h"
+#include "qemu/config-file.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "qemu/option.h"
+
+#include "sysemu/gpiodev.h"
+
+#include "hw/irq.h"
+#include "hw/qdev-core.h"
+
+DeviceState *the_pl061_dev;
+
+static void gpio_irq_handler(void *opaque, int n, int level)
+{
+    struct gpiod_line *line = opaque;
+    int status;
+
+    status = gpiod_line_set_value(line, level);
+    if (status < 0) {
+        struct gpiod_chip *chip = gpiod_line_get_chip(line);
+
+        error_report("%s/%s: Cannot set GPIO line %u: %s",
+                     gpiod_chip_name(chip), gpiod_chip_label(chip),
+                     gpiod_line_offset(line), strerror(errno));
+    }
+}
+
+static int gpio_connect_line(unsigned int vgpio, struct gpiod_chip *chip,
+                             unsigned int gpio)
+{
+    const char *name = gpiod_chip_name(chip);
+    const char *label = gpiod_chip_label(chip);
+    struct gpiod_line *line;
+    qemu_irq irq;
+    int status;
+
+    if (!the_pl061_dev) {
+        error_report("PL061 GPIO controller not available");
+        return -1;
+    }
+
+    line = gpiod_chip_get_line(chip, gpio);
+    if (!line) {
+        error_report("%s/%s: Cannot obtain GPIO line %u: %s", name, label,
+                     gpio, strerror(errno));
+        return -1;
+    }
+
+    status = gpiod_line_request_output(line, "qemu", 0);
+    if (status < 0) {
+        error_report("%s/%s: Cannot request GPIO line %u for output: %s", name,
+                     label, gpio, strerror(errno));
+        return -1;
+    }
+
+    irq = qemu_allocate_irq(gpio_irq_handler, line, 0);
+    qdev_connect_gpio_out(the_pl061_dev, vgpio, irq);
+
+    info_report("%s/%s: Connected PL061 GPIO %u to GPIO line %u", name, label,
+                vgpio, gpio);
+    return 0;
+}
+
+static int gpio_count_gpios(const char *opt)
+{
+    unsigned int len = 0;
+    unsigned int n = 0;
+
+    do {
+        switch (*opt) {
+        case '0' ... '9':
+            len++;
+            break;
+
+        case ':':
+        case '\0':
+            if (!len) {
+                return -1;
+            }
+
+            n++;
+            len = 0;
+            break;
+
+        default:
+            return -1;
+        }
+    } while (*opt++);
+
+    return n;
+}
+
+int qemu_gpiodev_add(QemuOpts *opts)
+{
+    const char *name = qemu_opt_get(opts, "name");
+    const char *vgpios = qemu_opt_get(opts, "vgpios");
+    const char *gpios = qemu_opt_get(opts, "gpios");
+    unsigned int vgpio, gpio;
+    struct gpiod_chip *chip;
+    int n1, n2, i, status;
+
+    if (!name || !vgpios || !gpios) {
+        error_report("Missing parameters");
+        return -1;
+    }
+
+    n1 = gpio_count_gpios(vgpios);
+    if (n1 < 0) {
+        error_report("Invalid vgpios parameter");
+        return n1;
+    }
+
+    n2 = gpio_count_gpios(gpios);
+    if (n2 < 0) {
+        error_report("Invalid gpios parameter");
+        return n2;
+    }
+
+    if (n1 != n2) {
+        error_report("Number of vgpios and gpios do not match");
+        return -1;
+    }
+
+    chip = gpiod_chip_open_lookup(name);
+    if (!chip) {
+        error_report("Cannot open GPIO chip %s: %s", name, strerror(errno));
+        return -1;
+    }
+
+    for (i = 0; i < n1; i++, vgpios++, gpios++) {
+        qemu_strtoui(vgpios, &vgpios, 10, &vgpio);
+        qemu_strtoui(gpios, &gpios, 10, &gpio);
+
+        status = gpio_connect_line(vgpio, chip, gpio);
+        if (status) {
+            return status;
+        }
+    }
+
+    return 0;
+}
+
+static QemuOptsList qemu_gpiodev_opts = {
+    .name = "gpiodev",
+    .implied_opt_name = "name",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_gpiodev_opts.head),
+    .desc = {
+        {
+            .name = "name",
+            .type = QEMU_OPT_STRING,
+            .help = "Sets the GPIO chip specifier",
+        }, {
+            .name = "vgpios",
+            .type = QEMU_OPT_STRING,
+            .help = "Sets the list of virtual GPIO offsets",
+        }, {
+            .name = "gpios",
+            .type = QEMU_OPT_STRING,
+            .help = "Sets the list of physical GPIO offsets",
+        },
+        { /* end of list */ }
+    },
+};
+
+static void gpiodev_register_config(void)
+{
+    qemu_add_opts(&qemu_gpiodev_opts);
+}
+
+opts_init(gpiodev_register_config);
diff --git a/configure b/configure
index f3d4b799a5b08b1d..eec12eb766e28959 100755
--- a/configure
+++ b/configure
@@ -476,6 +476,7 @@ libxml2=""
 docker="no"
 debug_mutex="no"
 libpmem=""
+gpio=""
 
 # cross compilers defaults, can be overridden with --cross-cc-ARCH
 cross_cc_aarch64="aarch64-linux-gnu-gcc"
@@ -1444,6 +1445,10 @@ for opt do
   ;;
   --disable-libpmem) libpmem=no
   ;;
+  --disable-gpio) gpio="no"
+  ;;
+  --enable-gpio) gpio="yes"
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -1721,6 +1726,7 @@ disabled with --disable-FEATURE, default is enabled if available:
   capstone        capstone disassembler support
   debug-mutex     mutex debugging support
   libpmem         libpmem support
+  gpio            gpio support
 
 NOTE: The object files are built at the place where configure is launched
 EOF
@@ -5632,6 +5638,24 @@ if test "$libpmem" != "no"; then
 	fi
 fi
 
+##########################################
+# check for libgpiod
+
+if test "$gpio" != "no"; then
+	if $pkg_config --exists "libgpiod"; then
+		gpio="yes"
+		libgpiod_libs=$($pkg_config --libs libgpiod)
+		libgpiod_cflags=$($pkg_config --cflags libgpiod)
+		libs_softmmu="$libs_softmmu $libgpiod_libs"
+		QEMU_CFLAGS="$QEMU_CFLAGS $libgpiod_cflags"
+	else
+		if test "$gpio" = "yes" ; then
+			feature_not_found "gpio" "Install libgpiod"
+		fi
+		gpio="no"
+	fi
+fi
+
 ##########################################
 # End of CC checks
 # After here, no more $cc or $ld runs
@@ -6102,6 +6126,7 @@ echo "VxHS block device $vxhs"
 echo "capstone          $capstone"
 echo "docker            $docker"
 echo "libpmem support   $libpmem"
+echo "gpio support      $gpio"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -6863,6 +6888,10 @@ if test "$libpmem" = "yes" ; then
   echo "CONFIG_LIBPMEM=y" >> $config_host_mak
 fi
 
+if test "$gpio" = "yes" ; then
+  echo "CONFIG_GPIO=y" >> $config_host_mak
+fi
+
 if test "$tcg_interpreter" = "yes"; then
   QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 0b57f87abcbfd54b..78585e11c8d7dfa8 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -40,6 +40,7 @@
 #include "hw/devices.h"
 #include "net/net.h"
 #include "sysemu/device_tree.h"
+#include "sysemu/gpiodev.h"
 #include "sysemu/numa.h"
 #include "sysemu/sysemu.h"
 #include "sysemu/kvm.h"
@@ -761,6 +762,11 @@ static void create_gpio(const VirtMachineState *vms, qemu_irq *pic)
     const char compat[] = "arm,pl061\0arm,primecell";
 
     pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
+#ifdef CONFIG_GPIO
+    if (!the_pl061_dev) {
+        the_pl061_dev = pl061_dev;
+    }
+#endif
 
     uint32_t phandle = qemu_fdt_alloc_phandle(vms->fdt);
     nodename = g_strdup_printf("/pl061@%" PRIx64, base);
diff --git a/include/sysemu/gpiodev.h b/include/sysemu/gpiodev.h
new file mode 100644
index 0000000000000000..a5168bd90f53efa0
--- /dev/null
+++ b/include/sysemu/gpiodev.h
@@ -0,0 +1,11 @@
+/*
+ * QEMU GPIO Backend
+ *
+ * Copyright (C) 2018 Glider bvba
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+extern DeviceState *the_pl061_dev;
+
+int qemu_gpiodev_add(QemuOpts *opts);
diff --git a/qemu-options.hx b/qemu-options.hx
index f139459e802a3185..6bbcc062b09c9053 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2903,6 +2903,26 @@ DEFHEADING()
 
 #endif
 
+#ifdef CONFIG_GPIO
+DEFHEADING(GPIO device options:)
+
+DEF("gpiodev", HAS_ARG, QEMU_OPTION_gpiodev,
+    "-gpiodev gpiochip,vgpios=x:y:...:z,gpios=x:y:...:z\n", QEMU_ARCH_ALL)
+STEXI
+@item -gpiodev @var{gpiochip},vgpios=@var{vgpios},gpios=@var{gpios}
+@findef -gpiodev
+Define a new GPIO device. Valid options are:
+@table @option
+@item @var{gpiochip}
+This option specifies the GPIO chip to map virtual gpios to.
+@item vgpios=@var{vgpios}
+Specifies an array of virtual GPIOs to be mapped to physical GPIOs.
+@item gpios=@var{gpios}
+Specifies an array of physical GPIOs to be used as mapping targets.
+@end table
+ETEXI
+#endif
+
 DEFHEADING(Linux/Multiboot boot specific:)
 STEXI
 
diff --git a/vl.c b/vl.c
index 0388852deb9e5be3..110b88d1d821cb91 100644
--- a/vl.c
+++ b/vl.c
@@ -102,6 +102,9 @@ int main(int argc, char **argv)
 #ifdef CONFIG_VIRTFS
 #include "fsdev/qemu-fsdev.h"
 #endif
+#ifdef CONFIG_GPIO
+#include "sysemu/gpiodev.h"
+#endif
 #include "sysemu/qtest.h"
 
 #include "disas/disas.h"
@@ -2259,6 +2262,14 @@ static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp)
 }
 #endif
 
+#ifdef CONFIG_GPIO
+static int gpiodev_init_func(void *opaque, QemuOpts *opts, Error **errp)
+{
+
+    return qemu_gpiodev_add(opts);
+}
+#endif
+
 static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
 {
     Chardev *chr;
@@ -3333,6 +3344,15 @@ int main(int argc, char **argv, char **envp)
                     exit(1);
                 }
                 break;
+#ifdef CONFIG_GPIO
+            case QEMU_OPTION_gpiodev:
+                opts = qemu_opts_parse_noisily(qemu_find_opts("gpiodev"),
+                                               optarg, true);
+                if (!opts) {
+                    exit(1);
+                }
+                break;
+#endif
             case QEMU_OPTION_virtfs: {
                 QemuOpts *fsdev;
                 QemuOpts *device;
@@ -4470,6 +4490,13 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+#ifdef CONFIG_GPIO
+    if (qemu_opts_foreach(qemu_find_opts("gpiodev"),
+                          gpiodev_init_func, NULL, NULL)) {
+        exit(1);
+    }
+#endif
+
     cpu_synchronize_all_post_init();
 
     rom_reset_order_override();
