From 4e46c3421b4209b6fa582e23a4438138ae2efa7e Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Mon, 21 Nov 2005 02:05:28 +0000 Subject: [PATCH] New driver by Dan Block via anders.blomdell@control.lth.se (Anders Blomdell). --- comedi/drivers/Makefile.am | 1 + comedi/drivers/c6xdigio.c | 518 +++++++++++++++++++++++++++++++++++++ 2 files changed, 519 insertions(+) create mode 100644 comedi/drivers/c6xdigio.c diff --git a/comedi/drivers/Makefile.am b/comedi/drivers/Makefile.am index 0670e7f4..5c1dd3ac 100644 --- a/comedi/drivers/Makefile.am +++ b/comedi/drivers/Makefile.am @@ -99,6 +99,7 @@ module_PROGRAMS = \ amplc_pc236.ko \ amplc_pc263.ko \ amplc_dio200.ko \ + c6xdigio.ko \ cb_pcidas.ko \ cb_pcidas64.ko \ cb_pcidda.ko \ diff --git a/comedi/drivers/c6xdigio.c b/comedi/drivers/c6xdigio.c new file mode 100644 index 00000000..8b2162a8 --- /dev/null +++ b/comedi/drivers/c6xdigio.c @@ -0,0 +1,518 @@ +/* + module/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. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u8 ReadByteFromHwPort(int addr) +{ + u8 result = inb(addr); + return result; +} + +static void WriteByteToHwPort(int 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 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 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 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 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;in;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;nn;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; + int iobase; + int irq; + comedi_subdevice *s; + + iobase = it->options[0]; + printk("comedi%d: c6xdigio: 0x%04x\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 = %d 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; +} + + +#ifdef MODULE +int init_module(void) +{ + comedi_driver_register(&driver_c6xdigio); + + return 0; +} + +void cleanup_module(void) +{ + comedi_driver_unregister(&driver_c6xdigio); +} +#endif + + + -- 2.26.2