v4.19.13 snapshot.
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
new file mode 100644
index 0000000..7e8dc78
--- /dev/null
+++ b/drivers/uio/Kconfig
@@ -0,0 +1,167 @@
+menuconfig UIO
+	tristate "Userspace I/O drivers"
+	depends on MMU
+	help
+	  Enable this to allow the userspace driver core code to be
+	  built.  This code allows userspace programs easy access to
+	  kernel interrupts and memory locations, allowing some drivers
+	  to be written in userspace.  Note that a small kernel driver
+	  is also required for interrupt handling to work properly.
+
+	  If you don't know what to do here, say N.
+
+if UIO
+
+config UIO_CIF
+	tristate "generic Hilscher CIF Card driver"
+	depends on PCI
+	help
+	  Driver for Hilscher CIF DeviceNet and Profibus cards.  This
+  	  driver requires a userspace component called cif that handles
+	  all of the heavy lifting and can be found at:
+	        <http://www.osadl.org/projects/downloads/UIO/user/>
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called uio_cif.
+
+config UIO_PDRV_GENIRQ
+	tristate "Userspace I/O platform driver with generic IRQ handling"
+	help
+	  Platform driver for Userspace I/O devices, including generic
+	  interrupt handling code. Shared interrupts are not supported.
+
+	  This kernel driver requires that the matching userspace driver
+	  handles interrupts in a special way. Userspace is responsible
+	  for acknowledging the hardware device if needed, and re-enabling
+	  interrupts in the interrupt controller using the write() syscall.
+
+	  If you don't know what to do here, say N.
+
+config UIO_DMEM_GENIRQ
+	tristate "Userspace platform driver with generic irq and dynamic memory"
+	depends on HAS_DMA
+	help
+	  Platform driver for Userspace I/O devices, including generic
+	  interrupt handling code. Shared interrupts are not supported.
+
+	  Memory regions can be specified with the same platform device
+	  resources as the UIO_PDRV drivers, but dynamic regions can also
+	  be specified.
+	  The number and size of these regions is static,
+	  but the memory allocation is not performed until
+	  the associated device file is opened. The
+	  memory is freed once the uio device is closed.
+
+	  If you don't know what to do here, say N.
+
+config UIO_AEC
+	tristate "AEC video timestamp device"
+	depends on PCI
+	help
+
+	  UIO driver for the Adrienne Electronics Corporation PCI time
+	  code device.
+
+	  This device differs from other UIO devices since it uses I/O
+	  ports instead of memory mapped I/O. In order to make it
+	  possible for UIO to work with this device a utility, uioport,
+	  can be used to read and write the ports:
+
+	    git clone git://ifup.org/philips/uioport.git
+
+	  If you compile this as a module, it will be called uio_aec.
+
+config UIO_SERCOS3
+	tristate "Automata Sercos III PCI card driver"
+	depends on PCI
+	help
+	  Userspace I/O interface for the Sercos III PCI card from
+	  Automata GmbH. The userspace part of this driver will be
+	  available for download from the Automata GmbH web site.
+
+	  Automata GmbH:        http://www.automataweb.com
+	  Sercos III interface: http://www.sercos.com
+
+	  If you compile this as a module, it will be called uio_sercos3.
+
+config UIO_PCI_GENERIC
+	tristate "Generic driver for PCI 2.3 and PCI Express cards"
+	depends on PCI
+	help
+	  Generic driver that you can bind, dynamically, to any
+	  PCI 2.3 compliant and PCI Express card. It is useful,
+	  primarily, for virtualization scenarios.
+	  If you compile this as a module, it will be called uio_pci_generic.
+
+config UIO_NETX
+	tristate "Hilscher NetX Card driver"
+	depends on PCI
+	help
+	  Driver for Hilscher NetX based fieldbus cards (cifX, comX).
+	  This driver requires a userspace component that comes with the card
+	  or is available from Hilscher (http://www.hilscher.com).
+
+	  To compile this driver as a module, choose M here; the module
+	  will be called uio_netx.
+
+config UIO_FSL_ELBC_GPCM
+	tristate "eLBC/GPCM driver"
+	depends on FSL_LBC
+	help
+	  Generic driver for accessing a peripheral connected to an eLBC port
+	  that is running in GPCM mode. GPCM is an interface for simple lower
+	  performance memories and memory-mapped devices. For devices using
+	  FCM or UPM eLBC modes, other device-specific drivers are available.
+
+config UIO_FSL_ELBC_GPCM_NETX5152
+	bool "eLBC/GPCM netX 51/52 support"
+	depends on UIO_FSL_ELBC_GPCM
+	help
+	  This will add support for netX 51/52 devices connected via eLBC/GPCM.
+	  In particular, it implements interrupt handling. This can be used
+	  together with the userspace netX stack from Hilscher.
+
+	  Information about this hardware can be found at:
+	  http://www.hilscher.com/netx
+
+config UIO_PRUSS
+	tristate "Texas Instruments PRUSS driver"
+	select GENERIC_ALLOCATOR
+	depends on HAS_IOMEM && HAS_DMA
+	help
+	  PRUSS driver for OMAPL138/DA850/AM18XX devices
+	  PRUSS driver requires user space components, examples and user space
+	  driver is available from below SVN repo - you may use anonymous login
+
+	  https://gforge.ti.com/gf/project/pru_sw/
+
+	  More info on API is available at below wiki
+
+	  http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called uio_pruss.
+
+config UIO_MF624
+	tristate "Humusoft MF624 DAQ PCI card driver"
+	depends on PCI
+	help
+	  Userspace I/O interface for the Humusoft MF624 PCI card.
+	  A sample userspace application using this driver is available
+	  (among other MF624 related information and software components)
+	  for download in a git repository:
+
+	    git clone git://rtime.felk.cvut.cz/mf6xx.git
+
+	  If you compile this as a module, it will be called uio_mf624.
+
+config UIO_HV_GENERIC
+	tristate "Generic driver for Hyper-V VMBus"
+	depends on HYPERV
+	help
+	  Generic driver that you can bind, dynamically, to any
+	  Hyper-V VMBus device. It is useful to provide direct access
+	  to network and storage devices from userspace.
+
+	  If you compile this as a module, it will be called uio_hv_generic.
+endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
new file mode 100644
index 0000000..c285dd2
--- /dev/null
+++ b/drivers/uio/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_UIO)	+= uio.o
+obj-$(CONFIG_UIO_CIF)	+= uio_cif.o
+obj-$(CONFIG_UIO_PDRV_GENIRQ)	+= uio_pdrv_genirq.o
+obj-$(CONFIG_UIO_DMEM_GENIRQ)	+= uio_dmem_genirq.o
+obj-$(CONFIG_UIO_AEC)	+= uio_aec.o
+obj-$(CONFIG_UIO_SERCOS3)	+= uio_sercos3.o
+obj-$(CONFIG_UIO_PCI_GENERIC)	+= uio_pci_generic.o
+obj-$(CONFIG_UIO_NETX)	+= uio_netx.o
+obj-$(CONFIG_UIO_PRUSS)         += uio_pruss.o
+obj-$(CONFIG_UIO_MF624)         += uio_mf624.o
+obj-$(CONFIG_UIO_FSL_ELBC_GPCM)	+= uio_fsl_elbc_gpcm.o
+obj-$(CONFIG_UIO_HV_GENERIC)	+= uio_hv_generic.o
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
new file mode 100644
index 0000000..2762148
--- /dev/null
+++ b/drivers/uio/uio.c
@@ -0,0 +1,1037 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/uio/uio.c
+ *
+ * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de>
+ * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright(C) 2006, Hans J. Koch <hjk@hansjkoch.de>
+ * Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Userspace IO
+ *
+ * Base Functions
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/idr.h>
+#include <linux/sched/signal.h>
+#include <linux/string.h>
+#include <linux/kobject.h>
+#include <linux/cdev.h>
+#include <linux/uio_driver.h>
+
+#define UIO_MAX_DEVICES		(1U << MINORBITS)
+
+static int uio_major;
+static struct cdev *uio_cdev;
+static DEFINE_IDR(uio_idr);
+static const struct file_operations uio_fops;
+
+/* Protect idr accesses */
+static DEFINE_MUTEX(minor_lock);
+
+/*
+ * attributes
+ */
+
+struct uio_map {
+	struct kobject kobj;
+	struct uio_mem *mem;
+};
+#define to_map(map) container_of(map, struct uio_map, kobj)
+
+static ssize_t map_name_show(struct uio_mem *mem, char *buf)
+{
+	if (unlikely(!mem->name))
+		mem->name = "";
+
+	return sprintf(buf, "%s\n", mem->name);
+}
+
+static ssize_t map_addr_show(struct uio_mem *mem, char *buf)
+{
+	return sprintf(buf, "%pa\n", &mem->addr);
+}
+
+static ssize_t map_size_show(struct uio_mem *mem, char *buf)
+{
+	return sprintf(buf, "%pa\n", &mem->size);
+}
+
+static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
+{
+	return sprintf(buf, "0x%llx\n", (unsigned long long)mem->offs);
+}
+
+struct map_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct uio_mem *, char *);
+	ssize_t (*store)(struct uio_mem *, const char *, size_t);
+};
+
+static struct map_sysfs_entry name_attribute =
+	__ATTR(name, S_IRUGO, map_name_show, NULL);
+static struct map_sysfs_entry addr_attribute =
+	__ATTR(addr, S_IRUGO, map_addr_show, NULL);
+static struct map_sysfs_entry size_attribute =
+	__ATTR(size, S_IRUGO, map_size_show, NULL);
+static struct map_sysfs_entry offset_attribute =
+	__ATTR(offset, S_IRUGO, map_offset_show, NULL);
+
+static struct attribute *attrs[] = {
+	&name_attribute.attr,
+	&addr_attribute.attr,
+	&size_attribute.attr,
+	&offset_attribute.attr,
+	NULL,	/* need to NULL terminate the list of attributes */
+};
+
+static void map_release(struct kobject *kobj)
+{
+	struct uio_map *map = to_map(kobj);
+	kfree(map);
+}
+
+static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr,
+			     char *buf)
+{
+	struct uio_map *map = to_map(kobj);
+	struct uio_mem *mem = map->mem;
+	struct map_sysfs_entry *entry;
+
+	entry = container_of(attr, struct map_sysfs_entry, attr);
+
+	if (!entry->show)
+		return -EIO;
+
+	return entry->show(mem, buf);
+}
+
+static const struct sysfs_ops map_sysfs_ops = {
+	.show = map_type_show,
+};
+
+static struct kobj_type map_attr_type = {
+	.release	= map_release,
+	.sysfs_ops	= &map_sysfs_ops,
+	.default_attrs	= attrs,
+};
+
+struct uio_portio {
+	struct kobject kobj;
+	struct uio_port *port;
+};
+#define to_portio(portio) container_of(portio, struct uio_portio, kobj)
+
+static ssize_t portio_name_show(struct uio_port *port, char *buf)
+{
+	if (unlikely(!port->name))
+		port->name = "";
+
+	return sprintf(buf, "%s\n", port->name);
+}
+
+static ssize_t portio_start_show(struct uio_port *port, char *buf)
+{
+	return sprintf(buf, "0x%lx\n", port->start);
+}
+
+static ssize_t portio_size_show(struct uio_port *port, char *buf)
+{
+	return sprintf(buf, "0x%lx\n", port->size);
+}
+
+static ssize_t portio_porttype_show(struct uio_port *port, char *buf)
+{
+	const char *porttypes[] = {"none", "x86", "gpio", "other"};
+
+	if ((port->porttype < 0) || (port->porttype > UIO_PORT_OTHER))
+		return -EINVAL;
+
+	return sprintf(buf, "port_%s\n", porttypes[port->porttype]);
+}
+
+struct portio_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct uio_port *, char *);
+	ssize_t (*store)(struct uio_port *, const char *, size_t);
+};
+
+static struct portio_sysfs_entry portio_name_attribute =
+	__ATTR(name, S_IRUGO, portio_name_show, NULL);
+static struct portio_sysfs_entry portio_start_attribute =
+	__ATTR(start, S_IRUGO, portio_start_show, NULL);
+static struct portio_sysfs_entry portio_size_attribute =
+	__ATTR(size, S_IRUGO, portio_size_show, NULL);
+static struct portio_sysfs_entry portio_porttype_attribute =
+	__ATTR(porttype, S_IRUGO, portio_porttype_show, NULL);
+
+static struct attribute *portio_attrs[] = {
+	&portio_name_attribute.attr,
+	&portio_start_attribute.attr,
+	&portio_size_attribute.attr,
+	&portio_porttype_attribute.attr,
+	NULL,
+};
+
+static void portio_release(struct kobject *kobj)
+{
+	struct uio_portio *portio = to_portio(kobj);
+	kfree(portio);
+}
+
+static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr,
+			     char *buf)
+{
+	struct uio_portio *portio = to_portio(kobj);
+	struct uio_port *port = portio->port;
+	struct portio_sysfs_entry *entry;
+
+	entry = container_of(attr, struct portio_sysfs_entry, attr);
+
+	if (!entry->show)
+		return -EIO;
+
+	return entry->show(port, buf);
+}
+
+static const struct sysfs_ops portio_sysfs_ops = {
+	.show = portio_type_show,
+};
+
+static struct kobj_type portio_attr_type = {
+	.release	= portio_release,
+	.sysfs_ops	= &portio_sysfs_ops,
+	.default_attrs	= portio_attrs,
+};
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct uio_device *idev = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info) {
+		ret = -EINVAL;
+		dev_err(dev, "the device has been unregistered\n");
+		goto out;
+	}
+
+	ret = sprintf(buf, "%s\n", idev->info->name);
+
+out:
+	mutex_unlock(&idev->info_lock);
+	return ret;
+}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t version_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct uio_device *idev = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info) {
+		ret = -EINVAL;
+		dev_err(dev, "the device has been unregistered\n");
+		goto out;
+	}
+
+	ret = sprintf(buf, "%s\n", idev->info->version);
+
+out:
+	mutex_unlock(&idev->info_lock);
+	return ret;
+}
+static DEVICE_ATTR_RO(version);
+
+static ssize_t event_show(struct device *dev,
+			  struct device_attribute *attr, char *buf)
+{
+	struct uio_device *idev = dev_get_drvdata(dev);
+	return sprintf(buf, "%u\n", (unsigned int)atomic_read(&idev->event));
+}
+static DEVICE_ATTR_RO(event);
+
+static struct attribute *uio_attrs[] = {
+	&dev_attr_name.attr,
+	&dev_attr_version.attr,
+	&dev_attr_event.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(uio);
+
+/* UIO class infrastructure */
+static struct class uio_class = {
+	.name = "uio",
+	.dev_groups = uio_groups,
+};
+
+bool uio_class_registered;
+
+/*
+ * device functions
+ */
+static int uio_dev_add_attributes(struct uio_device *idev)
+{
+	int ret;
+	int mi, pi;
+	int map_found = 0;
+	int portio_found = 0;
+	struct uio_mem *mem;
+	struct uio_map *map;
+	struct uio_port *port;
+	struct uio_portio *portio;
+
+	for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
+		mem = &idev->info->mem[mi];
+		if (mem->size == 0)
+			break;
+		if (!map_found) {
+			map_found = 1;
+			idev->map_dir = kobject_create_and_add("maps",
+							&idev->dev.kobj);
+			if (!idev->map_dir) {
+				ret = -ENOMEM;
+				goto err_map;
+			}
+		}
+		map = kzalloc(sizeof(*map), GFP_KERNEL);
+		if (!map) {
+			ret = -ENOMEM;
+			goto err_map;
+		}
+		kobject_init(&map->kobj, &map_attr_type);
+		map->mem = mem;
+		mem->map = map;
+		ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
+		if (ret)
+			goto err_map_kobj;
+		ret = kobject_uevent(&map->kobj, KOBJ_ADD);
+		if (ret)
+			goto err_map_kobj;
+	}
+
+	for (pi = 0; pi < MAX_UIO_PORT_REGIONS; pi++) {
+		port = &idev->info->port[pi];
+		if (port->size == 0)
+			break;
+		if (!portio_found) {
+			portio_found = 1;
+			idev->portio_dir = kobject_create_and_add("portio",
+							&idev->dev.kobj);
+			if (!idev->portio_dir) {
+				ret = -ENOMEM;
+				goto err_portio;
+			}
+		}
+		portio = kzalloc(sizeof(*portio), GFP_KERNEL);
+		if (!portio) {
+			ret = -ENOMEM;
+			goto err_portio;
+		}
+		kobject_init(&portio->kobj, &portio_attr_type);
+		portio->port = port;
+		port->portio = portio;
+		ret = kobject_add(&portio->kobj, idev->portio_dir,
+							"port%d", pi);
+		if (ret)
+			goto err_portio_kobj;
+		ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
+		if (ret)
+			goto err_portio_kobj;
+	}
+
+	return 0;
+
+err_portio:
+	pi--;
+err_portio_kobj:
+	for (; pi >= 0; pi--) {
+		port = &idev->info->port[pi];
+		portio = port->portio;
+		kobject_put(&portio->kobj);
+	}
+	kobject_put(idev->portio_dir);
+err_map:
+	mi--;
+err_map_kobj:
+	for (; mi >= 0; mi--) {
+		mem = &idev->info->mem[mi];
+		map = mem->map;
+		kobject_put(&map->kobj);
+	}
+	kobject_put(idev->map_dir);
+	dev_err(&idev->dev, "error creating sysfs files (%d)\n", ret);
+	return ret;
+}
+
+static void uio_dev_del_attributes(struct uio_device *idev)
+{
+	int i;
+	struct uio_mem *mem;
+	struct uio_port *port;
+
+	for (i = 0; i < MAX_UIO_MAPS; i++) {
+		mem = &idev->info->mem[i];
+		if (mem->size == 0)
+			break;
+		kobject_put(&mem->map->kobj);
+	}
+	kobject_put(idev->map_dir);
+
+	for (i = 0; i < MAX_UIO_PORT_REGIONS; i++) {
+		port = &idev->info->port[i];
+		if (port->size == 0)
+			break;
+		kobject_put(&port->portio->kobj);
+	}
+	kobject_put(idev->portio_dir);
+}
+
+static int uio_get_minor(struct uio_device *idev)
+{
+	int retval = -ENOMEM;
+
+	mutex_lock(&minor_lock);
+	retval = idr_alloc(&uio_idr, idev, 0, UIO_MAX_DEVICES, GFP_KERNEL);
+	if (retval >= 0) {
+		idev->minor = retval;
+		retval = 0;
+	} else if (retval == -ENOSPC) {
+		dev_err(&idev->dev, "too many uio devices\n");
+		retval = -EINVAL;
+	}
+	mutex_unlock(&minor_lock);
+	return retval;
+}
+
+static void uio_free_minor(struct uio_device *idev)
+{
+	mutex_lock(&minor_lock);
+	idr_remove(&uio_idr, idev->minor);
+	mutex_unlock(&minor_lock);
+}
+
+/**
+ * uio_event_notify - trigger an interrupt event
+ * @info: UIO device capabilities
+ */
+void uio_event_notify(struct uio_info *info)
+{
+	struct uio_device *idev = info->uio_dev;
+
+	atomic_inc(&idev->event);
+	wake_up_interruptible(&idev->wait);
+	kill_fasync(&idev->async_queue, SIGIO, POLL_IN);
+}
+EXPORT_SYMBOL_GPL(uio_event_notify);
+
+/**
+ * uio_interrupt - hardware interrupt handler
+ * @irq: IRQ number, can be UIO_IRQ_CYCLIC for cyclic timer
+ * @dev_id: Pointer to the devices uio_device structure
+ */
+static irqreturn_t uio_interrupt(int irq, void *dev_id)
+{
+	struct uio_device *idev = (struct uio_device *)dev_id;
+	irqreturn_t ret;
+
+	ret = idev->info->handler(irq, idev->info);
+	if (ret == IRQ_HANDLED)
+		uio_event_notify(idev->info);
+
+	return ret;
+}
+
+struct uio_listener {
+	struct uio_device *dev;
+	s32 event_count;
+};
+
+static int uio_open(struct inode *inode, struct file *filep)
+{
+	struct uio_device *idev;
+	struct uio_listener *listener;
+	int ret = 0;
+
+	mutex_lock(&minor_lock);
+	idev = idr_find(&uio_idr, iminor(inode));
+	mutex_unlock(&minor_lock);
+	if (!idev) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	get_device(&idev->dev);
+
+	if (!try_module_get(idev->owner)) {
+		ret = -ENODEV;
+		goto err_module_get;
+	}
+
+	listener = kmalloc(sizeof(*listener), GFP_KERNEL);
+	if (!listener) {
+		ret = -ENOMEM;
+		goto err_alloc_listener;
+	}
+
+	listener->dev = idev;
+	listener->event_count = atomic_read(&idev->event);
+	filep->private_data = listener;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info) {
+		mutex_unlock(&idev->info_lock);
+		ret = -EINVAL;
+		goto err_alloc_listener;
+	}
+
+	if (idev->info && idev->info->open)
+		ret = idev->info->open(idev->info, inode);
+	mutex_unlock(&idev->info_lock);
+	if (ret)
+		goto err_infoopen;
+
+	return 0;
+
+err_infoopen:
+	kfree(listener);
+
+err_alloc_listener:
+	module_put(idev->owner);
+
+err_module_get:
+	put_device(&idev->dev);
+
+out:
+	return ret;
+}
+
+static int uio_fasync(int fd, struct file *filep, int on)
+{
+	struct uio_listener *listener = filep->private_data;
+	struct uio_device *idev = listener->dev;
+
+	return fasync_helper(fd, filep, on, &idev->async_queue);
+}
+
+static int uio_release(struct inode *inode, struct file *filep)
+{
+	int ret = 0;
+	struct uio_listener *listener = filep->private_data;
+	struct uio_device *idev = listener->dev;
+
+	mutex_lock(&idev->info_lock);
+	if (idev->info && idev->info->release)
+		ret = idev->info->release(idev->info, inode);
+	mutex_unlock(&idev->info_lock);
+
+	module_put(idev->owner);
+	kfree(listener);
+	put_device(&idev->dev);
+	return ret;
+}
+
+static __poll_t uio_poll(struct file *filep, poll_table *wait)
+{
+	struct uio_listener *listener = filep->private_data;
+	struct uio_device *idev = listener->dev;
+	__poll_t ret = 0;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info || !idev->info->irq)
+		ret = -EIO;
+	mutex_unlock(&idev->info_lock);
+
+	if (ret)
+		return ret;
+
+	poll_wait(filep, &idev->wait, wait);
+	if (listener->event_count != atomic_read(&idev->event))
+		return EPOLLIN | EPOLLRDNORM;
+	return 0;
+}
+
+static ssize_t uio_read(struct file *filep, char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	struct uio_listener *listener = filep->private_data;
+	struct uio_device *idev = listener->dev;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t retval = 0;
+	s32 event_count;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info || !idev->info->irq)
+		retval = -EIO;
+	mutex_unlock(&idev->info_lock);
+
+	if (retval)
+		return retval;
+
+	if (count != sizeof(s32))
+		return -EINVAL;
+
+	add_wait_queue(&idev->wait, &wait);
+
+	do {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		event_count = atomic_read(&idev->event);
+		if (event_count != listener->event_count) {
+			__set_current_state(TASK_RUNNING);
+			if (copy_to_user(buf, &event_count, count))
+				retval = -EFAULT;
+			else {
+				listener->event_count = event_count;
+				retval = count;
+			}
+			break;
+		}
+
+		if (filep->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	} while (1);
+
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(&idev->wait, &wait);
+
+	return retval;
+}
+
+static ssize_t uio_write(struct file *filep, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	struct uio_listener *listener = filep->private_data;
+	struct uio_device *idev = listener->dev;
+	ssize_t retval;
+	s32 irq_on;
+
+	if (count != sizeof(s32))
+		return -EINVAL;
+
+	if (copy_from_user(&irq_on, buf, count))
+		return -EFAULT;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	if (!idev->info || !idev->info->irq) {
+		retval = -EIO;
+		goto out;
+	}
+
+	if (!idev->info->irqcontrol) {
+		retval = -ENOSYS;
+		goto out;
+	}
+
+	retval = idev->info->irqcontrol(idev->info, irq_on);
+
+out:
+	mutex_unlock(&idev->info_lock);
+	return retval ? retval : sizeof(s32);
+}
+
+static int uio_find_mem_index(struct vm_area_struct *vma)
+{
+	struct uio_device *idev = vma->vm_private_data;
+
+	if (vma->vm_pgoff < MAX_UIO_MAPS) {
+		if (idev->info->mem[vma->vm_pgoff].size == 0)
+			return -1;
+		return (int)vma->vm_pgoff;
+	}
+	return -1;
+}
+
+static vm_fault_t uio_vma_fault(struct vm_fault *vmf)
+{
+	struct uio_device *idev = vmf->vma->vm_private_data;
+	struct page *page;
+	unsigned long offset;
+	void *addr;
+	int ret = 0;
+	int mi;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info) {
+		ret = VM_FAULT_SIGBUS;
+		goto out;
+	}
+
+	mi = uio_find_mem_index(vmf->vma);
+	if (mi < 0) {
+		ret = VM_FAULT_SIGBUS;
+		goto out;
+	}
+
+	/*
+	 * We need to subtract mi because userspace uses offset = N*PAGE_SIZE
+	 * to use mem[N].
+	 */
+	offset = (vmf->pgoff - mi) << PAGE_SHIFT;
+
+	addr = (void *)(unsigned long)idev->info->mem[mi].addr + offset;
+	if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
+		page = virt_to_page(addr);
+	else
+		page = vmalloc_to_page(addr);
+	get_page(page);
+	vmf->page = page;
+
+out:
+	mutex_unlock(&idev->info_lock);
+
+	return ret;
+}
+
+static const struct vm_operations_struct uio_logical_vm_ops = {
+	.fault = uio_vma_fault,
+};
+
+static int uio_mmap_logical(struct vm_area_struct *vma)
+{
+	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+	vma->vm_ops = &uio_logical_vm_ops;
+	return 0;
+}
+
+static const struct vm_operations_struct uio_physical_vm_ops = {
+#ifdef CONFIG_HAVE_IOREMAP_PROT
+	.access = generic_access_phys,
+#endif
+};
+
+static int uio_mmap_physical(struct vm_area_struct *vma)
+{
+	struct uio_device *idev = vma->vm_private_data;
+	int mi = uio_find_mem_index(vma);
+	struct uio_mem *mem;
+
+	if (mi < 0)
+		return -EINVAL;
+	mem = idev->info->mem + mi;
+
+	if (mem->addr & ~PAGE_MASK)
+		return -ENODEV;
+	if (vma->vm_end - vma->vm_start > mem->size)
+		return -EINVAL;
+
+	vma->vm_ops = &uio_physical_vm_ops;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	/*
+	 * We cannot use the vm_iomap_memory() helper here,
+	 * because vma->vm_pgoff is the map index we looked
+	 * up above in uio_find_mem_index(), rather than an
+	 * actual page offset into the mmap.
+	 *
+	 * So we just do the physical mmap without a page
+	 * offset.
+	 */
+	return remap_pfn_range(vma,
+			       vma->vm_start,
+			       mem->addr >> PAGE_SHIFT,
+			       vma->vm_end - vma->vm_start,
+			       vma->vm_page_prot);
+}
+
+static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+	struct uio_listener *listener = filep->private_data;
+	struct uio_device *idev = listener->dev;
+	int mi;
+	unsigned long requested_pages, actual_pages;
+	int ret = 0;
+
+	if (vma->vm_end < vma->vm_start)
+		return -EINVAL;
+
+	vma->vm_private_data = idev;
+
+	mutex_lock(&idev->info_lock);
+	if (!idev->info) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	mi = uio_find_mem_index(vma);
+	if (mi < 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	requested_pages = vma_pages(vma);
+	actual_pages = ((idev->info->mem[mi].addr & ~PAGE_MASK)
+			+ idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT;
+	if (requested_pages > actual_pages) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (idev->info->mmap) {
+		ret = idev->info->mmap(idev->info, vma);
+		goto out;
+	}
+
+	switch (idev->info->mem[mi].memtype) {
+		case UIO_MEM_PHYS:
+			ret = uio_mmap_physical(vma);
+			break;
+		case UIO_MEM_LOGICAL:
+		case UIO_MEM_VIRTUAL:
+			ret = uio_mmap_logical(vma);
+			break;
+		default:
+			ret = -EINVAL;
+	}
+
+out:
+	mutex_unlock(&idev->info_lock);
+	return ret;
+}
+
+static const struct file_operations uio_fops = {
+	.owner		= THIS_MODULE,
+	.open		= uio_open,
+	.release	= uio_release,
+	.read		= uio_read,
+	.write		= uio_write,
+	.mmap		= uio_mmap,
+	.poll		= uio_poll,
+	.fasync		= uio_fasync,
+	.llseek		= noop_llseek,
+};
+
+static int uio_major_init(void)
+{
+	static const char name[] = "uio";
+	struct cdev *cdev = NULL;
+	dev_t uio_dev = 0;
+	int result;
+
+	result = alloc_chrdev_region(&uio_dev, 0, UIO_MAX_DEVICES, name);
+	if (result)
+		goto out;
+
+	result = -ENOMEM;
+	cdev = cdev_alloc();
+	if (!cdev)
+		goto out_unregister;
+
+	cdev->owner = THIS_MODULE;
+	cdev->ops = &uio_fops;
+	kobject_set_name(&cdev->kobj, "%s", name);
+
+	result = cdev_add(cdev, uio_dev, UIO_MAX_DEVICES);
+	if (result)
+		goto out_put;
+
+	uio_major = MAJOR(uio_dev);
+	uio_cdev = cdev;
+	return 0;
+out_put:
+	kobject_put(&cdev->kobj);
+out_unregister:
+	unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES);
+out:
+	return result;
+}
+
+static void uio_major_cleanup(void)
+{
+	unregister_chrdev_region(MKDEV(uio_major, 0), UIO_MAX_DEVICES);
+	cdev_del(uio_cdev);
+}
+
+static int init_uio_class(void)
+{
+	int ret;
+
+	/* This is the first time in here, set everything up properly */
+	ret = uio_major_init();
+	if (ret)
+		goto exit;
+
+	ret = class_register(&uio_class);
+	if (ret) {
+		printk(KERN_ERR "class_register failed for uio\n");
+		goto err_class_register;
+	}
+
+	uio_class_registered = true;
+
+	return 0;
+
+err_class_register:
+	uio_major_cleanup();
+exit:
+	return ret;
+}
+
+static void release_uio_class(void)
+{
+	uio_class_registered = false;
+	class_unregister(&uio_class);
+	uio_major_cleanup();
+}
+
+static void uio_device_release(struct device *dev)
+{
+	struct uio_device *idev = dev_get_drvdata(dev);
+
+	kfree(idev);
+}
+
+/**
+ * uio_register_device - register a new userspace IO device
+ * @owner:	module that creates the new device
+ * @parent:	parent device
+ * @info:	UIO device capabilities
+ *
+ * returns zero on success or a negative error code.
+ */
+int __uio_register_device(struct module *owner,
+			  struct device *parent,
+			  struct uio_info *info)
+{
+	struct uio_device *idev;
+	int ret = 0;
+
+	if (!uio_class_registered)
+		return -EPROBE_DEFER;
+
+	if (!parent || !info || !info->name || !info->version)
+		return -EINVAL;
+
+	info->uio_dev = NULL;
+
+	idev = kzalloc(sizeof(*idev), GFP_KERNEL);
+	if (!idev) {
+		return -ENOMEM;
+	}
+
+	idev->owner = owner;
+	idev->info = info;
+	mutex_init(&idev->info_lock);
+	init_waitqueue_head(&idev->wait);
+	atomic_set(&idev->event, 0);
+
+	ret = uio_get_minor(idev);
+	if (ret)
+		return ret;
+
+	idev->dev.devt = MKDEV(uio_major, idev->minor);
+	idev->dev.class = &uio_class;
+	idev->dev.parent = parent;
+	idev->dev.release = uio_device_release;
+	dev_set_drvdata(&idev->dev, idev);
+
+	ret = dev_set_name(&idev->dev, "uio%d", idev->minor);
+	if (ret)
+		goto err_device_create;
+
+	ret = device_register(&idev->dev);
+	if (ret)
+		goto err_device_create;
+
+	ret = uio_dev_add_attributes(idev);
+	if (ret)
+		goto err_uio_dev_add_attributes;
+
+	info->uio_dev = idev;
+
+	if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
+		/*
+		 * Note that we deliberately don't use devm_request_irq
+		 * here. The parent module can unregister the UIO device
+		 * and call pci_disable_msi, which requires that this
+		 * irq has been freed. However, the device may have open
+		 * FDs at the time of unregister and therefore may not be
+		 * freed until they are released.
+		 */
+		ret = request_irq(info->irq, uio_interrupt,
+				  info->irq_flags, info->name, idev);
+		if (ret) {
+			info->uio_dev = NULL;
+			goto err_request_irq;
+		}
+	}
+
+	return 0;
+
+err_request_irq:
+	uio_dev_del_attributes(idev);
+err_uio_dev_add_attributes:
+	device_unregister(&idev->dev);
+err_device_create:
+	uio_free_minor(idev);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(__uio_register_device);
+
+/**
+ * uio_unregister_device - unregister a industrial IO device
+ * @info:	UIO device capabilities
+ *
+ */
+void uio_unregister_device(struct uio_info *info)
+{
+	struct uio_device *idev;
+
+	if (!info || !info->uio_dev)
+		return;
+
+	idev = info->uio_dev;
+
+	uio_free_minor(idev);
+
+	mutex_lock(&idev->info_lock);
+	uio_dev_del_attributes(idev);
+
+	if (info->irq && info->irq != UIO_IRQ_CUSTOM)
+		free_irq(info->irq, idev);
+
+	idev->info = NULL;
+	mutex_unlock(&idev->info_lock);
+
+	device_unregister(&idev->dev);
+
+	return;
+}
+EXPORT_SYMBOL_GPL(uio_unregister_device);
+
+static int __init uio_init(void)
+{
+	return init_uio_class();
+}
+
+static void __exit uio_exit(void)
+{
+	release_uio_class();
+	idr_destroy(&uio_idr);
+}
+
+module_init(uio_init)
+module_exit(uio_exit)
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c
new file mode 100644
index 0000000..1549fab
--- /dev/null
+++ b/drivers/uio/uio_aec.c
@@ -0,0 +1,163 @@
+/*
+ * uio_aec.c -- simple driver for Adrienne Electronics Corp time code PCI device
+ *
+ * Copyright (C) 2008 Brandon Philips <brandon@ifup.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify it
+ *   under the terms of the GNU General Public License version 2 as published
+ *   by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License along
+ *   with this program; if not, write to the Free Software Foundation, Inc., 59
+ *   Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/uio_driver.h>
+#include <linux/slab.h>
+
+#define PCI_VENDOR_ID_AEC 0xaecb
+#define PCI_DEVICE_ID_AEC_VITCLTC 0x6250
+
+#define INT_ENABLE_ADDR		0xFC
+#define INT_ENABLE		0x10
+#define INT_DISABLE		0x0
+
+#define INT_MASK_ADDR		0x2E
+#define INT_MASK_ALL		0x3F
+
+#define INTA_DRVR_ADDR		0xFE
+#define INTA_ENABLED_FLAG	0x08
+#define INTA_FLAG		0x01
+
+#define MAILBOX			0x0F
+
+static struct pci_device_id ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_AEC, PCI_DEVICE_ID_AEC_VITCLTC), },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ids);
+
+static irqreturn_t aectc_irq(int irq, struct uio_info *dev_info)
+{
+	void __iomem *int_flag = dev_info->priv + INTA_DRVR_ADDR;
+	unsigned char status = ioread8(int_flag);
+
+
+	if ((status & INTA_ENABLED_FLAG) && (status & INTA_FLAG)) {
+		/* application writes 0x00 to 0x2F to get next interrupt */
+		status = ioread8(dev_info->priv + MAILBOX);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void print_board_data(struct pci_dev *pdev, struct uio_info *i)
+{
+	dev_info(&pdev->dev, "PCI-TC board vendor: %x%x number: %x%x"
+		" revision: %c%c\n",
+		ioread8(i->priv + 0x01),
+		ioread8(i->priv + 0x00),
+		ioread8(i->priv + 0x03),
+		ioread8(i->priv + 0x02),
+		ioread8(i->priv + 0x06),
+		ioread8(i->priv + 0x07));
+}
+
+static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct uio_info *info;
+	int ret;
+
+	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	if (pci_enable_device(pdev))
+		goto out_free;
+
+	if (pci_request_regions(pdev, "aectc"))
+		goto out_disable;
+
+	info->name = "aectc";
+	info->port[0].start = pci_resource_start(pdev, 0);
+	if (!info->port[0].start)
+		goto out_release;
+	info->priv = pci_iomap(pdev, 0, 0);
+	if (!info->priv)
+		goto out_release;
+	info->port[0].size = pci_resource_len(pdev, 0);
+	info->port[0].porttype = UIO_PORT_GPIO;
+
+	info->version = "0.0.1";
+	info->irq = pdev->irq;
+	info->irq_flags = IRQF_SHARED;
+	info->handler = aectc_irq;
+
+	print_board_data(pdev, info);
+	ret = uio_register_device(&pdev->dev, info);
+	if (ret)
+		goto out_unmap;
+
+	iowrite32(INT_ENABLE, info->priv + INT_ENABLE_ADDR);
+	iowrite8(INT_MASK_ALL, info->priv + INT_MASK_ADDR);
+	if (!(ioread8(info->priv + INTA_DRVR_ADDR)
+			& INTA_ENABLED_FLAG))
+		dev_err(&pdev->dev, "aectc: interrupts not enabled\n");
+
+	pci_set_drvdata(pdev, info);
+
+	return 0;
+
+out_unmap:
+	pci_iounmap(pdev, info->priv);
+out_release:
+	pci_release_regions(pdev);
+out_disable:
+	pci_disable_device(pdev);
+out_free:
+	kfree(info);
+	return -ENODEV;
+}
+
+static void remove(struct pci_dev *pdev)
+{
+	struct uio_info *info = pci_get_drvdata(pdev);
+
+	/* disable interrupts */
+	iowrite8(INT_DISABLE, info->priv + INT_MASK_ADDR);
+	iowrite32(INT_DISABLE, info->priv + INT_ENABLE_ADDR);
+	/* read mailbox to ensure board drops irq */
+	ioread8(info->priv + MAILBOX);
+
+	uio_unregister_device(info);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	iounmap(info->priv);
+
+	kfree(info);
+}
+
+static struct pci_driver pci_driver = {
+	.name = "aectc",
+	.id_table = ids,
+	.probe = probe,
+	.remove = remove,
+};
+
+module_pci_driver(pci_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c
new file mode 100644
index 0000000..ab60186
--- /dev/null
+++ b/drivers/uio/uio_cif.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UIO Hilscher CIF card driver
+ *
+ * (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
+ * Original code (C) 2005 Benedikt Spranger <b.spranger@linutronix.de>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/uio_driver.h>
+
+#include <asm/io.h>
+
+#define PLX9030_INTCSR		0x4C
+#define INTSCR_INT1_ENABLE	0x01
+#define INTSCR_INT1_STATUS	0x04
+#define INT1_ENABLED_AND_ACTIVE	(INTSCR_INT1_ENABLE | INTSCR_INT1_STATUS)
+
+#define PCI_SUBVENDOR_ID_PEP	0x1518
+#define CIF_SUBDEVICE_PROFIBUS	0x430
+#define CIF_SUBDEVICE_DEVICENET	0x432
+
+
+static irqreturn_t hilscher_handler(int irq, struct uio_info *dev_info)
+{
+	void __iomem *plx_intscr = dev_info->mem[0].internal_addr
+					+ PLX9030_INTCSR;
+
+	if ((ioread8(plx_intscr) & INT1_ENABLED_AND_ACTIVE)
+	    != INT1_ENABLED_AND_ACTIVE)
+		return IRQ_NONE;
+
+	/* Disable interrupt */
+	iowrite8(ioread8(plx_intscr) & ~INTSCR_INT1_ENABLE, plx_intscr);
+	return IRQ_HANDLED;
+}
+
+static int hilscher_pci_probe(struct pci_dev *dev,
+					const struct pci_device_id *id)
+{
+	struct uio_info *info;
+
+	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	if (pci_enable_device(dev))
+		goto out_free;
+
+	if (pci_request_regions(dev, "hilscher"))
+		goto out_disable;
+
+	info->mem[0].addr = pci_resource_start(dev, 0);
+	if (!info->mem[0].addr)
+		goto out_release;
+	info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
+	if (!info->mem[0].internal_addr)
+		goto out_release;
+
+	info->mem[0].size = pci_resource_len(dev, 0);
+	info->mem[0].memtype = UIO_MEM_PHYS;
+	info->mem[1].addr = pci_resource_start(dev, 2);
+	info->mem[1].size = pci_resource_len(dev, 2);
+	info->mem[1].memtype = UIO_MEM_PHYS;
+	switch (id->subdevice) {
+		case CIF_SUBDEVICE_PROFIBUS:
+			info->name = "CIF_Profibus";
+			break;
+		case CIF_SUBDEVICE_DEVICENET:
+			info->name = "CIF_Devicenet";
+			break;
+		default:
+			info->name = "CIF_???";
+	}
+	info->version = "0.0.1";
+	info->irq = dev->irq;
+	info->irq_flags = IRQF_SHARED;
+	info->handler = hilscher_handler;
+
+	if (uio_register_device(&dev->dev, info))
+		goto out_unmap;
+
+	pci_set_drvdata(dev, info);
+
+	return 0;
+out_unmap:
+	iounmap(info->mem[0].internal_addr);
+out_release:
+	pci_release_regions(dev);
+out_disable:
+	pci_disable_device(dev);
+out_free:
+	kfree (info);
+	return -ENODEV;
+}
+
+static void hilscher_pci_remove(struct pci_dev *dev)
+{
+	struct uio_info *info = pci_get_drvdata(dev);
+
+	uio_unregister_device(info);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	iounmap(info->mem[0].internal_addr);
+
+	kfree (info);
+}
+
+static struct pci_device_id hilscher_pci_ids[] = {
+	{
+		.vendor =	PCI_VENDOR_ID_PLX,
+		.device =	PCI_DEVICE_ID_PLX_9030,
+		.subvendor =	PCI_SUBVENDOR_ID_PEP,
+		.subdevice =	CIF_SUBDEVICE_PROFIBUS,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_PLX,
+		.device =	PCI_DEVICE_ID_PLX_9030,
+		.subvendor =	PCI_SUBVENDOR_ID_PEP,
+		.subdevice =	CIF_SUBDEVICE_DEVICENET,
+	},
+	{ 0, }
+};
+
+static struct pci_driver hilscher_pci_driver = {
+	.name = "hilscher",
+	.id_table = hilscher_pci_ids,
+	.probe = hilscher_pci_probe,
+	.remove = hilscher_pci_remove,
+};
+
+module_pci_driver(hilscher_pci_driver);
+MODULE_DEVICE_TABLE(pci, hilscher_pci_ids);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Hans J. Koch, Benedikt Spranger");
diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
new file mode 100644
index 0000000..e1134a4
--- /dev/null
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -0,0 +1,356 @@
+/*
+ * drivers/uio/uio_dmem_genirq.c
+ *
+ * Userspace I/O platform driver with generic IRQ handling code.
+ *
+ * Copyright (C) 2012 Damian Hobson-Garcia
+ *
+ * Based on uio_pdrv_genirq.c by Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/uio_dmem_genirq.h>
+#include <linux/stringify.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+
+#define DRIVER_NAME "uio_dmem_genirq"
+#define DMEM_MAP_ERROR (~0)
+
+struct uio_dmem_genirq_platdata {
+	struct uio_info *uioinfo;
+	spinlock_t lock;
+	unsigned long flags;
+	struct platform_device *pdev;
+	unsigned int dmem_region_start;
+	unsigned int num_dmem_regions;
+	void *dmem_region_vaddr[MAX_UIO_MAPS];
+	struct mutex alloc_lock;
+	unsigned int refcnt;
+};
+
+static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
+{
+	struct uio_dmem_genirq_platdata *priv = info->priv;
+	struct uio_mem *uiomem;
+	int ret = 0;
+	int dmem_region = priv->dmem_region_start;
+
+	uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
+
+	mutex_lock(&priv->alloc_lock);
+	while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
+		void *addr;
+		if (!uiomem->size)
+			break;
+
+		addr = dma_alloc_coherent(&priv->pdev->dev, uiomem->size,
+				(dma_addr_t *)&uiomem->addr, GFP_KERNEL);
+		if (!addr) {
+			uiomem->addr = DMEM_MAP_ERROR;
+		}
+		priv->dmem_region_vaddr[dmem_region++] = addr;
+		++uiomem;
+	}
+	priv->refcnt++;
+
+	mutex_unlock(&priv->alloc_lock);
+	/* Wait until the Runtime PM code has woken up the device */
+	pm_runtime_get_sync(&priv->pdev->dev);
+	return ret;
+}
+
+static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
+{
+	struct uio_dmem_genirq_platdata *priv = info->priv;
+	struct uio_mem *uiomem;
+	int dmem_region = priv->dmem_region_start;
+
+	/* Tell the Runtime PM code that the device has become idle */
+	pm_runtime_put_sync(&priv->pdev->dev);
+
+	uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
+
+	mutex_lock(&priv->alloc_lock);
+
+	priv->refcnt--;
+	while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
+		if (!uiomem->size)
+			break;
+		if (priv->dmem_region_vaddr[dmem_region]) {
+			dma_free_coherent(&priv->pdev->dev, uiomem->size,
+					priv->dmem_region_vaddr[dmem_region],
+					uiomem->addr);
+		}
+		uiomem->addr = DMEM_MAP_ERROR;
+		++dmem_region;
+		++uiomem;
+	}
+
+	mutex_unlock(&priv->alloc_lock);
+	return 0;
+}
+
+static irqreturn_t uio_dmem_genirq_handler(int irq, struct uio_info *dev_info)
+{
+	struct uio_dmem_genirq_platdata *priv = dev_info->priv;
+
+	/* Just disable the interrupt in the interrupt controller, and
+	 * remember the state so we can allow user space to enable it later.
+	 */
+
+	if (!test_and_set_bit(0, &priv->flags))
+		disable_irq_nosync(irq);
+
+	return IRQ_HANDLED;
+}
+
+static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
+{
+	struct uio_dmem_genirq_platdata *priv = dev_info->priv;
+	unsigned long flags;
+
+	/* Allow user space to enable and disable the interrupt
+	 * in the interrupt controller, but keep track of the
+	 * state to prevent per-irq depth damage.
+	 *
+	 * Serialize this operation to support multiple tasks.
+	 */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (irq_on) {
+		if (test_and_clear_bit(0, &priv->flags))
+			enable_irq(dev_info->irq);
+	} else {
+		if (!test_and_set_bit(0, &priv->flags))
+			disable_irq(dev_info->irq);
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int uio_dmem_genirq_probe(struct platform_device *pdev)
+{
+	struct uio_dmem_genirq_pdata *pdata = dev_get_platdata(&pdev->dev);
+	struct uio_info *uioinfo = &pdata->uioinfo;
+	struct uio_dmem_genirq_platdata *priv;
+	struct uio_mem *uiomem;
+	int ret = -EINVAL;
+	int i;
+
+	if (pdev->dev.of_node) {
+		int irq;
+
+		/* alloc uioinfo for one device */
+		uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
+		if (!uioinfo) {
+			ret = -ENOMEM;
+			dev_err(&pdev->dev, "unable to kmalloc\n");
+			goto bad2;
+		}
+		uioinfo->name = pdev->dev.of_node->name;
+		uioinfo->version = "devicetree";
+
+		/* Multiple IRQs are not supported */
+		irq = platform_get_irq(pdev, 0);
+		if (irq == -ENXIO)
+			uioinfo->irq = UIO_IRQ_NONE;
+		else
+			uioinfo->irq = irq;
+	}
+
+	if (!uioinfo || !uioinfo->name || !uioinfo->version) {
+		dev_err(&pdev->dev, "missing platform_data\n");
+		goto bad0;
+	}
+
+	if (uioinfo->handler || uioinfo->irqcontrol ||
+	    uioinfo->irq_flags & IRQF_SHARED) {
+		dev_err(&pdev->dev, "interrupt configuration error\n");
+		goto bad0;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "unable to kmalloc\n");
+		goto bad0;
+	}
+
+	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+	priv->uioinfo = uioinfo;
+	spin_lock_init(&priv->lock);
+	priv->flags = 0; /* interrupt is enabled to begin with */
+	priv->pdev = pdev;
+	mutex_init(&priv->alloc_lock);
+
+	if (!uioinfo->irq) {
+		ret = platform_get_irq(pdev, 0);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to get IRQ\n");
+			goto bad1;
+		}
+		uioinfo->irq = ret;
+	}
+	uiomem = &uioinfo->mem[0];
+
+	for (i = 0; i < pdev->num_resources; ++i) {
+		struct resource *r = &pdev->resource[i];
+
+		if (r->flags != IORESOURCE_MEM)
+			continue;
+
+		if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
+			dev_warn(&pdev->dev, "device has more than "
+					__stringify(MAX_UIO_MAPS)
+					" I/O memory resources.\n");
+			break;
+		}
+
+		uiomem->memtype = UIO_MEM_PHYS;
+		uiomem->addr = r->start;
+		uiomem->size = resource_size(r);
+		++uiomem;
+	}
+
+	priv->dmem_region_start = uiomem - &uioinfo->mem[0];
+	priv->num_dmem_regions = pdata->num_dynamic_regions;
+
+	for (i = 0; i < pdata->num_dynamic_regions; ++i) {
+		if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
+			dev_warn(&pdev->dev, "device has more than "
+					__stringify(MAX_UIO_MAPS)
+					" dynamic and fixed memory regions.\n");
+			break;
+		}
+		uiomem->memtype = UIO_MEM_PHYS;
+		uiomem->addr = DMEM_MAP_ERROR;
+		uiomem->size = pdata->dynamic_region_sizes[i];
+		++uiomem;
+	}
+
+	while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
+		uiomem->size = 0;
+		++uiomem;
+	}
+
+	/* This driver requires no hardware specific kernel code to handle
+	 * interrupts. Instead, the interrupt handler simply disables the
+	 * interrupt in the interrupt controller. User space is responsible
+	 * for performing hardware specific acknowledge and re-enabling of
+	 * the interrupt in the interrupt controller.
+	 *
+	 * Interrupt sharing is not supported.
+	 */
+
+	uioinfo->handler = uio_dmem_genirq_handler;
+	uioinfo->irqcontrol = uio_dmem_genirq_irqcontrol;
+	uioinfo->open = uio_dmem_genirq_open;
+	uioinfo->release = uio_dmem_genirq_release;
+	uioinfo->priv = priv;
+
+	/* Enable Runtime PM for this device:
+	 * The device starts in suspended state to allow the hardware to be
+	 * turned off by default. The Runtime PM bus code should power on the
+	 * hardware and enable clocks at open().
+	 */
+	pm_runtime_enable(&pdev->dev);
+
+	ret = uio_register_device(&pdev->dev, priv->uioinfo);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register uio device\n");
+		pm_runtime_disable(&pdev->dev);
+		goto bad1;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+ bad1:
+	kfree(priv);
+ bad0:
+	/* kfree uioinfo for OF */
+	if (pdev->dev.of_node)
+		kfree(uioinfo);
+ bad2:
+	return ret;
+}
+
+static int uio_dmem_genirq_remove(struct platform_device *pdev)
+{
+	struct uio_dmem_genirq_platdata *priv = platform_get_drvdata(pdev);
+
+	uio_unregister_device(priv->uioinfo);
+	pm_runtime_disable(&pdev->dev);
+
+	priv->uioinfo->handler = NULL;
+	priv->uioinfo->irqcontrol = NULL;
+
+	/* kfree uioinfo for OF */
+	if (pdev->dev.of_node)
+		kfree(priv->uioinfo);
+
+	kfree(priv);
+	return 0;
+}
+
+static int uio_dmem_genirq_runtime_nop(struct device *dev)
+{
+	/* Runtime PM callback shared between ->runtime_suspend()
+	 * and ->runtime_resume(). Simply returns success.
+	 *
+	 * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
+	 * are used at open() and release() time. This allows the
+	 * Runtime PM code to turn off power to the device while the
+	 * device is unused, ie before open() and after release().
+	 *
+	 * This Runtime PM callback does not need to save or restore
+	 * any registers since user space is responsbile for hardware
+	 * register reinitialization after open().
+	 */
+	return 0;
+}
+
+static const struct dev_pm_ops uio_dmem_genirq_dev_pm_ops = {
+	.runtime_suspend = uio_dmem_genirq_runtime_nop,
+	.runtime_resume = uio_dmem_genirq_runtime_nop,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id uio_of_genirq_match[] = {
+	{ /* empty for now */ },
+};
+MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
+#endif
+
+static struct platform_driver uio_dmem_genirq = {
+	.probe = uio_dmem_genirq_probe,
+	.remove = uio_dmem_genirq_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.pm = &uio_dmem_genirq_dev_pm_ops,
+		.of_match_table = of_match_ptr(uio_of_genirq_match),
+	},
+};
+
+module_platform_driver(uio_dmem_genirq);
+
+MODULE_AUTHOR("Damian Hobson-Garcia");
+MODULE_DESCRIPTION("Userspace I/O platform driver with dynamic memory.");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c
new file mode 100644
index 0000000..bbc17ef
--- /dev/null
+++ b/drivers/uio/uio_fsl_elbc_gpcm.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0
+/* uio_fsl_elbc_gpcm: UIO driver for eLBC/GPCM peripherals
+
+   Copyright (C) 2014 Linutronix GmbH
+     Author: John Ogness <john.ogness@linutronix.de>
+
+   This driver provides UIO access to memory of a peripheral connected
+   to the Freescale enhanced local bus controller (eLBC) interface
+   using the general purpose chip-select mode (GPCM).
+
+   Here is an example of the device tree entries:
+
+	localbus@ffe05000 {
+		ranges = <0x2 0x0 0x0 0xff810000 0x10000>;
+
+		dpm@2,0 {
+			compatible = "fsl,elbc-gpcm-uio";
+			reg = <0x2 0x0 0x10000>;
+			elbc-gpcm-br = <0xff810800>;
+			elbc-gpcm-or = <0xffff09f7>;
+			interrupt-parent = <&mpic>;
+			interrupts = <4 1>;
+			device_type = "netx5152";
+			uio_name = "netx_custom";
+			netx5152,init-win0-offset = <0x0>;
+		};
+	};
+
+   Only the entries reg (to identify bank) and elbc-gpcm-* (initial BR/OR
+   values) are required. The entries interrupt*, device_type, and uio_name
+   are optional (as well as any type-specific options such as
+   netx5152,init-win0-offset). As long as no interrupt handler is needed,
+   this driver can be used without any type-specific implementation.
+
+   The netx5152 type has been tested to work with the netX 51/52 hardware
+   from Hilscher using the Hilscher userspace netX stack.
+
+   The netx5152 type should serve as a model to add new type-specific
+   devices as needed.
+*/
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/fsl_lbc.h>
+
+#define MAX_BANKS 8
+
+struct fsl_elbc_gpcm {
+	struct device *dev;
+	struct fsl_lbc_regs __iomem *lbc;
+	u32 bank;
+	const char *name;
+
+	void (*init)(struct uio_info *info);
+	void (*shutdown)(struct uio_info *info, bool init_err);
+	irqreturn_t (*irq_handler)(int irq, struct uio_info *info);
+};
+
+static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
+			char *buf);
+static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count);
+
+DEVICE_ATTR(reg_br, S_IRUGO|S_IWUSR|S_IWGRP, reg_show, reg_store);
+DEVICE_ATTR(reg_or, S_IRUGO|S_IWUSR|S_IWGRP, reg_show, reg_store);
+
+static ssize_t reg_show(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uio_info *info = platform_get_drvdata(pdev);
+	struct fsl_elbc_gpcm *priv = info->priv;
+	struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank];
+
+	if (attr == &dev_attr_reg_br) {
+		return scnprintf(buf, PAGE_SIZE, "0x%08x\n",
+				 in_be32(&bank->br));
+
+	} else if (attr == &dev_attr_reg_or) {
+		return scnprintf(buf, PAGE_SIZE, "0x%08x\n",
+				 in_be32(&bank->or));
+	}
+
+	return 0;
+}
+
+static ssize_t reg_store(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct uio_info *info = platform_get_drvdata(pdev);
+	struct fsl_elbc_gpcm *priv = info->priv;
+	struct fsl_lbc_bank *bank = &priv->lbc->bank[priv->bank];
+	unsigned long val;
+	u32 reg_br_cur;
+	u32 reg_or_cur;
+	u32 reg_new;
+
+	/* parse use input */
+	if (kstrtoul(buf, 0, &val) != 0)
+		return -EINVAL;
+	reg_new = (u32)val;
+
+	/* read current values */
+	reg_br_cur = in_be32(&bank->br);
+	reg_or_cur = in_be32(&bank->or);
+
+	if (attr == &dev_attr_reg_br) {
+		/* not allowed to change effective base address */
+		if ((reg_br_cur & reg_or_cur & BR_BA) !=
+		    (reg_new & reg_or_cur & BR_BA)) {
+			return -EINVAL;
+		}
+
+		/* not allowed to change mode */
+		if ((reg_new & BR_MSEL) != BR_MS_GPCM)
+			return -EINVAL;
+
+		/* write new value (force valid) */
+		out_be32(&bank->br, reg_new | BR_V);
+
+	} else if (attr == &dev_attr_reg_or) {
+		/* not allowed to change access mask */
+		if ((reg_or_cur & OR_GPCM_AM) != (reg_new & OR_GPCM_AM))
+			return -EINVAL;
+
+		/* write new value */
+		out_be32(&bank->or, reg_new);
+
+	} else {
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
+#define DPM_HOST_WIN0_OFFSET	0xff00
+#define DPM_HOST_INT_STAT0	0xe0
+#define DPM_HOST_INT_EN0	0xf0
+#define DPM_HOST_INT_MASK	0xe600ffff
+#define DPM_HOST_INT_GLOBAL_EN	0x80000000
+
+static irqreturn_t netx5152_irq_handler(int irq, struct uio_info *info)
+{
+	void __iomem *reg_int_en = info->mem[0].internal_addr +
+					DPM_HOST_WIN0_OFFSET +
+					DPM_HOST_INT_EN0;
+	void __iomem *reg_int_stat = info->mem[0].internal_addr +
+					DPM_HOST_WIN0_OFFSET +
+					DPM_HOST_INT_STAT0;
+
+	/* check if an interrupt is enabled and active */
+	if ((ioread32(reg_int_en) & ioread32(reg_int_stat) &
+	     DPM_HOST_INT_MASK) == 0) {
+		return IRQ_NONE;
+	}
+
+	/* disable interrupts */
+	iowrite32(ioread32(reg_int_en) & ~DPM_HOST_INT_GLOBAL_EN, reg_int_en);
+
+	return IRQ_HANDLED;
+}
+
+static void netx5152_init(struct uio_info *info)
+{
+	unsigned long win0_offset = DPM_HOST_WIN0_OFFSET;
+	struct fsl_elbc_gpcm *priv = info->priv;
+	const void *prop;
+
+	/* get an optional initial win0 offset */
+	prop = of_get_property(priv->dev->of_node,
+			       "netx5152,init-win0-offset", NULL);
+	if (prop)
+		win0_offset = of_read_ulong(prop, 1);
+
+	/* disable interrupts */
+	iowrite32(0, info->mem[0].internal_addr + win0_offset +
+		     DPM_HOST_INT_EN0);
+}
+
+static void netx5152_shutdown(struct uio_info *info, bool init_err)
+{
+	if (init_err)
+		return;
+
+	/* disable interrupts */
+	iowrite32(0, info->mem[0].internal_addr + DPM_HOST_WIN0_OFFSET +
+		     DPM_HOST_INT_EN0);
+}
+#endif
+
+static void setup_periph(struct fsl_elbc_gpcm *priv,
+				   const char *type)
+{
+#ifdef CONFIG_UIO_FSL_ELBC_GPCM_NETX5152
+	if (strcmp(type, "netx5152") == 0) {
+		priv->irq_handler = netx5152_irq_handler;
+		priv->init = netx5152_init;
+		priv->shutdown = netx5152_shutdown;
+		priv->name = "netX 51/52";
+		return;
+	}
+#endif
+}
+
+static int check_of_data(struct fsl_elbc_gpcm *priv,
+				   struct resource *res,
+				   u32 reg_br, u32 reg_or)
+{
+	/* check specified bank */
+	if (priv->bank >= MAX_BANKS) {
+		dev_err(priv->dev, "invalid bank\n");
+		return -ENODEV;
+	}
+
+	/* check specified mode (BR_MS_GPCM is 0) */
+	if ((reg_br & BR_MSEL) != BR_MS_GPCM) {
+		dev_err(priv->dev, "unsupported mode\n");
+		return -ENODEV;
+	}
+
+	/* check specified mask vs. resource size */
+	if ((~(reg_or & OR_GPCM_AM) + 1) != resource_size(res)) {
+		dev_err(priv->dev, "address mask / size mismatch\n");
+		return -ENODEV;
+	}
+
+	/* check specified address */
+	if ((reg_br & reg_or & BR_BA) != fsl_lbc_addr(res->start)) {
+		dev_err(priv->dev, "base address mismatch\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int get_of_data(struct fsl_elbc_gpcm *priv, struct device_node *node,
+		       struct resource *res, u32 *reg_br,
+		       u32 *reg_or, unsigned int *irq, char **name)
+{
+	const char *dt_name;
+	const char *type;
+	int ret;
+
+	/* get the memory resource */
+	ret = of_address_to_resource(node, 0, res);
+	if (ret) {
+		dev_err(priv->dev, "failed to get resource\n");
+		return ret;
+	}
+
+	/* get the bank number */
+	ret = of_property_read_u32(node, "reg", &priv->bank);
+	if (ret) {
+		dev_err(priv->dev, "failed to get bank number\n");
+		return ret;
+	}
+
+	/* get BR value to set */
+	ret = of_property_read_u32(node, "elbc-gpcm-br", reg_br);
+	if (ret) {
+		dev_err(priv->dev, "missing elbc-gpcm-br value\n");
+		return ret;
+	}
+
+	/* get OR value to set */
+	ret = of_property_read_u32(node, "elbc-gpcm-or", reg_or);
+	if (ret) {
+		dev_err(priv->dev, "missing elbc-gpcm-or value\n");
+		return ret;
+	}
+
+	/* get optional peripheral type */
+	priv->name = "generic";
+	if (of_property_read_string(node, "device_type", &type) == 0)
+		setup_periph(priv, type);
+
+	/* get optional irq value */
+	*irq = irq_of_parse_and_map(node, 0);
+
+	/* sanity check device tree data */
+	ret = check_of_data(priv, res, *reg_br, *reg_or);
+	if (ret)
+		return ret;
+
+	/* get optional uio name */
+	if (of_property_read_string(node, "uio_name", &dt_name) != 0)
+		dt_name = "eLBC_GPCM";
+	*name = kstrdup(dt_name, GFP_KERNEL);
+	if (!*name)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct fsl_elbc_gpcm *priv;
+	struct uio_info *info;
+	char *uio_name = NULL;
+	struct resource res;
+	unsigned int irq;
+	u32 reg_br_cur;
+	u32 reg_or_cur;
+	u32 reg_br_new;
+	u32 reg_or_new;
+	int ret;
+
+	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+		return -ENODEV;
+
+	/* allocate private data */
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	priv->dev = &pdev->dev;
+	priv->lbc = fsl_lbc_ctrl_dev->regs;
+
+	/* get device tree data */
+	ret = get_of_data(priv, node, &res, &reg_br_new, &reg_or_new,
+			  &irq, &uio_name);
+	if (ret)
+		goto out_err0;
+
+	/* allocate UIO structure */
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		ret = -ENOMEM;
+		goto out_err0;
+	}
+
+	/* get current BR/OR values */
+	reg_br_cur = in_be32(&priv->lbc->bank[priv->bank].br);
+	reg_or_cur = in_be32(&priv->lbc->bank[priv->bank].or);
+
+	/* if bank already configured, make sure it matches */
+	if ((reg_br_cur & BR_V)) {
+		if ((reg_br_cur & BR_MSEL) != BR_MS_GPCM ||
+		    (reg_br_cur & reg_or_cur & BR_BA)
+		     != fsl_lbc_addr(res.start)) {
+			dev_err(priv->dev,
+				"bank in use by another peripheral\n");
+			ret = -ENODEV;
+			goto out_err1;
+		}
+
+		/* warn if behavior settings changing */
+		if ((reg_br_cur & ~(BR_BA | BR_V)) !=
+		    (reg_br_new & ~(BR_BA | BR_V))) {
+			dev_warn(priv->dev,
+				 "modifying BR settings: 0x%08x -> 0x%08x",
+				 reg_br_cur, reg_br_new);
+		}
+		if ((reg_or_cur & ~OR_GPCM_AM) != (reg_or_new & ~OR_GPCM_AM)) {
+			dev_warn(priv->dev,
+				 "modifying OR settings: 0x%08x -> 0x%08x",
+				 reg_or_cur, reg_or_new);
+		}
+	}
+
+	/* configure the bank (force base address and GPCM) */
+	reg_br_new &= ~(BR_BA | BR_MSEL);
+	reg_br_new |= fsl_lbc_addr(res.start) | BR_MS_GPCM | BR_V;
+	out_be32(&priv->lbc->bank[priv->bank].or, reg_or_new);
+	out_be32(&priv->lbc->bank[priv->bank].br, reg_br_new);
+
+	/* map the memory resource */
+	info->mem[0].internal_addr = ioremap(res.start, resource_size(&res));
+	if (!info->mem[0].internal_addr) {
+		dev_err(priv->dev, "failed to map chip region\n");
+		ret = -ENODEV;
+		goto out_err1;
+	}
+
+	/* set all UIO data */
+	if (node->name)
+		info->mem[0].name = kstrdup(node->name, GFP_KERNEL);
+	info->mem[0].addr = res.start;
+	info->mem[0].size = resource_size(&res);
+	info->mem[0].memtype = UIO_MEM_PHYS;
+	info->priv = priv;
+	info->name = uio_name;
+	info->version = "0.0.1";
+	if (irq != NO_IRQ) {
+		if (priv->irq_handler) {
+			info->irq = irq;
+			info->irq_flags = IRQF_SHARED;
+			info->handler = priv->irq_handler;
+		} else {
+			irq = NO_IRQ;
+			dev_warn(priv->dev, "ignoring irq, no handler\n");
+		}
+	}
+
+	if (priv->init)
+		priv->init(info);
+
+	/* register UIO device */
+	if (uio_register_device(priv->dev, info) != 0) {
+		dev_err(priv->dev, "UIO registration failed\n");
+		ret = -ENODEV;
+		goto out_err2;
+	}
+
+	/* store private data */
+	platform_set_drvdata(pdev, info);
+
+	/* create sysfs files */
+	ret = device_create_file(priv->dev, &dev_attr_reg_br);
+	if (ret)
+		goto out_err3;
+	ret = device_create_file(priv->dev, &dev_attr_reg_or);
+	if (ret)
+		goto out_err4;
+
+	dev_info(priv->dev,
+		 "eLBC/GPCM device (%s) at 0x%llx, bank %d, irq=%d\n",
+		 priv->name, (unsigned long long)res.start, priv->bank,
+		 irq != NO_IRQ ? irq : -1);
+
+	return 0;
+out_err4:
+	device_remove_file(priv->dev, &dev_attr_reg_br);
+out_err3:
+	platform_set_drvdata(pdev, NULL);
+	uio_unregister_device(info);
+out_err2:
+	if (priv->shutdown)
+		priv->shutdown(info, true);
+	iounmap(info->mem[0].internal_addr);
+out_err1:
+	kfree(info->mem[0].name);
+	kfree(info);
+out_err0:
+	kfree(uio_name);
+	kfree(priv);
+	return ret;
+}
+
+static int uio_fsl_elbc_gpcm_remove(struct platform_device *pdev)
+{
+	struct uio_info *info = platform_get_drvdata(pdev);
+	struct fsl_elbc_gpcm *priv = info->priv;
+
+	device_remove_file(priv->dev, &dev_attr_reg_or);
+	device_remove_file(priv->dev, &dev_attr_reg_br);
+	platform_set_drvdata(pdev, NULL);
+	uio_unregister_device(info);
+	if (priv->shutdown)
+		priv->shutdown(info, false);
+	iounmap(info->mem[0].internal_addr);
+	kfree(info->mem[0].name);
+	kfree(info->name);
+	kfree(info);
+	kfree(priv);
+
+	return 0;
+
+}
+
+static const struct of_device_id uio_fsl_elbc_gpcm_match[] = {
+	{ .compatible = "fsl,elbc-gpcm-uio", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, uio_fsl_elbc_gpcm_match);
+
+static struct platform_driver uio_fsl_elbc_gpcm_driver = {
+	.driver = {
+		.name = "fsl,elbc-gpcm-uio",
+		.of_match_table = uio_fsl_elbc_gpcm_match,
+	},
+	.probe = uio_fsl_elbc_gpcm_probe,
+	.remove = uio_fsl_elbc_gpcm_remove,
+};
+module_platform_driver(uio_fsl_elbc_gpcm_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
+MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller GPCM driver");
diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
new file mode 100644
index 0000000..e401be8
--- /dev/null
+++ b/drivers/uio/uio_hv_generic.c
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * uio_hv_generic - generic UIO driver for VMBus
+ *
+ * Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
+ * Copyright (c) 2016, Microsoft Corporation.
+ *
+ * Since the driver does not declare any device ids, you must allocate
+ * id and bind the device to the driver yourself.  For example:
+ *
+ * Associate Network GUID with UIO device
+ * # echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" \
+ *    > /sys/bus/vmbus/drivers/uio_hv_generic/new_id
+ * Then rebind
+ * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \
+ *    > /sys/bus/vmbus/drivers/hv_netvsc/unbind
+ * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \
+ *    > /sys/bus/vmbus/drivers/uio_hv_generic/bind
+ */
+#define DEBUG 1
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uio_driver.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/hyperv.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include "../hv/hyperv_vmbus.h"
+
+#define DRIVER_VERSION	"0.02.0"
+#define DRIVER_AUTHOR	"Stephen Hemminger <sthemmin at microsoft.com>"
+#define DRIVER_DESC	"Generic UIO driver for VMBus devices"
+
+#define HV_RING_SIZE	 512	/* pages */
+#define SEND_BUFFER_SIZE (15 * 1024 * 1024)
+#define RECV_BUFFER_SIZE (15 * 1024 * 1024)
+
+/*
+ * List of resources to be mapped to user space
+ * can be extended up to MAX_UIO_MAPS(5) items
+ */
+enum hv_uio_map {
+	TXRX_RING_MAP = 0,
+	INT_PAGE_MAP,
+	MON_PAGE_MAP,
+	RECV_BUF_MAP,
+	SEND_BUF_MAP
+};
+
+struct hv_uio_private_data {
+	struct uio_info info;
+	struct hv_device *device;
+
+	void	*recv_buf;
+	u32	recv_gpadl;
+	char	recv_name[32];	/* "recv_4294967295" */
+
+	void	*send_buf;
+	u32	send_gpadl;
+	char	send_name[32];
+};
+
+/*
+ * This is the irqcontrol callback to be registered to uio_info.
+ * It can be used to disable/enable interrupt from user space processes.
+ *
+ * @param info
+ *  pointer to uio_info.
+ * @param irq_state
+ *  state value. 1 to enable interrupt, 0 to disable interrupt.
+ */
+static int
+hv_uio_irqcontrol(struct uio_info *info, s32 irq_state)
+{
+	struct hv_uio_private_data *pdata = info->priv;
+	struct hv_device *dev = pdata->device;
+
+	dev->channel->inbound.ring_buffer->interrupt_mask = !irq_state;
+	virt_mb();
+
+	return 0;
+}
+
+/*
+ * Callback from vmbus_event when something is in inbound ring.
+ */
+static void hv_uio_channel_cb(void *context)
+{
+	struct vmbus_channel *chan = context;
+	struct hv_device *hv_dev = chan->device_obj;
+	struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
+
+	chan->inbound.ring_buffer->interrupt_mask = 1;
+	virt_mb();
+
+	uio_event_notify(&pdata->info);
+}
+
+/*
+ * Callback from vmbus_event when channel is rescinded.
+ */
+static void hv_uio_rescind(struct vmbus_channel *channel)
+{
+	struct hv_device *hv_dev = channel->primary_channel->device_obj;
+	struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev);
+
+	/*
+	 * Turn off the interrupt file handle
+	 * Next read for event will return -EIO
+	 */
+	pdata->info.irq = 0;
+
+	/* Wake up reader */
+	uio_event_notify(&pdata->info);
+}
+
+/* Sysfs API to allow mmap of the ring buffers
+ * The ring buffer is allocated as contiguous memory by vmbus_open
+ */
+static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj,
+			    struct bin_attribute *attr,
+			    struct vm_area_struct *vma)
+{
+	struct vmbus_channel *channel
+		= container_of(kobj, struct vmbus_channel, kobj);
+	struct hv_device *dev = channel->primary_channel->device_obj;
+	u16 q_idx = channel->offermsg.offer.sub_channel_index;
+
+	dev_dbg(&dev->device, "mmap channel %u pages %#lx at %#lx\n",
+		q_idx, vma_pages(vma), vma->vm_pgoff);
+
+	return vm_iomap_memory(vma, virt_to_phys(channel->ringbuffer_pages),
+			       channel->ringbuffer_pagecount << PAGE_SHIFT);
+}
+
+static const struct bin_attribute ring_buffer_bin_attr = {
+	.attr = {
+		.name = "ring",
+		.mode = 0600,
+	},
+	.size = 2 * HV_RING_SIZE * PAGE_SIZE,
+	.mmap = hv_uio_ring_mmap,
+};
+
+/* Callback from VMBUS subsystem when new channel created. */
+static void
+hv_uio_new_channel(struct vmbus_channel *new_sc)
+{
+	struct hv_device *hv_dev = new_sc->primary_channel->device_obj;
+	struct device *device = &hv_dev->device;
+	const size_t ring_bytes = HV_RING_SIZE * PAGE_SIZE;
+	int ret;
+
+	/* Create host communication ring */
+	ret = vmbus_open(new_sc, ring_bytes, ring_bytes, NULL, 0,
+			 hv_uio_channel_cb, new_sc);
+	if (ret) {
+		dev_err(device, "vmbus_open subchannel failed: %d\n", ret);
+		return;
+	}
+
+	/* Disable interrupts on sub channel */
+	new_sc->inbound.ring_buffer->interrupt_mask = 1;
+	set_channel_read_mode(new_sc, HV_CALL_ISR);
+
+	ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr);
+	if (ret) {
+		dev_err(device, "sysfs create ring bin file failed; %d\n", ret);
+		vmbus_close(new_sc);
+	}
+}
+
+static void
+hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata)
+{
+	if (pdata->send_gpadl)
+		vmbus_teardown_gpadl(dev->channel, pdata->send_gpadl);
+	vfree(pdata->send_buf);
+
+	if (pdata->recv_gpadl)
+		vmbus_teardown_gpadl(dev->channel, pdata->recv_gpadl);
+	vfree(pdata->recv_buf);
+}
+
+static int
+hv_uio_probe(struct hv_device *dev,
+	     const struct hv_vmbus_device_id *dev_id)
+{
+	struct hv_uio_private_data *pdata;
+	int ret;
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE,
+			 HV_RING_SIZE * PAGE_SIZE, NULL, 0,
+			 hv_uio_channel_cb, dev->channel);
+	if (ret)
+		goto fail;
+
+	/* Communicating with host has to be via shared memory not hypercall */
+	if (!dev->channel->offermsg.monitor_allocated) {
+		dev_err(&dev->device, "vmbus channel requires hypercall\n");
+		ret = -ENOTSUPP;
+		goto fail_close;
+	}
+
+	dev->channel->inbound.ring_buffer->interrupt_mask = 1;
+	set_channel_read_mode(dev->channel, HV_CALL_ISR);
+
+	/* Fill general uio info */
+	pdata->info.name = "uio_hv_generic";
+	pdata->info.version = DRIVER_VERSION;
+	pdata->info.irqcontrol = hv_uio_irqcontrol;
+	pdata->info.irq = UIO_IRQ_CUSTOM;
+
+	/* mem resources */
+	pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings";
+	pdata->info.mem[TXRX_RING_MAP].addr
+		= (uintptr_t)dev->channel->ringbuffer_pages;
+	pdata->info.mem[TXRX_RING_MAP].size
+		= dev->channel->ringbuffer_pagecount << PAGE_SHIFT;
+	pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL;
+
+	pdata->info.mem[INT_PAGE_MAP].name = "int_page";
+	pdata->info.mem[INT_PAGE_MAP].addr
+		= (uintptr_t)vmbus_connection.int_page;
+	pdata->info.mem[INT_PAGE_MAP].size = PAGE_SIZE;
+	pdata->info.mem[INT_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
+
+	pdata->info.mem[MON_PAGE_MAP].name = "monitor_page";
+	pdata->info.mem[MON_PAGE_MAP].addr
+		= (uintptr_t)vmbus_connection.monitor_pages[1];
+	pdata->info.mem[MON_PAGE_MAP].size = PAGE_SIZE;
+	pdata->info.mem[MON_PAGE_MAP].memtype = UIO_MEM_LOGICAL;
+
+	pdata->recv_buf = vzalloc(RECV_BUFFER_SIZE);
+	if (pdata->recv_buf == NULL) {
+		ret = -ENOMEM;
+		goto fail_close;
+	}
+
+	ret = vmbus_establish_gpadl(dev->channel, pdata->recv_buf,
+				    RECV_BUFFER_SIZE, &pdata->recv_gpadl);
+	if (ret)
+		goto fail_close;
+
+	/* put Global Physical Address Label in name */
+	snprintf(pdata->recv_name, sizeof(pdata->recv_name),
+		 "recv:%u", pdata->recv_gpadl);
+	pdata->info.mem[RECV_BUF_MAP].name = pdata->recv_name;
+	pdata->info.mem[RECV_BUF_MAP].addr
+		= (uintptr_t)pdata->recv_buf;
+	pdata->info.mem[RECV_BUF_MAP].size = RECV_BUFFER_SIZE;
+	pdata->info.mem[RECV_BUF_MAP].memtype = UIO_MEM_VIRTUAL;
+
+
+	pdata->send_buf = vzalloc(SEND_BUFFER_SIZE);
+	if (pdata->send_buf == NULL) {
+		ret = -ENOMEM;
+		goto fail_close;
+	}
+
+	ret = vmbus_establish_gpadl(dev->channel, pdata->send_buf,
+				    SEND_BUFFER_SIZE, &pdata->send_gpadl);
+	if (ret)
+		goto fail_close;
+
+	snprintf(pdata->send_name, sizeof(pdata->send_name),
+		 "send:%u", pdata->send_gpadl);
+	pdata->info.mem[SEND_BUF_MAP].name = pdata->send_name;
+	pdata->info.mem[SEND_BUF_MAP].addr
+		= (uintptr_t)pdata->send_buf;
+	pdata->info.mem[SEND_BUF_MAP].size = SEND_BUFFER_SIZE;
+	pdata->info.mem[SEND_BUF_MAP].memtype = UIO_MEM_VIRTUAL;
+
+	pdata->info.priv = pdata;
+	pdata->device = dev;
+
+	ret = uio_register_device(&dev->device, &pdata->info);
+	if (ret) {
+		dev_err(&dev->device, "hv_uio register failed\n");
+		goto fail_close;
+	}
+
+	vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind);
+	vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel);
+
+	ret = sysfs_create_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr);
+	if (ret)
+		dev_notice(&dev->device,
+			   "sysfs create ring bin file failed; %d\n", ret);
+
+	hv_set_drvdata(dev, pdata);
+
+	return 0;
+
+fail_close:
+	hv_uio_cleanup(dev, pdata);
+	vmbus_close(dev->channel);
+fail:
+	kfree(pdata);
+
+	return ret;
+}
+
+static int
+hv_uio_remove(struct hv_device *dev)
+{
+	struct hv_uio_private_data *pdata = hv_get_drvdata(dev);
+
+	if (!pdata)
+		return 0;
+
+	uio_unregister_device(&pdata->info);
+	hv_uio_cleanup(dev, pdata);
+	hv_set_drvdata(dev, NULL);
+	vmbus_close(dev->channel);
+	kfree(pdata);
+	return 0;
+}
+
+static struct hv_driver hv_uio_drv = {
+	.name = "uio_hv_generic",
+	.id_table = NULL, /* only dynamic id's */
+	.probe = hv_uio_probe,
+	.remove = hv_uio_remove,
+};
+
+static int __init
+hyperv_module_init(void)
+{
+	return vmbus_driver_register(&hv_uio_drv);
+}
+
+static void __exit
+hyperv_module_exit(void)
+{
+	vmbus_driver_unregister(&hv_uio_drv);
+}
+
+module_init(hyperv_module_init);
+module_exit(hyperv_module_exit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/uio/uio_mf624.c b/drivers/uio/uio_mf624.c
new file mode 100644
index 0000000..35187c0
--- /dev/null
+++ b/drivers/uio/uio_mf624.c
@@ -0,0 +1,242 @@
+/*
+ * UIO driver fo Humusoft MF624 DAQ card.
+ * Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
+ *                    Czech Technical University in Prague
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/uio_driver.h>
+
+#define PCI_VENDOR_ID_HUMUSOFT		0x186c
+#define PCI_DEVICE_ID_MF624		0x0624
+#define PCI_SUBVENDOR_ID_HUMUSOFT	0x186c
+#define PCI_SUBDEVICE_DEVICE		0x0624
+
+/* BAR0 Interrupt control/status register */
+#define INTCSR				0x4C
+#define INTCSR_ADINT_ENABLE		(1 << 0)
+#define INTCSR_CTR4INT_ENABLE		(1 << 3)
+#define INTCSR_PCIINT_ENABLE		(1 << 6)
+#define INTCSR_ADINT_STATUS		(1 << 2)
+#define INTCSR_CTR4INT_STATUS		(1 << 5)
+
+enum mf624_interrupt_source {ADC, CTR4, ALL};
+
+static void mf624_disable_interrupt(enum mf624_interrupt_source source,
+			     struct uio_info *info)
+{
+	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
+
+	switch (source) {
+	case ADC:
+		iowrite32(ioread32(INTCSR_reg)
+			& ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
+			INTCSR_reg);
+		break;
+
+	case CTR4:
+		iowrite32(ioread32(INTCSR_reg)
+			& ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
+			INTCSR_reg);
+		break;
+
+	case ALL:
+	default:
+		iowrite32(ioread32(INTCSR_reg)
+			& ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
+			    | INTCSR_PCIINT_ENABLE),
+			INTCSR_reg);
+		break;
+	}
+}
+
+static void mf624_enable_interrupt(enum mf624_interrupt_source source,
+			    struct uio_info *info)
+{
+	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
+
+	switch (source) {
+	case ADC:
+		iowrite32(ioread32(INTCSR_reg)
+			| INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
+			INTCSR_reg);
+		break;
+
+	case CTR4:
+		iowrite32(ioread32(INTCSR_reg)
+			| INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
+			INTCSR_reg);
+		break;
+
+	case ALL:
+	default:
+		iowrite32(ioread32(INTCSR_reg)
+			| INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
+			| INTCSR_PCIINT_ENABLE,
+			INTCSR_reg);
+		break;
+	}
+}
+
+static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
+{
+	void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
+
+	if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
+	    && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
+		mf624_disable_interrupt(ADC, info);
+		return IRQ_HANDLED;
+	}
+
+	if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
+	    && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
+		mf624_disable_interrupt(CTR4, info);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
+{
+	if (irq_on == 0)
+		mf624_disable_interrupt(ALL, info);
+	else if (irq_on == 1)
+		mf624_enable_interrupt(ALL, info);
+
+	return 0;
+}
+
+static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
+{
+	resource_size_t start = pci_resource_start(dev, bar);
+	resource_size_t len = pci_resource_len(dev, bar);
+
+	mem->name = name;
+	mem->addr = start & PAGE_MASK;
+	mem->offs = start & ~PAGE_MASK;
+	if (!mem->addr)
+		return -ENODEV;
+	mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
+	mem->memtype = UIO_MEM_PHYS;
+	mem->internal_addr = pci_ioremap_bar(dev, bar);
+	if (!mem->internal_addr)
+		return -ENODEV;
+	return 0;
+}
+
+static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct uio_info *info;
+
+	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	if (pci_enable_device(dev))
+		goto out_free;
+
+	if (pci_request_regions(dev, "mf624"))
+		goto out_disable;
+
+	info->name = "mf624";
+	info->version = "0.0.1";
+
+	/* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
+
+	/* BAR0 */
+	if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
+			    "bits, special functions"))
+		goto out_release;
+	/* BAR2 */
+	if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
+		goto out_unmap0;
+
+	/* BAR4 */
+	if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
+		goto out_unmap1;
+
+	info->irq = dev->irq;
+	info->irq_flags = IRQF_SHARED;
+	info->handler = mf624_irq_handler;
+
+	info->irqcontrol = mf624_irqcontrol;
+
+	if (uio_register_device(&dev->dev, info))
+		goto out_unmap2;
+
+	pci_set_drvdata(dev, info);
+
+	return 0;
+
+out_unmap2:
+	iounmap(info->mem[2].internal_addr);
+out_unmap1:
+	iounmap(info->mem[1].internal_addr);
+out_unmap0:
+	iounmap(info->mem[0].internal_addr);
+
+out_release:
+	pci_release_regions(dev);
+
+out_disable:
+	pci_disable_device(dev);
+
+out_free:
+	kfree(info);
+	return -ENODEV;
+}
+
+static void mf624_pci_remove(struct pci_dev *dev)
+{
+	struct uio_info *info = pci_get_drvdata(dev);
+
+	mf624_disable_interrupt(ALL, info);
+
+	uio_unregister_device(info);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+
+	iounmap(info->mem[0].internal_addr);
+	iounmap(info->mem[1].internal_addr);
+	iounmap(info->mem[2].internal_addr);
+
+	kfree(info);
+}
+
+static const struct pci_device_id mf624_pci_id[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
+	{ 0, }
+};
+
+static struct pci_driver mf624_pci_driver = {
+	.name = "mf624",
+	.id_table = mf624_pci_id,
+	.probe = mf624_pci_probe,
+	.remove = mf624_pci_remove,
+};
+MODULE_DEVICE_TABLE(pci, mf624_pci_id);
+
+module_pci_driver(mf624_pci_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
diff --git a/drivers/uio/uio_netx.c b/drivers/uio/uio_netx.c
new file mode 100644
index 0000000..9ae29ff
--- /dev/null
+++ b/drivers/uio/uio_netx.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UIO driver for Hilscher NetX based fieldbus cards (cifX, comX).
+ * See http://www.hilscher.com for details.
+ *
+ * (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
+ * (C) 2008 Manuel Traut <manut@linutronix.de>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/uio_driver.h>
+
+#define PCI_VENDOR_ID_HILSCHER		0x15CF
+#define PCI_DEVICE_ID_HILSCHER_NETX	0x0000
+#define PCI_DEVICE_ID_HILSCHER_NETPLC	0x0010
+#define PCI_SUBDEVICE_ID_NETPLC_RAM	0x0000
+#define PCI_SUBDEVICE_ID_NETPLC_FLASH	0x0001
+#define PCI_SUBDEVICE_ID_NXSB_PCA	0x3235
+#define PCI_SUBDEVICE_ID_NXPCA		0x3335
+
+#define DPM_HOST_INT_EN0	0xfff0
+#define DPM_HOST_INT_STAT0	0xffe0
+
+#define DPM_HOST_INT_MASK	0xe600ffff
+#define DPM_HOST_INT_GLOBAL_EN	0x80000000
+
+static irqreturn_t netx_handler(int irq, struct uio_info *dev_info)
+{
+	void __iomem *int_enable_reg = dev_info->mem[0].internal_addr
+					+ DPM_HOST_INT_EN0;
+	void __iomem *int_status_reg = dev_info->mem[0].internal_addr
+					+ DPM_HOST_INT_STAT0;
+
+	/* Is one of our interrupts enabled and active ? */
+	if (!(ioread32(int_enable_reg) & ioread32(int_status_reg)
+		& DPM_HOST_INT_MASK))
+		return IRQ_NONE;
+
+	/* Disable interrupt */
+	iowrite32(ioread32(int_enable_reg) & ~DPM_HOST_INT_GLOBAL_EN,
+		int_enable_reg);
+	return IRQ_HANDLED;
+}
+
+static int netx_pci_probe(struct pci_dev *dev,
+					const struct pci_device_id *id)
+{
+	struct uio_info *info;
+	int bar;
+
+	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	if (pci_enable_device(dev))
+		goto out_free;
+
+	if (pci_request_regions(dev, "netx"))
+		goto out_disable;
+
+	switch (id->device) {
+	case PCI_DEVICE_ID_HILSCHER_NETX:
+		bar = 0;
+		info->name = "netx";
+		break;
+	case PCI_DEVICE_ID_HILSCHER_NETPLC:
+		bar = 0;
+		info->name = "netplc";
+		break;
+	default:
+		bar = 2;
+		info->name = "netx_plx";
+	}
+
+	/* BAR0 or 2 points to the card's dual port memory */
+	info->mem[0].addr = pci_resource_start(dev, bar);
+	if (!info->mem[0].addr)
+		goto out_release;
+	info->mem[0].internal_addr = ioremap(pci_resource_start(dev, bar),
+						pci_resource_len(dev, bar));
+
+	if (!info->mem[0].internal_addr)
+			goto out_release;
+
+	info->mem[0].size = pci_resource_len(dev, bar);
+	info->mem[0].memtype = UIO_MEM_PHYS;
+	info->irq = dev->irq;
+	info->irq_flags = IRQF_SHARED;
+	info->handler = netx_handler;
+	info->version = "0.0.1";
+
+	/* Make sure all interrupts are disabled */
+	iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0);
+
+	if (uio_register_device(&dev->dev, info))
+		goto out_unmap;
+
+	pci_set_drvdata(dev, info);
+	dev_info(&dev->dev, "Found %s card, registered UIO device.\n",
+				info->name);
+
+	return 0;
+
+out_unmap:
+	iounmap(info->mem[0].internal_addr);
+out_release:
+	pci_release_regions(dev);
+out_disable:
+	pci_disable_device(dev);
+out_free:
+	kfree(info);
+	return -ENODEV;
+}
+
+static void netx_pci_remove(struct pci_dev *dev)
+{
+	struct uio_info *info = pci_get_drvdata(dev);
+
+	/* Disable all interrupts */
+	iowrite32(0, info->mem[0].internal_addr + DPM_HOST_INT_EN0);
+	uio_unregister_device(info);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	iounmap(info->mem[0].internal_addr);
+
+	kfree(info);
+}
+
+static struct pci_device_id netx_pci_ids[] = {
+	{
+		.vendor =	PCI_VENDOR_ID_HILSCHER,
+		.device =	PCI_DEVICE_ID_HILSCHER_NETX,
+		.subvendor =	0,
+		.subdevice =	0,
+	},
+	{
+		.vendor =       PCI_VENDOR_ID_HILSCHER,
+		.device =       PCI_DEVICE_ID_HILSCHER_NETPLC,
+		.subvendor =    PCI_VENDOR_ID_HILSCHER,
+		.subdevice =    PCI_SUBDEVICE_ID_NETPLC_RAM,
+	},
+	{
+		.vendor =       PCI_VENDOR_ID_HILSCHER,
+		.device =       PCI_DEVICE_ID_HILSCHER_NETPLC,
+		.subvendor =    PCI_VENDOR_ID_HILSCHER,
+		.subdevice =    PCI_SUBDEVICE_ID_NETPLC_FLASH,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_PLX,
+		.device =	PCI_DEVICE_ID_PLX_9030,
+		.subvendor =	PCI_VENDOR_ID_PLX,
+		.subdevice =	PCI_SUBDEVICE_ID_NXSB_PCA,
+	},
+	{
+		.vendor =	PCI_VENDOR_ID_PLX,
+		.device =	PCI_DEVICE_ID_PLX_9030,
+		.subvendor =	PCI_VENDOR_ID_PLX,
+		.subdevice =	PCI_SUBDEVICE_ID_NXPCA,
+	},
+	{ 0, }
+};
+
+static struct pci_driver netx_pci_driver = {
+	.name = "netx",
+	.id_table = netx_pci_ids,
+	.probe = netx_pci_probe,
+	.remove = netx_pci_remove,
+};
+
+module_pci_driver(netx_pci_driver);
+MODULE_DEVICE_TABLE(pci, netx_pci_ids);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Hans J. Koch, Manuel Traut");
diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c
new file mode 100644
index 0000000..8773e37
--- /dev/null
+++ b/drivers/uio/uio_pci_generic.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/* uio_pci_generic - generic UIO driver for PCI 2.3 devices
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * Since the driver does not declare any device ids, you must allocate
+ * id and bind the device to the driver yourself.  For example:
+ *
+ * # echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id
+ * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
+ * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind
+ * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver
+ * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic
+ *
+ * Driver won't bind to devices which do not support the Interrupt Disable Bit
+ * in the command register. All devices compliant to PCI 2.3 (circa 2002) and
+ * all compliant PCI Express devices should support this bit.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/uio_driver.h>
+
+#define DRIVER_VERSION	"0.01.0"
+#define DRIVER_AUTHOR	"Michael S. Tsirkin <mst@redhat.com>"
+#define DRIVER_DESC	"Generic UIO driver for PCI 2.3 devices"
+
+struct uio_pci_generic_dev {
+	struct uio_info info;
+	struct pci_dev *pdev;
+};
+
+static inline struct uio_pci_generic_dev *
+to_uio_pci_generic_dev(struct uio_info *info)
+{
+	return container_of(info, struct uio_pci_generic_dev, info);
+}
+
+/* Interrupt handler. Read/modify/write the command register to disable
+ * the interrupt. */
+static irqreturn_t irqhandler(int irq, struct uio_info *info)
+{
+	struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info);
+
+	if (!pci_check_and_mask_intx(gdev->pdev))
+		return IRQ_NONE;
+
+	/* UIO core will signal the user process. */
+	return IRQ_HANDLED;
+}
+
+static int probe(struct pci_dev *pdev,
+			   const struct pci_device_id *id)
+{
+	struct uio_pci_generic_dev *gdev;
+	int err;
+
+	err = pci_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n",
+			__func__, err);
+		return err;
+	}
+
+	if (pdev->irq && !pci_intx_mask_supported(pdev)) {
+		err = -ENODEV;
+		goto err_verify;
+	}
+
+	gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL);
+	if (!gdev) {
+		err = -ENOMEM;
+		goto err_alloc;
+	}
+
+	gdev->info.name = "uio_pci_generic";
+	gdev->info.version = DRIVER_VERSION;
+	gdev->pdev = pdev;
+	if (pdev->irq) {
+		gdev->info.irq = pdev->irq;
+		gdev->info.irq_flags = IRQF_SHARED;
+		gdev->info.handler = irqhandler;
+	} else {
+		dev_warn(&pdev->dev, "No IRQ assigned to device: "
+			 "no support for interrupts?\n");
+	}
+
+	err = uio_register_device(&pdev->dev, &gdev->info);
+	if (err)
+		goto err_register;
+	pci_set_drvdata(pdev, gdev);
+
+	return 0;
+err_register:
+	kfree(gdev);
+err_alloc:
+err_verify:
+	pci_disable_device(pdev);
+	return err;
+}
+
+static void remove(struct pci_dev *pdev)
+{
+	struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev);
+
+	uio_unregister_device(&gdev->info);
+	pci_disable_device(pdev);
+	kfree(gdev);
+}
+
+static struct pci_driver uio_pci_driver = {
+	.name = "uio_pci_generic",
+	.id_table = NULL, /* only dynamic id's */
+	.probe = probe,
+	.remove = remove,
+};
+
+module_pci_driver(uio_pci_driver);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
new file mode 100644
index 0000000..f598ecd
--- /dev/null
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -0,0 +1,279 @@
+/*
+ * drivers/uio/uio_pdrv_genirq.c
+ *
+ * Userspace I/O platform driver with generic IRQ handling code.
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on uio_pdrv.c by Uwe Kleine-Koenig,
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/stringify.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+
+#define DRIVER_NAME "uio_pdrv_genirq"
+
+struct uio_pdrv_genirq_platdata {
+	struct uio_info *uioinfo;
+	spinlock_t lock;
+	unsigned long flags;
+	struct platform_device *pdev;
+};
+
+/* Bits in uio_pdrv_genirq_platdata.flags */
+enum {
+	UIO_IRQ_DISABLED = 0,
+};
+
+static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode)
+{
+	struct uio_pdrv_genirq_platdata *priv = info->priv;
+
+	/* Wait until the Runtime PM code has woken up the device */
+	pm_runtime_get_sync(&priv->pdev->dev);
+	return 0;
+}
+
+static int uio_pdrv_genirq_release(struct uio_info *info, struct inode *inode)
+{
+	struct uio_pdrv_genirq_platdata *priv = info->priv;
+
+	/* Tell the Runtime PM code that the device has become idle */
+	pm_runtime_put_sync(&priv->pdev->dev);
+	return 0;
+}
+
+static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
+{
+	struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
+
+	/* Just disable the interrupt in the interrupt controller, and
+	 * remember the state so we can allow user space to enable it later.
+	 */
+
+	spin_lock(&priv->lock);
+	if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
+		disable_irq_nosync(irq);
+	spin_unlock(&priv->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
+{
+	struct uio_pdrv_genirq_platdata *priv = dev_info->priv;
+	unsigned long flags;
+
+	/* Allow user space to enable and disable the interrupt
+	 * in the interrupt controller, but keep track of the
+	 * state to prevent per-irq depth damage.
+	 *
+	 * Serialize this operation to support multiple tasks and concurrency
+	 * with irq handler on SMP systems.
+	 */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	if (irq_on) {
+		if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
+			enable_irq(dev_info->irq);
+	} else {
+		if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
+			disable_irq_nosync(dev_info->irq);
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+
+static int uio_pdrv_genirq_probe(struct platform_device *pdev)
+{
+	struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
+	struct uio_pdrv_genirq_platdata *priv;
+	struct uio_mem *uiomem;
+	int ret = -EINVAL;
+	int i;
+
+	if (pdev->dev.of_node) {
+		/* alloc uioinfo for one device */
+		uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
+				       GFP_KERNEL);
+		if (!uioinfo) {
+			dev_err(&pdev->dev, "unable to kmalloc\n");
+			return -ENOMEM;
+		}
+		uioinfo->name = pdev->dev.of_node->name;
+		uioinfo->version = "devicetree";
+		/* Multiple IRQs are not supported */
+	}
+
+	if (!uioinfo || !uioinfo->name || !uioinfo->version) {
+		dev_err(&pdev->dev, "missing platform_data\n");
+		return ret;
+	}
+
+	if (uioinfo->handler || uioinfo->irqcontrol ||
+	    uioinfo->irq_flags & IRQF_SHARED) {
+		dev_err(&pdev->dev, "interrupt configuration error\n");
+		return ret;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "unable to kmalloc\n");
+		return -ENOMEM;
+	}
+
+	priv->uioinfo = uioinfo;
+	spin_lock_init(&priv->lock);
+	priv->flags = 0; /* interrupt is enabled to begin with */
+	priv->pdev = pdev;
+
+	if (!uioinfo->irq) {
+		ret = platform_get_irq(pdev, 0);
+		uioinfo->irq = ret;
+		if (ret == -ENXIO && pdev->dev.of_node)
+			uioinfo->irq = UIO_IRQ_NONE;
+		else if (ret < 0) {
+			dev_err(&pdev->dev, "failed to get IRQ\n");
+			return ret;
+		}
+	}
+
+	uiomem = &uioinfo->mem[0];
+
+	for (i = 0; i < pdev->num_resources; ++i) {
+		struct resource *r = &pdev->resource[i];
+
+		if (r->flags != IORESOURCE_MEM)
+			continue;
+
+		if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
+			dev_warn(&pdev->dev, "device has more than "
+					__stringify(MAX_UIO_MAPS)
+					" I/O memory resources.\n");
+			break;
+		}
+
+		uiomem->memtype = UIO_MEM_PHYS;
+		uiomem->addr = r->start;
+		uiomem->size = resource_size(r);
+		uiomem->name = r->name;
+		++uiomem;
+	}
+
+	while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
+		uiomem->size = 0;
+		++uiomem;
+	}
+
+	/* This driver requires no hardware specific kernel code to handle
+	 * interrupts. Instead, the interrupt handler simply disables the
+	 * interrupt in the interrupt controller. User space is responsible
+	 * for performing hardware specific acknowledge and re-enabling of
+	 * the interrupt in the interrupt controller.
+	 *
+	 * Interrupt sharing is not supported.
+	 */
+
+	uioinfo->handler = uio_pdrv_genirq_handler;
+	uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol;
+	uioinfo->open = uio_pdrv_genirq_open;
+	uioinfo->release = uio_pdrv_genirq_release;
+	uioinfo->priv = priv;
+
+	/* Enable Runtime PM for this device:
+	 * The device starts in suspended state to allow the hardware to be
+	 * turned off by default. The Runtime PM bus code should power on the
+	 * hardware and enable clocks at open().
+	 */
+	pm_runtime_enable(&pdev->dev);
+
+	ret = uio_register_device(&pdev->dev, priv->uioinfo);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register uio device\n");
+		pm_runtime_disable(&pdev->dev);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+}
+
+static int uio_pdrv_genirq_remove(struct platform_device *pdev)
+{
+	struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev);
+
+	uio_unregister_device(priv->uioinfo);
+	pm_runtime_disable(&pdev->dev);
+
+	priv->uioinfo->handler = NULL;
+	priv->uioinfo->irqcontrol = NULL;
+
+	return 0;
+}
+
+static int uio_pdrv_genirq_runtime_nop(struct device *dev)
+{
+	/* Runtime PM callback shared between ->runtime_suspend()
+	 * and ->runtime_resume(). Simply returns success.
+	 *
+	 * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
+	 * are used at open() and release() time. This allows the
+	 * Runtime PM code to turn off power to the device while the
+	 * device is unused, ie before open() and after release().
+	 *
+	 * This Runtime PM callback does not need to save or restore
+	 * any registers since user space is responsbile for hardware
+	 * register reinitialization after open().
+	 */
+	return 0;
+}
+
+static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
+	.runtime_suspend = uio_pdrv_genirq_runtime_nop,
+	.runtime_resume = uio_pdrv_genirq_runtime_nop,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id uio_of_genirq_match[] = {
+	{ /* This is filled with module_parm */ },
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
+module_param_string(of_id, uio_of_genirq_match[0].compatible, 128, 0);
+MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
+#endif
+
+static struct platform_driver uio_pdrv_genirq = {
+	.probe = uio_pdrv_genirq_probe,
+	.remove = uio_pdrv_genirq_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.pm = &uio_pdrv_genirq_dev_pm_ops,
+		.of_match_table = of_match_ptr(uio_of_genirq_match),
+	},
+};
+
+module_platform_driver(uio_pdrv_genirq);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c
new file mode 100644
index 0000000..1cc175d
--- /dev/null
+++ b/drivers/uio/uio_pruss.c
@@ -0,0 +1,272 @@
+/*
+ * Programmable Real-Time Unit Sub System (PRUSS) UIO driver (uio_pruss)
+ *
+ * This driver exports PRUSS host event out interrupts and PRUSS, L3 RAM,
+ * and DDR RAM to user space for applications interacting with PRUSS firmware
+ *
+ * Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/platform_data/uio_pruss.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/genalloc.h>
+
+#define DRV_NAME "pruss_uio"
+#define DRV_VERSION "1.0"
+
+static int sram_pool_sz = SZ_16K;
+module_param(sram_pool_sz, int, 0);
+MODULE_PARM_DESC(sram_pool_sz, "sram pool size to allocate ");
+
+static int extram_pool_sz = SZ_256K;
+module_param(extram_pool_sz, int, 0);
+MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate");
+
+/*
+ * Host event IRQ numbers from PRUSS - PRUSS can generate up to 8 interrupt
+ * events to AINTC of ARM host processor - which can be used for IPC b/w PRUSS
+ * firmware and user space application, async notification from PRU firmware
+ * to user space application
+ * 3	PRU_EVTOUT0
+ * 4	PRU_EVTOUT1
+ * 5	PRU_EVTOUT2
+ * 6	PRU_EVTOUT3
+ * 7	PRU_EVTOUT4
+ * 8	PRU_EVTOUT5
+ * 9	PRU_EVTOUT6
+ * 10	PRU_EVTOUT7
+*/
+#define MAX_PRUSS_EVT	8
+
+#define PINTC_HIDISR	0x0038
+#define PINTC_HIPIR	0x0900
+#define HIPIR_NOPEND	0x80000000
+#define PINTC_HIER	0x1500
+
+struct uio_pruss_dev {
+	struct uio_info *info;
+	struct clk *pruss_clk;
+	dma_addr_t sram_paddr;
+	dma_addr_t ddr_paddr;
+	void __iomem *prussio_vaddr;
+	unsigned long sram_vaddr;
+	void *ddr_vaddr;
+	unsigned int hostirq_start;
+	unsigned int pintc_base;
+	struct gen_pool *sram_pool;
+};
+
+static irqreturn_t pruss_handler(int irq, struct uio_info *info)
+{
+	struct uio_pruss_dev *gdev = info->priv;
+	int intr_bit = (irq - gdev->hostirq_start + 2);
+	int val, intr_mask = (1 << intr_bit);
+	void __iomem *base = gdev->prussio_vaddr + gdev->pintc_base;
+	void __iomem *intren_reg = base + PINTC_HIER;
+	void __iomem *intrdis_reg = base + PINTC_HIDISR;
+	void __iomem *intrstat_reg = base + PINTC_HIPIR + (intr_bit << 2);
+
+	val = ioread32(intren_reg);
+	/* Is interrupt enabled and active ? */
+	if (!(val & intr_mask) && (ioread32(intrstat_reg) & HIPIR_NOPEND))
+		return IRQ_NONE;
+	/* Disable interrupt */
+	iowrite32(intr_bit, intrdis_reg);
+	return IRQ_HANDLED;
+}
+
+static void pruss_cleanup(struct device *dev, struct uio_pruss_dev *gdev)
+{
+	int cnt;
+	struct uio_info *p = gdev->info;
+
+	for (cnt = 0; cnt < MAX_PRUSS_EVT; cnt++, p++) {
+		uio_unregister_device(p);
+		kfree(p->name);
+	}
+	iounmap(gdev->prussio_vaddr);
+	if (gdev->ddr_vaddr) {
+		dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr,
+			gdev->ddr_paddr);
+	}
+	if (gdev->sram_vaddr)
+		gen_pool_free(gdev->sram_pool,
+			      gdev->sram_vaddr,
+			      sram_pool_sz);
+	kfree(gdev->info);
+	clk_disable(gdev->pruss_clk);
+	clk_put(gdev->pruss_clk);
+	kfree(gdev);
+}
+
+static int pruss_probe(struct platform_device *pdev)
+{
+	struct uio_info *p;
+	struct uio_pruss_dev *gdev;
+	struct resource *regs_prussio;
+	struct device *dev = &pdev->dev;
+	int ret, cnt, i, len;
+	struct uio_pruss_pdata *pdata = dev_get_platdata(dev);
+
+	gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL);
+	if (!gdev)
+		return -ENOMEM;
+
+	gdev->info = kcalloc(MAX_PRUSS_EVT, sizeof(*p), GFP_KERNEL);
+	if (!gdev->info) {
+		ret = -ENOMEM;
+		goto err_free_gdev;
+	}
+
+	/* Power on PRU in case its not done as part of boot-loader */
+	gdev->pruss_clk = clk_get(dev, "pruss");
+	if (IS_ERR(gdev->pruss_clk)) {
+		dev_err(dev, "Failed to get clock\n");
+		ret = PTR_ERR(gdev->pruss_clk);
+		goto err_free_info;
+	}
+
+	ret = clk_enable(gdev->pruss_clk);
+	if (ret) {
+		dev_err(dev, "Failed to enable clock\n");
+		goto err_clk_put;
+	}
+
+	regs_prussio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs_prussio) {
+		dev_err(dev, "No PRUSS I/O resource specified\n");
+		ret = -EIO;
+		goto err_clk_disable;
+	}
+
+	if (!regs_prussio->start) {
+		dev_err(dev, "Invalid memory resource\n");
+		ret = -EIO;
+		goto err_clk_disable;
+	}
+
+	if (pdata->sram_pool) {
+		gdev->sram_pool = pdata->sram_pool;
+		gdev->sram_vaddr =
+			(unsigned long)gen_pool_dma_alloc(gdev->sram_pool,
+					sram_pool_sz, &gdev->sram_paddr);
+		if (!gdev->sram_vaddr) {
+			dev_err(dev, "Could not allocate SRAM pool\n");
+			ret = -ENOMEM;
+			goto err_clk_disable;
+		}
+	}
+
+	gdev->ddr_vaddr = dma_alloc_coherent(dev, extram_pool_sz,
+				&(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA);
+	if (!gdev->ddr_vaddr) {
+		dev_err(dev, "Could not allocate external memory\n");
+		ret = -ENOMEM;
+		goto err_free_sram;
+	}
+
+	len = resource_size(regs_prussio);
+	gdev->prussio_vaddr = ioremap(regs_prussio->start, len);
+	if (!gdev->prussio_vaddr) {
+		dev_err(dev, "Can't remap PRUSS I/O  address range\n");
+		ret = -ENOMEM;
+		goto err_free_ddr_vaddr;
+	}
+
+	gdev->pintc_base = pdata->pintc_base;
+	gdev->hostirq_start = platform_get_irq(pdev, 0);
+
+	for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) {
+		p->mem[0].addr = regs_prussio->start;
+		p->mem[0].size = resource_size(regs_prussio);
+		p->mem[0].memtype = UIO_MEM_PHYS;
+
+		p->mem[1].addr = gdev->sram_paddr;
+		p->mem[1].size = sram_pool_sz;
+		p->mem[1].memtype = UIO_MEM_PHYS;
+
+		p->mem[2].addr = gdev->ddr_paddr;
+		p->mem[2].size = extram_pool_sz;
+		p->mem[2].memtype = UIO_MEM_PHYS;
+
+		p->name = kasprintf(GFP_KERNEL, "pruss_evt%d", cnt);
+		p->version = DRV_VERSION;
+
+		/* Register PRUSS IRQ lines */
+		p->irq = gdev->hostirq_start + cnt;
+		p->handler = pruss_handler;
+		p->priv = gdev;
+
+		ret = uio_register_device(dev, p);
+		if (ret < 0) {
+			kfree(p->name);
+			goto err_unloop;
+		}
+	}
+
+	platform_set_drvdata(pdev, gdev);
+	return 0;
+
+err_unloop:
+	for (i = 0, p = gdev->info; i < cnt; i++, p++) {
+		uio_unregister_device(p);
+		kfree(p->name);
+	}
+	iounmap(gdev->prussio_vaddr);
+err_free_ddr_vaddr:
+	dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr,
+			  gdev->ddr_paddr);
+err_free_sram:
+	if (pdata->sram_pool)
+		gen_pool_free(gdev->sram_pool, gdev->sram_vaddr, sram_pool_sz);
+err_clk_disable:
+	clk_disable(gdev->pruss_clk);
+err_clk_put:
+	clk_put(gdev->pruss_clk);
+err_free_info:
+	kfree(gdev->info);
+err_free_gdev:
+	kfree(gdev);
+
+	return ret;
+}
+
+static int pruss_remove(struct platform_device *dev)
+{
+	struct uio_pruss_dev *gdev = platform_get_drvdata(dev);
+
+	pruss_cleanup(&dev->dev, gdev);
+	return 0;
+}
+
+static struct platform_driver pruss_driver = {
+	.probe = pruss_probe,
+	.remove = pruss_remove,
+	.driver = {
+		   .name = DRV_NAME,
+		   },
+};
+
+module_platform_driver(pruss_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("Amit Chatterjee <amit.chatterjee@ti.com>");
+MODULE_AUTHOR("Pratheesh Gangadhar <pratheesh@ti.com>");
diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c
new file mode 100644
index 0000000..9658a08
--- /dev/null
+++ b/drivers/uio/uio_sercos3.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/* sercos3: UIO driver for the Automata Sercos III PCI card
+
+   Copyright (C) 2008 Linutronix GmbH
+     Author: John Ogness <john.ogness@linutronix.de>
+
+   This is a straight-forward UIO driver, where interrupts are disabled
+   by the interrupt handler and re-enabled via a write to the UIO device
+   by the userspace-part.
+
+   The only part that may seem odd is the use of a logical OR when
+   storing and restoring enabled interrupts. This is done because the
+   userspace-part could directly modify the Interrupt Enable Register
+   at any time. To reduce possible conflicts, the kernel driver uses
+   a logical OR to make more controlled changes (rather than blindly
+   overwriting previous values).
+
+   Race conditions exist if the userspace-part directly modifies the
+   Interrupt Enable Register while in operation. The consequences are
+   that certain interrupts would fail to be enabled or disabled. For
+   this reason, the userspace-part should only directly modify the
+   Interrupt Enable Register at the beginning (to get things going).
+   The userspace-part can safely disable interrupts at any time using
+   a write to the UIO device.
+*/
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/uio_driver.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/* ID's for SERCOS III PCI card (PLX 9030) */
+#define SERCOS_SUB_VENDOR_ID  0x1971
+#define SERCOS_SUB_SYSID_3530 0x3530
+#define SERCOS_SUB_SYSID_3535 0x3535
+#define SERCOS_SUB_SYSID_3780 0x3780
+
+/* Interrupt Enable Register */
+#define IER0_OFFSET 0x08
+
+/* Interrupt Status Register */
+#define ISR0_OFFSET 0x18
+
+struct sercos3_priv {
+	u32 ier0_cache;
+	spinlock_t ier0_cache_lock;
+};
+
+/* this function assumes ier0_cache_lock is locked! */
+static void sercos3_disable_interrupts(struct uio_info *info,
+				       struct sercos3_priv *priv)
+{
+	void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
+
+	/* add enabled interrupts to cache */
+	priv->ier0_cache |= ioread32(ier0);
+
+	/* disable interrupts */
+	iowrite32(0, ier0);
+}
+
+/* this function assumes ier0_cache_lock is locked! */
+static void sercos3_enable_interrupts(struct uio_info *info,
+				      struct sercos3_priv *priv)
+{
+	void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
+
+	/* restore previously enabled interrupts */
+	iowrite32(ioread32(ier0) | priv->ier0_cache, ier0);
+	priv->ier0_cache = 0;
+}
+
+static irqreturn_t sercos3_handler(int irq, struct uio_info *info)
+{
+	struct sercos3_priv *priv = info->priv;
+	void __iomem *isr0 = info->mem[3].internal_addr + ISR0_OFFSET;
+	void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
+
+	if (!(ioread32(isr0) & ioread32(ier0)))
+		return IRQ_NONE;
+
+	spin_lock(&priv->ier0_cache_lock);
+	sercos3_disable_interrupts(info, priv);
+	spin_unlock(&priv->ier0_cache_lock);
+
+	return IRQ_HANDLED;
+}
+
+static int sercos3_irqcontrol(struct uio_info *info, s32 irq_on)
+{
+	struct sercos3_priv *priv = info->priv;
+
+	spin_lock_irq(&priv->ier0_cache_lock);
+	if (irq_on)
+		sercos3_enable_interrupts(info, priv);
+	else
+		sercos3_disable_interrupts(info, priv);
+	spin_unlock_irq(&priv->ier0_cache_lock);
+
+	return 0;
+}
+
+static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
+			       int n, int pci_bar)
+{
+	info->mem[n].addr = pci_resource_start(dev, pci_bar);
+	if (!info->mem[n].addr)
+		return -1;
+	info->mem[n].internal_addr = ioremap(pci_resource_start(dev, pci_bar),
+					     pci_resource_len(dev, pci_bar));
+	if (!info->mem[n].internal_addr)
+		return -1;
+	info->mem[n].size = pci_resource_len(dev, pci_bar);
+	info->mem[n].memtype = UIO_MEM_PHYS;
+	return 0;
+}
+
+static int sercos3_pci_probe(struct pci_dev *dev,
+				       const struct pci_device_id *id)
+{
+	struct uio_info *info;
+	struct sercos3_priv *priv;
+	int i;
+
+	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	priv = kzalloc(sizeof(struct sercos3_priv), GFP_KERNEL);
+	if (!priv)
+		goto out_free;
+
+	if (pci_enable_device(dev))
+		goto out_free_priv;
+
+	if (pci_request_regions(dev, "sercos3"))
+		goto out_disable;
+
+	/* we only need PCI BAR's 0, 2, 3, 4, 5 */
+	if (sercos3_setup_iomem(dev, info, 0, 0))
+		goto out_unmap;
+	if (sercos3_setup_iomem(dev, info, 1, 2))
+		goto out_unmap;
+	if (sercos3_setup_iomem(dev, info, 2, 3))
+		goto out_unmap;
+	if (sercos3_setup_iomem(dev, info, 3, 4))
+		goto out_unmap;
+	if (sercos3_setup_iomem(dev, info, 4, 5))
+		goto out_unmap;
+
+	spin_lock_init(&priv->ier0_cache_lock);
+	info->priv = priv;
+	info->name = "Sercos_III_PCI";
+	info->version = "0.0.1";
+	info->irq = dev->irq;
+	info->irq_flags = IRQF_SHARED;
+	info->handler = sercos3_handler;
+	info->irqcontrol = sercos3_irqcontrol;
+
+	pci_set_drvdata(dev, info);
+
+	if (uio_register_device(&dev->dev, info))
+		goto out_unmap;
+
+	return 0;
+
+out_unmap:
+	for (i = 0; i < 5; i++) {
+		if (info->mem[i].internal_addr)
+			iounmap(info->mem[i].internal_addr);
+	}
+	pci_release_regions(dev);
+out_disable:
+	pci_disable_device(dev);
+out_free_priv:
+	kfree(priv);
+out_free:
+	kfree(info);
+	return -ENODEV;
+}
+
+static void sercos3_pci_remove(struct pci_dev *dev)
+{
+	struct uio_info *info = pci_get_drvdata(dev);
+	int i;
+
+	uio_unregister_device(info);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	for (i = 0; i < 5; i++) {
+		if (info->mem[i].internal_addr)
+			iounmap(info->mem[i].internal_addr);
+	}
+	kfree(info->priv);
+	kfree(info);
+}
+
+static struct pci_device_id sercos3_pci_ids[] = {
+	{
+		.vendor =       PCI_VENDOR_ID_PLX,
+		.device =       PCI_DEVICE_ID_PLX_9030,
+		.subvendor =    SERCOS_SUB_VENDOR_ID,
+		.subdevice =    SERCOS_SUB_SYSID_3530,
+	},
+	{
+		.vendor =       PCI_VENDOR_ID_PLX,
+		.device =       PCI_DEVICE_ID_PLX_9030,
+		.subvendor =    SERCOS_SUB_VENDOR_ID,
+		.subdevice =    SERCOS_SUB_SYSID_3535,
+	},
+	{
+		.vendor =       PCI_VENDOR_ID_PLX,
+		.device =       PCI_DEVICE_ID_PLX_9030,
+		.subvendor =    SERCOS_SUB_VENDOR_ID,
+		.subdevice =    SERCOS_SUB_SYSID_3780,
+	},
+	{ 0, }
+};
+
+static struct pci_driver sercos3_pci_driver = {
+	.name = "sercos3",
+	.id_table = sercos3_pci_ids,
+	.probe = sercos3_pci_probe,
+	.remove = sercos3_pci_remove,
+};
+
+module_pci_driver(sercos3_pci_driver);
+MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
+MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
+MODULE_LICENSE("GPL v2");