From 0720b5b87e16dbb57bf8e0abb89df688ebe11dbb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 19 Feb 2009 10:20:28 -0800 Subject: Staging: comedi: add c6xdigio driver Driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card Written by Dan Block, email address unknown Cc: David Schleef Cc: Ian Abbott Cc: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/c6xdigio.c | 517 ++++++++++++++++++++++++++++++ 1 file changed, 517 insertions(+) --- /dev/null +++ b/drivers/staging/comedi/drivers/c6xdigio.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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);