summaryrefslogtreecommitdiffstats
path: root/drivers/uio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/uio')
-rw-r--r--drivers/uio/Kconfig43
-rw-r--r--drivers/uio/Makefile3
-rw-r--r--drivers/uio/uio.c174
-rw-r--r--drivers/uio/uio_aec.c1
-rw-r--r--drivers/uio/uio_cif.c5
-rw-r--r--drivers/uio/uio_netx.c173
-rw-r--r--drivers/uio/uio_pci_generic.c209
-rw-r--r--drivers/uio/uio_pdrv.c1
-rw-r--r--drivers/uio/uio_pdrv_genirq.c57
-rw-r--r--drivers/uio/uio_sercos3.c3
-rw-r--r--drivers/uio/uio_smx.c140
11 files changed, 547 insertions, 262 deletions
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index 7f86534de26..bb440792a1b 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -1,7 +1,6 @@
menuconfig UIO
tristate "Userspace I/O drivers"
depends on !S390
- default n
help
Enable this to allow the userspace driver core code to be
built. This code allows userspace programs easy access to
@@ -16,12 +15,11 @@ if UIO
config UIO_CIF
tristate "generic Hilscher CIF Card driver"
depends on PCI
- default n
help
Driver for Hilscher CIF DeviceNet and Profibus cards. This
- driver requires a userspace component that handles all of the
- heavy lifting and can be found at:
- http://www.osadl.org/projects/downloads/UIO/user/cif-*
+ 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.
@@ -46,22 +44,9 @@ config UIO_PDRV_GENIRQ
If you don't know what to do here, say N.
-config UIO_SMX
- tristate "SMX cryptengine UIO interface"
- default n
- help
- Userspace IO interface to the Cryptography engine found on the
- Nias Digital SMX boards. These will be available from Q4 2008
- from http://www.niasdigital.com. The userspace part of this
- driver will be released under the GPL at the same time as the
- hardware and will be able to be downloaded from the same site.
-
- If you compile this as a module, it will be called uio_smx.
-
config UIO_AEC
tristate "AEC video timestamp device"
depends on PCI
- default n
help
UIO driver for the Adrienne Electronics Corporation PCI time
@@ -78,7 +63,7 @@ config UIO_AEC
config UIO_SERCOS3
tristate "Automata Sercos III PCI card driver"
- default n
+ 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
@@ -89,4 +74,24 @@ config UIO_SERCOS3
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.
+
endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index 5c2586d7579..18fd818c5b9 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_UIO) += uio.o
obj-$(CONFIG_UIO_CIF) += uio_cif.o
obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
-obj-$(CONFIG_UIO_SMX) += uio_smx.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
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 03efb065455..51fe1795d5a 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -3,7 +3,7 @@
*
* Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de>
* Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de>
- * Copyright(C) 2006, Hans J. Koch <hjk@linutronix.de>
+ * Copyright(C) 2006, Hans J. Koch <hjk@hansjkoch.de>
* Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com>
*
* Userspace IO
@@ -17,13 +17,16 @@
#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.h>
#include <linux/string.h>
#include <linux/kobject.h>
+#include <linux/cdev.h>
#include <linux/uio_driver.h>
-#define UIO_MAX_DEVICES 255
+#define UIO_MAX_DEVICES (1U << MINORBITS)
struct uio_device {
struct module *owner;
@@ -39,15 +42,10 @@ struct uio_device {
};
static int uio_major;
+static struct cdev *uio_cdev;
static DEFINE_IDR(uio_idr);
static const struct file_operations uio_fops;
-/* UIO class infrastructure */
-static struct uio_class {
- struct kref kref;
- struct class *class;
-} *uio_class;
-
/* Protect idr accesses */
static DEFINE_MUTEX(minor_lock);
@@ -128,7 +126,7 @@ static ssize_t map_type_show(struct kobject *kobj, struct attribute *attr,
return entry->show(mem, buf);
}
-static struct sysfs_ops map_sysfs_ops = {
+static const struct sysfs_ops map_sysfs_ops = {
.show = map_type_show,
};
@@ -216,7 +214,7 @@ static ssize_t portio_type_show(struct kobject *kobj, struct attribute *attr,
return entry->show(port, buf);
}
-static struct sysfs_ops portio_sysfs_ops = {
+static const struct sysfs_ops portio_sysfs_ops = {
.show = portio_type_show,
};
@@ -230,45 +228,34 @@ static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uio_device *idev = dev_get_drvdata(dev);
- if (idev)
- return sprintf(buf, "%s\n", idev->info->name);
- else
- return -ENODEV;
+ return sprintf(buf, "%s\n", idev->info->name);
}
-static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static ssize_t show_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uio_device *idev = dev_get_drvdata(dev);
- if (idev)
- return sprintf(buf, "%s\n", idev->info->version);
- else
- return -ENODEV;
+ return sprintf(buf, "%s\n", idev->info->version);
}
-static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
static ssize_t show_event(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uio_device *idev = dev_get_drvdata(dev);
- if (idev)
- return sprintf(buf, "%u\n",
- (unsigned int)atomic_read(&idev->event));
- else
- return -ENODEV;
+ return sprintf(buf, "%u\n", (unsigned int)atomic_read(&idev->event));
}
-static DEVICE_ATTR(event, S_IRUGO, show_event, NULL);
-static struct attribute *uio_attrs[] = {
- &dev_attr_name.attr,
- &dev_attr_version.attr,
- &dev_attr_event.attr,
- NULL,
+static struct device_attribute uio_class_attributes[] = {
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(version, S_IRUGO, show_version, NULL),
+ __ATTR(event, S_IRUGO, show_event, NULL),
+ {}
};
-static struct attribute_group uio_attr_grp = {
- .attrs = uio_attrs,
+/* UIO class infrastructure */
+static struct class uio_class = {
+ .name = "uio",
+ .dev_attrs = uio_class_attributes,
};
/*
@@ -285,10 +272,6 @@ static int uio_dev_add_attributes(struct uio_device *idev)
struct uio_port *port;
struct uio_portio *portio;
- ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
- if (ret)
- goto err_group;
-
for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
mem = &idev->info->mem[mi];
if (mem->size == 0)
@@ -356,8 +339,6 @@ err_map:
kobject_put(&map->kobj);
}
kobject_put(idev->map_dir);
- sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
-err_group:
dev_err(idev->dev, "error creating sysfs files (%d)\n", ret);
return ret;
}
@@ -383,8 +364,6 @@ static void uio_dev_del_attributes(struct uio_device *idev)
kobject_put(&port->portio->kobj);
}
kobject_put(idev->portio_dir);
-
- sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
}
static int uio_get_minor(struct uio_device *idev)
@@ -523,7 +502,7 @@ static unsigned int uio_poll(struct file *filep, poll_table *wait)
struct uio_listener *listener = filep->private_data;
struct uio_device *idev = listener->dev;
- if (idev->info->irq == UIO_IRQ_NONE)
+ if (!idev->info->irq)
return -EIO;
poll_wait(filep, &idev->wait, wait);
@@ -541,7 +520,7 @@ static ssize_t uio_read(struct file *filep, char __user *buf,
ssize_t retval;
s32 event_count;
- if (idev->info->irq == UIO_IRQ_NONE)
+ if (!idev->info->irq)
return -EIO;
if (count != sizeof(s32))
@@ -589,7 +568,7 @@ static ssize_t uio_write(struct file *filep, const char __user *buf,
ssize_t retval;
s32 irq_on;
- if (idev->info->irq == UIO_IRQ_NONE)
+ if (!idev->info->irq)
return -EIO;
if (count != sizeof(s32))
@@ -658,7 +637,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return 0;
}
-static struct vm_operations_struct uio_vm_ops = {
+static const struct vm_operations_struct uio_vm_ops = {
.open = uio_vma_open,
.close = uio_vma_close,
.fault = uio_vma_fault,
@@ -738,72 +717,77 @@ static const struct file_operations uio_fops = {
.mmap = uio_mmap,
.poll = uio_poll,
.fasync = uio_fasync,
+ .llseek = noop_llseek,
};
static int uio_major_init(void)
{
- uio_major = register_chrdev(0, "uio", &uio_fops);
- if (uio_major < 0)
- return uio_major;
- return 0;
+ 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;
+ result = 0;
+out:
+ return result;
+out_put:
+ kobject_put(&cdev->kobj);
+out_unregister:
+ unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES);
+ goto out;
}
static void uio_major_cleanup(void)
{
- unregister_chrdev(uio_major, "uio");
+ unregister_chrdev_region(MKDEV(uio_major, 0), UIO_MAX_DEVICES);
+ cdev_del(uio_cdev);
}
static int init_uio_class(void)
{
- int ret = 0;
-
- if (uio_class != NULL) {
- kref_get(&uio_class->kref);
- goto exit;
- }
+ int ret;
/* This is the first time in here, set everything up properly */
ret = uio_major_init();
if (ret)
goto exit;
- uio_class = kzalloc(sizeof(*uio_class), GFP_KERNEL);
- if (!uio_class) {
- ret = -ENOMEM;
- goto err_kzalloc;
- }
-
- kref_init(&uio_class->kref);
- uio_class->class = class_create(THIS_MODULE, "uio");
- if (IS_ERR(uio_class->class)) {
- ret = IS_ERR(uio_class->class);
- printk(KERN_ERR "class_create failed for uio\n");
- goto err_class_create;
+ ret = class_register(&uio_class);
+ if (ret) {
+ printk(KERN_ERR "class_register failed for uio\n");
+ goto err_class_register;
}
return 0;
-err_class_create:
- kfree(uio_class);
- uio_class = NULL;
-err_kzalloc:
+err_class_register:
uio_major_cleanup();
exit:
return ret;
}
-static void release_uio_class(struct kref *kref)
+static void release_uio_class(void)
{
- /* Ok, we cheat as we know we only have one uio_class */
- class_destroy(uio_class->class);
- kfree(uio_class);
+ class_unregister(&uio_class);
uio_major_cleanup();
- uio_class = NULL;
-}
-
-static void uio_class_destroy(void)
-{
- if (uio_class)
- kref_put(&uio_class->kref, release_uio_class);
}
/**
@@ -826,10 +810,6 @@ int __uio_register_device(struct module *owner,
info->uio_dev = NULL;
- ret = init_uio_class();
- if (ret)
- return ret;
-
idev = kzalloc(sizeof(*idev), GFP_KERNEL);
if (!idev) {
ret = -ENOMEM;
@@ -845,7 +825,7 @@ int __uio_register_device(struct module *owner,
if (ret)
goto err_get_minor;
- idev->dev = device_create(uio_class->class, parent,
+ idev->dev = device_create(&uio_class, parent,
MKDEV(uio_major, idev->minor), idev,
"uio%d", idev->minor);
if (IS_ERR(idev->dev)) {
@@ -860,9 +840,9 @@ int __uio_register_device(struct module *owner,
info->uio_dev = idev;
- if (idev->info->irq >= 0) {
- ret = request_irq(idev->info->irq, uio_interrupt,
- idev->info->irq_flags, idev->info->name, idev);
+ if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
+ ret = request_irq(info->irq, uio_interrupt,
+ info->irq_flags, info->name, idev);
if (ret)
goto err_request_irq;
}
@@ -872,13 +852,12 @@ int __uio_register_device(struct module *owner,
err_request_irq:
uio_dev_del_attributes(idev);
err_uio_dev_add_attributes:
- device_destroy(uio_class->class, MKDEV(uio_major, idev->minor));
+ device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
err_device_create:
uio_free_minor(idev);
err_get_minor:
kfree(idev);
err_kzalloc:
- uio_class_destroy();
return ret;
}
EXPORT_SYMBOL_GPL(__uio_register_device);
@@ -899,15 +878,13 @@ void uio_unregister_device(struct uio_info *info)
uio_free_minor(idev);
- if (info->irq >= 0)
+ if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
free_irq(info->irq, idev);
uio_dev_del_attributes(idev);
- dev_set_drvdata(idev->dev, NULL);
- device_destroy(uio_class->class, MKDEV(uio_major, idev->minor));
+ device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
kfree(idev);
- uio_class_destroy();
return;
}
@@ -915,11 +892,12 @@ EXPORT_SYMBOL_GPL(uio_unregister_device);
static int __init uio_init(void)
{
- return 0;
+ return init_uio_class();
}
static void __exit uio_exit(void)
{
+ release_uio_class();
}
module_init(uio_init)
diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c
index b7830e9a3ba..72b22d44e8b 100644
--- a/drivers/uio/uio_aec.c
+++ b/drivers/uio/uio_aec.c
@@ -27,6 +27,7 @@
#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
diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c
index 28034c81291..a84a451159e 100644
--- a/drivers/uio/uio_cif.c
+++ b/drivers/uio/uio_cif.c
@@ -1,7 +1,7 @@
/*
* UIO Hilscher CIF card driver
*
- * (C) 2007 Hans J. Koch <hjk@linutronix.de>
+ * (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
* Original code (C) 2005 Benedikt Spranger <b.spranger@linutronix.de>
*
* Licensed under GPL version 2 only.
@@ -11,6 +11,7 @@
#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>
@@ -78,7 +79,7 @@ static int __devinit hilscher_pci_probe(struct pci_dev *dev,
}
info->version = "0.0.1";
info->irq = dev->irq;
- info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
+ info->irq_flags = IRQF_SHARED;
info->handler = hilscher_handler;
if (uio_register_device(&dev->dev, info))
diff --git a/drivers/uio/uio_netx.c b/drivers/uio/uio_netx.c
new file mode 100644
index 00000000000..5ffdb483b01
--- /dev/null
+++ b/drivers/uio/uio_netx.c
@@ -0,0 +1,173 @@
+/*
+ * 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>
+ *
+ * Licensed under GPL version 2 only.
+ *
+ */
+
+#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_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 __devinit 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;
+ 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);
+ pci_set_drvdata(dev, NULL);
+ 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_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,
+};
+
+static int __init netx_init_module(void)
+{
+ return pci_register_driver(&netx_pci_driver);
+}
+
+static void __exit netx_exit_module(void)
+{
+ pci_unregister_driver(&netx_pci_driver);
+}
+
+module_init(netx_init_module);
+module_exit(netx_exit_module);
+
+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 00000000000..fc22e1e6f21
--- /dev/null
+++ b/drivers/uio/uio_pci_generic.c
@@ -0,0 +1,209 @@
+/* uio_pci_generic - generic UIO driver for PCI 2.3 devices
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * 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>
+#include <linux/spinlock.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;
+ spinlock_t lock; /* guards command register accesses */
+};
+
+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);
+ struct pci_dev *pdev = gdev->pdev;
+ irqreturn_t ret = IRQ_NONE;
+ u32 cmd_status_dword;
+ u16 origcmd, newcmd, status;
+
+ /* We do a single dword read to retrieve both command and status.
+ * Document assumptions that make this possible. */
+ BUILD_BUG_ON(PCI_COMMAND % 4);
+ BUILD_BUG_ON(PCI_COMMAND + 2 != PCI_STATUS);
+
+ spin_lock_irq(&gdev->lock);
+ pci_block_user_cfg_access(pdev);
+
+ /* Read both command and status registers in a single 32-bit operation.
+ * Note: we could cache the value for command and move the status read
+ * out of the lock if there was a way to get notified of user changes
+ * to command register through sysfs. Should be good for shared irqs. */
+ pci_read_config_dword(pdev, PCI_COMMAND, &cmd_status_dword);
+ origcmd = cmd_status_dword;
+ status = cmd_status_dword >> 16;
+
+ /* Check interrupt status register to see whether our device
+ * triggered the interrupt. */
+ if (!(status & PCI_STATUS_INTERRUPT))
+ goto done;
+
+ /* We triggered the interrupt, disable it. */
+ newcmd = origcmd | PCI_COMMAND_INTX_DISABLE;
+ if (newcmd != origcmd)
+ pci_write_config_word(pdev, PCI_COMMAND, newcmd);
+
+ /* UIO core will signal the user process. */
+ ret = IRQ_HANDLED;
+done:
+
+ pci_unblock_user_cfg_access(pdev);
+ spin_unlock_irq(&gdev->lock);
+ return ret;
+}
+
+/* Verify that the device supports Interrupt Disable bit in command register,
+ * per PCI 2.3, by flipping this bit and reading it back: this bit was readonly
+ * in PCI 2.2. */
+static int __devinit verify_pci_2_3(struct pci_dev *pdev)
+{
+ u16 orig, new;
+ int err = 0;
+
+ pci_block_user_cfg_access(pdev);
+ pci_read_config_word(pdev, PCI_COMMAND, &orig);
+ pci_write_config_word(pdev, PCI_COMMAND,
+ orig ^ PCI_COMMAND_INTX_DISABLE);
+ pci_read_config_word(pdev, PCI_COMMAND, &new);
+ /* There's no way to protect against
+ * hardware bugs or detect them reliably, but as long as we know
+ * what the value should be, let's go ahead and check it. */
+ if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) {
+ err = -EBUSY;
+ dev_err(&pdev->dev, "Command changed from 0x%x to 0x%x: "
+ "driver or HW bug?\n", orig, new);
+ goto err;
+ }
+ if (!((new ^ orig) & PCI_COMMAND_INTX_DISABLE)) {
+ dev_warn(&pdev->dev, "Device does not support "
+ "disabling interrupts: unable to bind.\n");
+ err = -ENODEV;
+ goto err;
+ }
+ /* Now restore the original value. */
+ pci_write_config_word(pdev, PCI_COMMAND, orig);
+err:
+ pci_unblock_user_cfg_access(pdev);
+ return err;
+}
+
+static int __devinit 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) {
+ dev_warn(&pdev->dev, "No IRQ assigned to device: "
+ "no support for interrupts?\n");
+ pci_disable_device(pdev);
+ return -ENODEV;
+ }
+
+ err = verify_pci_2_3(pdev);
+ if (err)
+ 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->info.irq = pdev->irq;
+ gdev->info.irq_flags = IRQF_SHARED;
+ gdev->info.handler = irqhandler;
+ gdev->pdev = pdev;
+ spin_lock_init(&gdev->lock);
+
+ if (uio_register_device(&pdev->dev, &gdev->info))
+ 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 driver = {
+ .name = "uio_pci_generic",
+ .id_table = NULL, /* only dynamic id's */
+ .probe = probe,
+ .remove = remove,
+};
+
+static int __init init(void)
+{
+ pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+ return pci_register_driver(&driver);
+}
+
+static void __exit cleanup(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_init(init);
+module_exit(cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c
index d494ce9288c..7d3e469b990 100644
--- a/drivers/uio/uio_pdrv.c
+++ b/drivers/uio/uio_pdrv.c
@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/uio_driver.h>
#include <linux/stringify.h>
+#include <linux/slab.h>
#define DRIVER_NAME "uio_pdrv"
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 3f06818cf9f..7174d518b8a 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -20,6 +20,8 @@
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/stringify.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#define DRIVER_NAME "uio_pdrv_genirq"
@@ -27,8 +29,27 @@ struct uio_pdrv_genirq_platdata {
struct uio_info *uioinfo;
spinlock_t lock;
unsigned long flags;
+ struct platform_device *pdev;
};
+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;
@@ -97,6 +118,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
priv->uioinfo = uioinfo;
spin_lock_init(&priv->lock);
priv->flags = 0; /* interrupt is enabled to begin with */
+ priv->pdev = pdev;
uiomem = &uioinfo->mem[0];
@@ -133,11 +155,19 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
* Interrupt sharing is not supported.
*/
- uioinfo->irq_flags |= IRQF_DISABLED;
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");
@@ -148,6 +178,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
return 0;
bad1:
kfree(priv);
+ pm_runtime_disable(&pdev->dev);
bad0:
return ret;
}
@@ -157,16 +188,40 @@ 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);
kfree(priv);
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,
+};
+
static struct platform_driver uio_pdrv_genirq = {
.probe = uio_pdrv_genirq_probe,
.remove = uio_pdrv_genirq_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &uio_pdrv_genirq_dev_pm_ops,
},
};
diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c
index a6d1b2bc47f..a187fa14c5c 100644
--- a/drivers/uio/uio_sercos3.c
+++ b/drivers/uio/uio_sercos3.c
@@ -28,6 +28,7 @@
#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
@@ -153,7 +154,7 @@ static int __devinit sercos3_pci_probe(struct pci_dev *dev,
info->name = "Sercos_III_PCI";
info->version = "0.0.1";
info->irq = dev->irq;
- info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
+ info->irq_flags = IRQF_SHARED;
info->handler = sercos3_handler;
info->irqcontrol = sercos3_irqcontrol;
diff --git a/drivers/uio/uio_smx.c b/drivers/uio/uio_smx.c
deleted file mode 100644
index 44054a650a8..00000000000
--- a/drivers/uio/uio_smx.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * UIO SMX Cryptengine driver.
- *
- * (C) 2008 Nias Digital P/L <bn@niasdigital.com>
- *
- * 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/device.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/uio_driver.h>
-#include <linux/io.h>
-
-#define DRV_NAME "smx-ce"
-#define DRV_VERSION "0.03"
-
-#define SMX_CSR 0x00000000
-#define SMX_EnD 0x00000001
-#define SMX_RUN 0x00000002
-#define SMX_DRDY 0x00000004
-#define SMX_ERR 0x00000008
-
-static irqreturn_t smx_handler(int irq, struct uio_info *dev_info)
-{
- void __iomem *csr = dev_info->mem[0].internal_addr + SMX_CSR;
-
- u32 status = ioread32(csr);
-
- if (!(status & SMX_DRDY))
- return IRQ_NONE;
-
- /* Disable interrupt */
- iowrite32(status & ~SMX_DRDY, csr);
- return IRQ_HANDLED;
-}
-
-static int __devinit smx_ce_probe(struct platform_device *dev)
-{
-
- int ret = -ENODEV;
- struct uio_info *info;
- struct resource *regs;
-
- info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- regs = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!regs) {
- dev_err(&dev->dev, "No memory resource specified\n");
- goto out_free;
- }
-
- info->mem[0].addr = regs->start;
- if (!info->mem[0].addr) {
- dev_err(&dev->dev, "Invalid memory resource\n");
- goto out_free;
- }
-
- info->mem[0].size = regs->end - regs->start + 1;
- info->mem[0].internal_addr = ioremap(regs->start, info->mem[0].size);
-
- if (!info->mem[0].internal_addr) {
- dev_err(&dev->dev, "Can't remap memory address range\n");
- goto out_free;
- }
-
- info->mem[0].memtype = UIO_MEM_PHYS;
-
- info->name = "smx-ce";
- info->version = "0.03";
-
- info->irq = platform_get_irq(dev, 0);
- if (info->irq < 0) {
- ret = info->irq;
- dev_err(&dev->dev, "No (or invalid) IRQ resource specified\n");
- goto out_unmap;
- }
-
- info->irq_flags = IRQF_SHARED;
- info->handler = smx_handler;
-
- platform_set_drvdata(dev, info);
-
- ret = uio_register_device(&dev->dev, info);
-
- if (ret)
- goto out_unmap;
-
- return 0;
-
-out_unmap:
- iounmap(info->mem[0].internal_addr);
-out_free:
- kfree(info);
-
- return ret;
-}
-
-static int __devexit smx_ce_remove(struct platform_device *dev)
-{
- struct uio_info *info = platform_get_drvdata(dev);
-
- uio_unregister_device(info);
- platform_set_drvdata(dev, NULL);
- iounmap(info->mem[0].internal_addr);
-
- kfree(info);
-
- return 0;
-}
-
-static struct platform_driver smx_ce_driver = {
- .probe = smx_ce_probe,
- .remove = __devexit_p(smx_ce_remove),
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init smx_ce_init_module(void)
-{
- return platform_driver_register(&smx_ce_driver);
-}
-module_init(smx_ce_init_module);
-
-static void __exit smx_ce_exit_module(void)
-{
- platform_driver_unregister(&smx_ce_driver);
-}
-module_exit(smx_ce_exit_module);
-
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION(DRV_VERSION);
-MODULE_AUTHOR("Ben Nizette <bn@niasdigital.com>");