From 1b8fde1aaf66ef427e93f2c543b8767351cabf4e Mon Sep 17 00:00:00 2001 From: David Schleef Date: Tue, 12 Dec 2000 04:58:07 +0000 Subject: [PATCH] update from frank --- comedi/drivers/das1800.c | 1008 +++++++++++++++++++++++++++++--------- 1 file changed, 779 insertions(+), 229 deletions(-) diff --git a/comedi/drivers/das1800.c b/comedi/drivers/das1800.c index a39142ff..9f8a78dc 100644 --- a/comedi/drivers/das1800.c +++ b/comedi/drivers/das1800.c @@ -23,37 +23,36 @@ This driver supports the following Keithley boards: -DAS-1801ST -DAS-1802ST -DAS-1802HR +das-1701st/ao +das-1702st/hr/ao +das-1801st/hc/ao +das-1802st/hr/hc/ao Options: [0] - base io address - [1] - irq (supports shareable interrupts) + [1] - irq (optional, required for timed or externally triggered conversions) + [2] - dma0 (optional) + [3] - dma1 (optional) -cmd triggers supported: - start_src: TRIG_NOW +irq can be omitted, although the cmd interface will not work without it. + +analog input cmd triggers supported: + start_src: TRIG_NOW | TRIG_EXT scan_begin_src: TRIG_FOLLOW scan_end_src: TRIG_COUNT - convert_src: TRIG_TIMER + convert_src: TRIG_TIMER | TRIG_EXT scan_end_src: TRIG_COUNT - stop_src: TRIG_END | TRIG_COUNT + stop_src: TRIG_COUNT | TRIG_NONE TODO: - Support unipolar gains and single ended inputs - Support more cmd triggers - Speed up interrupt routine - Support dma transfers - Add support for cards' digital i/o + Support TRIG_TIMER and TRIG_EXT for scan_begin_src (burst mode) Add support for cards with analog out + Support waveform out for 'ao' cards NOTES: Only the DAS-1801ST has been tested by me. +Unipolar and bipolar ranges cannot be mixed in the channel/gain list. -BUGS: -The DAS-1802ST cards are identified as DAS-1801ST cards, since the -two boards have identical id bits. The only difference between the -cards are their gains. */ #include @@ -67,13 +66,16 @@ cards are their gains. #include #include #include -#include #include +#include +#include +#include "8253.h" -#define DAS1800_SIZE 16 -#define HALFFIFO 512 +#define DAS1800_SIZE 16 //uses 16 io addresses +#define HALF_FIFO 512 // 1024 sample fifo +#define TIMER_BASE 200 // 5 Mhz master clock -/* Registers for the das800 */ +/* Registers for the das1800 */ #define DAS1800_FIFO 0x0 #define DAS1800_QRAM 0x0 #define DAS1800_SELECT 0x2 @@ -81,6 +83,9 @@ cards are their gains. #define DAS1800_CONTROL_A 0x4 #define FFEN 0x1 #define CGEN 0x4 +#define CGSL 0x8 +#define TGEN 0x10 +#define TGSL 0x20 #define DAS1800_CONTROL_B 0x5 #define FIMD 0x40 #define DAS1800_CONTROL_C 0X6 @@ -91,6 +96,7 @@ cards are their gains. #define UB 0x80 #define DAS1800_STATUS 0x7 #define INT 0x1 +#define DMATC 0x2 #define OVF 0x10 #define FHF 0x20 #define FNE 0x40 @@ -99,32 +105,118 @@ cards are their gains. #define DAS1800_COUNTER(a) (0xc + a) #define DAS1800_COUNTER_CONTROL 0xf +enum{das1701st, das1702st, das1702hr, das1701ao, das1702ao, das1801st, das1802st, das1802hr, das1801hc, das1802hc, das1801ao, das1802ao}; + typedef struct das1800_board_struct{ char *name; - int ai_speed; - int resolution; + int ai_speed; /* max conversion period in nanoseconds */ + int resolution; /* bits of ai resolution */ + int qram_len; /* length of card's channel / gain queue */ + int common; /* supports AREF_COMMON flag */ + int do_n_chan; /* number of digital output channels */ }das1800_board; das1800_board das1800_boards[] = { { - name: "DAS-1801ST", + name: "DAS-1701ST", /* Warning: the maximum conversion speeds listed below are * not always achievable depending on board setup (see * user manual.) */ + ai_speed: 6250, + resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1702ST", + ai_speed: 6250, + resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1702HR", + ai_speed: 20000, + resolution: 16, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1701AO", + ai_speed: 6250, + resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1702AO", + ai_speed: 6250, + resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1801ST", ai_speed: 3000, resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, }, { name: "DAS-1802ST", ai_speed: 3000, resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, }, { name: "DAS-1802HR", ai_speed: 10000, resolution: 16, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1801HC", + ai_speed: 3000, + resolution: 12, + qram_len: 64, + common: 0, + do_n_chan: 8, + }, + { + name: "DAS-1802HC", + ai_speed: 3000, + resolution: 12, + qram_len: 64, + common: 0, + do_n_chan: 8, + }, + { + name: "DAS-1801AO", + ai_speed: 3000, + resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, + }, + { + name: "DAS-1802AO", + ai_speed: 3000, + resolution: 12, + qram_len: 256, + common: 1, + do_n_chan: 4, }, }; /* @@ -135,62 +227,68 @@ das1800_board das1800_boards[] = typedef struct{ unsigned long count; /* number of data points left to be taken */ int forever; /* flag indicating whether we should take data forever */ - unsigned short divisor1; /* value to load into board's counter 1 for timed conversions */ - unsigned short divisor2; /* value to load into board's counter 2 for timed conversions */ + unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */ + unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */ + int do_bits; /* digital output bits */ + int irq_dma_bits; /* bits for control register b */ + unsigned int dma0; /* dma channels used */ + unsigned int dma1; + unsigned int dma_current; /* dma channel currently in use */ + short *dma0_buf; /* pointers to dma buffers */ + short *dma1_buf; + short *dma_current_buf; /* pointer to dma buffer currently being used */ + unsigned int dma_buf_size; /* size in bytes of dma buffers */ + int dual_dma; /* flag that indicates whether we have dual dma */ }das1800_private; #define devpriv ((das1800_private *)dev->private) -static comedi_lrange range_das1801st_ai = { - 4, +static comedi_lrange range_das1801_ai = { + 8, { RANGE( -5, 5 ), RANGE( -1, 1 ), RANGE( -0.1, 0.1 ), RANGE( -0.02, 0.02 ), -/* RANGE( 0, 5 ), + RANGE( 0, 5 ), RANGE( 0, 1 ), RANGE( 0, 0.1 ), RANGE( 0, 0.02 ), -*/ } -}; - -static comedi_lrange range_das1802st_ai = { - 4, - { - RANGE(-10, 10), - RANGE(-5, 5), - RANGE(-2.5, 2.5), - RANGE(-1.25, 1.25), -/* RANGE(0, 10), - RANGE(0, 5), - RANGE(0, 2.5), - RANGE(0, 1.25), -*/ } + } }; -static comedi_lrange range_das1802hr_ai = { - 4, +static comedi_lrange range_das1802_ai = { + 8, { RANGE(-10, 10), RANGE(-5, 5), RANGE(-2.5, 2.5), RANGE(-1.25, 1.25), -/* RANGE(0, 10), + RANGE(0, 10), RANGE(0, 5), RANGE(0, 2.5), RANGE(0, 1.25), -*/ } + } }; static comedi_lrange *das1800_range_lkup[] = { - &range_das1801st_ai, - &range_das1802st_ai, - &range_das1802hr_ai, + &range_das1801_ai, + &range_das1802_ai, + &range_das1802_ai, + &range_das1801_ai, + &range_das1802_ai, + &range_das1801_ai, + &range_das1802_ai, + &range_das1801_ai, + &range_das1802_ai, + &range_das1802_ai, + &range_das1801_ai, + &range_das1802_ai, }; static int das1800_attach(comedi_device *dev, comedi_devconfig *it); static int das1800_detach(comedi_device *dev); +static int das1800_recognize(char *name); static int das1800_cancel(comedi_device *dev, comedi_subdevice *s); comedi_driver driver_das1800={ @@ -198,46 +296,180 @@ comedi_driver driver_das1800={ module: THIS_MODULE, attach: das1800_attach, detach: das1800_detach, + recognize: das1800_recognize, }; static void das1800_interrupt(int irq, void *d, struct pt_regs *regs); -void enable_das1800(comedi_device *dev); +static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s); +static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s); +static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s); void disable_das1800(comedi_device *dev); static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd); static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s); static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +static int das1800_di_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); +static int das1800_do_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data); int das1800_probe(comedi_device *dev); -int das1800_set_frequency( unsigned int period, comedi_device *dev); -int das1800_load_counter(unsigned int counterNumber, int counterValue, comedi_device *dev); -unsigned int das1800_find_divisors(unsigned int period, comedi_device *dev); +int das1800_set_frequency(comedi_device *dev); +int das1800_load_counter(unsigned int counterNumber, unsigned int counterValue, comedi_device *dev); + +static int das1800_recognize(char *name) +{ + if(!strcmp(name, "das-1701st")) + return das1701st; + if(!strcmp(name, "das-1702st")) + return das1702st; + if(!strcmp(name, "das-1702hr")) + return das1702hr; + if(!strcmp(name, "das-1701ao")) + return das1701ao; + if(!strcmp(name, "das-1702ao")) + return das1702ao; + if(!strcmp(name, "das-1801st") || !strcmp(name, "das1800")) + return das1801st; + if(!strcmp(name, "das-1802st")) + return das1802st; + if(!strcmp(name, "das-1802hr")) + return das1802hr; + if(!strcmp(name, "das-1801hc")) + return das1801hc; + if(!strcmp(name, "das-1802hc")) + return das1802hc; + if(!strcmp(name, "das-1801ao")) + return das1801ao; + if(!strcmp(name, "das-1802ao")) + return das1802ao; -/* probes das-1800st/hr series board type - stupid boards have incomplete id - this isn't going to work */ + return -1; +} + +/* probes and checks das-1800 series board type + */ int das1800_probe(comedi_device *dev) { int id; - id = inb(dev->iobase + DAS1800_DIGITAL) >> 4; /* get id bits */ + id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0x7; /* get id bits */ switch(id) { - case 0x7: - printk(" Board model: DAS-1800ST series\n"); - return 0; - break; case 0x3: - printk(" Board model: DAS-1800ST-DA series\n"); - return 0; + if(dev->board == das1801st) + { + printk(" Board model: DAS-1801ST-DA\n"); + return dev->board; + } + if(dev->board == das1802st) + { + printk(" Board model: DAS-1802ST-DA\n"); + return dev->board; + } + if(dev->board == das1701st) + { + printk(" Board model: DAS-1701ST-DA\n"); + return dev->board; + } + if(dev->board == das1702st) + { + printk(" Board model: DAS-1702ST-DA\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1800ST-DA series\n"); + return das1801st; + break; + case 0x4: + if(dev->board == das1802hr) + { + printk(" Board model: DAS-1802HR-DA\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1802HR-DA\n"); + if(dev->board == das1702hr) + { + printk(" Board model: DAS-1702HR-DA\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1802HR-DA\n"); + return das1802hr; + break; + case 0x5: + if(dev->board == das1801ao) + { + printk(" Board model: DAS-1801AO\n"); + return dev->board; + } + if(dev->board == das1802ao) + { + printk(" Board model: DAS-1802AO\n"); + return dev->board; + } + if(dev->board == das1701ao) + { + printk(" Board model: DAS-1701AO\n"); + return dev->board; + } + if(dev->board == das1702ao) + { + printk(" Board model: DAS-1702AO\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1800AO series\n"); + return das1801ao; break; case 0x6: - printk(" Board model: DAS-1802HR\n"); - return 2; + if(dev->board == das1802hr) + { + printk(" Board model: DAS-1802HR\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1802HR\n"); + if(dev->board == das1702hr) + { + printk(" Board model: DAS-1702HR\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1702HR\n"); + return das1802hr; break; - case 0x4: - printk(" Board model: DAS-1802HR-DA\n"); - return 2; + case 0x7: + if(dev->board == das1801st) + { + printk(" Board model: DAS-1801ST\n"); + return dev->board; + } + if(dev->board == das1802st) + { + printk(" Board model: DAS-1802ST\n"); + return dev->board; + } + if(dev->board == das1701st) + { + printk(" Board model: DAS-1701ST\n"); + return dev->board; + } + if(dev->board == das1702st) + { + printk(" Board model: DAS-1702ST\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1800ST series\n"); + return das1801st; + break; + case 0x8: + if(dev->board == das1801hc) + { + printk(" Board model: DAS-1801HC\n"); + return dev->board; + } + if(dev->board == das1802hc) + { + printk(" Board model: DAS-1802HC\n"); + return dev->board; + } + printk(" Board model (probed): DAS-1800HC series\n"); + return das1801hc; break; default : printk(" Board model: probe returned 0x%x (unknown)\n", id); - return -1; + return dev->board; break; } return -1; @@ -251,74 +483,246 @@ COMEDI_INITCLEANUP(driver_das1800); static void das1800_interrupt(int irq, void *d, struct pt_regs *regs) { - int i; /* loop index */ - int numPoints = HALFFIFO; /* number of points to read */ - int ret; + int status; comedi_device *dev = d; comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */ - sampl_t dpnt; - ret = inb(dev->iobase + DAS1800_STATUS); + status = inb(dev->iobase + DAS1800_STATUS); /* if interrupt was not caused by das-1800 */ - if(!(ret & INT)) + if(!(status & INT)) + { return; - if(ret & FHF) /* if fifo half full */ + } else if(devpriv->dma0) /* if dma is enabled and generated the interrupt */ { - /* if we only need some of the points */ - if( devpriv->forever == 0 && devpriv->count < numPoints) - numPoints = devpriv->count; - for( i = 0; i < numPoints; i++) - { - /* write data point to buffer */ - if(s->buf_int_ptr + sizeof(sampl_t) > s->cur_trig.data_len ) - { - s->buf_int_ptr = 0; - comedi_eobuf(dev, s); - } - dpnt = inw(dev->iobase + DAS1800_FIFO); - /* convert to offset binary */ - dpnt += 1 << (thisboard->resolution - 1); - *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = dpnt; - s->cur_chan++; - if( s->cur_chan >= s->cur_chanlist_len ) - { - s->cur_chan = 0; - comedi_eos(dev, s); - } - s->buf_int_count += sizeof(sampl_t); - s->buf_int_ptr += sizeof(sampl_t); - if(devpriv->count > 0) devpriv->count--; - } + if(status & DMATC) + das1800_handle_dma(dev, s); + } else if(status & FHF) + { + das1800_handle_fifo_half_full(dev, s); + } else if(status & FNE) + { + das1800_handle_fifo_not_empty(dev, s); } - comedi_bufcheck(dev,s); - if(ret & OVF) + comedi_bufcheck(dev, s); + /* if the card's fifo has overflowed */ + if(status & OVF) { comedi_error(dev, "DAS1800 FIFO overflow"); - das1800_cancel(dev, dev->subdevices + 0); + das1800_cancel(dev, s); comedi_error_done(dev, s); return; } - /* if there are more data points to collect */ - if(devpriv->count > 0 || devpriv->forever == 1) - /* Re-enable card's interrupt */ - outb(CVEN, dev->iobase + DAS1800_STATUS); - /* otherwise, stop taking data */ - else + /* stop taking data if appropriate */ + if(devpriv->count == 0 && devpriv->forever == 0) { - disable_das1800(dev); /* diable hardware triggered conversions */ + disable_das1800(dev); /* diable hardware conversions */ comedi_done(dev, s); } + + return; +} + +static void das1800_handle_dma(comedi_device *dev, comedi_subdevice *s) +{ + unsigned long flags; + int numPoints; + short dpnt; + short *buffer; + int unipolar; + int i; + + if(get_dma_residue(devpriv->dma_current)) + return; + numPoints = devpriv->count; + if(numPoints > devpriv->dma_buf_size / sizeof(sampl_t)) + numPoints = devpriv->dma_buf_size / sizeof(sampl_t); + buffer = devpriv->dma_current_buf; + flags = claim_dma_lock(); + disable_dma(devpriv->dma_current); + if(devpriv->dual_dma) + { + if(devpriv->dma_current == devpriv->dma0) + { + devpriv->dma_current = devpriv->dma1; + devpriv->dma_current_buf = devpriv->dma1_buf; + } + else + { + devpriv->dma_current = devpriv->dma0; + devpriv->dma_current_buf = devpriv->dma0_buf; + } + } + set_dma_addr(devpriv->dma_current, (unsigned int) devpriv->dma_current_buf); + if((devpriv->count - numPoints) * sizeof(short) > devpriv->dma_buf_size || devpriv->forever) + set_dma_count(devpriv->dma_current, devpriv->dma_buf_size); + else + set_dma_count(devpriv->dma_current, (devpriv->count - numPoints) * sizeof(short)); + + // if we are doing dual channel dma, enable the second channel immediately + if(devpriv->dual_dma) + { + enable_dma(devpriv->dma_current); + release_dma_lock(flags); + } + + unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; + + for( i = 0; i < numPoints; i++) + { + /* write data point to buffer */ + if(s->buf_int_ptr + sizeof(sampl_t) > s->cur_trig.data_len ) + { + s->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + dpnt = buffer[i]; + /* convert to unsigned type if we are in a bipolar mode */ + if(!unipolar); + dpnt += 1 << (thisboard->resolution - 1); + *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = dpnt; + s->cur_chan++; + if( s->cur_chan >= s->cur_chanlist_len ) + { + s->cur_chan = 0; + comedi_eos(dev, s); + } + s->buf_int_count += sizeof(sampl_t); + s->buf_int_ptr += sizeof(sampl_t); + if(devpriv->count > 0) devpriv->count--; + } + + // for single channel dma, re-enable after old data has been read + if(devpriv->dual_dma == 0) + { + enable_dma(devpriv->dma_current); + release_dma_lock(flags); + } + + /* clear interrupt */ + outb(FNE, dev->iobase + DAS1800_STATUS); + + return; +} + +static void das1800_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s) +{ + int i; /* loop index */ + int numPoints = 0; /* number of points to read */ + sampl_t dpnt; + int unipolar; + + unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; + + numPoints = HALF_FIFO; + /* if we only need some of the points */ + if( devpriv->forever == 0 && devpriv->count < numPoints) + numPoints = devpriv->count; + for( i = 0; i < numPoints; i++) + { + /* write data point to buffer */ + if(s->buf_int_ptr + sizeof(sampl_t) > s->cur_trig.data_len ) + { + s->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + dpnt = inw(dev->iobase + DAS1800_FIFO); + /* convert to unsigned type if we are in a bipolar mode */ + if(!unipolar); + dpnt += 1 << (thisboard->resolution - 1); + *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = dpnt; + s->cur_chan++; + if( s->cur_chan >= s->cur_chanlist_len ) + { + s->cur_chan = 0; + comedi_eos(dev, s); + } + s->buf_int_count += sizeof(sampl_t); + s->buf_int_ptr += sizeof(sampl_t); + if(devpriv->count > 0) devpriv->count--; + } + /* clear interrupt */ + outb(FNE, dev->iobase + DAS1800_STATUS); + // if there are just a few points left, switch to interrupt on end of conversion + if(devpriv->count < HALF_FIFO && devpriv->count > 0 && devpriv->forever == 0) + { + devpriv->irq_dma_bits &= ~FIMD; // interrupt fifo not empty + outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); + } + return; +} + +static void das1800_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s) +{ + sampl_t dpnt; + int unipolar; + + unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB; + + while(inb(dev->iobase + DAS1800_STATUS) & FNE) + { + /* write data point to buffer */ + if(s->buf_int_ptr + sizeof(sampl_t) > s->cur_trig.data_len ) + { + s->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + dpnt = inw(dev->iobase + DAS1800_FIFO); + /* convert to unsigned type if we are in a bipolar mode */ + if(!unipolar); + dpnt += 1 << (thisboard->resolution - 1); + *((sampl_t *)((void *)s->cur_trig.data + s->buf_int_ptr)) = dpnt; + s->cur_chan++; + if( s->cur_chan >= s->cur_chanlist_len ) + { + s->cur_chan = 0; + comedi_eos(dev, s); + } + s->buf_int_count += sizeof(sampl_t); + s->buf_int_ptr += sizeof(sampl_t); + if(devpriv->count > 0) devpriv->count--; + if(devpriv->count == 0 && devpriv->forever == 0) + break; + } + /* clear interrupt */ + outb(FNE, dev->iobase + DAS1800_STATUS); + return; } static int das1800_attach(comedi_device *dev, comedi_devconfig *it) { comedi_subdevice *s; - unsigned char byte; + unsigned long flags; int iobase = it->options[0]; int irq = it->options[1]; + int dma0 = it->options[2]; + int dma1 = it->options[3]; - printk("comedi%d: das1800: io 0x%x, irq %i\n", dev->minor, iobase, irq); + /* allocate and initialize dev->private */ + if(alloc_private(dev, sizeof(das1800_private)) < 0) + return -ENOMEM; + + printk("comedi%d: das1800: io 0x%x", dev->minor, iobase); + if(irq) + { + printk(", irq %i", irq); + if(dma0) + { + printk(", dma %i", dma0); + if(dma1) printk(" and %i", dma1); + } + } + printk("\n"); + + dev->board = das1800_probe(dev); + if(dev->board < 0) + { + printk("unable to determine board type\n"); + return -ENODEV; + } + + dev->board_ptr = das1800_boards + dev->board; + dev->board_name = thisboard->name; /* check if io addresses are available */ if(check_region(iobase, DAS1800_SIZE) < 0) @@ -326,77 +730,137 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it) printk("I/O port conflict\n"); return -EIO; } - request_region(iobase, DAS1800_SIZE, "das1800"); + request_region(iobase, DAS1800_SIZE, thisboard->name); dev->iobase = iobase; dev->iosize = DAS1800_SIZE; /* grab our IRQ */ - byte = FIMD; /* interrupt on half full fifo */ + if(irq) + { + if(comedi_request_irq( irq, das1800_interrupt, 0, thisboard->name, dev )) + { + printk( "unable to allocate irq %d\n", irq); + return -EINVAL; + } + } + dev->irq = irq; + + // set bits that tell card which irq to use switch(irq) { case 0: break; case 3: - byte |= 0x8; + devpriv->irq_dma_bits |= 0x8; break; case 5: - byte |= 0x10; + devpriv->irq_dma_bits |= 0x10; break; case 7: - byte |= 0x18; + devpriv->irq_dma_bits |= 0x18; break; case 10: - byte |= 0x28; + devpriv->irq_dma_bits |= 0x28; break; case 11: - byte |= 0x30; + devpriv->irq_dma_bits |= 0x30; break; case 15: - byte |= 0x38; + devpriv->irq_dma_bits |= 0x38; break; default: printk("irq out of range\n"); return -EINVAL; break; } - outb(byte, dev->iobase + DAS1800_CONTROL_B); // tell board what irq to use +//dma stuff + // need an irq to do dma if(irq) { - if(comedi_request_irq( irq, das1800_interrupt, SA_SHIRQ, "das1800", dev )) + if(dma0) { - printk( "unable to allocate irq %d\n", irq); - return -EINVAL; + switch(dma0 | (dma1 << 4)) + { + case 0x5: // dma0 == 5 + devpriv->irq_dma_bits |= 0x1; + break; + case 0x6: // dma0 == 6 + devpriv->irq_dma_bits |= 0x2; + break; + case 0x7: // dma0 == 7 + devpriv->irq_dma_bits |= 0x3; + break; + case 0x65: // dma0 == 5, dma1 == 6 + devpriv->irq_dma_bits |= 0x5; + break; + case 0x76: // dma0 == 6, dma1 == 7 + devpriv->irq_dma_bits |= 0x6; + break; + case 0x57: // dma0 == 7, dma1 == 5 + devpriv->irq_dma_bits |= 0x7; + break; + default: + printk("%s only supports dma channels 5 through 7\n" + " Dual dma only allows the following combinations:\n" + " dma 5,6 / 6,7 / or 7,5\n", thisboard->name); + return -EINVAL; + break; + } + if(request_dma(dma0, thisboard->name)) + { + printk("failed to allocate dma channel %i\n", dma0); + return -EINVAL; + } + devpriv->dma0 = dma0; + devpriv->dma_current = dma0; + if(dma1) + { + if(request_dma(dma1, thisboard->name)) + { + printk("failed to allocate dma channel %i\n", dma1); + return -EINVAL; + } + devpriv->dma1 = dma1; + devpriv->dual_dma = 1; + } + devpriv->dma_buf_size = 0x1ff00; + devpriv->dma0_buf = kmalloc(devpriv->dma_buf_size, GFP_BUFFER | GFP_DMA); + if(devpriv->dma0_buf == 0) + return -ENOMEM; + devpriv->dma_current_buf = devpriv->dma0_buf; + if(dma1) + { + devpriv->dma1_buf = kmalloc(devpriv->dma_buf_size, GFP_BUFFER | GFP_DMA); + if(devpriv->dma1_buf == 0) + return -ENOMEM; + } + flags = claim_dma_lock(); + disable_dma(devpriv->dma0); + clear_dma_ff(devpriv->dma0); + set_dma_mode(devpriv->dma0, DMA_MODE_READ); + if(dma1) + { + disable_dma(devpriv->dma1); + clear_dma_ff(devpriv->dma1); + set_dma_mode(devpriv->dma1, DMA_MODE_READ); + } + release_dma_lock(flags); } } - dev->irq = irq; - dev->board = das1800_probe(dev); - if(dev->board < 0) - { - printk("unable to determine board type\n"); - return -ENODEV; - } - - dev->board_ptr = das1800_boards + dev->board; - dev->board_name = thisboard->name; - - /* allocate and initialize dev->private */ - if(alloc_private(dev, sizeof(das1800_private)) < 0) - return -ENOMEM; - devpriv->count = 0; - devpriv->forever = 0; - - dev->n_subdevices = 1; + dev->n_subdevices = 4; if(alloc_subdevices(dev) < 0) return -ENOMEM; /* analog input subdevice */ s = dev->subdevices + 0; s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_DIFF; - s->n_chan = 256; - s->len_chanlist = 256; + s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND; + if(thisboard->common) + s->subdev_flags |= SDF_COMMON; + s->n_chan = thisboard->qram_len; + s->len_chanlist = thisboard->qram_len; s->maxdata = (1 << thisboard->resolution) - 1; s->range_table = das1800_range_lkup[dev->board]; s->do_cmd = das1800_ai_do_cmd; @@ -404,6 +868,28 @@ static int das1800_attach(comedi_device *dev, comedi_devconfig *it) s->insn_read = das1800_ai_rinsn; s->cancel = das1800_cancel; + /* analog out (TODO!)*/ + s = dev->subdevices + 1; + s->type = COMEDI_SUBD_UNUSED; + + /* di */ + s = dev->subdevices + 2; + s->type = COMEDI_SUBD_DI; + s->subdev_flags = SDF_READABLE; + s->n_chan = 4; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_read = das1800_di_rinsn; + + /* do */ + s = dev->subdevices + 3; + s->type=COMEDI_SUBD_DO; + s->subdev_flags = SDF_WRITEABLE; + s->n_chan = thisboard->do_n_chan; + s->maxdata = 1; + s->range_table = &range_digital; + s->insn_write = das1800_do_winsn; + return 0; }; @@ -413,15 +899,18 @@ static int das1800_detach(comedi_device *dev) /* only free stuff if it has been allocated by _attach */ if(dev->iobase) - { release_region(dev->iobase, DAS1800_SIZE); - dev->iobase = 0; - } if(dev->irq) - { comedi_free_irq(dev->irq, dev); - dev->irq = 0; - } + if(devpriv->dma0) + free_dma(devpriv->dma0); + if(devpriv->dma0_buf) + kfree(devpriv->dma0_buf); + if(devpriv->dma1) + free_dma(devpriv->dma1); + if(devpriv->dma1_buf) + kfree(devpriv->dma1_buf); + return 0; }; @@ -433,18 +922,13 @@ static int das1800_cancel(comedi_device *dev, comedi_subdevice *s) return 0; } -/* enable_das1800 makes the card start taking hardware triggered conversions */ -void enable_das1800(comedi_device *dev) -{ - outb(CGEN | FFEN, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and hardware triggering */ - outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */ -} - -/* disable_das1800 stops hardware triggered conversions */ void disable_das1800(comedi_device *dev) { outb(0x0, dev->iobase + DAS1800_STATUS); /* disable conversions */ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* disable and clear fifo and stop triggering */ + outb(0x0, dev->iobase + DAS1800_CONTROL_B); /* disable interrupts and dma */ + if(devpriv->dma0) disable_dma(devpriv->dma0); + if(devpriv->dma1) disable_dma(devpriv->dma1); } static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd) @@ -455,7 +939,7 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c /* step 1: make sure trigger sources are trivially valid */ tmp = cmd->start_src; - cmd->start_src &= TRIG_NOW; + cmd->start_src &= TRIG_NOW | TRIG_EXT; if(!cmd->start_src && tmp != cmd->start_src) err++; tmp = cmd->scan_begin_src; @@ -463,7 +947,7 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c if(!cmd->scan_begin_src && tmp != cmd->scan_begin_src) err++; tmp = cmd->convert_src; - cmd->convert_src &= TRIG_TIMER; + cmd->convert_src &= TRIG_TIMER | TRIG_EXT; if(!cmd->convert_src && tmp != cmd->convert_src) err++; tmp = cmd->scan_end_src; @@ -472,15 +956,17 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c tmp=cmd->stop_src; cmd->stop_src &= TRIG_COUNT | TRIG_NONE; - if(!cmd->stop_src && tmp!=cmd->stop_src) err++; + if(!cmd->stop_src && tmp != cmd->stop_src) err++; if(err) return 1; /* step 2: make sure trigger sources are unique and mutually compatible */ - if(cmd->start_src != TRIG_NOW) err++; + if(cmd->start_src != TRIG_NOW && + cmd->start_src != TRIG_EXT) err++; if(cmd->scan_begin_src != TRIG_FOLLOW) err++; - if(cmd->convert_src != TRIG_TIMER) err++; + if(cmd->convert_src != TRIG_TIMER && + cmd->convert_src != TRIG_EXT) err++; if(cmd->scan_end_src != TRIG_COUNT) err++; if(cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE) err++; @@ -539,7 +1025,8 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c if(cmd->convert_src == TRIG_TIMER) { tmp = cmd->convert_arg; - cmd->convert_arg = das1800_find_divisors(cmd->convert_arg, dev); + /* calculate counter values that give desired timing */ + i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); if(tmp != cmd->convert_arg) err++; } @@ -551,6 +1038,10 @@ static int das1800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_c static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s) { int i, n, chan_range; + int aref; + int conv_flags; + int control_a; + int lock_flags; if(!dev->irq) { @@ -565,65 +1056,131 @@ static int das1800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s) outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*set QRAM address start */ for(i = 0; i < n; i++) /* make channel / gain list */ { - chan_range = CR_CHAN(s->cmd.chanlist[i]) | (CR_RANGE(s->cmd.chanlist[i]) << 8); + /* mask off unipolar/bipolar bit from range */ + chan_range = CR_CHAN(s->cmd.chanlist[i]) | ((CR_RANGE(s->cmd.chanlist[i]) & 0x3) << 8); outw(chan_range, dev->iobase + DAS1800_QRAM); } outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */ outb(0x0, dev->iobase + DAS1800_SELECT); /* select ADC for baseAddress + 0x0 */ + /* enable auto channel scan, send interrupts on end of conversion, + * set clock source to internal or external, select analog reference, + * select unipolar / bipolar + */ + aref = CR_AREF(s->cmd.chanlist[0]); + conv_flags = UQEN; + if(aref != AREF_DIFF) + conv_flags |= SD; + if(aref == AREF_COMMON) + conv_flags |= CMEN; + /* if a unipolar range was selected */ + if(CR_RANGE(s->cmd.chanlist[0]) & 0x4) + conv_flags |= UB; + switch(s->cmd.convert_src) + { + case TRIG_TIMER: + /* trig on cascaded counters */ + outb(conv_flags | 0x1, dev->iobase + DAS1800_CONTROL_C); + /* set conversion frequency */ + i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(s->cmd.convert_arg), s->cmd.flags & TRIG_ROUND_MASK); + if(das1800_set_frequency(dev) < 0) + { + comedi_error(dev, "Error setting up counters"); + return -1; + } + break; + case TRIG_EXT: + /* trig on falling edge */ + outb(conv_flags | 0x3, dev->iobase + DAS1800_CONTROL_C); + break; + default: + break; + } + switch(s->cmd.stop_src) { case TRIG_COUNT: devpriv->count = s->cmd.stop_arg; devpriv->forever = 0; + /* set interrupt mode */ + if(s->cmd.stop_arg < HALF_FIFO) + devpriv->irq_dma_bits &= ~FIMD; // interrupt fifo not empty + else + devpriv->irq_dma_bits |= FIMD; //interrupt fifo half full break; case TRIG_NONE: devpriv->forever = 1; devpriv->count = 0; + devpriv->irq_dma_bits |= FIMD; break; - default : + default: break; } + /* set FIMD bit in control register b */ + outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); - /* enable auto channel scan, send interrupts on end of conversion - * and set clock source to internal or external - */ - switch(s->cmd.convert_src) + // enable fifo + control_a = FFEN; + switch(s->cmd.start_src) { - case TRIG_TIMER: - /* differential, bipolar, pacer clocks are clocks 1 and 2 */ - outb(UQEN | 0x1, dev->iobase + DAS1800_CONTROL_C); - /* set conversion frequency */ - if(das1800_set_frequency(s->cmd.convert_arg, dev) < 0) - { - comedi_error(dev, "Error setting up counters"); - return -1; - } + case TRIG_EXT: + control_a |= TGEN | CGSL; break; -/* case TRIG_EXT: + case TRIG_NOW: + control_a |= CGEN; break; -*/ default: + default: break; } - enable_das1800(dev); + outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); // enable irq/dma + if(devpriv->dma0) + { + lock_flags = claim_dma_lock(); + disable_dma(devpriv->dma0); + set_dma_addr(devpriv->dma0, (unsigned int) devpriv->dma0_buf); + if(devpriv->count * sizeof(short) > devpriv->dma_buf_size || devpriv->forever) + set_dma_count(devpriv->dma0, devpriv->dma_buf_size); + else + set_dma_count(devpriv->dma0, devpriv->count * sizeof(short)); + devpriv->dma_current = devpriv->dma0; + devpriv->dma_current_buf = devpriv->dma0_buf; + enable_dma(devpriv->dma0); + release_dma_lock(lock_flags); + } + outb(control_a, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and triggering */ + outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */ + return 0; } static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) { int i, n; - int chan, range, chan_range; + int chan, range, aref, chan_range; int timeout = 1000; short dpnt; - - outb(UQEN, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */ + int conv_flags = 0; + + /* set up analog reference and unipolar / bipolar mode */ + aref = CR_AREF(insn->chanspec); + conv_flags |= UQEN; + if(aref != AREF_DIFF) + conv_flags |= SD; + if(aref == AREF_COMMON) + conv_flags |= CMEN; + /* if a unipolar range was selected */ + if(CR_RANGE(insn->chanspec) & 0x4) + conv_flags |= UB; + + outb(conv_flags, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* reset fifo */ outb(FFEN, dev->iobase + DAS1800_CONTROL_A); - chan = CR_CHAN(insn->chanspec) & 0xff; + chan = CR_CHAN(insn->chanspec); + /* mask of unipolar/bipolar bit from range */ range = CR_RANGE(insn->chanspec) & 0x3; chan_range = chan | (range << 8); outb(0x1, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */ @@ -648,24 +1205,48 @@ static int das1800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn comedi_error(dev, "timeout"); return -ETIME; } - /* shift data from true binary to offset binary */ dpnt = inw(dev->iobase + DAS1800_FIFO); - dpnt += 1 << (thisboard->resolution - 1); + /* shift data to offset binary for bipolar ranges */ + if((conv_flags & UB) == 0) + dpnt += 1 << (thisboard->resolution - 1); data[n] = dpnt; } return n; } +static int das1800_di_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) +{ + int chan = CR_CHAN(insn->chanspec); + int ret; -/* finds best values for cascaded counters to obtain desired frequency, - * and loads counters with calculated values - */ -int das1800_set_frequency(unsigned int period, comedi_device *dev) + ret = inb(dev->iobase + DAS1800_DIGITAL) & (1 << chan); + if(ret) data[0] = 1; + else data[0] = 0; + + return 1; +} + +static int das1800_do_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data) +{ + int chan = CR_CHAN(insn->chanspec); + + // set channel to 1 + if(data[0]) + devpriv->do_bits |= (1 << chan); + // set channel to 0 + else + devpriv->do_bits &= ~(1 << chan); + outb(devpriv->do_bits, dev->iobase + DAS1800_DIGITAL); + + return 1; +} + +/* loads counters with divisor1, divisor2 from private structure */ +int das1800_set_frequency(comedi_device *dev) { int err = 0; - das1800_find_divisors(period, dev); if(das1800_load_counter(1, devpriv->divisor1, dev)) err++; if(das1800_load_counter(2, devpriv->divisor2, dev)) err++; if(err) @@ -674,12 +1255,12 @@ int das1800_set_frequency(unsigned int period, comedi_device *dev) return 0; } -int das1800_load_counter(unsigned int counterNumber, int counterValue, comedi_device *dev) +int das1800_load_counter(unsigned int counterNumber, unsigned int counterValue, comedi_device *dev) { unsigned char byte; if(counterNumber > 2) return -1; - if(counterValue < 2 || counterValue > 0xffff) return -1; + if(counterValue == 1 || counterValue > 0xffff) return -1; byte = counterNumber << 6; byte = byte | 0x30; // load low then high byte @@ -691,34 +1272,3 @@ int das1800_load_counter(unsigned int counterNumber, int counterValue, comedi_de outb(byte, dev->iobase + DAS1800_COUNTER(counterNumber)); return 0; } - -unsigned int das1800_find_divisors(unsigned int period, comedi_device *dev) -{ - int clock = 200; // 5 MHz master clock - int temp, close; - unsigned short i, j; - unsigned int max, min; - - if((devpriv->divisor1 * devpriv->divisor2 * clock) == period) return period; - - max = (period / (2 * clock)); - if(max > 0xffff) - max = 0xffff; - min = 2; - close = period; - for(i = min; i <= max; i++) - { - for(j = (period / (i * clock)); j <= (period / (i * clock)) + 1; j++) - { - temp = period - clock * i * j; - if(temp < 0) temp = -temp; - if(temp < close && j >= min) - { - close = temp; - devpriv->divisor1 = i; devpriv->divisor2 = j; - if(close == 0) return period; - } - } - } - return devpriv->divisor1 * devpriv->divisor2 * clock; -} -- 2.26.2