Staging: comedi: add c6xdigio driver
authorGreg Kroah-Hartman <gregkh@suse.de>
Thu, 19 Feb 2009 18:20:28 +0000 (10:20 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 3 Apr 2009 21:53:51 +0000 (14:53 -0700)
Driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card

Written by Dan Block, email address unknown

Cc: David Schleef <ds@schleef.org>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/comedi/drivers/c6xdigio.c [new file with mode: 0644]

diff --git a/drivers/staging/comedi/drivers/c6xdigio.c b/drivers/staging/comedi/drivers/c6xdigio.c
new file mode 100644 (file)
index 0000000..1bf1375
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+   comedi/drivers/c6xdigio.c
+
+   Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
+   (http://robot0.ge.uiuc.edu/~spong/mecha/)
+
+   COMEDI - Linux Control and Measurement Device Interface
+   Copyright (C) 1999 Dan Block
+
+   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.
+
+ */
+/*
+Driver: c6xdigio
+Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
+Author: Dan Block
+Status: unknown
+Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
+Updated: Sun Nov 20 20:18:34 EST 2005
+
+This driver will not work with a 2.4 kernel.
+http://robot0.ge.uiuc.edu/~spong/mecha/
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <linux/pnp.h>
+
+#include "../comedidev.h"
+
+static u8 ReadByteFromHwPort(unsigned long addr)
+{
+       u8 result = inb(addr);
+       return result;
+}
+
+static void WriteByteToHwPort(unsigned long addr, u8 val)
+{
+       outb_p(val, addr);
+}
+
+#define C6XDIGIO_SIZE 3
+
+/*
+ * port offsets
+ */
+#define C6XDIGIO_PARALLEL_DATA 0
+#define C6XDIGIO_PARALLEL_STATUS 1
+#define C6XDIGIO_PARALLEL_CONTROL 2
+struct pwmbitstype {
+       unsigned sb0:2;
+       unsigned sb1:2;
+       unsigned sb2:2;
+       unsigned sb3:2;
+       unsigned sb4:2;
+};
+union pwmcmdtype {
+       unsigned cmd;           // assuming here that int is 32bit
+       struct pwmbitstype bits;
+};
+struct encbitstype {
+       unsigned sb0:3;
+       unsigned sb1:3;
+       unsigned sb2:3;
+       unsigned sb3:3;
+       unsigned sb4:3;
+       unsigned sb5:3;
+       unsigned sb6:3;
+       unsigned sb7:3;
+};
+union encvaluetype {
+       unsigned value;
+       struct encbitstype bits;
+};
+
+#define C6XDIGIO_TIME_OUT 20
+
+static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it);
+static int c6xdigio_detach(comedi_device * dev);
+comedi_driver driver_c6xdigio = {
+      driver_name:"c6xdigio",
+      module:THIS_MODULE,
+      attach:c6xdigio_attach,
+      detach:c6xdigio_detach,
+};
+
+static void C6X_pwmInit(unsigned long baseAddr)
+{
+       int timeout = 0;
+
+//printk("Inside C6X_pwmInit\n");
+
+       WriteByteToHwPort(baseAddr, 0x70);
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, 0x74);
+       timeout = 0;
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, 0x70);
+       timeout = 0;
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, 0x0);
+       timeout = 0;
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+
+}
+
+static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
+{
+       unsigned ppcmd;
+       union pwmcmdtype pwm;
+       int timeout = 0;
+       unsigned tmp;
+
+       //printk("Inside C6X_pwmOutput\n");
+
+       pwm.cmd = value;
+       if (pwm.cmd > 498)
+               pwm.cmd = 498;
+       if (pwm.cmd < 2)
+               pwm.cmd = 2;
+
+       if (channel == 0) {
+               ppcmd = 0x28;
+       } else {                // if channel == 1
+               ppcmd = 0x30;
+       }                       /* endif */
+
+       WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, 0x0);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+}
+
+static int C6X_encInput(unsigned long baseAddr, unsigned channel)
+{
+       unsigned ppcmd;
+       union encvaluetype enc;
+       int timeout = 0;
+       int tmp;
+
+       //printk("Inside C6X_encInput\n");
+
+       enc.value = 0;
+       if (channel == 0) {
+               ppcmd = 0x48;
+       } else {
+               ppcmd = 0x50;
+       }
+       WriteByteToHwPort(baseAddr, ppcmd);
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd + 0x4);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+       enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
+       WriteByteToHwPort(baseAddr, ppcmd);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       WriteByteToHwPort(baseAddr, 0x0);
+       timeout = 0;
+       tmp = ReadByteFromHwPort(baseAddr + 1);
+       while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
+               tmp = ReadByteFromHwPort(baseAddr + 1);
+               timeout++;
+       }
+
+       return (enc.value ^ 0x800000);
+}
+
+static void C6X_encResetAll(unsigned long baseAddr)
+{
+       unsigned timeout = 0;
+
+//printk("Inside C6X_encResetAll\n");
+
+       WriteByteToHwPort(baseAddr, 0x68);
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+       WriteByteToHwPort(baseAddr, 0x6C);
+       timeout = 0;
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+       WriteByteToHwPort(baseAddr, 0x68);
+       timeout = 0;
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+       WriteByteToHwPort(baseAddr, 0x0);
+       timeout = 0;
+       while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
+               && (timeout < C6XDIGIO_TIME_OUT)) {
+               timeout++;
+       }
+}
+
+static int c6xdigio_pwmo_insn_read(comedi_device * dev,
+       comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+       printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
+       return insn->n;
+}
+
+static int c6xdigio_pwmo_insn_write(comedi_device * dev,
+       comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+       int i;
+       int chan = CR_CHAN(insn->chanspec);
+
+       //  printk("c6xdigio_pwmo_insn_write %x\n", insn->n);
+       for (i = 0; i < insn->n; i++) {
+               C6X_pwmOutput(dev->iobase, chan, data[i]);
+               /*    devpriv->ao_readback[chan] = data[i]; */
+       }
+       return i;
+}
+
+//static int c6xdigio_ei_init_insn_read(comedi_device *dev,
+//                                 comedi_subdevice *s,
+//                                 comedi_insn *insn,
+//                                 lsampl_t *data)
+//{
+//  printk("c6xdigio_ei_init_insn_read %x\n", insn->n);
+//  return insn->n;
+//}
+
+//static int c6xdigio_ei_init_insn_write(comedi_device *dev,
+//                                 comedi_subdevice *s,
+//                                 comedi_insn *insn,
+//                                 lsampl_t *data)
+//{
+//  int i;
+//  int chan = CR_CHAN(insn->chanspec);
+//
+//  C6X_encResetAll( dev->iobase );
+//
+//  return insn->n;
+//}
+
+static int c6xdigio_ei_insn_read(comedi_device * dev,
+       comedi_subdevice * s, comedi_insn * insn, lsampl_t * data)
+{
+       //  printk("c6xdigio_ei__insn_read %x\n", insn->n);
+       int n;
+       int chan = CR_CHAN(insn->chanspec);
+
+       for (n = 0; n < insn->n; n++) {
+               data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
+       }
+
+       return n;
+}
+
+static void board_init(comedi_device * dev)
+{
+
+       //printk("Inside board_init\n");
+
+       C6X_pwmInit(dev->iobase);
+       C6X_encResetAll(dev->iobase);
+
+}
+
+//static void board_halt(comedi_device *dev) {
+//  C6X_pwmInit(dev->iobase);
+//}
+
+/*
+   options[0] - I/O port
+   options[1] - irq
+   options[2] - number of encoder chips installed
+ */
+
+static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
+       /* Standard LPT Printer Port */
+       {.id = "PNP0400",.driver_data = 0},
+       /* ECP Printer Port */
+       {.id = "PNP0401",.driver_data = 0},
+       {}
+};
+
+static struct pnp_driver c6xdigio_pnp_driver = {
+       .name = "c6xdigio",
+       .id_table = c6xdigio_pnp_tbl,
+};
+
+static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int result = 0;
+       unsigned long iobase;
+       unsigned int irq;
+       comedi_subdevice *s;
+
+       iobase = it->options[0];
+       printk("comedi%d: c6xdigio: 0x%04lx\n", dev->minor, iobase);
+       if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
+               printk("comedi%d: I/O port conflict\n", dev->minor);
+               return -EIO;
+       }
+       dev->iobase = iobase;
+       dev->board_name = "c6xdigio";
+
+       result = alloc_subdevices(dev, 2);      // 3 with encoder_init write
+       if (result < 0)
+               return result;
+
+       // Make sure that PnP ports gets activated
+       pnp_register_driver(&c6xdigio_pnp_driver);
+
+       irq = it->options[1];
+       if (irq > 0) {
+               printk("comedi%d: irq = %u ignored\n", dev->minor, irq);
+       } else if (irq == 0) {
+               printk("comedi%d: no irq\n", dev->minor);
+       }
+
+       s = dev->subdevices + 0;
+       /* pwm output subdevice */
+       s->type = COMEDI_SUBD_AO;       // Not sure what to put here
+       s->subdev_flags = SDF_WRITEABLE;
+       s->n_chan = 2;
+       /*      s->trig[0] = c6xdigio_pwmo; */
+       s->insn_read = c6xdigio_pwmo_insn_read;
+       s->insn_write = c6xdigio_pwmo_insn_write;
+       s->maxdata = 500;
+       s->range_table = &range_bipolar10;      // A suitable lie
+
+       s = dev->subdevices + 1;
+       /* encoder (counter) subdevice */
+       s->type = COMEDI_SUBD_COUNTER;
+       s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+       s->n_chan = 2;
+       /* s->trig[0] = c6xdigio_ei; */
+       s->insn_read = c6xdigio_ei_insn_read;
+       s->maxdata = 0xffffff;
+       s->range_table = &range_unknown;
+
+       //          s = dev->subdevices + 2;
+       //      /* pwm output subdevice */
+       //      s->type = COMEDI_SUBD_COUNTER;  // Not sure what to put here
+       //      s->subdev_flags = SDF_WRITEABLE;
+       //      s->n_chan = 1;
+       //      /* s->trig[0] = c6xdigio_ei_init; */
+       //      s->insn_read = c6xdigio_ei_init_insn_read;
+       //      s->insn_write = c6xdigio_ei_init_insn_write;
+       //      s->maxdata = 0xFFFF;  // Really just a don't care
+       //      s->range_table = &range_unknown; // Not sure what to put here
+
+       // I will call this init anyway but more than likely the DSP board will not be connect
+       // when device driver is loaded.
+       board_init(dev);
+
+       return 0;
+}
+
+static int c6xdigio_detach(comedi_device * dev)
+{
+//  board_halt(dev);  // may not need this
+
+       printk("comedi%d: c6xdigio: remove\n", dev->minor);
+
+       if (dev->iobase) {
+               release_region(dev->iobase, C6XDIGIO_SIZE);
+       }
+       if (dev->irq) {
+               free_irq(dev->irq, dev);
+       }                       // Not using IRQ so I am not sure if I need this
+       pnp_unregister_driver(&c6xdigio_pnp_driver);
+
+       return 0;
+}
+
+COMEDI_INITCLEANUP(driver_c6xdigio);