in drivers/amplc_pci230.c [2076:2269]
static int pci230_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct pci230_private *devpriv = dev->private;
unsigned int i, chan, range, diff;
unsigned int res_mask;
unsigned short adccon, adcen;
unsigned char zgat;
/* Get the command. */
struct comedi_async *async = s->async;
struct comedi_cmd *cmd = &async->cmd;
/*
* Determine which shared resources are needed.
*/
res_mask = 0;
/*
* Need Z2-CT2 to supply a conversion trigger source at a high
* logic level, even if not doing timed conversions.
*/
res_mask |= RES_Z2CT2;
if (cmd->scan_begin_src != TRIG_FOLLOW) {
/* Using Z2-CT0 monostable to gate Z2-CT2 conversion timer */
res_mask |= RES_Z2CT0;
if (cmd->scan_begin_src == TRIG_TIMER) {
/* Using Z2-CT1 for scan frequency */
res_mask |= RES_Z2CT1;
}
}
/* Claim resources. */
if (!pci230_claim_shared(dev, res_mask, OWNER_AICMD))
return -EBUSY;
/*
* Steps:
* - Set channel scan list.
* - Set channel gains.
* - Enable and reset FIFO, specify uni/bip, se/diff, and set
* start conversion source to point to something at a high logic
* level (we use the output of counter/timer 2 for this purpose.
* - PAUSE to allow things to settle down.
* - Reset the FIFO again because it needs resetting twice and there
* may have been a false conversion trigger on some versions of
* PCI230/260 due to the start conversion source being set to a
* high logic level.
* - Enable ADC FIFO level interrupt.
* - Set actual conversion trigger source and FIFO interrupt trigger
* level.
* - If convert_src is TRIG_TIMER, set up the timers.
*/
adccon = PCI230_ADC_FIFO_EN;
adcen = 0;
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;
}
range = CR_RANGE(cmd->chanlist[0]);
devpriv->ai_bipolar = comedi_range_is_bipolar(s, range);
if (devpriv->ai_bipolar)
adccon |= PCI230_ADC_IR_BIP;
else
adccon |= PCI230_ADC_IR_UNI;
for (i = 0; i < cmd->chanlist_len; i++) {
unsigned int gainshift;
chan = CR_CHAN(cmd->chanlist[i]);
range = CR_RANGE(cmd->chanlist[i]);
if (diff) {
gainshift = 2 * chan;
if (devpriv->hwver == 0) {
/*
* Original PCI230/260 expects both inputs of
* the differential channel to be enabled.
*/
adcen |= 3 << gainshift;
} else {
/*
* PCI230+/260+ expects only one input of the
* differential channel to be enabled.
*/
adcen |= 1 << gainshift;
}
} else {
gainshift = chan & ~1;
adcen |= 1 << chan;
}
devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) |
(pci230_ai_gain[range] << gainshift);
}
/* Set channel scan list. */
outw(adcen, devpriv->daqio + PCI230_ADCEN);
/* Set channel gains. */
outw(devpriv->adcg, devpriv->daqio + PCI230_ADCG);
/*
* Set counter/timer 2 output high for use as the initial start
* conversion source.
*/
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
/*
* Temporarily use CT2 output as conversion trigger source and
* temporarily set FIFO interrupt trigger level to 'full'.
*/
adccon |= PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_Z2CT2;
/*
* Enable and reset FIFO, specify FIFO trigger level full, specify
* uni/bip, se/diff, and temporarily set the start conversion source
* to CT2 output. Note that CT2 output is currently high, and this
* will produce a false conversion trigger on some versions of the
* PCI230/260, but that will be dealt with later.
*/
devpriv->adccon = adccon;
outw(adccon | PCI230_ADC_FIFO_RESET, devpriv->daqio + PCI230_ADCCON);
/*
* Delay -
* Failure to include this will result in the first few channels'-worth
* of data being corrupt, normally manifesting itself by large negative
* voltages. It seems the board needs time to settle between the first
* FIFO reset (above) and the second FIFO reset (below). Setting the
* channel gains and scan list _before_ the first FIFO reset also
* helps, though only slightly.
*/
usleep_range(25, 100);
/* Reset FIFO again. */
outw(adccon | PCI230_ADC_FIFO_RESET, devpriv->daqio + PCI230_ADCCON);
if (cmd->convert_src == TRIG_TIMER) {
/*
* Set up CT2 as conversion timer, but gate it off for now.
* Note, counter/timer output 2 can be monitored on the
* connector: PCI230 pin 21, PCI260 pin 18.
*/
zgat = pci230_gat_config(2, GAT_GND);
outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
/* Set counter/timer 2 to the specified conversion period. */
pci230_ct_setup_ns_mode(dev, 2, I8254_MODE3, cmd->convert_arg,
cmd->flags);
if (cmd->scan_begin_src != TRIG_FOLLOW) {
/*
* Set up monostable on CT0 output for scan timing. A
* rising edge on the trigger (gate) input of CT0 will
* trigger the monostable, causing its output to go low
* for the configured period. The period depends on
* the conversion period and the number of conversions
* in the scan.
*
* Set the trigger high before setting up the
* monostable to stop it triggering. The trigger
* source will be changed later.
*/
zgat = pci230_gat_config(0, GAT_VCC);
outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
pci230_ct_setup_ns_mode(dev, 0, I8254_MODE1,
((u64)cmd->convert_arg *
cmd->scan_end_arg),
CMDF_ROUND_UP);
if (cmd->scan_begin_src == TRIG_TIMER) {
/*
* Monostable on CT0 will be triggered by
* output of CT1 at configured scan frequency.
*
* Set up CT1 but gate it off for now.
*/
zgat = pci230_gat_config(1, GAT_GND);
outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3,
cmd->scan_begin_arg,
cmd->flags);
}
}
}
if (cmd->start_src == TRIG_INT)
s->async->inttrig = pci230_ai_inttrig_start;
else /* TRIG_NOW */
pci230_ai_start(dev, s);
return 0;
}