add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
menuconfig UIO
tristate "Userspace I/O drivers"
depends on !S390
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 that handles all of the
heavy lifting and can be found at:
http://www.osadl.org/projects/downloads/UIO/user/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
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_SMX
tristate "SMX cryptengine UIO interface"
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
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"
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
default n
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.
endif

View File

@@ -0,0 +1,8 @@
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

928
kernel/drivers/uio/uio.c Normal file
View File

@@ -0,0 +1,928 @@
/*
* 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@linutronix.de>
* Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com>
*
* Userspace IO
*
* Base Functions
*
* Licensed under the GPLv2 only.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/idr.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/kobject.h>
#include <linux/uio_driver.h>
#define UIO_MAX_DEVICES 255
struct uio_device {
struct module *owner;
struct device *dev;
int minor;
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;
};
static int uio_major;
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);
/*
* 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, "0x%lx\n", mem->addr);
}
static ssize_t map_size_show(struct uio_mem *mem, char *buf)
{
return sprintf(buf, "0x%lx\n", mem->size);
}
static ssize_t map_offset_show(struct uio_mem *mem, char *buf)
{
return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK);
}
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 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 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 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;
}
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;
}
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;
}
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 attribute_group uio_attr_grp = {
.attrs = uio_attrs,
};
/*
* 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;
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)
break;
if (!map_found) {
map_found = 1;
idev->map_dir = kobject_create_and_add("maps",
&idev->dev->kobj);
if (!idev->map_dir)
goto err_map;
}
map = kzalloc(sizeof(*map), GFP_KERNEL);
if (!map)
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;
ret = kobject_uevent(&map->kobj, KOBJ_ADD);
if (ret)
goto err_map;
}
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)
goto err_portio;
}
portio = kzalloc(sizeof(*portio), GFP_KERNEL);
if (!portio)
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;
ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
if (ret)
goto err_portio;
}
return 0;
err_portio:
for (pi--; 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--) {
mem = &idev->info->mem[mi];
map = mem->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;
}
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);
sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
}
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;
}
idev->minor = id & MAX_ID_MASK;
exit:
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 = 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;
}
if (!try_module_get(idev->owner)) {
ret = -ENODEV;
goto out;
}
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;
if (idev->info->open) {
ret = idev->info->open(idev->info, inode);
if (ret)
goto err_infoopen;
}
return 0;
err_infoopen:
kfree(listener);
err_alloc_listener:
module_put(idev->owner);
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;
if (idev->info->release)
ret = idev->info->release(idev->info, inode);
module_put(idev->owner);
kfree(listener);
return ret;
}
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)
return -EIO;
poll_wait(filep, &idev->wait, wait);
if (listener->event_count != atomic_read(&idev->event))
return POLLIN | POLLRDNORM;
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;
s32 event_count;
if (idev->info->irq == UIO_IRQ_NONE)
return -EIO;
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) {
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 (idev->info->irq == UIO_IRQ_NONE)
return -EIO;
if (count != sizeof(s32))
return -EINVAL;
if (!idev->info->irqcontrol)
return -ENOSYS;
if (copy_from_user(&irq_on, buf, count))
return -EFAULT;
retval = idev->info->irqcontrol(idev->info, irq_on);
return retval ? retval : sizeof(s32);
}
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)
return -1;
if (vma->vm_pgoff == mi)
return mi;
}
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;
int mi = uio_find_mem_index(vma);
if (mi < 0)
return VM_FAULT_SIGBUS;
/*
* We need to subtract mi because userspace uses offset = N*PAGE_SIZE
* to use mem[N].
*/
offset = (vmf->pgoff - mi) << PAGE_SHIFT;
if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
page = virt_to_page(idev->info->mem[mi].addr + offset);
else
page = vmalloc_to_page((void *)idev->info->mem[mi].addr
+ offset);
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,
.fault = uio_vma_fault,
};
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);
if (mi < 0)
return -EINVAL;
vma->vm_flags |= VM_IO | VM_RESERVED;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
return remap_pfn_range(vma,
vma->vm_start,
idev->info->mem[mi].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;
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;
mi = uio_find_mem_index(vma);
if (mi < 0)
return -EINVAL;
requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
actual_pages = ((idev->info->mem[mi].addr & ~PAGE_MASK)
+ idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT;
if (requested_pages > actual_pages)
return -EINVAL;
if (idev->info->mmap) {
ret = idev->info->mmap(idev->info, vma);
return ret;
}
switch (idev->info->mem[mi].memtype) {
case UIO_MEM_PHYS:
return uio_mmap_physical(vma);
case UIO_MEM_LOGICAL:
case UIO_MEM_VIRTUAL:
return uio_mmap_logical(vma);
default:
return -EINVAL;
}
}
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,
};
static int uio_major_init(void)
{
uio_major = register_chrdev(0, "uio", &uio_fops);
if (uio_major < 0)
return uio_major;
return 0;
}
static void uio_major_cleanup(void)
{
unregister_chrdev(uio_major, "uio");
}
static int init_uio_class(void)
{
int ret = 0;
if (uio_class != NULL) {
kref_get(&uio_class->kref);
goto exit;
}
/* 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;
}
return 0;
err_class_create:
kfree(uio_class);
uio_class = NULL;
err_kzalloc:
uio_major_cleanup();
exit:
return ret;
}
static void release_uio_class(struct kref *kref)
{
/* Ok, we cheat as we know we only have one uio_class */
class_destroy(uio_class->class);
kfree(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);
}
/**
* 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 (!parent || !info || !info->name || !info->version)
return -EINVAL;
info->uio_dev = NULL;
ret = init_uio_class();
if (ret)
return ret;
idev = kzalloc(sizeof(*idev), GFP_KERNEL);
if (!idev) {
ret = -ENOMEM;
goto err_kzalloc;
}
idev->owner = owner;
idev->info = info;
init_waitqueue_head(&idev->wait);
atomic_set(&idev->event, 0);
ret = uio_get_minor(idev);
if (ret)
goto err_get_minor;
idev->dev = device_create(uio_class->class, parent,
MKDEV(uio_major, idev->minor), idev,
"uio%d", idev->minor);
if (IS_ERR(idev->dev)) {
printk(KERN_ERR "UIO: device register failed\n");
ret = PTR_ERR(idev->dev);
goto err_device_create;
}
ret = uio_dev_add_attributes(idev);
if (ret)
goto err_uio_dev_add_attributes;
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 (ret)
goto err_request_irq;
}
return 0;
err_request_irq:
uio_dev_del_attributes(idev);
err_uio_dev_add_attributes:
device_destroy(uio_class->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);
/**
* 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);
if (info->irq >= 0)
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));
kfree(idev);
uio_class_destroy();
return;
}
EXPORT_SYMBOL_GPL(uio_unregister_device);
static int __init uio_init(void)
{
return 0;
}
static void __exit uio_exit(void)
{
}
module_init(uio_init)
module_exit(uio_exit)
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,175 @@
/*
* 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>
#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 __devinit 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);
pci_set_drvdata(pdev, NULL);
iounmap(info->priv);
kfree(info);
}
static struct pci_driver pci_driver = {
.name = "aectc",
.id_table = ids,
.probe = probe,
.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_LICENSE("GPL");
module_init(aectc_init);
module_exit(aectc_exit);

View File

@@ -0,0 +1,152 @@
/*
* UIO Hilscher CIF card driver
*
* (C) 2007 Hans J. Koch <hjk@linutronix.de>
* Original code (C) 2005 Benedikt Spranger <b.spranger@linutronix.de>
*
* Licensed under GPL version 2 only.
*
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.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 __devinit 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_DISABLED | 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);
pci_set_drvdata(dev, NULL);
iounmap(info->mem[0].internal_addr);
kfree (info);
}
static struct pci_device_id hilscher_pci_ids[] __devinitdata = {
{
.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,
};
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_DEVICE_TABLE(pci, hilscher_pci_ids);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Hans J. Koch, Benedikt Spranger");

View File

@@ -0,0 +1,207 @@
/* 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/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;
if (!pdev->irq) {
dev_warn(&pdev->dev, "No IRQ assigned to device: "
"no support for interrupts?\n");
return -ENODEV;
}
err = pci_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n",
__func__, err);
return err;
}
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);

View File

@@ -0,0 +1,120 @@
/*
* 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>
#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);

View File

@@ -0,0 +1,244 @@
/*
* 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/interrupt.h>
#include <linux/stringify.h>
#include <linux/pm_runtime.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;
};
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.
*/
if (!test_and_set_bit(0, &priv->flags))
disable_irq_nosync(irq);
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.
*/
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_pdrv_genirq_probe(struct platform_device *pdev)
{
struct uio_info *uioinfo = pdev->dev.platform_data;
struct uio_pdrv_genirq_platdata *priv;
struct uio_mem *uiomem;
int ret = -EINVAL;
int i;
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;
}
priv->uioinfo = uioinfo;
spin_lock_init(&priv->lock);
priv->flags = 0; /* interrupt is enabled to begin with */
priv->pdev = pdev;
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;
}
/* 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->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");
goto bad1;
}
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)
{
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 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,
},
};
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_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("Userspace I/O platform driver with generic IRQ handling");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);

View File

@@ -0,0 +1,243 @@
/* 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>
/* 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 __devinit 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_DISABLED | 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);
pci_set_drvdata(dev, NULL);
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[] __devinitdata = {
{
.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,
};
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_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,140 @@
/*
* 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>");