diff options
Diffstat (limited to 'drivers/uio')
-rw-r--r-- | drivers/uio/Kconfig | 18 | ||||
-rw-r--r-- | drivers/uio/Makefile | 1 | ||||
-rw-r--r-- | drivers/uio/uio.c | 25 | ||||
-rw-r--r-- | drivers/uio/uio_aec.c | 175 |
4 files changed, 218 insertions, 1 deletions
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 04b954cfce7..7f86534de26 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -58,6 +58,24 @@ config UIO_SMX If you compile this as a module, it will be called uio_smx. +config UIO_AEC + tristate "AEC video timestamp device" + depends on PCI + default n + help + + UIO driver for the Adrienne Electronics Corporation PCI time + 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" default n diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index e6955814985..5c2586d7579 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile @@ -3,4 +3,5 @@ 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 diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 4ca85a113aa..03efb065455 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -61,6 +61,14 @@ struct uio_map { }; #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); @@ -82,6 +90,8 @@ struct map_sysfs_entry { 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 = @@ -90,6 +100,7 @@ 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, @@ -133,6 +144,14 @@ struct uio_portio { }; #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); @@ -159,6 +178,8 @@ struct portio_sysfs_entry { 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 = @@ -167,6 +188,7 @@ 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, @@ -686,7 +708,8 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma) return -EINVAL; requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - actual_pages = (idev->info->mem[mi].size + PAGE_SIZE -1) >> 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; diff --git a/drivers/uio/uio_aec.c b/drivers/uio/uio_aec.c new file mode 100644 index 00000000000..b7830e9a3ba --- /dev/null +++ b/drivers/uio/uio_aec.c @@ -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); |