aboutsummaryrefslogtreecommitdiff
path: root/drivers/uio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/uio')
-rw-r--r--drivers/uio/Kconfig57
-rw-r--r--drivers/uio/Makefile4
-rw-r--r--drivers/uio/uio.c157
-rw-r--r--drivers/uio/uio_aec.c17
-rw-r--r--drivers/uio/uio_cif.c19
-rw-r--r--drivers/uio/uio_dmem_genirq.c357
-rw-r--r--drivers/uio/uio_mf624.c246
-rw-r--r--drivers/uio/uio_netx.c36
-rw-r--r--drivers/uio/uio_pci_generic.c102
-rw-r--r--drivers/uio/uio_pdrv.c121
-rw-r--r--drivers/uio/uio_pdrv_genirq.c98
-rw-r--r--drivers/uio/uio_pruss.c242
-rw-r--r--drivers/uio/uio_sercos3.c19
13 files changed, 1081 insertions, 394 deletions
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index bb440792a1b..5a90914d856 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -1,6 +1,6 @@
menuconfig UIO
tristate "Userspace I/O drivers"
- depends on !S390
+ depends on MMU
help
Enable this to allow the userspace driver core code to be
built. This code allows userspace programs easy access to
@@ -24,13 +24,6 @@ config UIO_CIF
To compile this driver as a module, choose M here: the module
will be called uio_cif.
-config UIO_PDRV
- tristate "Userspace I/O platform driver"
- help
- Generic platform driver for Userspace I/O devices.
-
- If you don't know what to do here, say N.
-
config UIO_PDRV_GENIRQ
tristate "Userspace I/O platform driver with generic IRQ handling"
help
@@ -44,6 +37,23 @@ config UIO_PDRV_GENIRQ
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
@@ -94,4 +104,35 @@ config UIO_NETX
To compile this driver as a module, choose M here; the module
will be called uio_netx.
+config UIO_PRUSS
+ tristate "Texas Instruments PRUSS driver"
+ depends on ARCH_DAVINCI_DA850
+ select GENERIC_ALLOCATOR
+ 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.
+
endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index 18fd818c5b9..d3218bde3ae 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -1,8 +1,10 @@
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_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
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 51fe1795d5a..a673e5b6a2e 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -35,7 +35,6 @@ struct uio_device {
atomic_t event;
struct fasync_struct *async_queue;
wait_queue_head_t wait;
- int vma_count;
struct uio_info *info;
struct kobject *map_dir;
struct kobject *portio_dir;
@@ -69,7 +68,7 @@ static ssize_t map_name_show(struct uio_mem *mem, char *buf)
static ssize_t map_addr_show(struct uio_mem *mem, char *buf)
{
- return sprintf(buf, "0x%lx\n", mem->addr);
+ return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr);
}
static ssize_t map_size_show(struct uio_mem *mem, char *buf)
@@ -79,7 +78,7 @@ static ssize_t map_size_show(struct uio_mem *mem, char *buf)
static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
{
- return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
+ return sprintf(buf, "0x%llx\n", (unsigned long long)mem->addr & ~PAGE_MASK);
}
struct map_sysfs_entry {
@@ -224,38 +223,42 @@ static struct kobj_type portio_attr_type = {
.default_attrs = portio_attrs,
};
-static ssize_t show_name(struct device *dev,
+static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uio_device *idev = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", idev->info->name);
}
+static DEVICE_ATTR_RO(name);
-static ssize_t show_version(struct device *dev,
+static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct uio_device *idev = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", idev->info->version);
}
+static DEVICE_ATTR_RO(version);
-static ssize_t show_event(struct device *dev,
+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 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 *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_attrs = uio_class_attributes,
+ .dev_groups = uio_groups,
};
/*
@@ -285,13 +288,13 @@ static int uio_dev_add_attributes(struct uio_device *idev)
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
- goto err_map;
+ goto err_map_kobj;
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;
+ goto err_map_kobj;
ret = kobject_uevent(&map->kobj, KOBJ_ADD);
if (ret)
goto err_map;
@@ -310,14 +313,14 @@ static int uio_dev_add_attributes(struct uio_device *idev)
}
portio = kzalloc(sizeof(*portio), GFP_KERNEL);
if (!portio)
- goto err_portio;
+ goto err_portio_kobj;
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;
+ goto err_portio_kobj;
ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
if (ret)
goto err_portio;
@@ -326,14 +329,18 @@ static int uio_dev_add_attributes(struct uio_device *idev)
return 0;
err_portio:
- for (pi--; pi >= 0; pi--) {
+ 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:
- for (mi--; mi>=0; mi--) {
+ mi--;
+err_map_kobj:
+ for (; mi >= 0; mi--) {
mem = &idev->info->mem[mi];
map = mem->map;
kobject_put(&map->kobj);
@@ -369,20 +376,16 @@ static void uio_dev_del_attributes(struct uio_device *idev)
static int uio_get_minor(struct uio_device *idev)
{
int retval = -ENOMEM;
- int id;
mutex_lock(&minor_lock);
- if (idr_pre_get(&uio_idr, GFP_KERNEL) == 0)
- goto exit;
-
- retval = idr_get_new(&uio_idr, idev, &id);
- if (retval < 0) {
- if (retval == -EAGAIN)
- retval = -ENOMEM;
- goto exit;
+ 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;
}
- idev->minor = id & MAX_ID_MASK;
-exit:
mutex_unlock(&minor_lock);
return retval;
}
@@ -587,35 +590,22 @@ static ssize_t uio_write(struct file *filep, const char __user *buf,
static int uio_find_mem_index(struct vm_area_struct *vma)
{
- int mi;
struct uio_device *idev = vma->vm_private_data;
- for (mi = 0; mi < MAX_UIO_MAPS; mi++) {
- if (idev->info->mem[mi].size == 0)
+ if (vma->vm_pgoff < MAX_UIO_MAPS) {
+ if (idev->info->mem[vma->vm_pgoff].size == 0)
return -1;
- if (vma->vm_pgoff == mi)
- return mi;
+ return (int)vma->vm_pgoff;
}
return -1;
}
-static void uio_vma_open(struct vm_area_struct *vma)
-{
- struct uio_device *idev = vma->vm_private_data;
- idev->vma_count++;
-}
-
-static void uio_vma_close(struct vm_area_struct *vma)
-{
- struct uio_device *idev = vma->vm_private_data;
- idev->vma_count--;
-}
-
static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct uio_device *idev = vma->vm_private_data;
struct page *page;
unsigned long offset;
+ void *addr;
int mi = uio_find_mem_index(vma);
if (mi < 0)
@@ -627,48 +617,66 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
*/
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(idev->info->mem[mi].addr + offset);
+ page = virt_to_page(addr);
else
- page = vmalloc_to_page((void *)idev->info->mem[mi].addr
- + offset);
+ page = vmalloc_to_page(addr);
get_page(page);
vmf->page = page;
return 0;
}
-static const struct vm_operations_struct uio_vm_ops = {
- .open = uio_vma_open,
- .close = uio_vma_close,
+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;
- vma->vm_flags |= VM_IO | VM_RESERVED;
+ 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,
- idev->info->mem[mi].addr >> PAGE_SHIFT,
+ mem->addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
-static int uio_mmap_logical(struct vm_area_struct *vma)
-{
- vma->vm_flags |= VM_RESERVED;
- vma->vm_ops = &uio_vm_ops;
- uio_vma_open(vma);
- return 0;
-}
-
static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
{
struct uio_listener *listener = filep->private_data;
@@ -686,7 +694,7 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
if (mi < 0)
return -EINVAL;
- requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ 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)
@@ -746,14 +754,13 @@ static int uio_major_init(void)
uio_major = MAJOR(uio_dev);
uio_cdev = cdev;
- result = 0;
-out:
- return result;
+ return 0;
out_put:
kobject_put(&cdev->kobj);
out_unregister:
unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES);
- goto out;
+out:
+ return result;
}
static void uio_major_cleanup(void)
@@ -810,10 +817,9 @@ int __uio_register_device(struct module *owner,
info->uio_dev = NULL;
- idev = kzalloc(sizeof(*idev), GFP_KERNEL);
+ idev = devm_kzalloc(parent, sizeof(*idev), GFP_KERNEL);
if (!idev) {
- ret = -ENOMEM;
- goto err_kzalloc;
+ return -ENOMEM;
}
idev->owner = owner;
@@ -823,7 +829,7 @@ int __uio_register_device(struct module *owner,
ret = uio_get_minor(idev);
if (ret)
- goto err_get_minor;
+ return ret;
idev->dev = device_create(&uio_class, parent,
MKDEV(uio_major, idev->minor), idev,
@@ -841,7 +847,7 @@ int __uio_register_device(struct module *owner,
info->uio_dev = idev;
if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
- ret = request_irq(info->irq, uio_interrupt,
+ ret = devm_request_irq(idev->dev, info->irq, uio_interrupt,
info->irq_flags, info->name, idev);
if (ret)
goto err_request_irq;
@@ -855,9 +861,6 @@ err_uio_dev_add_attributes:
device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
err_device_create:
uio_free_minor(idev);
-err_get_minor:
- kfree(idev);
-err_kzalloc:
return ret;
}
EXPORT_SYMBOL_GPL(__uio_register_device);
@@ -878,13 +881,9 @@ void uio_unregister_device(struct uio_info *info)
uio_free_minor(idev);
- if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
- free_irq(info->irq, idev);
-
uio_dev_del_attributes(idev);
device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
- kfree(idev);
return;
}
diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c
index 72b22d44e8b..1549fab633c 100644
--- a/drivers/uio/uio_aec.c
+++ b/drivers/uio/uio_aec.c
@@ -78,7 +78,7 @@ static void print_board_data(struct pci_dev *pdev, struct uio_info *i)
ioread8(i->priv + 0x07));
}
-static int __devinit probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct uio_info *info;
int ret;
@@ -147,7 +147,6 @@ static void remove(struct pci_dev *pdev)
uio_unregister_device(info);
pci_release_regions(pdev);
pci_disable_device(pdev);
- pci_set_drvdata(pdev, NULL);
iounmap(info->priv);
kfree(info);
@@ -160,17 +159,5 @@ static struct pci_driver pci_driver = {
.remove = remove,
};
-static int __init aectc_init(void)
-{
- return pci_register_driver(&pci_driver);
-}
-
-static void __exit aectc_exit(void)
-{
- pci_unregister_driver(&pci_driver);
-}
-
+module_pci_driver(pci_driver);
MODULE_LICENSE("GPL");
-
-module_init(aectc_init);
-module_exit(aectc_exit);
diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c
index a84a451159e..30f533ce375 100644
--- a/drivers/uio/uio_cif.c
+++ b/drivers/uio/uio_cif.c
@@ -40,7 +40,7 @@ static irqreturn_t hilscher_handler(int irq, struct uio_info *dev_info)
return IRQ_HANDLED;
}
-static int __devinit hilscher_pci_probe(struct pci_dev *dev,
+static int hilscher_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct uio_info *info;
@@ -106,13 +106,12 @@ static void hilscher_pci_remove(struct pci_dev *dev)
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 hilscher_pci_ids[] __devinitdata = {
+static struct pci_device_id hilscher_pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_PLX,
.device = PCI_DEVICE_ID_PLX_9030,
@@ -135,19 +134,7 @@ static struct pci_driver hilscher_pci_driver = {
.remove = hilscher_pci_remove,
};
-static int __init hilscher_init_module(void)
-{
- return pci_register_driver(&hilscher_pci_driver);
-}
-
-static void __exit hilscher_exit_module(void)
-{
- pci_unregister_driver(&hilscher_pci_driver);
-}
-
-module_init(hilscher_init_module);
-module_exit(hilscher_exit_module);
-
+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 00000000000..8d0bba46956
--- /dev/null
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -0,0 +1,357 @@
+/*
+ * 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 = i;
+ 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,
+ .owner = THIS_MODULE,
+ .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_mf624.c b/drivers/uio/uio_mf624.c
new file mode 100644
index 00000000000..d1f95a1567b
--- /dev/null
+++ b/drivers/uio/uio_mf624.c
@@ -0,0 +1,246 @@
+/*
+ * 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_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 */
+ info->mem[0].name = "PCI chipset, interrupts, status "
+ "bits, special functions";
+ info->mem[0].addr = pci_resource_start(dev, 0);
+ if (!info->mem[0].addr)
+ goto out_release;
+ info->mem[0].size = pci_resource_len(dev, 0);
+ info->mem[0].memtype = UIO_MEM_PHYS;
+ info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
+ if (!info->mem[0].internal_addr)
+ goto out_release;
+
+ /* BAR2 */
+ info->mem[1].name = "ADC, DAC, DIO";
+ info->mem[1].addr = pci_resource_start(dev, 2);
+ if (!info->mem[1].addr)
+ goto out_unmap0;
+ info->mem[1].size = pci_resource_len(dev, 2);
+ info->mem[1].memtype = UIO_MEM_PHYS;
+ info->mem[1].internal_addr = pci_ioremap_bar(dev, 2);
+ if (!info->mem[1].internal_addr)
+ goto out_unmap0;
+
+ /* BAR4 */
+ info->mem[2].name = "Counter/timer chip";
+ info->mem[2].addr = pci_resource_start(dev, 4);
+ if (!info->mem[2].addr)
+ goto out_unmap1;
+ info->mem[2].size = pci_resource_len(dev, 4);
+ info->mem[2].memtype = UIO_MEM_PHYS;
+ info->mem[2].internal_addr = pci_ioremap_bar(dev, 4);
+ if (!info->mem[2].internal_addr)
+ 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
index 5ffdb483b01..4c345db8b01 100644
--- a/drivers/uio/uio_netx.c
+++ b/drivers/uio/uio_netx.c
@@ -18,6 +18,9 @@
#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
@@ -45,7 +48,7 @@ static irqreturn_t netx_handler(int irq, struct uio_info *dev_info)
return IRQ_HANDLED;
}
-static int __devinit netx_pci_probe(struct pci_dev *dev,
+static int netx_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct uio_info *info;
@@ -66,6 +69,10 @@ static int __devinit netx_pci_probe(struct pci_dev *dev,
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";
@@ -120,7 +127,6 @@ static void netx_pci_remove(struct pci_dev *dev)
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);
@@ -134,6 +140,18 @@ static struct pci_device_id netx_pci_ids[] = {
.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,
@@ -155,19 +173,7 @@ static struct pci_driver netx_pci_driver = {
.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_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
index fc22e1e6f21..077ae12269c 100644
--- a/drivers/uio/uio_pci_generic.c
+++ b/drivers/uio/uio_pci_generic.c
@@ -24,7 +24,6 @@
#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>"
@@ -33,7 +32,6 @@
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 *
@@ -47,82 +45,15 @@ to_uio_pci_generic_dev(struct uio_info *info)
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;
-}
+ if (!pci_check_and_mask_intx(gdev->pdev))
+ return IRQ_NONE;
-/* 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;
+ /* UIO core will signal the user process. */
+ return IRQ_HANDLED;
}
-static int __devinit probe(struct pci_dev *pdev,
+static int probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct uio_pci_generic_dev *gdev;
@@ -142,9 +73,10 @@ static int __devinit probe(struct pci_dev *pdev,
return -ENODEV;
}
- err = verify_pci_2_3(pdev);
- if (err)
+ if (!pci_intx_mask_supported(pdev)) {
+ err = -ENODEV;
goto err_verify;
+ }
gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL);
if (!gdev) {
@@ -158,7 +90,6 @@ static int __devinit probe(struct pci_dev *pdev,
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;
@@ -182,27 +113,14 @@ static void remove(struct pci_dev *pdev)
kfree(gdev);
}
-static struct pci_driver driver = {
+static struct pci_driver uio_pci_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_pci_driver(uio_pci_driver);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c
deleted file mode 100644
index 7d3e469b990..00000000000
--- a/drivers/uio/uio_pdrv.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * drivers/uio/uio_pdrv.c
- *
- * 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/stringify.h>
-#include <linux/slab.h>
-
-#define DRIVER_NAME "uio_pdrv"
-
-struct uio_platdata {
- struct uio_info *uioinfo;
-};
-
-static int uio_pdrv_probe(struct platform_device *pdev)
-{
- struct uio_info *uioinfo = pdev->dev.platform_data;
- struct uio_platdata *pdata;
- struct uio_mem *uiomem;
- int ret = -ENODEV;
- int i;
-
- if (!uioinfo || !uioinfo->name || !uioinfo->version) {
- dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__);
- goto err_uioinfo;
- }
-
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__);
- goto err_alloc_pdata;
- }
-
- pdata->uioinfo = uioinfo;
-
- 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 = r->end - r->start + 1;
- ++uiomem;
- }
-
- while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
- uiomem->size = 0;
- ++uiomem;
- }
-
- pdata->uioinfo->priv = pdata;
-
- ret = uio_register_device(&pdev->dev, pdata->uioinfo);
-
- if (ret) {
- kfree(pdata);
-err_alloc_pdata:
-err_uioinfo:
- return ret;
- }
-
- platform_set_drvdata(pdev, pdata);
-
- return 0;
-}
-
-static int uio_pdrv_remove(struct platform_device *pdev)
-{
- struct uio_platdata *pdata = platform_get_drvdata(pdev);
-
- uio_unregister_device(pdata->uioinfo);
-
- kfree(pdata);
-
- return 0;
-}
-
-static struct platform_driver uio_pdrv = {
- .probe = uio_pdrv_probe,
- .remove = uio_pdrv_remove,
- .driver = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init uio_pdrv_init(void)
-{
- return platform_driver_register(&uio_pdrv);
-}
-
-static void __exit uio_pdrv_exit(void)
-{
- platform_driver_unregister(&uio_pdrv);
-}
-module_init(uio_pdrv_init);
-module_exit(uio_pdrv_exit);
-
-MODULE_AUTHOR("Uwe Kleine-Koenig");
-MODULE_DESCRIPTION("Userspace I/O platform driver");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 7174d518b8a..76669313e9a 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -18,11 +18,16 @@
#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 {
@@ -32,6 +37,11 @@ struct uio_pdrv_genirq_platdata {
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;
@@ -58,8 +68,10 @@ static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info)
* remember the state so we can allow user space to enable it later.
*/
- if (!test_and_set_bit(0, &priv->flags))
+ 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;
}
@@ -73,16 +85,17 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
* in the interrupt controller, but keep track of the
* state to prevent per-irq depth damage.
*
- * Serialize this operation to support multiple tasks.
+ * 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(0, &priv->flags))
+ if (__test_and_clear_bit(UIO_IRQ_DISABLED, &priv->flags))
enable_irq(dev_info->irq);
} else {
- if (!test_and_set_bit(0, &priv->flags))
- disable_irq(dev_info->irq);
+ if (!__test_and_set_bit(UIO_IRQ_DISABLED, &priv->flags))
+ disable_irq_nosync(dev_info->irq);
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -91,28 +104,40 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
static int uio_pdrv_genirq_probe(struct platform_device *pdev)
{
- struct uio_info *uioinfo = pdev->dev.platform_data;
+ 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");
- goto bad0;
+ return ret;
}
if (uioinfo->handler || uioinfo->irqcontrol ||
uioinfo->irq_flags & IRQF_SHARED) {
dev_err(&pdev->dev, "interrupt configuration error\n");
- goto bad0;
+ return ret;
}
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
- ret = -ENOMEM;
dev_err(&pdev->dev, "unable to kmalloc\n");
- goto bad0;
+ return -ENOMEM;
}
priv->uioinfo = uioinfo;
@@ -120,6 +145,17 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
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) {
@@ -137,7 +173,8 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
uiomem->memtype = UIO_MEM_PHYS;
uiomem->addr = r->start;
- uiomem->size = r->end - r->start + 1;
+ uiomem->size = resource_size(r);
+ uiomem->name = r->name;
++uiomem;
}
@@ -171,16 +208,12 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
ret = uio_register_device(&pdev->dev, priv->uioinfo);
if (ret) {
dev_err(&pdev->dev, "unable to register uio device\n");
- goto bad1;
+ pm_runtime_disable(&pdev->dev);
+ return ret;
}
platform_set_drvdata(pdev, priv);
return 0;
- bad1:
- kfree(priv);
- pm_runtime_disable(&pdev->dev);
- bad0:
- return ret;
}
static int uio_pdrv_genirq_remove(struct platform_device *pdev)
@@ -189,7 +222,10 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev)
uio_unregister_device(priv->uioinfo);
pm_runtime_disable(&pdev->dev);
- kfree(priv);
+
+ priv->uioinfo->handler = NULL;
+ priv->uioinfo->irqcontrol = NULL;
+
return 0;
}
@@ -215,6 +251,16 @@ static const struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = {
.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,
@@ -222,21 +268,11 @@ static struct platform_driver uio_pdrv_genirq = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &uio_pdrv_genirq_dev_pm_ops,
+ .of_match_table = of_match_ptr(uio_of_genirq_match),
},
};
-static int __init uio_pdrv_genirq_init(void)
-{
- return platform_driver_register(&uio_pdrv_genirq);
-}
-
-static void __exit uio_pdrv_genirq_exit(void)
-{
- platform_driver_unregister(&uio_pdrv_genirq);
-}
-
-module_init(uio_pdrv_genirq_init);
-module_exit(uio_pdrv_genirq_exit);
+module_platform_driver(uio_pdrv_genirq);
MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c
new file mode 100644
index 00000000000..96c4a19b191
--- /dev/null
+++ b/drivers/uio/uio_pruss.c
@@ -0,0 +1,242 @@
+/*
+ * 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/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 platform_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->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_put(gdev->pruss_clk);
+ kfree(gdev);
+}
+
+static int pruss_probe(struct platform_device *dev)
+{
+ struct uio_info *p;
+ struct uio_pruss_dev *gdev;
+ struct resource *regs_prussio;
+ int ret = -ENODEV, cnt = 0, len;
+ struct uio_pruss_pdata *pdata = dev_get_platdata(&dev->dev);
+
+ gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL);
+ if (!gdev)
+ return -ENOMEM;
+
+ gdev->info = kzalloc(sizeof(*p) * MAX_PRUSS_EVT, GFP_KERNEL);
+ if (!gdev->info) {
+ kfree(gdev);
+ return -ENOMEM;
+ }
+ /* Power on PRU in case its not done as part of boot-loader */
+ gdev->pruss_clk = clk_get(&dev->dev, "pruss");
+ if (IS_ERR(gdev->pruss_clk)) {
+ dev_err(&dev->dev, "Failed to get clock\n");
+ ret = PTR_ERR(gdev->pruss_clk);
+ kfree(gdev->info);
+ kfree(gdev);
+ return ret;
+ } else {
+ clk_enable(gdev->pruss_clk);
+ }
+
+ regs_prussio = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!regs_prussio) {
+ dev_err(&dev->dev, "No PRUSS I/O resource specified\n");
+ goto out_free;
+ }
+
+ if (!regs_prussio->start) {
+ dev_err(&dev->dev, "Invalid memory resource\n");
+ goto out_free;
+ }
+
+ 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->dev, "Could not allocate SRAM pool\n");
+ goto out_free;
+ }
+ }
+
+ gdev->ddr_vaddr = dma_alloc_coherent(&dev->dev, extram_pool_sz,
+ &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA);
+ if (!gdev->ddr_vaddr) {
+ dev_err(&dev->dev, "Could not allocate external memory\n");
+ goto out_free;
+ }
+
+ len = resource_size(regs_prussio);
+ gdev->prussio_vaddr = ioremap(regs_prussio->start, len);
+ if (!gdev->prussio_vaddr) {
+ dev_err(&dev->dev, "Can't remap PRUSS I/O address range\n");
+ goto out_free;
+ }
+
+ gdev->pintc_base = pdata->pintc_base;
+ gdev->hostirq_start = platform_get_irq(dev, 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->dev, p);
+ if (ret < 0)
+ goto out_free;
+ }
+
+ platform_set_drvdata(dev, gdev);
+ return 0;
+
+out_free:
+ pruss_cleanup(dev, gdev);
+ return ret;
+}
+
+static int pruss_remove(struct platform_device *dev)
+{
+ struct uio_pruss_dev *gdev = platform_get_drvdata(dev);
+
+ pruss_cleanup(dev, gdev);
+ return 0;
+}
+
+static struct platform_driver pruss_driver = {
+ .probe = pruss_probe,
+ .remove = pruss_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+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
index a187fa14c5c..9cfdfcafa26 100644
--- a/drivers/uio/uio_sercos3.c
+++ b/drivers/uio/uio_sercos3.c
@@ -116,7 +116,7 @@ static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
return 0;
}
-static int __devinit sercos3_pci_probe(struct pci_dev *dev,
+static int sercos3_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct uio_info *info;
@@ -188,7 +188,6 @@ static void sercos3_pci_remove(struct pci_dev *dev)
uio_unregister_device(info);
pci_release_regions(dev);
pci_disable_device(dev);
- pci_set_drvdata(dev, NULL);
for (i = 0; i < 5; i++) {
if (info->mem[i].internal_addr)
iounmap(info->mem[i].internal_addr);
@@ -197,7 +196,7 @@ static void sercos3_pci_remove(struct pci_dev *dev)
kfree(info);
}
-static struct pci_device_id sercos3_pci_ids[] __devinitdata = {
+static struct pci_device_id sercos3_pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_PLX,
.device = PCI_DEVICE_ID_PLX_9030,
@@ -226,19 +225,7 @@ static struct pci_driver sercos3_pci_driver = {
.remove = sercos3_pci_remove,
};
-static int __init sercos3_init_module(void)
-{
- return pci_register_driver(&sercos3_pci_driver);
-}
-
-static void __exit sercos3_exit_module(void)
-{
- pci_unregister_driver(&sercos3_pci_driver);
-}
-
-module_init(sercos3_init_module);
-module_exit(sercos3_exit_module);
-
+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");