DAS16 driver
COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 1995,1996 Sam Moore, Warren Jasper
Copyright (C) 2000 David A. Schleef <ds@stm.lbl.gov>
Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
Copyright (C) 2001 Frank Mori Hess <fmhess@uiuc.edu>
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+************************************************************************
+
+Options:
+ [0] - base io address
+ [1] - irq (optional)
+ [2] - dma (optional)
+ [3] - master clock speed in MHz (1 or 10, defaults to 1)
+
+Both an irq line and dma channel are required for timed or externally
+triggered conversions.
+
*/
#include <linux/kernel.h>
#define DAS16_PACER 0x0A
#define DAS16_CTR0 (1<<1)
#define DAS16_TRIG0 (1<<0)
+#define BURST_LEN_BITS(x) (((x) & 0xf) << 4)
#define DAS16_GAIN 0x0B
#define DAS16_CNTR0_DATA 0x0C
#define DAS16_CNTR1_DATA 0x0D
#define DAS1600_WS 0x02
#define DAS1600_CLK_10MHZ 0x01
-#define DAS16_SLOWEST_TIMER 0xffffffff
-#define DAS16_FASTEST_TIMER 10000
-
static comedi_lrange range_das1x01_bip = { 4, {
BIP_RANGE( 10 ),
BIP_RANGE( 1 ),
UNI_RANGE( 1.25 ),
}};
-
static int das16jr_gainlist[] = { 8, 0, 1, 2, 3, 4, 5, 6, 7 };
static int das1600_gainlist[] = { 0, 1, 2, 3 };
enum {
das1600_gainlist,
};
static comedi_lrange *das16_ai_uni_lranges[]={
- &range_bipolar10, /* XXX guess */
+ &range_unknown,
&range_das16jr,
&range_das1x01_unip,
&range_das1x02_unip,
};
static comedi_lrange *das16_ai_bip_lranges[]={
- &range_unipolar10, /* XXX guess */
+ &range_unknown,
&range_das16jr,
&range_das1x01_bip,
&range_das1x02_bip,
name: "das16/jr/ctr5", // ?
},
{
- name: "das16/n1", // cio-das-m1-16.pdf ?
+ name: "das16/m1/16", // cio-das-m1-16.pdf ?
},
{
name: "das1601/12-p5",//
static int das16_cmd_test(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
{
int err=0, tmp;
+ int gain, start_chan, i;
+ int mask;
/* make sure triggers are valid */
tmp=cmd->start_src;
cmd->start_src &= TRIG_NOW;
if(!cmd->start_src || tmp!=cmd->start_src)err++;
- /* XXX cards that support burst mode could do scan_begin_src
- * TRIG_TIMER and convert_src TRIG_NOW */
tmp=cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
+ mask = TRIG_FOLLOW;
+ // if board supports burst mode
+ if(thisboard->size > 0x400)
+ mask |= TRIG_TIMER | TRIG_EXT;
+ cmd->scan_begin_src &= mask;
if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
tmp=cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ mask = TRIG_TIMER | TRIG_EXT;
+ // if board supports burst mode
+ if(thisboard->size > 0x400)
+ mask |= TRIG_NOW;
+ cmd->convert_src &= mask;
if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
tmp=cmd->scan_end_src;
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->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT &&
+ cmd->scan_begin_src != TRIG_FOLLOW) err++;
if(cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT) err++;
+ cmd->convert_src != TRIG_EXT &&
+ cmd->convert_src != TRIG_NOW) err++;
if(cmd->stop_src != TRIG_NONE &&
cmd->stop_src != TRIG_COUNT) err++;
+ // make sure scan_begin_src and convert_src dont conflict
+ if(cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_NOW) err++;
+ if(cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->convert_src != TRIG_NOW) err++;
+
if(err)return 2;
/* step 3: make sure arguments are trivially compatible */
cmd->scan_end_arg = cmd->chanlist_len;
err++;
}
+
// check against maximum frequency
+ if(cmd->scan_begin_src == TRIG_TIMER)
+ {
+ if(cmd->scan_begin_arg < thisboard->ai_speed * cmd->chanlist_len)
+ {
+ cmd->convert_arg = thisboard->ai_speed * cmd->chanlist_len;
+ err++;
+ }
+ }
if(cmd->convert_src == TRIG_TIMER)
{
if(cmd->convert_arg < thisboard->ai_speed)
}
}
- if(cmd->stop_src == TRIG_COUNT)
- {
- /* any count is allowed */
- }else
+ if(cmd->stop_src == TRIG_NONE)
{
- /* TRIG_NONE */
if(cmd->stop_arg != 0)
{
cmd->stop_arg = 0;
err++;
}
}
- // XXX check chanlist
-
+ // check channel/gain list against card's limitations
+ gain = CR_RANGE(cmd->chanlist[0]);
+ start_chan = CR_CHAN(cmd->chanlist[0]);
+ for(i = 1; i < cmd->chanlist_len; i++)
+ {
+ if(CR_CHAN(cmd->chanlist[i]) != (start_chan + i) % s->n_chan)
+ {
+ comedi_error(dev, "entries in chanlist must be consecutive channels, counting upwards\n");
+ err++;
+ }
+ if(CR_RANGE(cmd->chanlist[i]) != gain)
+ {
+ comedi_error(dev, "entries in chanlist must all have the same gain\n");
+ err++;
+ }
+ }
if(err)return 3;
// step 4: fix up arguments
+ if(cmd->scan_begin_src == TRIG_TIMER)
+ {
+ // set divisors, correct timing arguments
+ i8253_cascade_ns_to_timer_2div(devpriv->clockbase, &(devpriv->divisor1),
+ &(devpriv->divisor2), &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK);
+ }
if(cmd->convert_src == TRIG_TIMER)
{
// set divisors, correct timing arguments
/* set counter mode and counts */
cmd->convert_arg = das16_set_pacer(dev, cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
rt_printk("pacer period: %d ns\n", cmd->convert_arg);
+
/* enable counters */
- outb(0x00, dev->iobase + DAS16_PACER);
+ byte = 0;
+ /* Enable burst mode if appropriate. */
+ if(thisboard->size > 0x400)
+ {
+ if(cmd->convert_src == TRIG_NOW)
+ {
+ outb(DAS1600_BURST_VAL, dev->iobase + DAS1600_BURST);
+ }else
+ {
+ outb(0, dev->iobase + DAS1600_BURST);
+ }
+ // set burst length
+ byte |= BURST_LEN_BITS(cmd->chanlist_len);
+ }
+ outb(byte, dev->iobase + DAS16_PACER);
+
// set up dma transfer
flags = claim_dma_lock();
dev->iobase+DAS16_GAIN);
}
- /* How long should we wait for MUX to settle? */
- //udelay(5);
-
for(n=0;n<insn->n;n++){
/* trigger conversion */
outb_p(0,dev->iobase+DAS16_TRIG);
diobits = inb(dev->iobase + DAS16_DIO) & 0xf0;
printk(" diobits 0x%02x",diobits);
-
switch(diobits){
case 0x80:
printk(" das16 or das16/f");
- /* only difference is speed, so not an issue yet */
if(it->options[3])
devpriv->clockbase = 1000 / it->options[3];
else
printk(" 1MHz pacer clock\n");
}
- /* for now, don't support das1400/1600 mode */
outb(0, dev->iobase + DAS1600_CONV);
outb(0, dev->iobase + DAS1600_BURST);
outb(0, dev->iobase + DAS1600_ENABLE);
return -EINVAL;
}
+ // initialize dma
dma_chan = it->options[2];
if(dma_chan == 1 || dma_chan == 3)
{
s->subdev_flags = SDF_READABLE;
if(devpriv->ai_singleended){
s->n_chan = 16;
- s->subdev_flags |= SDF_GROUND; /* XXX ? */
+ s->subdev_flags |= SDF_GROUND;
}else{
s->n_chan = 8;
s->subdev_flags |= SDF_DIFF;
}
s->len_chanlist = 16;
- s->maxdata = (1<<thisboard->ai_nbits)-1;
+ s->maxdata = (1 << thisboard->ai_nbits) - 1;
if(devpriv->ai_unipolar){
s->range_table = das16_ai_uni_lranges[thisboard->ai_pg];
}else{
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE;
s->n_chan = 2;
- s->maxdata = (1<<thisboard->ao_nbits)-1;
- s->range_table = &range_unknown; /* XXX */
+ s->maxdata = (1 << thisboard->ao_nbits) - 1;
+ s->range_table = &range_unknown;
s->insn_write = thisboard->ao;
}else{
s->type = COMEDI_SUBD_UNUSED;
unsigned int size;
unsigned int freq;
- if(cmd.convert_src == TRIG_TIMER && cmd.scan_begin_src == TRIG_FOLLOW)
+ if(cmd.convert_src == TRIG_TIMER)
freq = 1000000000 / cmd.convert_arg;
else if(cmd.scan_begin_src == TRIG_TIMER)
freq = (1000000000 / cmd.scan_begin_arg) * cmd.chanlist_len;