Copyright (C) 2001 Ivan Martinez <ivanmr@altavista.com>, with
valuable help from David Schleef, Frank Mori Hess, and the rest of
the Comedi developers comunity.
+ Copyright (C) 2001 Frank Mori Hess <fmhess@uiuc.edu>
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1997-8 David A. Schleef <ds@stm.lbl.gov>
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+************************************************************************
+
+Asynchronous analog input support added by Frank Mori Hess.
+
*/
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <linux/comedidev.h>
+#include "8253.h"
+#include "8255.h"
#define PCI_VENDOR_CB 0x1307 // PCI vendor number of ComputerBoards
-#define N_BOARDS 10 // Number of boards in cb_pcidas_boards
+#define TIMER_BASE 100 // 10MHz master clock
/* PCI-DAS base addresses */
-#define CONT_STAT_BADRINDEX 1
- // CONTROL/STATUS is devpriv->pci_dev->base_address[1]
-#define ADC_FIFO_BADRINDEX 2
- // ADC DATA, FIFO CLEAR is devpriv->pci_dev->base_address[2]
+
+// indices of base address regions
+#define S5933_BADRINDEX 0
+#define CONT_STAT_BADRINDEX 1
+#define ADC_FIFO_BADRINDEX 2
+#define PACER_BADRINDEX 3
+#define AO_BADRINDEX 4
+// sizes of io regions
+#define S5933_SIZE 256
+#define CONT_STAT_SIZE 10
+#define ADC_FIFO_SIZE 4
+#define PACER_SIZE 12
+#define AO_SIZE 4
+
+// amcc s5933 pci configuration registers
+#define INTCSR 0x38 // interrupt control/status
+#define INTLN 0x3c // read interrupt line
+#define INTPIN 0x3d // read interrupt pin
+
/* Control/Status registers */
#define INT_ADCFIFO 0 // INTERRUPT / ADC FIFO register
-#define NOINT 0020300 // Clears and disables interrupts
+#define INT_EOS 0x1 // interrupt end of scan
+#define INT_FHF 0x2 // interrupt fifo half full
+#define INT_FNE 0x3 // interrupt fifo not empty
+#define INTE 0x4 // interrupt enable
+#define EOACL 0x40 // write-clear for end of acquisition interrupt status
+#define EOAI 0x40 // read end of acq. interrupt status
+#define INTCL 0x80 // write-clear for EOS/FHF/FNE interrupt status
+#define INT 0x80 // read interrupt status
+#define ADHFI 0x400 // read half-full interrupt status
+#define ADNEI 0x800 // read fifo not empty interrupt latch status
+#define ADNE 0x1000 // fifo not empty (realtime, not latched) status
+#define ADFLCL 0x2000 // write-clear for fifo full status
+#define LADFUL 0x2000 // read fifo overflow
#define ADCMUX_CONT 2 // ADC CHANNEL MUX AND CONTROL register
-#define RANGE10V 0000000 // 10V
-#define RANGE5V 0000400 // 5V
-#define RANGE2V5 0001000 // 2.5V
-#define RANGE1V25 0001400 // 1.25V
-#define UNIP 0004000 // Analog front-end unipolar for range
-#define BIP 0000000 // Analog front-end bipolar for range
-#define SE 0002000 // Inputs in single-ended mode
-#define DIFF 0000000 // Inputs in differential mode
-#define SWPACER 0000000 // Pacer source is SW convert
-#define EOC 0040000 // End of conversion
+#define BEGIN_SCAN(x) ((x) & 0xf)
+#define END_SCAN(x) (((x) & 0xf) << 4)
+#define GAIN_BITS(x) (((x) & 0x3) << 8)
+#define PACER_INT 0x1000
+#define UNIP 0004000 // Analog front-end unipolar for range
+#define SE 0002000 // Inputs in single-ended mode
+#define EOC 0040000 // End of conversion
#define TRIG_CONTSTAT 4 // TRIGGER CONTROL/STATUS register
+#define SW_TRIGGER 0x1 // software start trigger
+#define EXT_TRIGGER 0x2 // external start trigger
+#define CLK_SRC 0x2000 // use internal 10MHz master clock
#define CALIBRATION 6 // CALIBRATION register
/* ADC data, FIFO clear registers */
#define ADCDATA 0 // ADC DATA register
-#define BEGSWCONV 0 // Begin software conversion
-
#define ADCFIFOCLR 2 // ADC FIFO CLEAR
-#define CLEARFIFO 0 // Clear fifo
-#define BIPRANGES 4 // Number of bipolar ranges in cb_pcidas_ranges
+// pacer, counter, dio registers
+#define ADC8254 0
+#define DIO_8255 4
+// bit in hexadecimal representation of range index that indicates bipolar range
+#define IS_BIPOLAR 0x4
comedi_lrange cb_pcidas_ranges =
{
8,
}
};
-/*
- * Board descriptions for two imaginary boards. Describing the
- * boards in this way is optional, and completely driver-dependent.
- * Some drivers use arrays such as this, other do not.
- */
+comedi_lrange cb_pcidas_alt_ranges =
+{
+ 8,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
typedef struct cb_pcidas_board_struct
{
char *name;
unsigned short device_id;
int ai_se_chans; // Inputs in single-ended mode
int ai_diff_chans; // Inputs in differential mode
- int ai_bits;
+ int ai_bits; // analog input resolution
+ int ai_speed; // fastest conversion period in ns
+ // number of analog outputs
+ int ao_nchan;
comedi_lrange *ranges;
} cb_pcidas_board;
static cb_pcidas_board cb_pcidas_boards[] =
ai_se_chans: 16,
ai_diff_chans: 8,
ai_bits: 16,
+ ai_speed: 5000,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 16,
ai_diff_chans: 8,
ai_bits: 12,
+ ai_speed: 3030,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 16,
ai_diff_chans: 8,
ai_bits: 12,
+ ai_speed: 3030,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 16,
ai_diff_chans: 8,
ai_bits: 12,
+ ai_speed: 3030,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 64,
ai_diff_chans: 8,
ai_bits: 16,
+ ai_speed: 5000,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 64,
ai_diff_chans: 32,
ai_bits: 16,
+ ai_speed: 5000,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 64,
ai_diff_chans: 32,
ai_bits: 16,
+ ai_speed: 1000,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 64,
ai_diff_chans: 32,
ai_bits: 16,
+ ai_speed: 500,
ranges: &cb_pcidas_ranges,
},
{
ai_se_chans: 64,
ai_diff_chans: 32,
ai_bits: 16,
+ ai_speed: 333,
ranges: &cb_pcidas_ranges,
},
{
name: "pci-das1000",
- status: 2,
+ status: 1,
device_id: 0x4C,
ai_se_chans: 16,
ai_diff_chans: 8,
ai_bits: 12,
+ ai_speed: 4000,
+ ao_nchan: 0,
+ ranges: &cb_pcidas_ranges,
+ },
+ {
+ name: "pci-das1001",
+ status: 1,
+ device_id: 0x1a,
+ ai_se_chans: 16,
+ ai_diff_chans: 8,
+ ai_bits: 12,
+ ai_speed: 6666,
+ ao_nchan: 2,
+ ranges: &cb_pcidas_alt_ranges,
+ },
+ {
+ name: "pci-das1002",
+ status: 1,
+ device_id: 0x1b,
+ ai_se_chans: 16,
+ ai_diff_chans: 8,
+ ai_bits: 12,
+ ai_speed: 6666,
+ ao_nchan: 2,
ranges: &cb_pcidas_ranges,
},
};
+// Number of boards in cb_pcidas_boards
+#define N_BOARDS (sizeof(cb_pcidas_boards) / sizeof(cb_pcidas_board))
/*
* Useful for shorthand access to the particular board structure
/* would be useful for a PCI device */
struct pci_dev *pci_dev;
+ // base addresses
+ unsigned long s5933_config;
unsigned long control_status;
unsigned long adc_fifo;
-
+ unsigned long pacer_counter_dio;
+ unsigned long ao_registers;
+ // divisors of master clock for pacing
+ unsigned int divisor1;
+ unsigned int divisor2;
+ unsigned int count; //number of samples remaining
} cb_pcidas_private;
/*
static int cb_pcidas_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
//static int cb_pcidas_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-//static int cb_pcidas_ai_cmd(comedi_device *dev,comedi_subdevice *s);
+static int cb_pcidas_ai_cmd(comedi_device *dev,comedi_subdevice *s);
static int cb_pcidas_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
comedi_cmd *cmd);
-static int cb_pcidas_ns_to_timer(unsigned int *ns,int round);
-
+static void cb_pcidas_interrupt(int irq, void *d, struct pt_regs *regs);
+static int cb_pcidas_cancel(comedi_device *dev, comedi_subdevice *s);
+void cb_pcidas_load_counters(comedi_device *dev, unsigned int *ns, int round_flags);
/*
* Attach is called by the Comedi core to configure the driver
* for a particular board.
comedi_subdevice *s;
struct pci_dev* pcidev;
int index;
+ unsigned long s5933_config, control_status, adc_fifo,
+ pacer_counter_dio, ao_registers;
+ int err;
printk("comedi%d: cb_pcidas: ",dev->minor);
-
+
/*
* Allocate the private structure area.
*/
goto found;
}
}
- printk("Not a supported ComputerBoards card on requested position\n");
+ printk("Not a supported ComputerBoards card on requested position\n");
return -EIO;
found:
* their base address.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
- devpriv->control_status =
+ s5933_config =
+ devpriv->pci_dev->base_address[S5933_BADRINDEX] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ control_status =
devpriv->pci_dev->base_address[CONT_STAT_BADRINDEX] &
- PCI_BASE_ADDRESS_IO_MASK;
- devpriv->adc_fifo =
+ PCI_BASE_ADDRESS_IO_MASK;
+ adc_fifo =
devpriv->pci_dev->base_address[ADC_FIFO_BADRINDEX] &
- PCI_BASE_ADDRESS_IO_MASK;
+ PCI_BASE_ADDRESS_IO_MASK;
+ pacer_counter_dio =
+ devpriv->pci_dev->base_address[PACER_BADRINDEX] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ ao_registers =
+ devpriv->pci_dev->base_address[AO_BADRINDEX] &
+ PCI_BASE_ADDRESS_IO_MASK;
#else
- devpriv->control_status =
+ s5933_config =
+ devpriv->pci_dev->resource[S5933_BADRINDEX] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ control_status =
devpriv->pci_dev->resource[CONT_STAT_BADRINDEX].start &
- PCI_BASE_ADDRESS_IO_MASK;
- devpriv->adc_fifo =
+ PCI_BASE_ADDRESS_IO_MASK;
+ adc_fifo =
devpriv->pci_dev->resource[ADC_FIFO_BADRINDEX].start &
- PCI_BASE_ADDRESS_IO_MASK;
+ PCI_BASE_ADDRESS_IO_MASK;
+ pacer_counter_dio =
+ devpriv->pci_dev->resource[PACER_BADRINDEX] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ ao_registers =
+ devpriv->pci_dev->resource[AO_BADRINDEX] &
+ PCI_BASE_ADDRESS_IO_MASK;
#endif
+ // reserve io ports
+ err = 0;
+ if(check_region(s5933_config, S5933_SIZE) < 0)
+ err++;
+ if(check_region(control_status, CONT_STAT_SIZE) < 0)
+ err++;
+ if(check_region(adc_fifo, ADC_FIFO_SIZE) < 0)
+ err++;
+ if(check_region(pacer_counter_dio, PACER_SIZE) < 0)
+ err++;
+ if(thisboard->ao_nchan)
+ if(check_region(ao_registers, AO_SIZE) < 0)
+ err++;
+ if(err)
+ {
+ printk(" I/O port conflict\n");
+ return -EIO;
+ }
+ request_region(s5933_config, S5933_SIZE, "cb_pcidas");
+ devpriv->s5933_config = s5933_config;
+ request_region(control_status, CONT_STAT_SIZE, "cb_pcidas");
+ devpriv->control_status = control_status;
+ request_region(adc_fifo, ADC_FIFO_SIZE, "cb_pcidas");
+ devpriv->adc_fifo = adc_fifo;
+ request_region(pacer_counter_dio, PACER_SIZE, "cb_pcidas");
+ devpriv->pacer_counter_dio = pacer_counter_dio;
+ if(thisboard->ao_nchan)
+ {
+ request_region(ao_registers, AO_SIZE, "cb_pcidas");
+ devpriv->ao_registers = ao_registers;
+ }
+
+ // get irq
+ if(comedi_request_irq(devpriv->pci_dev->irq, cb_pcidas_interrupt, 0, "cb_pcidas", dev ))
+ {
+ printk(" unable to allocate irq %d\n", devpriv->pci_dev->irq);
+ return -EINVAL;
+ }
+ dev->irq = devpriv->pci_dev->irq;
+
/*
* Warn about the status of the driver.
*/
/*
* Allocate the subdevice structures.
*/
- dev->n_subdevices=1;
+ dev->n_subdevices = 3;
if(alloc_subdevices(dev)<0)
return -ENOMEM;
s = dev->subdevices + 0;
/* analog input subdevice */
+ dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;
/* WARNING: Number of inputs in differential mode is ignored */
s->n_chan = thisboard->ai_se_chans;
s->maxdata = (1 << thisboard->ai_bits) - 1;
s->range_table = thisboard->ranges;
- s->insn_read = &cb_pcidas_ai_rinsn;
-// s->do_cmd = &cb_pcidas_ai_cmd;
- s->do_cmdtest = &cb_pcidas_ai_cmdtest;
-
+ s->insn_read = cb_pcidas_ai_rinsn;
+ s->do_cmd = cb_pcidas_ai_cmd;
+ s->do_cmdtest = cb_pcidas_ai_cmdtest;
+ s->cancel = cb_pcidas_cancel;
+
+ /* analog output subdevice */
+ s = dev->subdevices + 1;
+ if(thisboard->ao_nchan)
+ {
+ // XXX todo: support analog output
+ s->type = COMEDI_SUBD_UNUSED;
+ }else
+ {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8255 */
+ s = dev->subdevices + 2;
+ subdev_8255_init(dev, s, NULL,
+ (void *)(devpriv->pacer_counter_dio + DIO_8255));
+
return 1;
}
/*
* _detach is called to deconfigure a device. It should deallocate
- * resources.
+ * resources.
* This function is also called when _attach() fails, so it should be
* careful not to release resources that were not necessarily
* allocated by _attach(). dev->private and dev->subdevices are
{
printk("comedi%d: cb_pcidas: remove\n",dev->minor);
+ if(devpriv)
+ {
+ if(devpriv->s5933_config)
+ release_region(devpriv->s5933_config, S5933_SIZE);
+ if(devpriv->control_status)
+ release_region(devpriv->control_status, CONT_STAT_SIZE);
+ if(devpriv->adc_fifo)
+ release_region(devpriv->adc_fifo, ADC_FIFO_SIZE);
+ if(devpriv->pacer_counter_dio)
+ release_region(devpriv->pacer_counter_dio, PACER_SIZE);
+ if(devpriv->ao_registers)
+ release_region(devpriv->ao_registers, AO_SIZE);
+ }
+ if(dev->irq)
+ comedi_free_irq(dev->irq, dev);
+ if(dev->subdevices)
+ subdev_8255_cleanup(dev,dev->subdevices + 2);
+
return 0;
}
static int cb_pcidas_ai_rinsn(comedi_device *dev, comedi_subdevice *s,
comedi_insn *insn, lsampl_t *data)
{
- int n;
+ int n,i;
unsigned int d;
unsigned int command;
+ static const int timeout = 10000;
/* a typical programming sequence */
/* inputs mode */
- if (CR_AREF(insn->chanspec) == AREF_DIFF)
- command = DIFF;
- else
- command = SE;
+ command = 0;
+ if (CR_AREF(insn->chanspec) != AREF_DIFF)
+ command |= SE;
/* input signals range */
- if (CR_RANGE(insn->chanspec) < BIPRANGES)
- command |= BIP | (CR_RANGE(insn->chanspec) << 8);
+ if (CR_RANGE(insn->chanspec) & IS_BIPOLAR)
+ command |= CR_RANGE(insn->chanspec) << 8;
else
- command |= UNIP | ((CR_RANGE(insn->chanspec) - BIPRANGES) << 8);
-
+ command |= UNIP | GAIN_BITS(CR_RANGE(insn->chanspec));
+
/* write channel to multiplexer */
command |= CR_CHAN(insn->chanspec) | (CR_CHAN(insn->chanspec) << 4);
-
+
outw_p(command, devpriv->control_status + ADCMUX_CONT);
/* wait for mux to settle */
for (n = 0; n < insn->n; n++)
{
/* clear fifo */
- outw(CLEARFIFO, devpriv->adc_fifo + ADCFIFOCLR);
+ outw(0, devpriv->adc_fifo + ADCFIFOCLR);
/* trigger conversion */
- outw(BEGSWCONV, devpriv->adc_fifo + ADCDATA);
+ outw(0, devpriv->adc_fifo + ADCDATA);
/* wait for conversion to end */
/* return -ETIMEDOUT if there is a timeout */
- while (!(inw(devpriv->control_status + ADCMUX_CONT) & EOC));
+ for(i = 0; i < timeout; i++)
+ {
+ if (inw(devpriv->control_status + ADCMUX_CONT) & EOC)
+ break;
+ }
+ if(i == timeout)
+ return -ETIMEDOUT;
/* read data */
d = inw(devpriv->adc_fifo + ADCDATA);
return n;
}
-/*
- * I will program this later... ;-)
- *
-static int cb_pcidas_ai_cmd(comedi_device *dev,comedi_subdevice *s)
-{
- printk("cb_pcidas_ai_cmd\n");
- printk("subdev: %d\n", cmd->subdev);
- printk("flags: %d\n", cmd->flags);
- printk("start_src: %d\n", cmd->start_src);
- printk("start_arg: %d\n", cmd->start_arg);
- printk("scan_begin_src: %d\n", cmd->scan_begin_src);
- printk("convert_src: %d\n", cmd->convert_src);
- printk("convert_arg: %d\n", cmd->convert_arg);
- printk("scan_end_src: %d\n", cmd->scan_end_src);
- printk("scan_end_arg: %d\n", cmd->scan_end_arg);
- printk("stop_src: %d\n", cmd->stop_src);
- printk("stop_arg: %d\n", cmd->stop_arg);
- printk("chanlist_len: %d\n", cmd->chanlist_len);
-}
-*/
-
static int cb_pcidas_ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
comedi_cmd *cmd)
{
err++;
tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
+ cmd->scan_begin_src &= TRIG_FOLLOW; //TRIG_TIMER | TRIG_EXT; XXX
if(!cmd->scan_begin_src && tmp != cmd->scan_begin_src)
err++;
tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
+ cmd->convert_src &= TRIG_TIMER; // TRIG_NOW | TRIG_EXT; XXX
if(!cmd->convert_src && tmp != cmd->convert_src)
err++;
err++;
tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
+ cmd->stop_src &= TRIG_COUNT;// | TRIG_NONE; XXX
if(!cmd->stop_src && tmp != cmd->stop_src)
err++;
/* 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)
+ if(cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->scan_begin_src != TRIG_EXT)
+ err++;
+ if(cmd->convert_src != TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT &&
+ cmd->convert_src != TRIG_NOW)
err++;
- if(cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
+ if(cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
err++;
- if(cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT)
+ // can't have both scans and conversion timed
+ if(cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER)
err++;
if(err) return 2;
err++;
}
-#define MAX_SPEED 10000 /* in nanoseconds */
-#define MIN_SPEED 1000000000 /* in nanoseconds */
-
if (cmd->scan_begin_src == TRIG_TIMER)
{
- if (cmd->scan_begin_arg < MAX_SPEED)
+ if (cmd->scan_begin_arg < thisboard->ai_speed * cmd->chanlist_len)
{
- cmd->scan_begin_arg = MAX_SPEED;
- err++;
- }
- if (cmd->scan_begin_arg > MIN_SPEED)
- {
- cmd->scan_begin_arg = MIN_SPEED;
- err++;
- }
- }
- else
- {
- /* external trigger */
- /* should be level/edge, hi/lo specification here */
- /* should specify multiple external triggers */
- if (cmd->scan_begin_arg > 9)
- {
- cmd->scan_begin_arg = 9;
+ cmd->scan_begin_arg = thisboard->ai_speed * cmd->chanlist_len;
err++;
}
}
if (cmd->convert_src == TRIG_TIMER)
{
- if (cmd->convert_arg < MAX_SPEED)
- {
- cmd->convert_arg = MAX_SPEED;
- err++;
- }
- if (cmd->convert_arg>MIN_SPEED)
- {
- cmd->convert_arg = MIN_SPEED;
- err++;
- }
- }
- else
- {
- /* external trigger */
- /* see above */
- if (cmd->convert_arg > 9)
+ if (cmd->convert_arg < thisboard->ai_speed)
{
- cmd->convert_arg = 9;
+ cmd->convert_arg = thisboard->ai_speed;
err++;
}
}
cmd->scan_end_arg = cmd->chanlist_len;
err++;
}
- if(cmd->stop_src == TRIG_COUNT)
- {
- if(cmd->stop_arg > 0x00ffffff)
- {
- cmd->stop_arg = 0x00ffffff;
- err++;
- }
- }
- else
+ if(cmd->stop_src == TRIG_NONE)
{
/* TRIG_NONE */
if (cmd->stop_arg != 0)
err++;
}
}
+ // XXX check chanlist
if(err)return 3;
if(cmd->scan_begin_src == TRIG_TIMER)
{
tmp = cmd->scan_begin_arg;
- cb_pcidas_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &(devpriv->divisor1), &(devpriv->divisor2),
+ &(cmd->scan_begin_arg), cmd->flags & TRIG_ROUND_MASK);
if(tmp != cmd->scan_begin_arg)
err++;
}
if(cmd->convert_src == TRIG_TIMER)
{
tmp=cmd->convert_arg;
- cb_pcidas_ns_to_timer(&cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
+ 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++;
if(cmd->scan_begin_src == TRIG_TIMER &&
return 0;
}
-/* This function doesn't require a particular form, this is just
- * what happens to be used in some of the drivers. It should
- * convert ns nanoseconds to a counter value suitable for programming
- * the device. Also, it should adjust ns so that it cooresponds to
- * the actual time that the device will use. */
-static int cb_pcidas_ns_to_timer(unsigned int *ns,int round)
+static int cb_pcidas_ai_cmd(comedi_device *dev,comedi_subdevice *s)
{
- /* trivial timer */
- return *ns;
+ comedi_async *async = s->async;
+ comedi_cmd *cmd = &async->cmd;
+ int bits;
+
+ // initialize before settings pacer source and count values
+ outw(0, devpriv->control_status + TRIG_CONTSTAT);
+ // clear fifo
+ outw(0, devpriv->adc_fifo + ADCFIFOCLR);
+
+ // set mux limits and gain
+ bits = BEGIN_SCAN(CR_CHAN(cmd->chanlist[0])) |
+ END_SCAN(CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1])) |
+ GAIN_BITS(CR_RANGE(cmd->chanlist[0]));
+ // set unipolar/bipolar
+ if((CR_RANGE(cmd->chanlist[0]) & IS_BIPOLAR) == 0)
+ bits |= UNIP;
+ // set singleended/differential
+ if(CR_AREF(cmd->chanlist[0]) != AREF_DIFF)
+ bits |= SE;
+ // set pacer source to internal
+ bits |= PACER_INT;
+ outw(bits, devpriv->control_status + ADCMUX_CONT);
+
+ // load counters
+ if(cmd->convert_src == TRIG_TIMER)
+ cb_pcidas_load_counters(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
+
+ async->events = 0;
+
+ // clear interrupts
+ outw(EOACL | INTCL | ADFLCL, devpriv->control_status + INT_ADCFIFO);
+
+ // enable interrupts
+ bits = INTE;
+ if(cmd->flags & TRIG_WAKE_EOS)
+ {
+ bits |= INT_FNE; //interrupt fifo not empty
+ // for burst mode we will want INT_EOS
+ }else
+ {
+ bits |= INT_FHF; //interrupt fifo half full
+ }
+ outw(bits, devpriv->control_status + INT_ADCFIFO);
+ // set pacer master clock to be internal 10MHz, and set software start trigger
+ outw(CLK_SRC | SW_TRIGGER, devpriv->control_status + TRIG_CONTSTAT);
+
+ return 0;
}
+static void cb_pcidas_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+ comedi_device *dev = (comedi_device*) d;
+ comedi_subdevice *s = dev->read_subdev;
+ comedi_async *async;
+ int status;
+ static const int half_fifo = 512;
+ sampl_t data[half_fifo];
+ int i;
+ static const int timeout = 10000;
+
+ if(dev->attached == 0)
+ {
+ comedi_error(dev, "premature interrupt");
+ return;
+ }
+ async = s->async;
+ status = inw(devpriv->control_status + INT_ADCFIFO);
+ if((status & (INT | EOAI)) == 0)
+ {
+ comedi_error(dev, "spurious interrupt");
+ return;
+ }
+ // if fifo half-full
+ if(status & ADHFI)
+ {
+ insw(devpriv->adc_fifo, data, half_fifo);
+ for(i = 0; i < half_fifo; i++)
+ {
+ comedi_buf_put(async, data[i]);
+ if(async->cmd.stop_src == TRIG_COUNT)
+ {
+ if(--devpriv->count == 0)
+ { /* end of acquisition */
+ cb_pcidas_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ }
+ async->events |= COMEDI_CB_BLOCK;
+ // clear half-full interrupt latch
+ outw(INTCL, devpriv->control_status + INT_ADCFIFO);
+ // else if fifo not empty
+ }else if(status & ADNEI)
+ {
+ for(i = 0; i < timeout; i++)
+ {
+ data[0] = inw(devpriv->adc_fifo);
+ comedi_buf_put(async, data[0]);
+ if(async->cmd.stop_src == TRIG_COUNT)
+ {
+ if(--devpriv->count == 0)
+ { /* end of acquisition */
+ cb_pcidas_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ // break if fifo is empty
+ if((ADNE & inw(devpriv->control_status + INT_ADCFIFO)) == 0)
+ break;
+ }
+ async->events |= COMEDI_CB_BLOCK;
+ // clear not-empty interrupt latch
+ outw(INTCL, devpriv->control_status + INT_ADCFIFO);
+ }else if(status & ADNEI)
+ {
+ comedi_error(dev, "bug! encountered end of aquisition interrupt?");
+ // clear EOA interrupt latch
+ outw(EOACL, devpriv->control_status + INT_ADCFIFO);
+ }
+ //check for fifo overflow
+ if(status & LADFUL)
+ {
+ comedi_error(dev, "fifo overflow");
+ // clear overflow interrupt latch
+ outw(ADFLCL, devpriv->control_status + INT_ADCFIFO);
+ cb_pcidas_cancel(dev, s);
+ async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
+ }
+
+ comedi_event(dev, s, async->events);
+ async->events = 0;
+
+ return;
+}
+
+static int cb_pcidas_cancel(comedi_device *dev, comedi_subdevice *s)
+{
+ // disable interrupts
+ outw(0, devpriv->control_status + INT_ADCFIFO);
+ // software pacer source
+ outw(0, devpriv->control_status + ADCMUX_CONT);
+ // disable start trigger source
+ outw(0, devpriv->control_status + TRIG_CONTSTAT);
+
+ return 0;
+}
+
+void cb_pcidas_load_counters(comedi_device *dev, unsigned int *ns, int rounding_flags)
+{
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
+ &(devpriv->divisor2), ns, rounding_flags & TRIG_ROUND_MASK);
+
+ /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
+ i8254_load(devpriv->pacer_counter_dio + ADC8254, 1, devpriv->divisor1, 2);
+ i8254_load(devpriv->pacer_counter_dio + ADC8254, 2, devpriv->divisor2, 2);
+}
+
+
/*
* A convenient macro that defines init_module() and cleanup_module(),
* as necessary.