From ab038864fbd81fff95a35717975ee681eb8d78a8 Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Sun, 26 Aug 2001 23:23:33 +0000 Subject: [PATCH] driver supports insn now, no command support --- comedi/Config.in | 1 + comedi/drivers/Makefile | 1 + comedi/drivers/Makefile.in | 1 + comedi/drivers/cb_pcidas64.c | 420 ++++++++++++++++++++++++++++------- 4 files changed, 340 insertions(+), 83 deletions(-) diff --git a/comedi/Config.in b/comedi/Config.in index 19345225..a0913ecd 100644 --- a/comedi/Config.in +++ b/comedi/Config.in @@ -76,6 +76,7 @@ dep_tristate 'DAS-6402 and compatibles' CONFIG_COMEDI_DAS6402 $CONFIG_COMEDI dep_tristate 'DAS-800 and compatibles' CONFIG_COMEDI_DAS800 $CONFIG_COMEDI dep_tristate 'DAS-1800 and compatibles' CONFIG_COMEDI_DAS1800 $CONFIG_COMEDI dep_tristate 'Computer Boards PCI-DAS series' CONFIG_COMEDI_CB_PCIDAS $CONFIG_COMEDI +dep_tristate 'Computer Boards PCI-DAS64xx series' CONFIG_COMEDI_CB_PCIDAS64 $CONFIG_COMEDI dep_tristate 'Computer Boards PCI-DDA series' CONFIG_COMEDI_CB_PCIDDA $CONFIG_COMEDI dep_tristate 'Generic 8255 support' CONFIG_COMEDI_8255 $CONFIG_COMEDI dep_tristate 'Quanser Consulting MultiQ-3' CONFIG_COMEDI_MULTIQ3 $CONFIG_COMEDI diff --git a/comedi/drivers/Makefile b/comedi/drivers/Makefile index c343953f..26b0378f 100644 --- a/comedi/drivers/Makefile +++ b/comedi/drivers/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_COMEDI_ADV_PCI1710) += adv_pci1710.o obj-$(CONFIG_COMEDI_APMLC_PCI230) += amplc_pci230.o obj-$(CONFIG_COMEDI_CB_PCIDAS) += cb_pcidas.o +obj-$(CONFIG_COMEDI_CB_PCIDAS64) += cb_pcidas64.o obj-$(CONFIG_COMEDI_CB_PCIDDA) += cb_pcidda.o obj-$(CONFIG_COMEDI_DAQBOARD2000) += daqboard2000.o diff --git a/comedi/drivers/Makefile.in b/comedi/drivers/Makefile.in index fe33139b..773462de 100644 --- a/comedi/drivers/Makefile.in +++ b/comedi/drivers/Makefile.in @@ -7,6 +7,7 @@ select(CONFIG_COMEDI_ADL_PCI9118 adl_pci9118.o) select(CONFIG_COMEDI_ADV_PCI1710 adv_pci1710.o) select(CONFIG_COMEDI_APMLC_PCI230 amplc_pci230.o) select(CONFIG_COMEDI_CB_PCIDAS cb_pcidas.o) +select(CONFIG_COMEDI_CB_PCIDAS64 cb_pcidas64.o) select(CONFIG_COMEDI_CB_PCIDDA cb_pcidda.o) select(CONFIG_COMEDI_DAQBOARD2000 daqboard2000.o) select(CONFIG_COMEDI_DAS08 das08.o) diff --git a/comedi/drivers/cb_pcidas64.c b/comedi/drivers/cb_pcidas64.c index 95648f0a..4456bf09 100644 --- a/comedi/drivers/cb_pcidas64.c +++ b/comedi/drivers/cb_pcidas64.c @@ -3,6 +3,8 @@ This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS 64xxx cards. + Author: Frank Mori Hess + Options: [0] - PCI bus number [1] - PCI slot number @@ -29,10 +31,14 @@ ************************************************************************ 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 @@ -71,9 +77,45 @@ TODO: #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 = { @@ -124,7 +166,109 @@ static pcidas64_board pcidas64_boards[] = 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)) @@ -154,6 +298,8 @@ typedef struct 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; /* @@ -180,16 +326,17 @@ comedi_driver driver_cb_pcidas={ 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(), @@ -228,6 +375,10 @@ static int attach(comedi_device *dev, comedi_devconfig *it) // 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++) { @@ -248,8 +399,7 @@ static int attach(comedi_device *dev, comedi_devconfig *it) } } - printk("No supported ComputerBoards/MeasurementComputing card found on " - "requested position\n"); + printk("No supported ComputerBoards/MeasurementComputing card found\n"); return -EIO; found: @@ -316,15 +466,6 @@ 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 @@ -332,33 +473,43 @@ printk("irq %i\n", dev->irq); 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 */ @@ -366,39 +517,63 @@ printk("irq %i\n", dev->irq); 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; } @@ -435,65 +610,144 @@ static int detach(comedi_device *dev) } 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; +} -- 2.26.2