From dabc37d6b63e63d44075206ba1ca6d6ea492317c Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Tue, 19 Jun 2001 23:28:00 +0000 Subject: [PATCH] added das16m1 driver, compiles with warnings but completely untested. insns not yet implemented. --- comedi/Config.in | 1 + comedi/drivers/Makefile | 1 + comedi/drivers/das16m1.c | 584 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 comedi/drivers/das16m1.c diff --git a/comedi/Config.in b/comedi/Config.in index 8493bb66..c187d081 100644 --- a/comedi/Config.in +++ b/comedi/Config.in @@ -65,6 +65,7 @@ fi dep_tristate 'DAS08 compatible driver' CONFIG_COMEDI_DAS08 $CONFIG_COMEDI dep_tristate 'DAS16 compatible driver' CONFIG_COMEDI_DAS16 $CONFIG_COMEDI +dep_tristate 'CIO-DAS16/M1 driver' CONFIG_COMEDI_DAS16M1 $CONFIG_COMEDI dep_tristate 'old DAS-08 series driver' CONFIG_COMEDI_DAS08_OLD $CONFIG_COMEDI dep_tristate 'old DAS-08jr driver' CONFIG_COMEDI_DAS08JR_OLD $CONFIG_COMEDI dep_tristate 'old DAS-1600 and compatibles' CONFIG_COMEDI_DAS1600_OLD $CONFIG_COMEDI diff --git a/comedi/drivers/Makefile b/comedi/drivers/Makefile index ba63795d..bdcdfada 100644 --- a/comedi/drivers/Makefile +++ b/comedi/drivers/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_COMEDI_DAQBOARD2000) += daqboard2000.o obj-$(CONFIG_COMEDI_DAS08) += das08.o obj-$(CONFIG_COMEDI_DAS16) += das16.o +obj-$(CONFIG_COMEDI_DAS16M1) += das16m1.o obj-$(CONFIG_COMEDI_DAS08_OLD) += das08-old.o obj-$(CONFIG_COMEDI_DAS08JR_OLD) += das08jr-old.o obj-$(CONFIG_COMEDI_DAS16_OLD) += das16-old.o diff --git a/comedi/drivers/das16m1.c b/comedi/drivers/das16m1.c new file mode 100644 index 00000000..800671a4 --- /dev/null +++ b/comedi/drivers/das16m1.c @@ -0,0 +1,584 @@ +/* + comedi/drivers/das16m1.c + CIO-DAS16/M1 driver + Author: Frank Mori Hess, based on code from the das16 + driver by Chris Baugher. + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 2000 David A. Schleef + + 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. + +************************************************************************ + +TODO: add cio-das16/m1/16 support + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include <8255.h> +#include <8253.h> + +//#define DEBUG + +#define DAS16M1_SIZE 16 +#define DAS16M1_SIZE2 8 + +#define DAS16M1_XTAL 100 //10 MHz master clock + +#define HALF_FIFO 512 // 1024 sample fifo + +/* + CIO-DAS16_M1.pdf + + "cio-das16/m1" + + 0 a/d bits 0-3, mux start 12 bit + 1 a/d bits 4-11 unused + 2 status control + 3 di 4 bit do 4 bit + 4 unused clear interrupt + 5 interrupt, pacer + 6 channel/gain queue address + 7 channel/gain queue data + 89ab 8254 + cdef 8254 + 400 8255 + 404-407 8254 + +*/ + +#define DAS16M1_TRIG 0 +#define DAS16M1_AI 0 // 16-bit wide register +#define AI_CHAN(x) ((x) & 0xf) +#define AI_DATA(x) (((x) >> 4) & 0xfff) +#define DAS16M1_CS 2 +#define EXT_TRIG 0x1 +#define CLK_SRC 0x2 +#define OVRUN 0x20 +#define IRQDATA 0x80 +#define DAS16M1_DIO 3 +#define DAS16M1_CLEAR_INTR 4 +#define DAS16M1_INTR_CONTROL 5 +#define INT_PACER 0x3 +#define PACER_MASK 0x3 +#define IRQ(x) (((x) & 0x7) << 4) +#define INTE 0x80 +#define DAS16M1_QUEUE_ADDR 6 +#define DAS16M1_QUEUE_DATA 7 +#define Q_CHAN(x) ((x) & 0x7) +#define Q_RANGE(x) (((x) & 0xf) << 4) +#define UNIPOLAR 0x40 +#define DAS16M1_8254_FIRST 8 +#define DAS16M1_8254_SECOND 0xc +#define DAS16M1_82C55 0x400 +#define DAS16M1_8254_THIRD 0x404 + +static comedi_lrange range_das16m1 = +{ 9, + { + BIP_RANGE( 5 ), + BIP_RANGE( 2.5 ), + BIP_RANGE( 1.25 ), + BIP_RANGE( 0.625 ), + UNI_RANGE(10), + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25), + BIP_RANGE(10), + } +}; + +static int das16m1_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int das16m1_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int das16m1_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); + +static int das16m1_cmd_test(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd); +static int das16m1_cmd_exec(comedi_device *dev,comedi_subdevice *s); +static int das16m1_cancel(comedi_device *dev, comedi_subdevice *s); + +static void das16m1_reset(comedi_device *dev); +static void das16m1_interrupt(int irq, void *d, struct pt_regs *regs); + +static unsigned int das16m1_set_pacer(comedi_device *dev, unsigned int ns, int round_flag); +#ifdef DEBUG +static void reg_dump(comedi_device *dev); +#endif + +typedef struct das16m1_board_struct{ + char *name; + unsigned int ai_speed; +}das16m1_board; + +static das16m1_board das16m1_boards[]={ + { + name: "das16m1", // CIO-DAS16_M1.pdf + ai_speed: 1000, // 1MHz max speed + }, +}; + +#define das16m1_num_boards ((sizeof(das16m1_boards)) / (sizeof(das16m1_boards[0]))) + +static int das16m1_attach(comedi_device *dev, comedi_devconfig *it); +static int das16m1_detach(comedi_device *dev); +comedi_driver driver_das16m1={ + driver_name: "das16m1", + module: THIS_MODULE, + attach: das16m1_attach, + detach: das16m1_detach, + board_name: das16m1_boards, + num_names: das16m1_num_boards, + offset: sizeof(das16m1_boards[0]), +}; + +struct das16m1_private_struct { + unsigned int control_state; + volatile unsigned int adc_count; + unsigned int divisor1; // divides master clock to obtain conversion speed + unsigned int divisor2; // divides master clock to obtain conversion speed +}; +#define devpriv ((struct das16m1_private_struct *)(dev->private)) +#define thisboard ((struct das16m1_board_struct *)(dev->board_ptr)) + +COMEDI_INITCLEANUP(driver_das16m1); + +static int das16m1_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd) +{ + unsigned int err=0, tmp; + + /* make sure triggers are valid */ + tmp=cmd->start_src; + cmd->start_src &= TRIG_NOW; + if(!cmd->start_src || tmp!=cmd->start_src) err++; + + tmp=cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_FOLLOW; + if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src) err++; + + tmp=cmd->convert_src; + cmd->convert_src &= TRIG_TIMER; + if(!cmd->convert_src || tmp!=cmd->convert_src) err++; + + tmp=cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if(!cmd->scan_end_src || tmp!=cmd->scan_end_src) err++; + + tmp=cmd->stop_src; + cmd->stop_src &= TRIG_COUNT | TRIG_NONE; + if(!cmd->stop_src || tmp!=cmd->stop_src) err++; + + if(err)return 1; + + /* step 2: make sure trigger sources are unique and mutually compatible */ + /* note that mutual compatiblity is not an issue here */ + if(cmd->stop_src != TRIG_COUNT && + cmd->stop_src != TRIG_NONE) err++; + + if(err)return 2; + + /* step 3: make sure arguments are trivially compatible */ + if(cmd->start_arg != 0){ + cmd->start_arg = 0; + err++; + } + + if(cmd->scan_begin_src == TRIG_FOLLOW){ + /* internal trigger */ + if(cmd->scan_begin_arg != 0){ + cmd->scan_begin_arg = 0; + err++; + } + } + + if(cmd->convert_src == TRIG_TIMER) + { + if(cmd->convert_arg < thisboard->ai_speed) + { + cmd->convert_arg = thisboard->ai_speed; + err++; + } + } + + if(cmd->scan_end_arg != cmd->chanlist_len){ + cmd->scan_end_arg = cmd->chanlist_len; + err++; + } + + if(cmd->stop_src==TRIG_COUNT){ + /* any count is allowed */ + }else{ + /* TRIG_NONE */ + if(cmd->stop_arg!=0){ + cmd->stop_arg=0; + err++; + } + } + + if(err) return 3; + + /* step 4: fix up arguments */ + + if(cmd->convert_src == TRIG_TIMER) + { + tmp = cmd->convert_arg; + /* calculate counter values that give desired timing */ + i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1), + &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); + if(tmp != cmd->convert_arg) err++; + } + // XXX we need to check constraints on chanlist here + + if(err) return 4; + + return 0; +} + +static int das16m1_cmd_exec(comedi_device *dev,comedi_subdevice *s) +{ + comedi_cmd *cmd = &s->async->cmd; + unsigned int byte, i; + + devpriv->adc_count = cmd->stop_arg * cmd->chanlist_len; + + /* setup channel/gain queue */ + for(i = 0; i < cmd->chanlist_len; i++) + { + outb(i, dev->iobase + DAS16M1_QUEUE_ADDR); + byte = Q_CHAN(CR_CHAN(cmd->chanlist[i])) | Q_RANGE(CR_RANGE(cmd->chanlist[i])); + outb(byte, dev->iobase + DAS16M1_QUEUE_DATA); + } + + /* enable pacer clocked conversions */ + devpriv->control_state |= INT_PACER; + + /* set counter mode and counts */ + das16m1_set_pacer(dev, cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK); + + /* clear interrupt bit */ + outb(0, dev->iobase + DAS16M1_CLEAR_INTR); + /* enable interrupts and internal pacer */ + devpriv->control_state |= INTE | INT_PACER; + outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); + + return 0; +} + +static int das16m1_cancel(comedi_device *dev, comedi_subdevice *s) +{ + devpriv->adc_count = 0; + + return 0; +} + +#if 0 // insns haven't been converted from das16 driver yet +static int das16m1_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + int i,n; + int range; + int chan; + int msb,lsb; + + /* clear crap */ + inb(dev->iobase+DAS16M1_AI_LSB); + inb(dev->iobase+DAS16M1_AI_MSB); + + /* set multiplexer */ + chan = CR_CHAN(insn->chanspec); + outb_p(chan,dev->iobase+DAS16M1_MUX); + + /* set gain */ + if(thisboard->ai_pg != das16m1_pg_none){ + range = CR_RANGE(insn->chanspec); + outb((das16m1_gainlists[thisboard->ai_pg])[range], + dev->iobase+DAS16M1_GAIN); + } + + /* How long should we wait for MUX to settle? */ + //udelay(5); + + for(n=0;nn;n++){ + /* trigger conversion */ + outb_p(0,dev->iobase+DAS16M1_TRIG); + + for(i=0;iiobase + DAS16M1_AI_MSB); + lsb = inb(dev->iobase + DAS16M1_AI_LSB); + if(thisboard->ai_nbits==12){ + data[n] = (lsb>>4) | (msb << 4); + }else{ + data[n] = lsb | (msb << 8); + } + } + + return n; +} + +static int das16m1_di_rbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + data[0]=inb(dev->iobase+DAS16M1_DIO)&0xf; + + return 1; +} + +static int das16m1_do_wbits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + outb(data[0],dev->iobase+DAS16M1_DIO); + + return 1; +} + +static int das16m1_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + int i; + int lsb,msb; + int chan; + + chan=CR_CHAN(insn->chanspec); + + for(i=0;in;i++){ + if(thisboard->ao_nbits==12){ + lsb=(data[i]<<4)&0xff; + msb=(data[i]>>4)&0xff; + }else{ + lsb=data[i]&0xff; + msb=(data[i]>>8)&0xff; + } + +#if 0 + outb(lsb,dev->iobase+devpriv->ao_offset_lsb[chan]); + outb(msb,dev->iobase+devpriv->ao_offset_msb[chan]); +#else + outb(lsb,dev->iobase+DAS16M1_AO_LSB(chan)); + outb(msb,dev->iobase+DAS16M1_AO_MSB(chan)); +#endif + } + + return i; +} + +#endif // insns haven't been converted from das16 driver yet + +static void das16m1_interrupt(int irq, void *d, struct pt_regs *regs) +{ + int i, status; + sampl_t data_point; + + comedi_device *dev = d; + comedi_subdevice *s = dev->subdevices; + comedi_async *async = s->async; + + status = inb(dev->iobase + DAS16M1_CS); + + if((status & IRQDATA) == 0 || dev->attached == 0) + { + comedi_error(dev, "spurious interrupt"); + return; + } + + for(i = 0; i < HALF_FIFO; i++) + { + data_point = inw(dev->iobase + DAS16M1_AI); + data_point = AI_DATA(data_point); + comedi_buf_put(async, data_point); + + if(--devpriv->adc_count <= 0) { /* end of acquisition */ + devpriv->control_state &= ~INTE; + devpriv->control_state &= ~PACER_MASK; + outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); + async->events |= COMEDI_CB_EOA; + break; + } + } + + comedi_event(dev, s, async->events); + async->events = 0; + + /* clear interrupt */ + outb(0, dev->iobase + DAS16M1_CLEAR_INTR); +} + +/* This function takes a time in nanoseconds and sets the * + * 2 pacer clocks to the closest frequency possible. It also * + * returns the actual sampling period. */ +static unsigned int das16m1_set_pacer(comedi_device *dev, unsigned int ns, int rounding_flags) +{ + i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1), + &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK); + + /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ + i8254_load(dev->iobase + DAS16M1_8254_SECOND, 1, devpriv->divisor1, 2); + i8254_load(dev->iobase + DAS16M1_8254_SECOND, 2, devpriv->divisor2, 2); + + return ns; +} + +#ifdef DEBUG +static void reg_dump(comedi_device *dev) +{ + printk("********DAS16M100 REGISTER DUMP********\n"); + printk("DAS16M1_MUX: %x\n", inb(dev->iobase+DAS16M1_MUX) ); + printk("DAS16M1_DIO: %x\n", inb(dev->iobase+DAS16M1_DIO) ); + printk("DAS16M1_STATUS: %x\n", inb(dev->iobase+DAS16M1_STATUS) ); + printk("DAS16M1_CONTROL: %x\n", inb(dev->iobase+DAS16M1_CONTROL) ); + printk("DAS16M1_PACER: %x\n", inb(dev->iobase+DAS16M1_PACER) ); + printk("DAS16M1_GAIN: %x\n", inb(dev->iobase+DAS16M1_GAIN) ); + printk("DAS16M1_CNTR_CONTROL: %x\n", inb(dev->iobase+DAS16M1_CNTR_CONTROL) ); + printk("DAS16M100_CONV: %x\n", inb(dev->iobase+DAS16M100_CONV) ); + printk("DAS16M100_BURST: %x\n", inb(dev->iobase+DAS16M100_BURST) ); + printk("DAS16M100_ENABLE: %x\n", inb(dev->iobase+DAS16M100_ENABLE) ); + printk("DAS16M100_STATUS_B: %x\n", inb(dev->iobase+DAS16M100_STATUS_B) ); +} +#endif + +/* + * Options list: + * 0 I/O base + * 1 IRQ + */ + +static int das16m1_attach(comedi_device *dev, comedi_devconfig *it) +{ + comedi_subdevice *s; + int ret, irq; + int iobase; + + iobase = it->options[0]; + + printk("comedi%d: das16m1:", dev->minor); + + if((ret = alloc_private(dev, sizeof(struct das16m1_private_struct))) < 0) + return ret; + + dev->board_name = thisboard->name; + + printk(" io= 0x%04x-0x%04x 0x%04x-0x%04x", + iobase, iobase + DAS16M1_SIZE, + iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2); + if(check_region(iobase, DAS16M1_SIZE) < 0) { + printk(" I/O port conflict\n"); + return -EIO; + } + if(check_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2) < 0){ + printk(" I/O port conflict\n"); + return -EIO; + } + request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name); + request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2, driver_das16m1.driver_name); + dev->iobase = iobase; + + /* now for the irq */ + irq = it->options[1]; + // make sure it is valid + if(irq == 2 || irq == 3 || irq == 5 || irq == 7 || + irq == 10 || irq == 11 || irq == 12 || irq == 15) + { + if((ret = comedi_request_irq(irq, das16m1_interrupt, 0, + driver_das16m1.driver_name,dev)) < 0) + return ret; + dev->irq = irq; + printk(" irq = %d\n", irq); + }else if(irq == 0){ + printk(" irq = none\n"); + }else { + printk(" invalid irq\n"); + return -EINVAL; + } + + dev->n_subdevices = 4; + if((ret = alloc_subdevices(dev)) < 0) + return ret; + + s = dev->subdevices + 0; + dev->read_subdev = s; + /* ai */ + s->type = COMEDI_SUBD_AI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 8; + s->subdev_flags |= SDF_DIFF; + s->len_chanlist = 256; + s->maxdata = (1 << 12) - 1; + s->range_table = &range_das16m1; +// s->insn_read = das16m1_ai_rinsn; + s->do_cmdtest = das16m1_cmd_test; + s->do_cmd = das16m1_cmd_exec; + s->cancel = das16m1_cancel; + + s = dev->subdevices + 1; + /* di */ + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 4; + s->maxdata = 1; + s->range_table = &range_digital; +// s->insn_bits = das16m1_di_rbits; + + s = dev->subdevices + 2; + /* do */ + s->type = COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = 4; + s->maxdata = 1; + s->range_table = &range_digital; +// s->insn_write = das16m1_do_wbits; + + s = dev->subdevices + 3; + /* 8255 */ + subdev_8255_init(dev, s, NULL, (void*)(dev->iobase + DAS16M1_82C55)); + + // XXX should init digital output levels here also + /* set the interrupt level */ + devpriv->control_state = IRQ(dev->irq); + outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); + + return 0; +} + + +static int das16m1_detach(comedi_device *dev) +{ + printk("comedi%d: das16m1: remove\n", dev->minor); + +// das16m1_reset(dev); + + if(dev->subdevices) + subdev_8255_cleanup(dev,dev->subdevices+4); + + if(dev->irq) + free_irq(dev->irq, dev); + + if(dev->iobase){ + release_region(dev->iobase , DAS16M1_SIZE); + release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2); + } + + return 0; +} + + -- 2.26.2