summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Philips <brandon@ifup.org>2008-04-03 15:39:59 -0700
committerStephen Rothwell <sfr@canb.auug.org.au>2008-04-09 10:21:49 +1000
commit4885774052a3d6fb34aaaa2ac0fbadcb2ae3c3d2 (patch)
tree26b2142c72c7aba5c0091280fcd79de53bd772d0
parent1c2d0705f83395a5ca4e0019378a6e035b2d2c49 (diff)
aectc: add the aectc driver
TODO: - provide a description of what this driver is - make it work properly :) Signed-off-by: Brandon Philips <brandon@ifup.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/misc/Kconfig6
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/aectc/Makefile1
-rw-r--r--drivers/misc/aectc/aec_tc.c349
-rw-r--r--drivers/misc/aectc/aectc.h18
-rw-r--r--drivers/misc/aectc/read.c63
-rw-r--r--drivers/misc/aectc/write.c64
7 files changed, 502 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index fc128afd35ec..1738df4359ee 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -333,4 +333,10 @@ config ME4000
help
FIXME!
+config AECTC
+ tristate "AETC video timestamp device"
+ default n
+ help
+ Provides support for something that needs to be documented here.
+
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index bf0743918bc6..46f2343d0921 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -5,6 +5,7 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
+obj-$(CONFIG_AECTC) += aectc/
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
diff --git a/drivers/misc/aectc/Makefile b/drivers/misc/aectc/Makefile
new file mode 100644
index 000000000000..2b049a3907c5
--- /dev/null
+++ b/drivers/misc/aectc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_AECTC) := aec_tc.o
diff --git a/drivers/misc/aectc/aec_tc.c b/drivers/misc/aectc/aec_tc.c
new file mode 100644
index 000000000000..e8713e681378
--- /dev/null
+++ b/drivers/misc/aectc/aec_tc.c
@@ -0,0 +1,349 @@
+/*
+ * aec_tc.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 <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "aectc.h"
+
+struct aectc_dev {
+ void __iomem *map;
+ struct mutex mutex;
+ struct cdev cdev;
+};
+
+static struct mutex aectc_mutex;
+static int aectc_nr_devs = 4;
+static int aectc_cur_devs;
+static int aectc_minor;
+static int aectc_major;
+
+#define PCI_VENDOR_ID_AEC 0xaecb
+#define PCI_DEVICE_ID_AEC_VITCLTC 0x6250
+
+#define RW1_REG_START 0x20 /* Inclusive */
+#define RW1_REG_END 0x3F
+
+#define RW2_REG_START 0x60 /* Inclusive */
+#define RW2_REG_END 0x7F
+
+#define RES_REG_START 0x80 /* Inclusive */
+#define RES_REG_END 0xFD
+
+#define INT_ENABLE_ADDR 0xFC
+#define INT_ENABLE 0x10
+#define INT_DISABLE 0x0
+
+#define INTA_DRVR_ADDR 0xFE
+#define INTA_ENABLED_FLAG 0x08
+#define INTA_FLAG 0x02
+
+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, void *dev_id)
+{
+ struct aectc_dev *adev = (struct aectc_dev *)dev_id;
+ unsigned char * __iomem *int_flag = adev->map + INTA_DRVR_ADDR;
+ unsigned char status = ioread8(int_flag);
+
+ if (!((status & INTA_ENABLED_FLAG) && (status & INTA_FLAG)))
+ return IRQ_NONE;
+
+ printk(KERN_INFO "aectc interrupt received\n");
+
+ return IRQ_HANDLED;
+}
+
+static int regread(struct aectc_dev *dev, struct aectc_reg *reg)
+{
+ if (reg->reg > 0xFF)
+ return -EINVAL;
+
+ switch (reg->length) {
+ case 8:
+ reg->data = ioread8(dev->map + reg->reg);
+ break;
+ case 16:
+ reg->data = ioread16(dev->map + reg->reg);
+ break;
+ case 32:
+ reg->data = ioread32(dev->map + reg->reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/* TODO: Make sure we are writing to a valid RW area */
+static int regwrite(struct aectc_dev *dev, struct aectc_reg *reg)
+{
+ /* Refuse to write if value larger than length */
+ if (reg->data >= (1<<reg->length))
+ return -EINVAL;
+
+ if (reg->reg > 0xFF)
+ return -EINVAL;
+
+ switch (reg->length) {
+ case 8:
+ iowrite8((u8)reg->data, dev->map + reg->reg);
+ break;
+ case 16:
+ iowrite16((u16)reg->data, dev->map + reg->reg);
+ break;
+ case 32:
+ iowrite32((u32)reg->data, dev->map + reg->reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int aectc_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ struct aectc_reg reg;
+
+ struct aectc_dev *dev;
+ dev = container_of(inode->i_cdev, struct aectc_dev, cdev);
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ retval = !access_ok(VERIFY_WRITE, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ retval = !access_ok(VERIFY_READ, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ if (retval)
+ return -EFAULT;
+
+ retval = copy_from_user(&reg, (void __user *)arg, sizeof(reg));
+ if (retval)
+ return retval;
+
+ printk(KERN_DEBUG "aectc: ioctl addr: %x length: %x val: %x\n", reg.reg,
+ reg.length, reg.data);
+
+ switch (cmd) {
+ case AEC_IOC_WRITEREG:
+ return regwrite(dev, &reg);
+ break;
+ case AEC_IOC_READREG:
+ retval = regread(dev, &reg);
+ if (retval)
+ return retval;
+ return copy_to_user((void __user *)arg, &reg, sizeof(reg));
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ return -ENOTTY;
+}
+
+static int aectc_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int aectc_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static struct file_operations aectc_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = aectc_ioctl,
+ .open = aectc_open,
+ .release = aectc_release,
+};
+
+static void aectc_setup_cdev(struct aectc_dev *dev)
+{
+ int err, devno = MKDEV(aectc_major, aectc_minor);
+
+ cdev_init(&dev->cdev, &aectc_fops);
+ dev->cdev.owner = THIS_MODULE;
+ dev->cdev.ops = &aectc_fops;
+
+ err = cdev_add(&dev->cdev, devno, 1);
+ if (err)
+ printk(KERN_NOTICE "Error %d adding aectc", err);
+}
+
+static void print_board_data(struct aectc_dev *a)
+{
+ int cap;
+
+ printk("PCI-TC board vendor: %x%x number: %x%x revision: %c%c\n",
+ ioread8(a->map + 0x01),
+ ioread8(a->map + 0x00),
+ ioread8(a->map + 0x03),
+ ioread8(a->map + 0x02),
+ ioread8(a->map + 0x06),
+ ioread8(a->map + 0x07));
+
+ cap = ioread8(a->map + 0x08);
+ if (cap)
+ printk(KERN_INFO "Capabilities: ");
+ if (cap & 7)
+ printk(KERN_INFO "VITC generator ");
+ if (cap & 6)
+ printk(KERN_INFO "VITC reader ");
+ if (cap & 5)
+ printk(KERN_INFO "LTC generator ");
+ if (cap & 4)
+ printk(KERN_INFO "LTC reader ");
+ if (cap & 3)
+ printk(KERN_INFO "OSD option ");
+ if (cap & 2)
+ printk(KERN_INFO "L21 reader ");
+ if (cap & 1)
+ printk(KERN_INFO "serial option ");
+ if (cap)
+ printk(KERN_INFO "\n");
+}
+
+static int __devinit probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct aectc_dev *adev;
+ int ret;
+
+ adev = kzalloc(sizeof(struct aectc_dev), GFP_KERNEL);
+
+ if (pci_enable_device(dev))
+ goto out_free;
+
+ if (pci_request_regions(dev, "aectc"))
+ goto out_disable;
+
+ adev->map = pci_iomap(dev, 0, 0);
+ if (!adev->map)
+ goto out_release;
+
+ ret = request_irq(dev->irq, aectc_irq, IRQF_DISABLED | IRQF_SHARED,
+ "aectc", adev);
+
+ if (ret)
+ goto out_unmap;
+
+ mutex_lock(&aectc_mutex);
+ if (aectc_cur_devs < aectc_nr_devs)
+ aectc_cur_devs++;
+ else {
+ mutex_unlock(&aectc_mutex);
+ return -ENOSPC;
+ }
+ aectc_setup_cdev(adev);
+ mutex_unlock(&aectc_mutex);
+
+ iowrite32(INT_ENABLE, adev->map + INT_ENABLE_ADDR);
+ if (ioread8(adev->map + INTA_DRVR_ADDR) & INTA_ENABLED_FLAG)
+ printk(KERN_INFO "aectc: interrupts successfully enabled\n");
+
+ print_board_data(adev);
+ pci_set_drvdata(dev, adev);
+
+ return 0;
+
+out_unmap:
+ pci_iounmap(dev, adev->map);
+out_release:
+ pci_release_regions(dev);
+out_disable:
+ pci_disable_device(dev);
+out_free:
+ kfree(dev);
+ return -ENODEV;
+}
+
+static void remove(struct pci_dev *dev)
+{
+ struct aectc_dev *adev = pci_get_drvdata(dev);
+
+ /* Disable IRQ */
+ iowrite32(INT_DISABLE, adev->map + INT_ENABLE_ADDR);
+
+ free_irq(dev->irq, dev);
+ pci_release_regions(dev);
+ pci_disable_device(dev);
+ pci_set_drvdata(dev, NULL);
+ iounmap(adev->map);
+ cdev_del(&adev->cdev);
+ mutex_lock(&aectc_mutex);
+ aectc_cur_devs--;
+ mutex_unlock(&aectc_mutex);
+
+ kfree(adev);
+}
+
+static struct pci_driver pci_driver = {
+ .name = "aectc",
+ .id_table = ids,
+ .probe = probe,
+ .remove = remove,
+};
+
+static int __init aectc_init(void)
+{
+ int retval;
+ dev_t dev = 0;
+
+ retval = alloc_chrdev_region(&dev, aectc_minor, aectc_nr_devs, "aectc");
+ aectc_major = MAJOR(dev);
+
+ if (retval < 0) {
+ printk(KERN_WARNING "aectc: can't get minor %d\n", aectc_minor);
+ return retval;
+ }
+
+ printk(KERN_INFO "aectc major/minor: %i %i\n", aectc_major, 0);
+
+ mutex_init(&aectc_mutex);
+
+ 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);
diff --git a/drivers/misc/aectc/aectc.h b/drivers/misc/aectc/aectc.h
new file mode 100644
index 000000000000..099625ce31a6
--- /dev/null
+++ b/drivers/misc/aectc/aectc.h
@@ -0,0 +1,18 @@
+#ifndef _SCULL_H_
+#define _SCULL_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct aectc_reg {
+ __u16 reg;
+ __u16 length;
+ __u32 data;
+};
+
+#define AECTC_IOC_MAGIC 0xEC
+
+#define AEC_IOC_READREG _IOWR(AECTC_IOC_MAGIC, 0, struct aectc_reg)
+#define AEC_IOC_WRITEREG _IOW(AECTC_IOC_MAGIC, 0, struct aectc_reg)
+
+#endif
diff --git a/drivers/misc/aectc/read.c b/drivers/misc/aectc/read.c
new file mode 100644
index 000000000000..017f00ce6155
--- /dev/null
+++ b/drivers/misc/aectc/read.c
@@ -0,0 +1,63 @@
+/*
+ * read.c -- simple application for reading registers from a Adrienne
+ * Electronics Corp time code device under Linux
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "aectc.h"
+
+int main(int argc, char **argv)
+{
+ struct aectc_reg reg;
+ int fd;
+
+ if (argc == 4) {
+ sscanf(argv[1], "%lx", &reg.reg);
+ reg.length = atoi(argv[2]);
+ } else {
+ fprintf(stderr, "Usage: %s reg length file\n", argv[0]);
+ exit(1);
+ }
+
+ fd = open(argv[3], O_RDWR);
+
+ if (fd == -1) {
+ fprintf(stderr, "Couldn't open %s\n", argv[3]);
+ exit(1);
+ }
+
+ if (ioctl(fd, AEC_IOC_READREG, &reg) < 0) {
+ fprintf(stderr,"%s: ioctl(stdin, AEC_IOC_READREG): %s\n",
+ argv[0], strerror(errno));
+ exit(1);
+ }
+
+ printf("%x\n", reg.data);
+ exit(0);
+}
+
diff --git a/drivers/misc/aectc/write.c b/drivers/misc/aectc/write.c
new file mode 100644
index 000000000000..f0083bbf87d8
--- /dev/null
+++ b/drivers/misc/aectc/write.c
@@ -0,0 +1,64 @@
+/*
+ * read.c -- simple application for writing registers on a Adrienne
+ * Electronics Corp time code device under Linux
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "aectc.h"
+
+int main(int argc, char **argv)
+{
+ struct aectc_reg reg;
+ int fd;
+
+ if (argc == 5) {
+ sscanf(argv[1], "%lx", &reg.reg);
+ sscanf(argv[2], "%lx", &reg.data);
+ reg.length = atoi(argv[3]);
+ } else {
+ fprintf(stderr, "Usage: %s reg data length file\n", argv[0]);
+ exit(1);
+ }
+
+ fd = open(argv[4], O_RDWR);
+
+ if (fd == -1) {
+ fprintf(stderr, "Couldn't open %s\n", argv[3]);
+ exit(1);
+ }
+
+ if (ioctl(fd, AEC_IOC_WRITEREG, &reg) < 0) {
+ fprintf(stderr,"%s: ioctl(fd, AEC_IOC_READREG): %s\n",
+ argv[0], strerror(errno));
+ exit(1);
+ }
+
+ printf("%x\n", reg.data);
+ exit(0);
+}
+