From: David Schleef Date: Tue, 28 Aug 2001 23:23:44 +0000 (+0000) Subject: Update from Allan. X-Git-Tag: r0_7_61~132 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=af023b0c8b55816520ae8962661a152c74669ca6;p=comedi.git Update from Allan. --- diff --git a/comedi/drivers/amplc_pci230.c b/comedi/drivers/amplc_pci230.c index acddf4f6..a8367855 100644 --- a/comedi/drivers/amplc_pci230.c +++ b/comedi/drivers/amplc_pci230.c @@ -1,4 +1,4 @@ -/* + /* comedi/drivers/amplc_pci230.c Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards. @@ -20,12 +20,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -************************************************************************ - -TODO: -fix pci autodetection, add support for bus/slot config options - */ #include @@ -45,17 +39,15 @@ fix pci autodetection, add support for bus/slot config options #include "8253.h" #include "8255.h" - /* PCI230 PCI configuration register information */ - #define PCI_VENDOR_ID_AMPLICON 0x14dc #define PCI_DEVICE_ID_PCI230 0x0000 #define PCI_DEVICE_ID_PCI260 0x0006 #define PCI230_IO1_SIZE 32 /* Size of I/O space 1 */ -#define PCI230_IO2_SIZE 32 /* Size of I/O space 2 - Check this!!! only appears to be 8 16bit words = 16 bytes long*/ +#define PCI230_IO2_SIZE 16 /* Size of I/O space 2 */ -/* PCI230 I/O SPACE 1 REGISTERS */ +/* PCI230 i/o space 1 registers. */ #define PCI230_PPI_X_A 0x00 /* User PPI port A */ #define PCI230_PPI_X_B 0x01 /* User PPI port B */ #define PCI230_PPI_X_C 0x02 /* User PPI port C */ @@ -68,7 +60,7 @@ fix pci autodetection, add support for bus/slot config options #define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration Register */ #define PCI230_INT_SCE 0x1E /* ISR Interrupt source mask register/Interrupt status */ -/* PCI230 I/O SPACE 2 REGISTERS */ +/* PCI230 i/o space 2 registers. */ #define PCI230_DACCON 0x00 #define PCI230_DACOUT1 0x02 #define PCI230_DACOUT2 0x04 @@ -78,46 +70,45 @@ fix pci autodetection, add support for bus/slot config options #define PCI230_ADCEN 0x0C #define PCI230_ADCG 0x0E -/* CONVERTOR RELATED CONSTANTS */ -#define PCI230_DAC_SETTLE 5 /* Analogue output settling time in µS (DAC itself is 1µS nominally) */ -#define PCI230_ADC_SETTLE 1 /* Analogue input settling time in µS (ADC itself is 1.6µS nominally but we poll anyway) */ -#define PCI230_MUX_SETTLE 1 /* ADC MUX settling time in µS - guess */ +/* Convertor related constants. */ +#define PCI230_DAC_SETTLE 5 /* Analogue output settling time in µs (DAC itself is 1µs nominally). */ +#define PCI230_ADC_SETTLE 1 /* Analogue input settling time in µs (ADC itself is 1.6µs nominally but we poll anyway). */ +#define PCI230_MUX_SETTLE 10 /* ADC MUX settling time in µS - 10µs for se, 20µs de. */ -/* DACCON VALUES */ +/* DACCON values. */ #define PCI230_DAC_BUSY_BIT 1 #define PCI230_DAC_BIP_BIT 0 -/* ADCCON WRITE VALUES */ -#define PCI230_ADC_TRIG_NONE 0 -#define PCI230_ADC_TRIG_SW 1 -#define PCI230_ADC_TRIG_EXTP 2 -#define PCI230_ADC_TRIG_EXTN 3 -#define PCI230_ADC_TRIG_Z2CT0 4 -#define PCI230_ADC_TRIG_Z2CT1 5 -#define PCI230_ADC_TRIG_Z2CT2 6 -#define PCI230_ADC_IR_UNI (0<<3) /* Input range unipolar */ -#define PCI230_ADC_IR_BIP (1<<3) /* Input range bipolar */ -#define PCI230_ADC_IM_SE (0<<4) /* Input mode single ended */ -#define PCI230_ADC_IM_DIF (1<<4) /* Input mode differential */ -#define PCI230_ADC_FIFO_EN (1<<8) +/* ADCCON write values. */ +#define PCI230_ADC_TRIG_NONE 0 +#define PCI230_ADC_TRIG_SW 1 +#define PCI230_ADC_TRIG_EXTP 2 +#define PCI230_ADC_TRIG_EXTN 3 +#define PCI230_ADC_TRIG_Z2CT0 4 +#define PCI230_ADC_TRIG_Z2CT1 5 +#define PCI230_ADC_TRIG_Z2CT2 6 +#define PCI230_ADC_IR_UNI (0<<3) /* Input range unipolar */ +#define PCI230_ADC_IR_BIP (1<<3) /* Input range bipolar */ +#define PCI230_ADC_IM_SE (0<<4) /* Input mode single ended */ +#define PCI230_ADC_IM_DIF (1<<4) /* Input mode differential */ +#define PCI230_ADC_FIFO_EN (1<<8) #define PCI230_ADC_INT_FIFO_EMPTY 0 #define PCI230_ADC_INT_FIFO_NEMPTY (1<<9) #define PCI230_ADC_INT_FIFO_NHALF (2<<9) #define PCI230_ADC_INT_FIFO_HALF (3<<9) #define PCI230_ADC_INT_FIFO_NFULL (4<<9) #define PCI230_ADC_INT_FIFO_FULL (5<<9) -#define PCI230_ADC_FIFO_RESET (1<<12) -#define PCI230_ADC_GLOB_RESET (1<<13) -#define PCI230_ADC_CONV 0xffff /* Value to write to ADCDATA to trigger ADC conversion in sotware trigger mode */ -#define PCI230_ADC_SW_CHAN(n) ((n)<<12) /* ADCCON software trigger channel selection - bit shift channel into upper 4 bit nibble of word */ +#define PCI230_ADC_FIFO_RESET (1<<12) +#define PCI230_ADC_GLOB_RESET (1<<13) +#define PCI230_ADC_CONV 0xffff /* Value to write to ADCDATA to trigger ADC conversion in sotware trigger mode */ -/* ADCCON READ VALUES */ +/* ADCCON read values. */ #define PCI230_ADC_BUSY_BIT 15 #define PCI230_ADC_FIFO_EMPTY (1<<12) #define PCI230_ADC_FIFO_FULL (1<<13) #define PCI230_ADC_FIFO_HALF (1<<14) -/* GROUP Z CLOCK CONFIGURATION REGISTER VALUES */ +/* Group Z clock configuration register values. */ #define PCI230_ZCLK_CT0 0 #define PCI230_ZCLK_CT1 8 #define PCI230_ZCLK_CT2 16 @@ -131,21 +122,19 @@ fix pci autodetection, add support for bus/slot config options #define PCI230_ZCLK_SRC_OUTNM1 6 /* The output of the preceding counter/timer channel (OUT n-1). */ #define PCI230_ZCLK_SRC_EXTCLK 7 /* The dedicated external clock input for the group (X1/X2, Y1/Y2, Z1/Z2). */ -#define PCI230_TIMEBASE_10MHZ 100 /* 10MHz is 100ns. */ +#define PCI230_TIMEBASE_10MHZ 100 /* 10MHz => 100ns. */ -/* INTERRUPT ENABLES/STATUS REGISTER VALUES */ +/* Interrupt enables/status register values. */ #define PCI230_INT_DISABLE 0 #define PCI230_INT_PPI_C0 1 #define PCI230_INT_PPI_C3 2 -#define PCI230_INT_ADC_DAC 4 -#define PCI230_INT_ZCLK_CT0 32 +#define PCI230_INT_ADC 4 +#define PCI230_INT_ZCLK_CT1 32 -#define PCI230_TEST_BIT(val, n) ((val>>n)&1) /* Assumes you number bit with zero offset, ie. 0-15 */ +#define PCI230_TEST_BIT(val, n) ((val>>n)&1) /* Assumes bits numbered with zero offset, ie. 0-15 */ /* - * 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. + * Board descriptions for the two boards supported. */ typedef struct pci230_board_struct{ char *name; @@ -175,7 +164,7 @@ pci230_board pci230_boards[] = { ai_bits: 12, have_ao: 0, ao_chans: 0, - ao_bits: 0, + ao_bits: 0, have_dio: 0, }, }; @@ -189,17 +178,21 @@ pci230_board pci230_boards[] = { several hardware drivers keep similar information in this structure, feel free to suggest moving the variable to the comedi_device struct. */ struct pci230_private{ -// int data; struct pci_dev *pci_dev; - lsampl_t ao_readback[2]; /* Used for AO readback */ - unsigned int pci_iobase; /* PCI230's I/O space 1 */ + lsampl_t ao_readback[2]; /* Used for AO readback */ + unsigned int pci_iobase; /* PCI230's I/O space 1 */ /* Divisors for 8254 counter/timer. */ unsigned int divisor0; unsigned int divisor1; unsigned int divisor2; - unsigned int int_en; /* Interrupt Enables bits. */ - volatile unsigned int count; /* Number of samples remaining. */ - unsigned int bipolar; /* Set if bipolar range so we know to mangle it in interrupt handler. */ + unsigned int int_en; /* Interrupt enables bits. */ + unsigned int ai_count; /* Number of analogue input samples remaining. */ + unsigned int ao_count; /* Number of analogue output samples remaining. */ + unsigned int ai_stop; /* Flag set when cmd->stop_src == TRIG_NONE - user chooses to stop continuous conversion by cancelation. */ + unsigned int ao_stop; /* Flag set when cmd->stop_src == TRIG_NONE - user chooses to stop continuous conversion by cancelation. */ + unsigned int ai_bipolar; /* Set if bipolar input range so we know to mangle it. */ + unsigned int ao_bipolar; /* Set if bipolar output range so we know to mangle it. */ + unsigned int ier; /* Copy of interrupt enables/status register. */ }; #define devpriv ((struct pci230_private *)dev->private) @@ -240,21 +233,54 @@ COMEDI_INITCLEANUP(driver_amplc_pci230); static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); static int pci230_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); static int pci230_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int pci230_ct_insn_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static int pci230_ct_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data); +static void pci230_ns_to_timer(unsigned int *ns,int round); +static void pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round); +static void pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round); +static void pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round); +static void pci230_cancel_ct0(comedi_device *dev); +static void pci230_cancel_ct1(comedi_device *dev); +static void pci230_cancel_ct2(comedi_device *dev); +static void pci230_interrupt(int irq, void *d, struct pt_regs *regs); +static int pci230_ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd); +static int pci230_ao_cmd(comedi_device *dev, comedi_subdevice *s); +static int pci230_ao_cancel(comedi_device *dev, comedi_subdevice *s); +static void pci230_handle_ao(comedi_device *dev, comedi_subdevice *s); static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd); static int pci230_ai_cmd(comedi_device *dev, comedi_subdevice *s); -static int pci230_ns_to_timer(unsigned int *ns,int round); -static int pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round); -static int pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round); -static int pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round); -static void pci230_interrupt(int irq, void *d, struct pt_regs *regs); -static int pci230__cancel(comedi_device *dev, comedi_subdevice *s); +static int pci230_ai_cancel(comedi_device *dev, comedi_subdevice *s); +static void pci230_handle_ai(comedi_device *dev, comedi_subdevice *s); +static void pci230_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s); +static void pci230_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s); -/* print out string and val as bits - up to 16 */ -static void printb(int val) { - printk("\t%d%d%d%d ",PCI230_TEST_BIT(val, 15), PCI230_TEST_BIT(val, 14), PCI230_TEST_BIT(val, 13), PCI230_TEST_BIT(val, 12)); - printk("%d%d%d%d ", PCI230_TEST_BIT(val, 11), PCI230_TEST_BIT(val, 10), PCI230_TEST_BIT(val, 9), PCI230_TEST_BIT(val, 8)); - printk("%d%d%d%d ", PCI230_TEST_BIT(val, 7), PCI230_TEST_BIT(val, 6), PCI230_TEST_BIT(val, 5), PCI230_TEST_BIT(val, 4)); - printk("%d%d%d%d\n",PCI230_TEST_BIT(val, 3), PCI230_TEST_BIT(val, 2), PCI230_TEST_BIT(val, 1), PCI230_TEST_BIT(val, 0)); +static sampl_t pci230_ai_read(comedi_device *dev) +{ + /* Read sample. */ + sampl_t data = (sampl_t) inw(dev->iobase + PCI230_ADCDATA); + + /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */ + data = data>>4; + + /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ + if (devpriv->ai_bipolar) { + data ^= 1<<(thisboard->ai_bits-1); + } + return data; +} + +static void pci230_ao_write(comedi_device *dev, sampl_t data, int chan) +{ + /* If a bipolar range was specified, mangle it (straight binary->twos complement). */ + if (devpriv->ao_bipolar) { + data ^= 1<<(thisboard->ai_bits-1); + } + + /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */ + data = data<<4; + + /* Write data. */ + outw((unsigned int) data, dev->iobase + (((chan) == 0) ? PCI230_DACOUT1 : PCI230_DACOUT2)); } /* @@ -266,12 +292,12 @@ static void printb(int val) { static int pci230_attach(comedi_device *dev,comedi_devconfig *it) { comedi_subdevice *s; - int pci_iobase, iobase = 0; /* PCI230's I/O spaces 1 and 2 */ + int pci_iobase, iobase = 0; /* PCI230's I/O spaces 1 and 2 respectively. */ struct pci_dev *pci_dev; - int i=0,ret; + int i=0,irq_hdl; printk("comedi%d: amplc_pci230\n",dev->minor); - + /* Find card */ pci_for_each_dev(pci_dev){ if(pci_dev->vendor != PCI_VENDOR_ID_AMPLICON) @@ -289,20 +315,21 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it) } dev->board_ptr = pci230_boards+i; - /* Read base addressses of the PCI230's two I/O regions from PCI configuration register */ + /* Read base addressses of the PCI230's two I/O regions from PCI configuration register. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) pci_iobase = pci_dev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK; iobase = pci_dev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK; #else - if(pci_enable_device(pci_dev)) - return -EIO; - pci_iobase = pci_dev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK; - iobase = pci_dev->resource[3].start & PCI_BASE_ADDRESS_IO_MASK; + if(pci_enable_device(pci_dev)<0)return -EIO; + + pci_iobase = pci_dev->resource[2].start; + iobase = pci_dev->resource[3].start; #endif printk("comedi%d: amplc_pci230: I/O region 1 0x%04x I/O region 2 0x%04x\n",dev->minor, pci_iobase, iobase); - /* Allocate the private structure area using alloc_private() (macro defined in comedidev.h.) */ + /* Allocate the private structure area using alloc_private(). + * Macro defined in comedidev.h - memsets struct fields to 0. */ if((alloc_private(dev,sizeof(struct pci230_private)))<0) return -ENOMEM; devpriv->pci_dev = pci_dev; @@ -329,18 +356,26 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it) */ dev->board_name = thisboard->name; + /* Register the interrupt handler. */ + irq_hdl = comedi_request_irq(devpriv->pci_dev->irq, pci230_interrupt, 0, "PCI230", dev ); + if(irq_hdl<0) { + printk("comedi%d: amplc_pci230: unable to register irq, commands will not be available %d\n", dev->minor, devpriv->pci_dev->irq); + } + else { + dev->irq = devpriv->pci_dev->irq; + printk("comedi%d: amplc_pci230: registered irq %d\n", dev->minor, devpriv->pci_dev->irq); + } /* * Allocate the subdevice structures. alloc_subdevice() is a * convenient macro defined in comedidev.h. It relies on * n_subdevices being set correctly. */ - dev->n_subdevices=3; + dev->n_subdevices=4; if(alloc_subdevices(dev)<0) return -ENOMEM; s=dev->subdevices+0; - dev->read_subdev=s; /* analog input subdevice */ s->type=COMEDI_SUBD_AI; s->subdev_flags=SDF_READABLE; @@ -348,9 +383,14 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it) s->maxdata=(1<ai_bits)-1; s->range_table=&pci230_ai_range; s->insn_read = &pci230_ai_rinsn; - s->do_cmd = &pci230_ai_cmd; s->len_chanlist = thisboard->ai_chans; - s->do_cmdtest = &pci230_ai_cmdtest; + /* Only register commands if the interrupt handler is installed. */ + if(irq_hdl==0) { + dev->read_subdev=s; + s->do_cmd = &pci230_ai_cmd; + s->do_cmdtest = &pci230_ai_cmdtest; + s->cancel = pci230_ai_cancel; + } s=dev->subdevices+1; /* analog output subdevice */ @@ -361,6 +401,14 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it) s->range_table=&pci230_ao_range; s->insn_write = &pci230_ao_winsn; s->insn_read = &pci230_ao_rinsn; + s->len_chanlist = thisboard->ao_chans; + /* Only register commands if the interrupt handler is installed. */ + if(irq_hdl==0) { + dev->write_subdev=s; + s->do_cmd = &pci230_ao_cmd; + s->do_cmdtest = &pci230_ao_cmdtest; + s->cancel = pci230_ao_cancel; + } s=dev->subdevices+2; /* digital i/o subdevice */ @@ -370,15 +418,15 @@ static int pci230_attach(comedi_device *dev,comedi_devconfig *it) s->type = COMEDI_SUBD_UNUSED; } - /* Register the interrupt handler. */ - ret = comedi_request_irq(devpriv->pci_dev->irq, pci230_interrupt, 0, "PCI230", dev ); - if(ret<0) - { - printk("comedi%d: amplc_pci230: unable to register irq %d\n", dev->minor, devpriv->pci_dev->irq); - return ret; - } - dev->irq = devpriv->pci_dev->irq; - printk("comedi%d: amplc_pci230: registered irq %d\n", dev->minor, devpriv->pci_dev->irq); + s=dev->subdevices+3; + /* timer subdevice */ + s->type=COMEDI_SUBD_TIMER; + s->subdev_flags=SDF_READABLE; + s->n_chan=1; + s->maxdata=0xffff; + s->range_table=&range_digital; + s->insn_config = pci230_ct_insn_config; + s->insn_read = &pci230_ct_rinsn; printk("attached\n"); @@ -417,48 +465,58 @@ static int pci230_detach(comedi_device *dev) } /* - * "instructions" read/write data in "one-shot" or "software-triggered" - * mode. - */ + * COMEDI_SUBD_AI instruction; + */ static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) { int n,i; - int chan, range; - unsigned int d; + int chan, range, aref; unsigned int status; unsigned int adccon, adcen, adcg; - unsigned int bipolar; /* Unpack channel and range. */ chan = CR_CHAN(insn->chanspec); range = CR_RANGE(insn->chanspec); - - printk("\ncomedi%d: amplc_pci230::pci230_ai_rinsn() chan %d range %d\n",dev->minor, chan, range); + aref = CR_AREF(insn->chanspec); /* If bit 2 of range unset, range is referring to bipolar element in range table */ - bipolar = !PCI230_TEST_BIT(range, 2); - adccon = PCI230_ADC_IM_SE | PCI230_ADC_TRIG_SW | PCI230_ADC_SW_CHAN(chan); - if (bipolar) { - adccon = adccon | PCI230_ADC_IR_BIP; - adcg = range<<(chan-chan%2); + adccon = PCI230_ADC_TRIG_SW | PCI230_ADC_FIFO_RESET; + devpriv->ai_bipolar = !PCI230_TEST_BIT(range, 2); + if (aref==AREF_DIFF) { + /* Differential. */ + adcen = 3<<2*chan; + adccon |= PCI230_ADC_IM_DIF; + if (devpriv->ai_bipolar) { + adccon |= PCI230_ADC_IR_BIP; + adcg = range<<(2*chan-2*chan%2); + } + else { + adccon |= PCI230_ADC_IR_UNI; + adcg = ((range&(~4))+1)<<(2*chan-2*chan%2); + } } else { - adccon = adccon | PCI230_ADC_IR_UNI; - adcg = ((range&(~4))+1)<<(chan-chan%2); + /* Single ended. */ + adcen = 1<ai_bipolar) { + adccon |= PCI230_ADC_IR_BIP; + adcg = range<<(chan-chan%2); + } + else { + adccon |= PCI230_ADC_IR_UNI; + adcg = ((range&(~4))+1)<<(chan-chan%2); + } } - adcen = 1<iobase + PCI230_ADCCON); - printk("comedi%d: amplc_pci230::pci230_ai_rinsn() wrote PCI230_ADCCON",dev->minor); printb(adccon); /* Enable only this channel in the scan list - otherwise by default we'll get one sample from each channel. */ outw_p(adcen, dev->iobase + PCI230_ADCEN); - printk("comedi%d: amplc_pci230::pci230_ai_rinsn() wrote PCI230_ADCEN", dev->minor); printb(adcen); /* Set gain for channel. */ outw_p(adcg, dev->iobase + PCI230_ADCG); - printk("comedi%d: amplc_pci230::pci230_ai_rinsn() wrote PCI230_ADCG", dev->minor); printb(adcg); + + /* Specify uni/bip, se/diff, s/w conversion, and reset FIFO (even though we're not using it - MEV says so). */ + outw_p(adccon, dev->iobase + PCI230_ADCCON); /* Wait for mux to settle */ udelay(PCI230_MUX_SETTLE); @@ -472,7 +530,6 @@ static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *i /* wait for conversion to end */ for(i=0;iiobase + PCI230_ADCCON); - printk("comedi%d: amplc_pci230::pci230_ai_rinsn() read PCI230_ADCCON",dev->minor); printb(status); if(PCI230_TEST_BIT(status, PCI230_ADC_BUSY_BIT))break; } if(i==TIMEOUT){ @@ -483,31 +540,107 @@ static int pci230_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *i } /* read data */ - d = inw(dev->iobase + PCI230_ADCDATA); - printk("comedi%d: amplc_pci230::pci230_ai_rinsn() read PCI230_ADCDATA 0x%04x\n",dev->minor, d); + data[n] = pci230_ai_read(dev); + } - /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */ - d = d>>4; + /* return the number of samples read/written */ + return n; +} - /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ - if (bipolar) { - d ^= 1<<(thisboard->ai_bits-1); +/* + * COMEDI_SUBD_AO instructions; + */ +static int pci230_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + int i; + int chan, range; + + /* Unpack channel and range. */ + chan = CR_CHAN(insn->chanspec); + range = CR_RANGE(insn->chanspec); + + /* Set range - see analogue output range table; 0 => unipolar 10V, 1 => bipolar +/-10V range scale */ + devpriv->ao_bipolar = PCI230_TEST_BIT(range, PCI230_DAC_BIP_BIT); + outw(range, dev->iobase + PCI230_DACCON); + + /* Writing a list of values to an AO channel is probably not + * very useful, but that's how the interface is defined. */ + for(i=0;in;i++){ + /* Store the value to be written to the DAC in our pci230_private struct before mangling it. */ + devpriv->ao_readback[chan] = data[i]; + + /* Write value to DAC. */ + pci230_ao_write(dev, data[i], chan); + +#if 0 + /* XXX screw the user. Only do this if the board gets upset if you don't */ + /* If we're writing more than one sample, wait for output to settle between successive writes */ + if (insn->n > 1) { + udelay(PCI230_DAC_SETTLE); } - data[n] = d; +#endif } /* return the number of samples read/written */ - return n; + return i; } -static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, +/* AO subdevices should have a read insn as well as a write insn. + * Usually this means copying a value stored in devpriv. */ +static int pci230_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + + for(i=0;in;i++) + data[i] = devpriv->ao_readback[chan]; + + return i; +} + +/* + * COMEDI_SUBD_TIMER instructions; + * + * insn_config allows user to start and stop counter/timer 2 (SK1 pin 21). + * Period specified in ns. + * + * rinsn returns counter/timer's actual period in ns. + */ +static int pci230_ct_insn_config(comedi_device *dev,comedi_subdevice *s, + comedi_insn *insn,lsampl_t *data) +{ + unsigned int ns; + + if(insn->n!=1)return -EINVAL; + + ns = data[0]; + if (ns == 0) { + //Stop counter/timer 2. + pci230_cancel_ct2(dev); + } + else { + //Start conter/timer 2 with period ns. + pci230_z2_ct2(dev, &ns, TRIG_ROUND_MASK); + } + + return 1; +} + +static int pci230_ct_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +{ + if(insn->n!=1)return -EINVAL; + + /* Return the actual period set in ns. */ + data[0] = PCI230_TIMEBASE_10MHZ*devpriv->divisor1*devpriv->divisor2; + return 1; +} + +static int pci230_ao_cmdtest(comedi_device *dev,comedi_subdevice *s, comedi_cmd *cmd) { int err=0; int tmp; - printk("comedi%d: amplc_pci230::pci230_ai_cmdtest()\n",dev->minor); - /* cmdtest tests a particular command to see if it is valid. * Using the cmdtest ioctl, a user can create a valid cmd * and then have it executes by the cmd ioctl. @@ -520,15 +653,15 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, * if this fails. */ tmp=cmd->start_src; - cmd->start_src &= TRIG_NOW | TRIG_EXT; + cmd->start_src &= TRIG_INT; if(!cmd->start_src || tmp!=cmd->start_src)err++; tmp=cmd->scan_begin_src; - cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT; + cmd->scan_begin_src &= TRIG_TIMER; 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_NOW; if(!cmd->convert_src || tmp!=cmd->convert_src)err++; tmp=cmd->scan_end_src; @@ -545,18 +678,9 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, * "source conflict" returned by comedilib to user mode process * if this fails. */ - if(cmd->start_src!=TRIG_NOW && - cmd->start_src!=TRIG_EXT)err++; - if(cmd->scan_begin_src!=TRIG_TIMER && - cmd->scan_begin_src!=TRIG_EXT)err++; - if(cmd->convert_src!=TRIG_TIMER && - cmd->convert_src!=TRIG_EXT)err++; if(cmd->stop_src!=TRIG_COUNT && cmd->stop_src!=TRIG_NONE)err++; - /* XXX Should check here if we aren't able to have multiple - * sources as TRIG_EXT */ - if(err)return 2; /* Step 3: make sure arguments are trivially compatible. @@ -568,28 +692,167 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, err++; } -#define MAX_SPEED 10000 /* in nanoseconds */ -#define MIN_SPEED 1000000000 /* in nanoseconds */ +#define MAX_SPEED 3205 /* 3205ns => 312kHz */ +#define MIN_SPEED 4294967295u /* 4294967295ns = 4.29s - Comedi limit due to unsigned int cmd. Driver limit = 2^32 (2 cascaded 16bit counters) * 100ns (default 10MHz onboard clock) = 429s */ if(cmd->scan_begin_src==TRIG_TIMER){ - if(cmd->scan_begin_argscan_begin_arg=MAX_SPEED; + if(cmd->scan_begin_srcscan_begin_src=MAX_SPEED; err++; } - if(cmd->scan_begin_arg>MIN_SPEED){ - cmd->scan_begin_arg=MIN_SPEED; + if(cmd->scan_begin_src>MIN_SPEED){ + cmd->scan_begin_src=MIN_SPEED; err++; } - }else{ - /* external trigger */ - /* should be level/edge, hi/lo specification here */ - /* should specify multiple external triggers */ - /* XXX Do you really have 10 available triggering channels? */ - if(cmd->scan_begin_arg>9){ - cmd->scan_begin_arg=9; + } + + if(cmd->scan_end_arg!=cmd->chanlist_len){ + cmd->scan_end_arg=cmd->chanlist_len; + err++; + } + if(cmd->stop_src==TRIG_NONE){ + /* TRIG_NONE */ + if(cmd->stop_arg!=0){ + cmd->stop_arg=0; err++; } } + + if(err)return 3; + + /* Step 4: fix up any arguments. + * "argument conflict" returned by comedilib to user mode process + * if this fails. */ + + if(cmd->scan_begin_src==TRIG_TIMER){ + tmp=cmd->scan_begin_arg; + pci230_ns_to_timer(&cmd->scan_begin_arg,cmd->flags&TRIG_ROUND_MASK); + if(tmp!=cmd->scan_begin_arg)err++; + } + + if(err)return 4; + + return 0; +} + +static int pci230_ao_inttrig(comedi_device *dev,comedi_subdevice *s, + unsigned int x) +{ + if(x!=0)return -EINVAL; + + /* Enable DAC interrupt. */ + devpriv->ier |= PCI230_INT_ZCLK_CT1; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); + + s->async->inttrig=NULL; + + return 1; +} + +static int pci230_ao_cmd(comedi_device *dev,comedi_subdevice *s) +{ + int range; + + /* Get the command. */ + comedi_async *async = s->async; + comedi_cmd *cmd = &async->cmd; + + /* Calculate number of conversions required. */ + if(cmd->stop_src == TRIG_COUNT) { + devpriv->ao_count = cmd->stop_arg * cmd->chanlist_len; + devpriv->ao_stop = 0; + } + else { + /* TRIG_NONE, user calls cancel. */ + devpriv->ao_count = 0; + devpriv->ao_stop = 1; + } + + /* Disable DAC interrupt. */ + devpriv->ier &= ~PCI230_INT_ZCLK_CT1; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); + + /* Set range - see analogue output range table; 0 => unipolar 10V, 1 => bipolar +/-10V range scale */ + range = CR_RANGE(cmd->chanlist[0]); + devpriv->ao_bipolar = PCI230_TEST_BIT(range, PCI230_DAC_BIP_BIT); + outw(range, dev->iobase + PCI230_DACCON); + + /* Set the counter timers to the specified sampling frequency. + * TODO - when Comedi supports concurrent commands, this must be + * changed; using ct0 and ct1 for DAC will screw up ADC pacer + * which uses ct2 and ct0. Change to only use ct1 for DAC? + */ + pci230_z2_ct1(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); /* cmd->convert_arg is sampling period in ns */ + + s->async->inttrig=pci230_ao_inttrig; + + return 0; +} + +static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, + comedi_cmd *cmd) +{ + int err=0; + int tmp; + + /* cmdtest tests a particular command to see if it is valid. + * Using the cmdtest ioctl, a user can create a valid cmd + * and then have it executes by the cmd ioctl. + * + * cmdtest returns 1,2,3,4 or 0, depending on which tests + * the command passes. */ + + /* Step 1: make sure trigger sources are trivially valid. + * "invalid source" returned by comedilib to user mode process + * if this fails. */ + + tmp=cmd->start_src; + cmd->start_src &= TRIG_NOW; + if(!cmd->start_src || tmp!=cmd->start_src)err++; + + tmp=cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_FOLLOW; + if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++; + + tmp=cmd->convert_src; + cmd->convert_src &= TRIG_TIMER | TRIG_EXT; + if(!cmd->convert_src || tmp!=cmd->convert_src)err++; + + tmp=cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++; + + tmp=cmd->stop_src; + cmd->stop_src &= TRIG_COUNT | TRIG_NONE; + if(!cmd->stop_src || tmp!=cmd->stop_src)err++; + + if(err)return 1; + + /* Step 2: make sure trigger sources are unique and mutually compatible + * "source conflict" returned by comedilib to user mode process + * if this fails. */ + + if(cmd->start_src!=TRIG_NOW)err++; + if(cmd->scan_begin_src!=TRIG_FOLLOW)err++; + if(cmd->convert_src!=TRIG_TIMER && + cmd->convert_src!=TRIG_EXT)err++; + if(cmd->stop_src!=TRIG_COUNT && + cmd->stop_src!=TRIG_NONE)err++; + + if(err)return 2; + + /* Step 3: make sure arguments are trivially compatible. + * "invalid argument" returned by comedilib to user mode process + * if this fails. */ + + if(cmd->start_arg!=0){ + cmd->start_arg=0; + err++; + } + +#define MAX_SPEED 3205 /* 3205ns => 312kHz max sampling - claimed max for single channel; should really be fn(nchans) */ +#define MIN_SPEED 4294967295u /* 4294967295ns = 4.29s - Comedi limit due to unsigned int cmd. Driver limit = 2^32 (2 cascaded 16bit counters) * 100ns (default 10MHz onboard clock) = 429s */ + if(cmd->convert_src==TRIG_TIMER){ if(cmd->convert_argconvert_arg=MAX_SPEED; @@ -601,9 +864,10 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, } }else{ /* external trigger */ - /* see above */ - if(cmd->convert_arg>9){ - cmd->convert_arg=9; + /* convert_arg == 0 => trigger on -ve edge. */ + /* convert_arg == 1 => trigger on +ve edge. */ + if(cmd->convert_arg>1){ + cmd->convert_arg=1; /* Default to trigger on +ve edge. */ err++; } } @@ -612,12 +876,7 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, 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){ cmd->stop_arg=0; @@ -631,20 +890,10 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, * "argument conflict" returned by comedilib to user mode process * if this fails. */ - if(cmd->scan_begin_src==TRIG_TIMER){ - tmp=cmd->scan_begin_arg; - pci230_ns_to_timer(&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; pci230_ns_to_timer(&cmd->convert_arg,cmd->flags&TRIG_ROUND_MASK); if(tmp!=cmd->convert_arg)err++; - if(cmd->scan_begin_src==TRIG_TIMER && - cmd->scan_begin_argconvert_arg*cmd->scan_end_arg){ - cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg; - err++; - } } if(err)return 4; @@ -654,71 +903,69 @@ static int pci230_ai_cmdtest(comedi_device *dev,comedi_subdevice *s, static int pci230_ai_cmd(comedi_device *dev,comedi_subdevice *s) { - int i, chan, range, aref; + int i, chan, range, diff; unsigned int adccon, adcen, adcg; - + /* Get the command. */ comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; - /* Print the command */ - printk("comedi%d: amplc_pci230::pci230_ai_cmd()\n",dev->minor); - printk("\tflags \t\t %d\n", cmd->flags); - printk("\tstart \t\t src %d arg %d\n", cmd->start_src, cmd->start_arg); - printk("\tscan_begin \t src %d arg %d\n", cmd->scan_begin_src, cmd->scan_begin_arg); - printk("\tconvert \t src %d arg %d\n", cmd->convert_src, cmd->convert_arg); - printk("\tscan_end \t src %d arg %d\n", cmd->scan_end_src, cmd->scan_end_arg); - printk("\tstop \t\t src %d arg %d\n", cmd->stop_src, cmd->stop_arg); - for (chan = 0; chan < cmd->chanlist_len; chan++) { - printk("\tchannel %d\t range %d\t aref %d\n", CR_CHAN(cmd->chanlist[chan]), CR_RANGE(cmd->chanlist[chan]), CR_AREF(cmd->chanlist[chan])); - } - - if(!dev->irq) - { - /* XXX Instead of checking here, just don't set do_cmd in - * _attach() */ - comedi_error(dev, "no irq assigned for PCI230, cannot do hardware conversions"); - return -1; - } - /* Calculate number of conversions required. */ if(cmd->stop_src == TRIG_COUNT) { - devpriv->count = cmd->stop_arg * cmd->chanlist_len; - printk("comedi%d: amplc_pci230::pci230_ai_cmd() total number of conversions %d\n",dev->minor, devpriv->count); + devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len; + devpriv->ai_stop = 0; } else { - devpriv->count = 0; + /* TRIG_NONE, user calls cancel. */ + devpriv->ai_count = 0; + devpriv->ai_stop = 1; } - + /* Steps; - * - Disable board interrupts. - * - Reset FIFO, specify uni/bip, se/diff, and start conversion source to none. + * - Disable ADC interrupts. + * - Enable and reset FIFO, specify uni/bip, se/diff, and start conversion source to none. * - Set channel scan list. * - Set channel gains. - * - Set the counter timers to the specified sampling frequency. * - Enable conversion complete interrupt. - * - Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 1. + * - Set the counter timers to the specified sampling frequency. + * - Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 0. */ - /* Disable interrupts. */ - outb(PCI230_INT_DISABLE, devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_INT_SCE",dev->minor); printb(PCI230_INT_DISABLE); + /* Disable ADC interrupt. */ + devpriv->ier &= ~PCI230_INT_ADC; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); + + if (CR_AREF(cmd->chanlist[0])==AREF_DIFF) { + /* Differential - all channels must be differential. */ + diff = 1; + adccon = PCI230_ADC_IM_DIF; + } + else { + /* Single ended - all channels must be single-ended. */ + diff = 0; + adccon = PCI230_ADC_IM_SE; + } - adccon = PCI230_ADC_FIFO_RESET | PCI230_ADC_IM_SE | PCI230_ADC_TRIG_NONE; + adccon |= PCI230_ADC_FIFO_RESET | PCI230_ADC_FIFO_EN; adcg = 0; adcen = 0; /* If bit 2 of range unset, range is referring to bipolar element in range table */ - /* XXX set range? */ range = CR_RANGE(cmd->chanlist[0]); - devpriv->bipolar = !PCI230_TEST_BIT(range, 2); - if (devpriv->bipolar) { + devpriv->ai_bipolar = !PCI230_TEST_BIT(range, 2); + if (devpriv->ai_bipolar) { adccon |= PCI230_ADC_IR_BIP; for (i = 0; i < cmd->chanlist_len; i++) { chan = CR_CHAN(cmd->chanlist[i]); range = CR_RANGE(cmd->chanlist[i]); - adcg |= range<<(chan-chan%2); - adcen |= 1<chanlist_len; i++) { chan = CR_CHAN(cmd->chanlist[i]); range = CR_RANGE(cmd->chanlist[i]); - adcg |= ((range&(~4))+1)<<(chan-chan%2); - adcen |= 1<iobase + PCI230_ADCCON); - printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCCON",dev->minor); printb(adccon); + /* Enable and reset FIFO, specify FIFO trigger level full, specify uni/bip, se/diff, and start conversion source to none. */ + outw(adccon | PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON); /* Set channel scan list. */ - outw_p(adcen, dev->iobase + PCI230_ADCEN); - printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCEN", dev->minor); printb(adcen); + outw(adcen, dev->iobase + PCI230_ADCEN); /* Set channel gains. */ - outw_p(adcg, dev->iobase + PCI230_ADCG); - printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCG", dev->minor); printb(adcg); + outw(adcg, dev->iobase + PCI230_ADCG); - /* Set the counter timers to the specified sampling frequency. */ - pci230_z2_ct1(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); + /* Enable ADC (conversion complete) interrupt. */ + devpriv->ier |= PCI230_INT_ADC; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); - /* Enable conversion complete interrupt. */ - outb(PCI230_INT_ADC_DAC, devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_INT_SCE",dev->minor); printb(PCI230_INT_ADC_DAC); + /* Set start conversion source. */ + if(cmd->convert_src == TRIG_TIMER) { + /* Onboard counter/timer 0. */ + adccon = adccon | PCI230_ADC_TRIG_Z2CT0; - /* Enable FIFO, set FIFO interrupt trigger level, set start conversion source to counter 1. */ - adccon = (adccon & ~PCI230_ADC_FIFO_RESET) | PCI230_ADC_FIFO_EN | PCI230_ADC_TRIG_Z2CT1; - if (devpriv->count < 2048) { - adccon = adccon | PCI230_ADC_INT_FIFO_NEMPTY; + /* Set the counter timers to the specified sampling frequency. */ + pci230_z2_ct0(dev, &cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK); /* cmd->convert_arg is sampling period in ns */ } else { + /* TRIG_EXT - external trigger. */ + if (cmd->convert_arg) { + /* Trigger on +ve edge. */ + adccon = adccon | PCI230_ADC_TRIG_EXTP; + } + else { + /* Trigger on -ve edge. */ + adccon = adccon | PCI230_ADC_TRIG_EXTN; + } + } + + /* Set FIFO interrupt trigger level. */ + if(cmd->stop_src == TRIG_COUNT) { + if (devpriv->ai_count < 2048) { + adccon = adccon | PCI230_ADC_INT_FIFO_NEMPTY; + } + else { + adccon = adccon | PCI230_ADC_INT_FIFO_HALF; + } + } + else { + /* TRIG_NONE - trigger on half-full FIFO. */ adccon = adccon | PCI230_ADC_INT_FIFO_HALF; } - outw_p(adccon, dev->iobase + PCI230_ADCCON); - printk("comedi%d: amplc_pci230::pci230_ai_cmd wrote PCI230_ADCCON",dev->minor); printb(adccon); + + outw(adccon, dev->iobase + PCI230_ADCCON); return 0; } @@ -770,333 +1042,291 @@ static int pci230_ai_cmd(comedi_device *dev,comedi_subdevice *s) * 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 pci230_ns_to_timer(unsigned int *ns,int round) +static void pci230_ns_to_timer(unsigned int *ns,int round) { unsigned int divisor0, divisor1; i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &divisor0, &divisor1, ns, TRIG_ROUND_MASK); - printk("comedi: amplc_pci230::pci230_ns_to_timer divisor0 %d divisor1 %d ns %d\n",divisor0, divisor1, *ns); - return *ns; + return; } /* * Set ZCLK_CT0 to square wave mode with period of ns. - * Default clk source for DAC. + * Default clk source for ADC. */ -static int pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round) +static void pci230_z2_ct0(comedi_device *dev, unsigned int *ns,int round) { /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */ i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor2, &devpriv->divisor0, ns, TRIG_ROUND_MASK); - printk("comedi%d: amplc_pci230::pci230_z2_ct0() divisor2 %d divisor0 %d ns %d\n",dev->minor, devpriv->divisor2, devpriv->divisor0, *ns); /* Generic i8254_load calls; program counters' divide ratios. */ - i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 2); /* Counter 2, divisor2, square wave (8254 mode 2). */ - i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 2); /* Counter 0, divisor0, square wave (8254 mode 2). */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 3); /* Counter 2, divisor2, square wave (8254 mode 3). */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 3); /* Counter 0, divisor0, square wave (8254 mode 3). */ /* PCI 230 specific - ties up counter clk inputs with clk sources */ outb(PCI230_ZCLK_CT2 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 2's input clock source. */ outb(PCI230_ZCLK_CT0 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 0's input clock source. */ - return *ns; + return; +} + +static void pci230_cancel_ct0(comedi_device *dev) +{ + devpriv->divisor2 = 0; + devpriv->divisor0 = 0; + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 0); /* Counter 2, divisor2, 8254 mode 0. */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 0); /* Counter 0, divisor0, 8254 mode 0. */ } /* * Set ZCLK_CT1 to square wave mode with period of ns. - * Default clk source for ADC. + * Default clk source for DAC. */ -static int pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round) +static void pci230_z2_ct1(comedi_device *dev, unsigned int *ns,int round) { /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */ i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor0, &devpriv->divisor1, ns, TRIG_ROUND_MASK); - printk("comedi%d: amplc_pci230::pci230_z2_ct1() divisor0 %d divisor1 %d ns %d\n",dev->minor, devpriv->divisor0, devpriv->divisor1, *ns); /* Generic i8254_load calls; program counters' divide ratios. */ - i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 2); /* Counter 0, divisor0, square wave (8254 mode 2). */ - i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 2); /* Counter 1, divisor1, square wave (8254 mode 2). */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 3); /* Counter 0, divisor0, square wave (8254 mode 3). */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 3); /* Counter 1, divisor1, square wave (8254 mode 3). */ /* PCI 230 specific - ties up counter clk inputs with clk sources */ outb(PCI230_ZCLK_CT0 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 0's input clock source. */ outb(PCI230_ZCLK_CT1 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 1's input clock source. */ - return *ns; + return; +} + +static void pci230_cancel_ct1(comedi_device *dev) +{ + devpriv->divisor0 = 0; + devpriv->divisor1 = 0; + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 0, devpriv->divisor0, 0); /* Counter 0, divisor0, 8254 mode 0. */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 0); /* Counter 1, divisor1, 8254 mode 0. */ } /* * Set ZCLK_CT2 to square wave mode with period of ns. + * Default clk source for external freq. generator (COMEDI_SUBD_TIMER). */ -static int pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round) +static void pci230_z2_ct2(comedi_device *dev, unsigned int *ns,int round) { /* For two cascaded counter/timers, calculate the divide ratios required to give a square wave of period ns. */ i8253_cascade_ns_to_timer_2div(PCI230_TIMEBASE_10MHZ, &devpriv->divisor1, &devpriv->divisor2, ns, TRIG_ROUND_MASK); - printk("comedi%d: amplc_pci230::pci230_z2_ct2() divisor1 %d divisor2 %d ns %d\n",dev->minor, devpriv->divisor1, devpriv->divisor2, *ns); /* Generic i8254_load calls; program counters' divide ratios. */ - i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 2); /* Counter 1, divisor1, square wave (8254 mode 2). */ - i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 2); /* Counter 2, divisor2, square wave (8254 mode 2). */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 3); /* Counter 1, divisor1, square wave (8254 mode 3). */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 3); /* Counter 2, divisor2, square wave (8254 mode 3). */ /* PCI 230 specific - ties up counter clk inputs with clk sources */ outb(PCI230_ZCLK_CT1 | PCI230_ZCLK_SRC_10MHZ, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 1's input clock source. */ outb(PCI230_ZCLK_CT2 | PCI230_ZCLK_SRC_OUTNM1, devpriv->pci_iobase + PCI230_ZCLK_SCE); /* Program counter 2's input clock source. */ - return *ns; -} - -static int pci230_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) -{ - int i; - int chan, range; - unsigned int d; - unsigned int bipolar; - - /* Unpack channel and range. */ - chan = CR_CHAN(insn->chanspec); - range = CR_RANGE(insn->chanspec); - - /* Set range - see analogue output range table; 0 => unipolar 10V, 1 => bipolar +/-10V range scale */ - bipolar = PCI230_TEST_BIT(range, PCI230_DAC_BIP_BIT); - outw(range, dev->iobase + PCI230_DACCON); - - /* Writing a list of values to an AO channel is probably not - * very useful, but that's how the interface is defined. */ - for(i=0;in;i++){ - d = data[i]; - - /* Store the value to be written to the DAC in our pci230_private struct before mangling it. */ - devpriv->ao_readback[chan] = d; - - /* If a bipolar range was specified, mangle it (straight binary->twos complement). */ - if (bipolar) { - d ^= 1<<(thisboard->ai_bits-1); - } - - /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */ - d = d<<4; - - /* Write data. */ - outw(d, dev->iobase + (((chan) == 0) ? PCI230_DACOUT1 : PCI230_DACOUT2)); - printk("comedi%d: amplc_pci230::pci230_ao_rinsn() wrote PCI230_DACOUTx 0x%04x\n",dev->minor, d); - -#if 0 - /* XXX screw the user. Only do this if the board gets upset if you don't */ - /* If we're writing more than one sample, wait for output to settle between successive writes */ - if (insn->n > 1) { - udelay(PCI230_DAC_SETTLE); - } -#endif - } - - /* return the number of samples read/written */ - return i; + return; } -/* AO subdevices should have a read insn as well as a write insn. - * Usually this means copying a value stored in devpriv. */ -static int pci230_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data) +static void pci230_cancel_ct2(comedi_device *dev) { - int i; - int chan = CR_CHAN(insn->chanspec); - - for(i=0;in;i++) - data[i] = devpriv->ao_readback[chan]; - - return i; + devpriv->divisor1 = 0; + devpriv->divisor2 = 0; + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 1, devpriv->divisor1, 0); /* Counter 1, divisor1, 8254 mode 0. */ + i8254_load(devpriv->pci_iobase + PCI230_Z2_CT0, 2, devpriv->divisor2, 0); /* Counter 2, divisor2, 8254 mode 0. */ } /* Interrupt handler */ static void pci230_interrupt(int irq, void *d, struct pt_regs *regs) { + int status_int; comedi_device *dev = (comedi_device*) d; - comedi_subdevice *s = dev->read_subdev; - comedi_async *async; - int status_int, status_fifo; - int i; - unsigned int data; - - /* - * Check to see whether this driver has been configured yet. - * This must be done first, because if the board is asserting - * interrupts and our driver's attach fn. hasn't been called - * we can't even talk to the board (base addresses for native - * IO regions aren't set...) - */ - - /* XXX doesn't make sense, since interrupt is allocated in the - * _attach function, _after_ the IO regions are allocated */ - - /* XXX I (Frank Hess) do this in drivers to prevent a null - * dereference when the handler tries to use async. This - * probably won't help too much here as it is though, since - * it doesn't clear the interrupt before returning and so - * will lock up the (single cpu) computer. */ - - if(dev->attached == 0) { - comedi_error(dev, "premature interrupt"); - return; - } - - printk("comedi%d: amplc_pci230::pci230_interrupt executing interrupt handler\n", dev->minor); + comedi_subdevice *s; /* Read interrupt status/enable register. */ status_int = inb(devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230_interrupt read PCI230_INT_SCE",dev->minor); printb(status_int); - /* Disable board interrupts. */ - outb(PCI230_INT_DISABLE, devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230_interrupt wrote PCI230_INT_SCE",dev->minor); printb(PCI230_INT_DISABLE); + /* Disable all of board's interrupts. + * (Only those interrrupts that need re-enabling, are, later in the handler). */ + devpriv->ier = PCI230_INT_DISABLE; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); /* - * Check to see whether this board emitted the interrupt, and if it did, - * whether it is for a supported function. + * Check the source of interrupt and handle it. + * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3 interrupts. + * However, at present (Comedi-0.7.60) does not allow concurrent + * execution of commands, instructions or a mixture of the two. */ if (status_int == PCI230_INT_DISABLE) { printk("comedi%d: amplc_pci230::pci230_interrupt spurious interrupt",dev->minor); - return; } - else if (status_int & PCI230_INT_PPI_C0) { - printk("comedi%d: amplc_pci230::pci230_interrupt PPI C0 interrupt, not implemented",dev->minor); - return; + + if (status_int & PCI230_INT_ZCLK_CT1) { + s = dev->write_subdev; + s->async->events = 0; + pci230_handle_ao(dev, s); + comedi_event(dev, s, s->async->events); + s->async->events = 0; + } + + if (status_int & PCI230_INT_ADC) { + s = dev->read_subdev; + s->async->events = 0; + pci230_handle_ai(dev, s); + comedi_event(dev, s, s->async->events); + s->async->events = 0; } - else if (status_int & PCI230_INT_PPI_C3) { - printk("comedi%d: amplc_pci230::pci230_interrupt PPI C3 interrupt, not implemented",dev->minor); - return; + + return; +} + +static void pci230_handle_ao(comedi_device *dev, comedi_subdevice *s) { + sampl_t data; + int i, ret; + comedi_async *async = s->async; + comedi_cmd *cmd = &async->cmd; + + for (i = 0; i < cmd->chanlist_len; i++) { + /* Read sample from Comedi's circular buffer. */ + ret = comedi_buf_get(s->async, &data); + if(ret < 0) { + comedi_error(dev, "buffer underrun"); + return; // XXX does comedi_buf_get set s->async->events with appropriate flags in this instance? + } + /* Write value to DAC. */ + pci230_ao_write(dev, data, cmd->chanlist[i]); + + if(async->cmd.stop_src == TRIG_COUNT) { + if(devpriv->ao_count > 0) devpriv->ao_count--; + if(devpriv->ao_count == 0) break; + } + } + + if(devpriv->ao_count == 0 && devpriv->ao_stop == 0) { + /* End of DAC. */ + async->events |= COMEDI_CB_EOA; + pci230_ao_cancel(dev, s); } - else if (status_int & PCI230_INT_ZCLK_CT0) { - printk("comedi%d: amplc_pci230::pci230_interrupt ZCLK CT0 interrupt, not implemented",dev->minor); - return; + else { + /* More samples required, tell Comedi to block. */ + async->events |= COMEDI_CB_BLOCK; + /* Enable DAC (conversion complete) interrupt (and leave any other enabled interrupts as they are). */ + devpriv->ier |= PCI230_INT_ZCLK_CT1; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); } + return; +} +static void pci230_handle_ai(comedi_device *dev, comedi_subdevice *s) { + int error = 0; + int status_fifo; + /* Read FIFO state. */ status_fifo = inw(dev->iobase + PCI230_ADCCON); - printk("comedi%d: amplc_pci230::pci230_interrupt read PCI230_ADCCON",dev->minor); printb(status_fifo); - /* Check to see whether FIFO enabled. */ - if (!(status_fifo & PCI230_ADC_FIFO_EN)) { - printk("comedi%d: amplc_pci230::pci230_interrupt FIFO not enabled",dev->minor); - return; - } - - /* Ok., only reason we're here is because; - * - Board has raised a conversion complete interrupt. - * - FIFO is enabled. - */ - - async = s->async; - async->events = 0; - if (status_fifo & PCI230_ADC_FIFO_FULL) { - /* - * Report error and return - if we didn´t do this, but instead handled it in a similar - * manner to the half full FIFO case, FIFO overruns would go unnoticed by the caller. - */ - printk("comedi%d: amplc_pci230::pci230_interrupt FIFO overflow",dev->minor); - - /* Cancel sampled conversion. */ - pci230__cancel(dev, s); + /* Report error otherwise FIFO overruns will go unnoticed by the caller. */ comedi_error(dev, "FIFO overrun"); - async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; - comedi_event(dev, s, async->events); - async->events = 0; - return; + error++; } else if (status_fifo & PCI230_ADC_FIFO_HALF) { - /* FIFO is at least half full */ - printk("comedi%d: amplc_pci230::pci230_interrupt FIFO half full\n",dev->minor); - for (i = 0; i < 2048; i++) { - /* Read sample. */ - data = inw(dev->iobase + PCI230_ADCDATA); - - /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */ - data = data>>4; - - /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ - if (devpriv->bipolar) { - data ^= 1<<(thisboard->ai_bits-1); - } - - /* Store in Comedi's circular buffer. */ - comedi_buf_put(async, (sampl_t) data); - - if(async->cmd.stop_src == TRIG_COUNT) - { - if(--devpriv->count == 0) { - /* Acquisition complete. */ - printk("comedi%d: amplc_pci230::pci230_interrupt acquisition complete\n",dev->minor); - pci230__cancel(dev, s); - async->events |= COMEDI_CB_EOA; - break; - } - } - } - /* More samples required, tell Comedi to block, and enable boards interrupt (don´t change trigger level). */ - async->events |= COMEDI_CB_BLOCK; - outb(PCI230_INT_ADC_DAC, devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230_interrupt wrote PCI230_INT_SCE\n",dev->minor); printb(PCI230_INT_ADC_DAC); + /* FIFO is at least half full. */ + pci230_handle_fifo_half_full(dev, s); } else if (status_fifo & PCI230_ADC_FIFO_EMPTY) { - /* FIFO empty but we got an interrupt */ - printk("comedi%d: amplc_pci230::pci230_interrupt FIFO empty - spurious interrupt\n",dev->minor); + /* FIFO empty but we got an interrupt. */ + printk("comedi%d: amplc_pci230::pci230_handle_ai FIFO empty - spurious interrupt\n",dev->minor); } else { /* FIFO is less than half full, but not empty. */ - /* We should only ever get here if no. samples to read < half fifo size. */ - printk("comedi%d: amplc_pci230::pci230_interrupt FIFO less than half full, but not empty\n",dev->minor); - while (devpriv->count != 0) { - if (inw(dev->iobase + PCI230_ADCCON) & PCI230_ADC_FIFO_EMPTY) { - /* The FIFO is empty, block. */ - printk("comedi%d: amplc_pci230::pci230_interrupt FIFO now empty, want %d samples\n",dev->minor, devpriv->count); - - /* More samples required, tell Comedi to block, and enable boards interrupt (don´t change trigger level). */ - outb(PCI230_INT_ADC_DAC, devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230_interrupt wrote PCI230_INT_SCE\n",dev->minor); printb(PCI230_INT_ADC_DAC); - async->events |= COMEDI_CB_BLOCK; - comedi_event(dev, s, async->events); - return; - } - /* There are sample(s) to read from FIFO, read one. */ - data = inw(dev->iobase + PCI230_ADCDATA); + pci230_handle_fifo_not_empty(dev, s); + } + + if (error) { + /* Cancel sampled conversion. */ + s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; + pci230_ai_cancel(dev, s); + } + if(devpriv->ai_count == 0 && devpriv->ai_stop == 0) { + /* Acquisition complete. */ + s->async->events |= COMEDI_CB_EOA; + pci230_ai_cancel(dev, s); /* disable hardware conversions */ + } + else { + /* More samples required, tell Comedi to block. */ + s->async->events |= COMEDI_CB_BLOCK; + /* Enable ADC (conversion complete) interrupt (and leave any other enabled interrupts as they are). */ + devpriv->ier |= PCI230_INT_ADC; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); + } + return; +} - /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower four bits reserved for expansion). */ - data = data>>4; +static void pci230_handle_fifo_half_full(comedi_device *dev, comedi_subdevice *s) { + int i; - /* If a bipolar range was specified, mangle it (twos complement->straight binary). */ - if (devpriv->bipolar) { - data ^= 1<<(thisboard->ai_bits-1); - } + for (i = 0; i < 2048; i++) { + /* Read sample and store in Comedi's circular buffer. */ + comedi_buf_put(s->async, pci230_ai_read(dev)); - /* Store in Comedi's circular buffer. */ - comedi_buf_put(async, (sampl_t) data); + if(s->async->cmd.stop_src == TRIG_COUNT) + { + if(--devpriv->ai_count == 0) { + /* Acquisition complete. */ + return; + } + } + } + /* More samples required. */ + return; +} - if(devpriv->count > 0) devpriv->count--; +static void pci230_handle_fifo_not_empty(comedi_device *dev, comedi_subdevice *s) { + while (devpriv->ai_count != 0) { + if (inw(dev->iobase + PCI230_ADCCON) & PCI230_ADC_FIFO_EMPTY) { + /* The FIFO is empty, block. */ + return; } + /* There are sample(s) to read from FIFO, read one and store in Comedi's circular buffer. */ + comedi_buf_put(s->async, pci230_ai_read(dev)); - /* Acquisition complete. */ - printk("comedi%d: amplc_pci230::pci230_interrupt acquisition complete\n",dev->minor); - pci230__cancel(dev, s); - async->events |= COMEDI_CB_EOA; + if(devpriv->ai_count > 0) devpriv->ai_count--; } + /* Acquisition complete. */ + return; +} - comedi_event(dev, s, async->events); - async->events = 0; - return; +static int pci230_ao_cancel(comedi_device *dev, comedi_subdevice *s) { + devpriv->ao_count = 0; + devpriv->ao_stop = 0; + + /* Stop counter/timers. */ + pci230_cancel_ct1(dev); + + /* Disable DAC interrupt. */ + devpriv->ier &= ~PCI230_INT_ZCLK_CT1; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); + + return 0; } -static int pci230__cancel(comedi_device *dev, comedi_subdevice *s) -{ - /* Disable interrupts. */ - outb(PCI230_INT_DISABLE, devpriv->pci_iobase + PCI230_INT_SCE); - printk("comedi%d: amplc_pci230::pci230__cancel disabled interrupts\n",dev->minor); +static int pci230_ai_cancel(comedi_device *dev, comedi_subdevice *s) { + devpriv->ai_count = 0; + devpriv->ai_stop = 0; + + /* Stop counter/timers. */ + pci230_cancel_ct0(dev); + + /* Disable ADC interrupt. */ + devpriv->ier &= ~PCI230_INT_ADC; + outb(devpriv->ier, devpriv->pci_iobase + PCI230_INT_SCE); /* Reset FIFO and set start conversion source to none. */ - outw_p(PCI230_ADC_FIFO_RESET | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON); - printk("comedi%d: amplc_pci230::pci230__cancel reset FIFO and set conv src to none\n", dev->minor); + outw(PCI230_ADC_FIFO_RESET | PCI230_ADC_TRIG_NONE, dev->iobase + PCI230_ADCCON); /* Clear channel scan list. */ - outw_p(0x0000, dev->iobase + PCI230_ADCEN); - printk("comedi%d: amplc_pci230::pci230__cancel cleared scan list\n", dev->minor); + outw(0x0000, dev->iobase + PCI230_ADCEN); /* Clear channel gains. */ - outw_p(0x0000, dev->iobase + PCI230_ADCG); - printk("comedi%d: amplc_pci230::pci230__cancel cleared channel gains\n", dev->minor); + outw(0x0000, dev->iobase + PCI230_ADCG); return 0; }