-/*
+ /*
comedi/drivers/amplc_pci230.c
Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards.
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 <linux/kernel.h>
#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 */
#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
#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
#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;
ai_bits: 12,
have_ao: 0,
ao_chans: 0,
- ao_bits: 0,
+ ao_bits: 0,
have_dio: 0,
},
};
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)
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));
}
/*
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)
}
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;
*/
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;
s->maxdata=(1<<thisboard->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 */
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 */
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");
}
/*
- * "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<<chan;
+ adccon |= PCI230_ADC_IM_SE;
+ if (devpriv->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<<chan;
-
- /* Specify uni/bip, se/diff, s/w conversion and channel. */
- outw_p(adccon, dev->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);
/* wait for conversion to end */
for(i=0;i<TIMEOUT;i++){
status = inw(dev->iobase + 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){
}
/* 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;i<insn->n;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;i<insn->n;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.
* 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;
* "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.
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_arg<MAX_SPEED){
- cmd->scan_begin_arg=MAX_SPEED;
+ if(cmd->scan_begin_src<MAX_SPEED){
+ cmd->scan_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_arg<MAX_SPEED){
cmd->convert_arg=MAX_SPEED;
}
}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++;
}
}
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;
* "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_arg<cmd->convert_arg*cmd->scan_end_arg){
- cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg;
- err++;
- }
}
if(err)return 4;
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<<chan;
+ if (diff) {
+ adcg |= range<<(2*chan-2*chan%2);
+ adcen |= 3<<2*chan;
+ }
+ else {
+ adcg |= range<<(chan-chan%2);
+ adcen |= 1<<chan;
+ }
}
}
else {
for (i = 0; i < cmd->chanlist_len; i++) {
chan = CR_CHAN(cmd->chanlist[i]);
range = CR_RANGE(cmd->chanlist[i]);
- adcg |= ((range&(~4))+1)<<(chan-chan%2);
- adcen |= 1<<chan;
+ if (diff) {
+ adcg |= ((range&(~4))+1)<<(2*chan-2*chan%2);
+ adcen |= 3<<2*chan;
+ }
+ else {
+ adcg |= ((range&(~4))+1)<<(chan-chan%2);
+ adcen |= 1<<chan;
+ }
}
}
- /* Reset FIFO, specify uni/bip, se/diff, and start conversion source to none. */
- outw_p(adccon, dev->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;
}
* 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;i<insn->n;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;i<insn->n;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;
}