This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
64xxx cards.
+ Author: Frank Mori Hess <fmhess@uiuc.edu>
+
Options:
[0] - PCI bus number
[1] - PCI slot number
************************************************************************
STATUS:
- quite experimental and unfinished
+ insn supported
TODO:
- just about everything
+ command support
+ calibration subdevice
+ user counter subdevice
+ there are a number of boards this driver could support, but does not since
+ I don't know the pci device id numbers
*/
#include <linux/kernel.h>
#define DIO_COUNTER_IOSIZE 0x29
// plx pci9080 configuration registers
+// XXX could steal plx9060 header file from kernel
+
+// devpriv->main_iobase registers
+// write-only
+#define INTR_ENABLE_REG 0x0 // interrupt enable register
+#define HW_CONFIG_REG 0x2 // hardware config register
+#define HW_CONFIG_DUMMY_BITS 0x2400 // bits that don't do anything yet but are given default values
+#define ADC_CONTROL0_REG 0x10 // adc control register 0
+#define ADC_ENABLE_BIT 0x8000 // master adc enable
+#define ADC_CONTROL1_REG 0x12 // adc control register 1
+#define SW_NOGATE_BIT 0x40 // disables software gate of adc
+#define ADC_CONVERT_REG 0x24 // initiates single conversion
+#define ADC_QUEUE_CLEAR_REG 0x26 // clears adc queue
+#define ADC_QUEUE_LOAD_REG 0x28 // loads adc queue
+#define CHAN_BITS(x) ((x) & 0x3f)
+#define GAIN_BITS(x) (((x) & 0x3) << 8) // translates range index to gain bits
+#define UNIP_BIT(x) (((x) & 0x4) << 11) // translates range index to unipolar/bipolar bit
+#define SE_BIT 0x1000 // single-ended/ differential bit
+#define QUEUE_EOS_BIT 0x8000 // queue end of scan
+#define ADC_BUFFER_CLEAR_REG 0x2a
+#define ADC_QUEUE_HIGH_REG 0x2c // high channel for internal queue, use CHAN_BITS() macro above
+#define DAC_CONTROL0_REG 0x50 // dac control register 0
+#define DAC_ENABLE_BIT 0x8000 // dac controller enable bit
+#define DAC_CONTROL1_REG 0x52 // dac control register 0
+#define DAC_RANGE_BITS(channel, range) (((range) & 0x3) << (2 * ((channel) & 0x1)))
+#define DAC_OUTPUT_ENABLE_BIT 0x80 // dac output enable bit
+#define DAC_BUFFER_CLEAR_REG 0x66 // clear dac buffer
+#define DAC_CONVERT_REG(channel) ((0x70) + (2 * ((channel) & 0x1)))
+// read-only
+#define HW_STATUS_REG 0x0
+#define ADC_BUSY_BIT 0x8
+#define HW_REVISION(x) (((x) >> 12) & 0xf)
+#define PIPE1_READ_REG 0x4
+
+// devpriv->dio_counter_iobase registers
+#define DIO_8255_OFFSET 0x0
+#define DO_REG 0x20
+#define DI_REG 0x28
-// bit in hexadecimal representation of range index that indicates unipolar input range
-#define IS_UNIPOLAR 0x4
// analog input ranges for most boards
static comedi_lrange ai_ranges =
{
ao_nchan: 2,
ao_scan_speed: 10000,
},
-// XXX supports more boards...
+ {
+ name: "pci-das6402/12", // XXX check
+ device_id: 0x1e,
+ ai_se_chans: 64,
+ ai_bits: 12,
+ ai_speed: 5000,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m1/16",
+ device_id: 0x35,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 1000,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m2/16",
+ device_id: 0x36,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 500,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m3/16",
+ device_id: 0x37,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 333,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+
+#if 0
+ {
+ name: "pci-das6402/16/jr",
+ device_id: 0 // XXX,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 5000,
+ ao_nchan: 0,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m1/16/jr",
+ device_id: 0 // XXX,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 1000,
+ ao_nchan: 0,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m2/16/jr",
+ device_id: 0 // XXX,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 500,
+ ao_nchan: 0,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m3/16/jr",
+ device_id: 0 // XXX,
+ ai_se_chans: 64,
+ ai_bits: 16,
+ ai_speed: 333,
+ ao_nchan: 0,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m1/14",
+ device_id: 0, // XXX
+ ai_se_chans: 64,
+ ai_bits: 14,
+ ai_speed: 1000,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m2/14",
+ device_id: 0, // XXX
+ ai_se_chans: 64,
+ ai_bits: 14,
+ ai_speed: 500,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+ {
+ name: "pci-das64/m3/14",
+ device_id: 0, // XXX
+ ai_se_chans: 64,
+ ai_bits: 14,
+ ai_speed: 333,
+ ao_nchan: 2,
+ ao_scan_speed: 10000,
+ },
+#endif
+
};
// Number of boards in cb_pcidas_boards
#define N_BOARDS (sizeof(pcidas64_boards) / sizeof(pcidas64_board))
unsigned int ao_divisor;
volatile unsigned int ao_count; // number of analog output samples remaining
unsigned int ao_value[2]; // remember what the analog outputs are set to, to allow readback
+ unsigned int hw_revision; // stc chip hardware revision number
+ unsigned int do_bits; // remember digital ouput levels
} pcidas64_private;
/*
static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
-static int ai_cmd(comedi_device *dev,comedi_subdevice *s);
-static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s,
- comedi_cmd *cmd);
-static int ao_cmd(comedi_device *dev,comedi_subdevice *s);
-static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);
-static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s,
- comedi_cmd *cmd);
-static void handle_interrupt(int irq, void *d, struct pt_regs *regs);
-static int ai_cancel(comedi_device *dev, comedi_subdevice *s);
-static int ao_cancel(comedi_device *dev, comedi_subdevice *s);
+//static int ai_cmd(comedi_device *dev,comedi_subdevice *s);
+//static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
+//static int ao_cmd(comedi_device *dev,comedi_subdevice *s);
+//static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num);
+//static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd);
+//static void handle_interrupt(int irq, void *d, struct pt_regs *regs);
+//static int ai_cancel(comedi_device *dev, comedi_subdevice *s);
+//static int ao_cancel(comedi_device *dev, comedi_subdevice *s);
+static int dio_callback(int dir, int port, int data, void *arg);
+static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
+static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
/*
* A convenient macro that defines init_module() and cleanup_module(),
// is it not a computer boards card?
if(pcidev->vendor != PCI_VENDOR_CB)
continue;
+#ifdef PCIDAS64_DEBUG
+ printk(" found computer boards device id 0x%x on bus %i slot %i\n",
+ pcidev->device, pcidev->bus->number, PCI_SLOT(pcidev->devfn));
+#endif
// loop through cards supported by this driver
for(index = 0; index < N_BOARDS; index++)
{
}
}
- printk("No supported ComputerBoards/MeasurementComputing card found on "
- "requested position\n");
+ printk("No supported ComputerBoards/MeasurementComputing card found\n");
return -EIO;
found:
devpriv->dio_counter_phys_iobase = dio_counter_iobase;
#endif
-#endif
-
-#ifdef PCIDAS64_DEBUG
-
-printk("plx9080 phys io addr 0x%lx\n", devpriv->plx9080_iobase);
-printk("main phys io addr 0x%lx\n", devpriv->main_iobase);
-printk("diocounter phys io addr 0x%lx\n", devpriv->dio_counter_iobase);
-printk("irq %i\n", dev->irq);
-
#endif
// remap, won't work with 2.0 kernels but who cares
devpriv->main_iobase = (unsigned long)ioremap(main_iobase, MAIN_IOSIZE);
devpriv->dio_counter_iobase = (unsigned long)ioremap(dio_counter_iobase, DIO_COUNTER_IOSIZE);
+ devpriv->hw_revision = HW_REVISION(readw(devpriv->main_iobase + HW_STATUS_REG));
+
// get irq
- if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev ))
+/* if(comedi_request_irq(pcidev->irq, handle_interrupt, SA_SHIRQ, "cb_pcidas64", dev ))
{
printk(" unable to allocate irq %d\n", pcidev->irq);
return -EINVAL;
}
dev->irq = pcidev->irq;
-
+*/
#ifdef PCIDAS64_DEBUG
-printk("plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase);
-printk("main virt io addr 0x%lx\n", devpriv->main_iobase);
-printk("diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);
-printk("irq %i\n", dev->irq);
+printk(" plx9080 phys io addr 0x%lx\n", devpriv->plx9080_phys_iobase);
+printk(" main phys io addr 0x%lx\n", devpriv->main_phys_iobase);
+printk(" diocounter phys io addr 0x%lx\n", devpriv->dio_counter_phys_iobase);
+printk(" irq %i\n", dev->irq);
+
+printk(" plx9080 virt io addr 0x%lx\n", devpriv->plx9080_iobase);
+printk(" main virt io addr 0x%lx\n", devpriv->main_iobase);
+printk(" diocounter virt io addr 0x%lx\n", devpriv->dio_counter_iobase);
+printk(" irq %i\n", dev->irq);
+
+printk(" stc hardware revision %i\n", devpriv->hw_revision);
#endif
+
/*
* Allocate the subdevice structures.
*/
- dev->n_subdevices = 5;
+ dev->n_subdevices = 7;
if(alloc_subdevices(dev)<0)
return -ENOMEM;
s = dev->subdevices + 0;
/* analog input subdevice */
- dev->read_subdev = s;
+// 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->len_chanlist = 8092;
s->maxdata = (1 << thisboard->ai_bits) - 1;
s->range_table = &ai_ranges;
-// s->insn_read = ai_rinsn;
+ s->insn_read = ai_rinsn;
// s->do_cmd = ai_cmd;
// s->do_cmdtest = ai_cmdtest;
// s->cancel = ai_cancel;
/* analog output subdevice */
s = dev->subdevices + 1;
- dev->write_subdev = s;
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND;
- s->n_chan = thisboard->ao_nchan;
- // analog out resolution is the same as analog input resolution, so use ai_bits
- s->maxdata = (1 << thisboard->ai_bits) - 1;
- s->range_table = &ao_ranges;
-// s->insn_read = ao_readback_insn;
-// s->insn_write = ao_winsn;
-// s->do_cmdtest = ao_cmdtest;
-// s->do_cmd = ao_cmd;
- s->len_chanlist = thisboard->ao_nchan;
-// s->cancel = ao_cancel;
+ if(thisboard->ao_nchan)
+ {
+ // dev->write_subdev = s;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITEABLE | SDF_GROUND;
+ s->n_chan = thisboard->ao_nchan;
+ // analog out resolution is the same as analog input resolution, so use ai_bits
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &ao_ranges;
+ s->insn_read = ao_readback_insn;
+ s->insn_write = ao_winsn;
+ // s->do_cmdtest = ao_cmdtest;
+ // s->do_cmd = ao_cmd;
+ // s->len_chanlist = thisboard->ao_nchan;
+ // s->cancel = ao_cancel;
+ } else
+ {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
// digital input
s = dev->subdevices + 2;
- s->type = COMEDI_SUBD_UNUSED;
+ 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 = di_rbits;
// digital output
s = dev->subdevices + 3;
- s->type = COMEDI_SUBD_UNUSED;
+ s->type=COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITEABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = do_wbits;
/* 8255 */
s = dev->subdevices + 4;
+ subdev_8255_init(dev, s, dio_callback,
+ (void*) (devpriv->dio_counter_iobase + DIO_8255_OFFSET));
+
+ // user counter subd XXX
+ s = dev->subdevices + 5;
+ s->type = COMEDI_SUBD_UNUSED;
+
+ // calibration subd XXX
+ s = dev->subdevices + 6;
s->type = COMEDI_SUBD_UNUSED;
-// subdev_8255_init(dev, s, );
return 0;
}
}
if(dev->irq)
comedi_free_irq(dev->irq, dev);
-// if(dev->subdevices)
-// subdev_8255_cleanup(dev,dev->subdevices + 4);
+ if(dev->subdevices)
+ subdev_8255_cleanup(dev,dev->subdevices + 4);
return 0;
}
static int ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
{
- return -1;
+ unsigned int bits, n, i;
+ const int timeout = 1000;
+
+ // disable card's interrupt sources
+ writew(0, devpriv->main_iobase + INTR_ENABLE_REG);
+
+ // use internal queue
+ writew(HW_CONFIG_DUMMY_BITS, devpriv->main_iobase + HW_CONFIG_REG);
+
+ /* disable pacing, triggering, etc */
+ writew(ADC_ENABLE_BIT, devpriv->main_iobase + ADC_CONTROL0_REG);
+ writew(0, devpriv->main_iobase + ADC_CONTROL1_REG);
+
+ // load internal queue
+ bits = 0;
+ // set channel
+ bits |= CHAN_BITS(CR_CHAN(insn->chanspec));
+ // set gain
+ bits |= GAIN_BITS(CR_RANGE(insn->chanspec));
+ // set unipolar / bipolar
+ bits |= UNIP_BIT(CR_RANGE(insn->chanspec));
+ // set single-ended / differential
+ if(CR_AREF(insn->chanspec) != AREF_DIFF)
+ bits |= SE_BIT;
+ // set stop channel
+ writew(CHAN_BITS(CR_CHAN(insn->chanspec)), devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
+ // set start channel, and rest of settings
+ writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+
+ // clear adc buffer
+ writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
+
+ for(n = 0; n < insn->n; n++)
+ {
+ /* trigger conversion */
+ writew(0, devpriv->main_iobase + ADC_CONVERT_REG);
+
+ // wait for data
+ for(i = 0; i < timeout; i++)
+ {
+ if(!(readw(devpriv->main_iobase + HW_STATUS_REG) & ADC_BUSY_BIT))
+ break;
+ }
+ if(i == timeout)
+ {
+ comedi_error(dev, " analog input read insn timed out");
+ return -ETIME;
+ }
+ data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);
+ }
+
+ return n;
}
-static int ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
+static int ao_winsn(comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data)
{
- return -1;
+ int chan = CR_CHAN(insn->chanspec);
+ int range = CR_RANGE(insn->chanspec);
+ unsigned int bits;
+
+ // do some initializing
+ writew(DAC_ENABLE_BIT, devpriv->main_iobase + DAC_CONTROL0_REG);
+
+ // set range
+ bits = DAC_OUTPUT_ENABLE_BIT;
+ bits |= DAC_RANGE_BITS(chan, range);
+ writew(bits, devpriv->main_iobase + DAC_CONTROL1_REG);
+
+ // clear buffer
+ writew(0, devpriv->main_iobase + DAC_BUFFER_CLEAR_REG);
+
+ // write to channel
+ writew(data[0], devpriv->main_iobase + DAC_CONVERT_REG(chan));
+
+ // remember output value
+ devpriv->ao_value[chan] = data[0];
+
+ return 1;
}
static int ao_readback_insn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
{
- return -1;
-}
+ data[0] = devpriv->ao_value[CR_CHAN(insn->chanspec)];
-static int ai_cmd(comedi_device *dev,comedi_subdevice *s)
-{
- return -1;
+ return 1;
}
-static int ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
+static int dio_callback(int dir, int port, int data, void *arg)
{
- return -1;
-}
+ unsigned long iobase = (int)arg;
-static int ao_cmd(comedi_device *dev,comedi_subdevice *s)
-{
- return -1;
+ if(dir)
+ {
+ writeb(data, iobase + port);
+ return 0;
+ }else
+ {
+ return readb(iobase + port);
+ }
}
-static int ao_inttrig(comedi_device *dev, comedi_subdevice *subdev, unsigned int trig_num)
+static int di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
{
- return -1;
-}
+ lsampl_t bits;
-static int ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd)
-{
- return -1;
-}
+ bits = readb(devpriv->dio_counter_iobase + DI_REG);
+ bits &= 0xf;
+ data[1] = bits;
+ data[0] = 0;
-static void handle_interrupt(int irq, void *d, struct pt_regs *regs)
-{
+ return 2;
}
-static int ai_cancel(comedi_device *dev, comedi_subdevice *s)
+static int do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
{
- return -1;
-}
+ lsampl_t wbits;
-static int ao_cancel(comedi_device *dev, comedi_subdevice *s)
-{
- return -1;
-}
+ data[0] &= 0xf;
+ wbits = devpriv->do_bits;
+ // zero bits we are going to change
+ wbits &= ~data[0];
+ // set new bits
+ wbits |= data[0] & data[1];
+ devpriv->do_bits = wbits;
+
+ writeb(devpriv->do_bits, devpriv->dio_counter_iobase + DO_REG);
+ data[1] = wbits;
+ return 2;
+}