// SPDX-License-Identifier: GPL-2.0+ /* * comedi/drivers/amplc_pci230.c * Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards. * * Copyright (C) 2001 Allan Willcox <allanwillcox@ozemail.com.au> * * COMEDI - Linux Control and Measurement Device Interface * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
*/
/* * Driver: amplc_pci230 * Description: Amplicon PCI230, PCI260 Multifunction I/O boards * Author: Allan Willcox <allanwillcox@ozemail.com.au>, * Steve D Sharples <steve.sharples@nottingham.ac.uk>, * Ian Abbott <abbotti@mev.co.uk> * Updated: Mon, 01 Sep 2014 10:09:16 +0000 * Devices: [Amplicon] PCI230 (amplc_pci230), PCI230+, PCI260, PCI260+ * Status: works * * Configuration options: * none * * Manual configuration of PCI cards is not supported; they are configured * automatically. * * The PCI230+ and PCI260+ have the same PCI device IDs as the PCI230 and * PCI260, but can be distinguished by the size of the PCI regions. A * card will be configured as a "+" model if detected as such. * * Subdevices: * * PCI230(+) PCI260(+) * --------- --------- * Subdevices 3 1 * 0 AI AI * 1 AO * 2 DIO * * AI Subdevice: * * The AI subdevice has 16 single-ended channels or 8 differential * channels. * * The PCI230 and PCI260 cards have 12-bit resolution. The PCI230+ and * PCI260+ cards have 16-bit resolution. * * For differential mode, use inputs 2N and 2N+1 for channel N (e.g. use * inputs 14 and 15 for channel 7). If the card is physically a PCI230 * or PCI260 then it actually uses a "pseudo-differential" mode where the * inputs are sampled a few microseconds apart. The PCI230+ and PCI260+ * use true differential sampling. Another difference is that if the * card is physically a PCI230 or PCI260, the inverting input is 2N, * whereas for a PCI230+ or PCI260+ the inverting input is 2N+1. So if a * PCI230 is physically replaced by a PCI230+ (or a PCI260 with a * PCI260+) and differential mode is used, the differential inputs need * to be physically swapped on the connector. * * The following input ranges are supported: * * 0 => [-10, +10] V * 1 => [-5, +5] V * 2 => [-2.5, +2.5] V * 3 => [-1.25, +1.25] V * 4 => [0, 10] V * 5 => [0, 5] V * 6 => [0, 2.5] V * * AI Commands: * * +=========+==============+===========+============+==========+ * |start_src|scan_begin_src|convert_src|scan_end_src| stop_src | * +=========+==============+===========+============+==========+ * |TRIG_NOW | TRIG_FOLLOW |TRIG_TIMER | TRIG_COUNT |TRIG_NONE | * |TRIG_INT | |TRIG_EXT(3)| |TRIG_COUNT| * | | |TRIG_INT | | | * | |--------------|-----------| | | * | | TRIG_TIMER(1)|TRIG_TIMER | | | * | | TRIG_EXT(2) | | | | * | | TRIG_INT | | | | * +---------+--------------+-----------+------------+----------+ * * Note 1: If AI command and AO command are used simultaneously, only * one may have scan_begin_src == TRIG_TIMER. * * Note 2: For PCI230 and PCI230+, scan_begin_src == TRIG_EXT uses * DIO channel 16 (pin 49) which will need to be configured as * a digital input. For PCI260+, the EXTTRIG/EXTCONVCLK input * (pin 17) is used instead. For PCI230, scan_begin_src == * TRIG_EXT is not supported. The trigger is a rising edge * on the input. * * Note 3: For convert_src == TRIG_EXT, the EXTTRIG/EXTCONVCLK input * (pin 25 on PCI230(+), pin 17 on PCI260(+)) is used. The * convert_arg value is interpreted as follows: * * convert_arg == (CR_EDGE | 0) => rising edge * convert_arg == (CR_EDGE | CR_INVERT | 0) => falling edge * convert_arg == 0 => falling edge (backwards compatibility) * convert_arg == 1 => rising edge (backwards compatibility) * * All entries in the channel list must use the same analogue reference. * If the analogue reference is not AREF_DIFF (not differential) each * pair of channel numbers (0 and 1, 2 and 3, etc.) must use the same * input range. The input ranges used in the sequence must be all * bipolar (ranges 0 to 3) or all unipolar (ranges 4 to 6). The channel * sequence must consist of 1 or more identical subsequences. Within the * subsequence, channels must be in ascending order with no repeated * channels. For example, the following sequences are valid: 0 1 2 3 * (single valid subsequence), 0 2 3 5 0 2 3 5 (repeated valid * subsequence), 1 1 1 1 (repeated valid subsequence). The following * sequences are invalid: 0 3 2 1 (invalid subsequence), 0 2 3 5 0 2 3 * (incompletely repeated subsequence). Some versions of the PCI230+ and * PCI260+ have a bug that requires a subsequence longer than one entry * long to include channel 0. * * AO Subdevice: * * The AO subdevice has 2 channels with 12-bit resolution. * The following output ranges are supported: * 0 => [0, 10] V * 1 => [-10, +10] V * * AO Commands: * * +=========+==============+===========+============+==========+ * |start_src|scan_begin_src|convert_src|scan_end_src| stop_src | * +=========+==============+===========+============+==========+ * |TRIG_INT | TRIG_TIMER(1)| TRIG_NOW | TRIG_COUNT |TRIG_NONE | * | | TRIG_EXT(2) | | |TRIG_COUNT| * | | TRIG_INT | | | | * +---------+--------------+-----------+------------+----------+ * * Note 1: If AI command and AO command are used simultaneously, only * one may have scan_begin_src == TRIG_TIMER. * * Note 2: scan_begin_src == TRIG_EXT is only supported if the card is * configured as a PCI230+ and is only supported on later * versions of the card. As a card configured as a PCI230+ is * not guaranteed to support external triggering, please consider * this support to be a bonus. It uses the EXTTRIG/ EXTCONVCLK * input (PCI230+ pin 25). Triggering will be on the rising edge * unless the CR_INVERT flag is set in scan_begin_arg. * * The channels in the channel sequence must be in ascending order with * no repeats. All entries in the channel sequence must use the same * output range. * * DIO Subdevice: * * The DIO subdevice is a 8255 chip providing 24 DIO channels. The DIO * channels are configurable as inputs or outputs in four groups: * * Port A - channels 0 to 7 * Port B - channels 8 to 15 * Port CL - channels 16 to 19 * Port CH - channels 20 to 23 * * Only mode 0 of the 8255 chip is supported. * * Bit 0 of port C (DIO channel 16) is also used as an external scan * trigger input for AI commands on PCI230 and PCI230+, so would need to * be configured as an input to use it for that purpose.
*/
/* * Extra triggered scan functionality, interrupt bug-fix added by Steve * Sharples. Support for PCI230+/260+, more triggered scan functionality, * and workarounds for (or detection of) various hardware problems added * by Ian Abbott.
*/
/* * PCI230 i/o space 1 registers.
*/ #define PCI230_PPI_X_BASE 0x00 /* User PPI (82C55) base */ #define PCI230_PPI_X_A 0x00 /* User PPI (82C55) port A */ #define PCI230_PPI_X_B 0x01 /* User PPI (82C55) port B */ #define PCI230_PPI_X_C 0x02 /* User PPI (82C55) port C */ #define PCI230_PPI_X_CMD 0x03 /* User PPI (82C55) control word */ #define PCI230_Z2_CT_BASE 0x14 /* 82C54 counter/timer base */ #define PCI230_ZCLK_SCE 0x1A /* Group Z Clock Configuration */ #define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration */ #define PCI230_INT_SCE 0x1E /* Interrupt source mask (w) */ #define PCI230_INT_STAT 0x1E /* Interrupt status (r) */
/* * DACCON read-write values.
*/ #define PCI230_DAC_OR(x) (((x) & 0x1) << 0) #define PCI230_DAC_OR_UNI PCI230_DAC_OR(0) /* Output unipolar */ #define PCI230_DAC_OR_BIP PCI230_DAC_OR(1) /* Output bipolar */ #define PCI230_DAC_OR_MASK PCI230_DAC_OR(1) /* * The following applies only if DAC FIFO support is enabled in the EXTFUNC * register (and only for PCI230+ hardware version 2 onwards).
*/ #define PCI230P2_DAC_FIFO_EN BIT(8) /* FIFO enable */ /* * The following apply only if the DAC FIFO is enabled (and only for PCI230+ * hardware version 2 onwards).
*/ #define PCI230P2_DAC_TRIG(x) (((x) & 0x7) << 2) #define PCI230P2_DAC_TRIG_NONE PCI230P2_DAC_TRIG(0) /* none */ #define PCI230P2_DAC_TRIG_SW PCI230P2_DAC_TRIG(1) /* soft trig */ #define PCI230P2_DAC_TRIG_EXTP PCI230P2_DAC_TRIG(2) /* ext + edge */ #define PCI230P2_DAC_TRIG_EXTN PCI230P2_DAC_TRIG(3) /* ext - edge */ #define PCI230P2_DAC_TRIG_Z2CT0 PCI230P2_DAC_TRIG(4) /* Z2 CT0 out */ #define PCI230P2_DAC_TRIG_Z2CT1 PCI230P2_DAC_TRIG(5) /* Z2 CT1 out */ #define PCI230P2_DAC_TRIG_Z2CT2 PCI230P2_DAC_TRIG(6) /* Z2 CT2 out */ #define PCI230P2_DAC_TRIG_MASK PCI230P2_DAC_TRIG(7) #define PCI230P2_DAC_FIFO_WRAP BIT(7) /* FIFO wraparound mode */ #define PCI230P2_DAC_INT_FIFO(x) (((x) & 7) << 9) #define PCI230P2_DAC_INT_FIFO_EMPTY PCI230P2_DAC_INT_FIFO(0) /* empty */ #define PCI230P2_DAC_INT_FIFO_NEMPTY PCI230P2_DAC_INT_FIFO(1) /* !empty */ #define PCI230P2_DAC_INT_FIFO_NHALF PCI230P2_DAC_INT_FIFO(2) /* !half */ #define PCI230P2_DAC_INT_FIFO_HALF PCI230P2_DAC_INT_FIFO(3) /* half */ #define PCI230P2_DAC_INT_FIFO_NFULL PCI230P2_DAC_INT_FIFO(4) /* !full */ #define PCI230P2_DAC_INT_FIFO_FULL PCI230P2_DAC_INT_FIFO(5) /* full */ #define PCI230P2_DAC_INT_FIFO_MASK PCI230P2_DAC_INT_FIFO(7)
/* * DACCON read-only values.
*/ #define PCI230_DAC_BUSY BIT(1) /* DAC busy. */ /* * The following apply only if the DAC FIFO is enabled (and only for PCI230+ * hardware version 2 onwards).
*/ #define PCI230P2_DAC_FIFO_UNDERRUN_LATCHED BIT(5) /* Underrun error */ #define PCI230P2_DAC_FIFO_EMPTY BIT(13) /* FIFO empty */ #define PCI230P2_DAC_FIFO_FULL BIT(14) /* FIFO full */ #define PCI230P2_DAC_FIFO_HALF BIT(15) /* FIFO half full */
/* * DACCON write-only, transient values.
*/ /* * The following apply only if the DAC FIFO is enabled (and only for PCI230+ * hardware version 2 onwards).
*/ #define PCI230P2_DAC_FIFO_UNDERRUN_CLEAR BIT(5) /* Clear underrun */ #define PCI230P2_DAC_FIFO_RESET BIT(12) /* FIFO reset */
struct pci230_private {
spinlock_t isr_spinlock; /* Interrupt spin lock */
spinlock_t res_spinlock; /* Shared resources spin lock */
spinlock_t ai_stop_spinlock; /* Spin lock for stopping AI command */
spinlock_t ao_stop_spinlock; /* Spin lock for stopping AO command */ unsignedlong daqio; /* PCI230's DAQ I/O space */ int intr_cpuid; /* ID of CPU running ISR */ unsignedshort hwver; /* Hardware version (for '+' models) */ unsignedshort adccon; /* ADCCON register value */ unsignedshort daccon; /* DACCON register value */ unsignedshort adcfifothresh; /* ADC FIFO threshold (PCI230+/260+) */ unsignedshort adcg; /* ADCG register value */ unsignedchar ier; /* Interrupt enable bits */ unsignedchar res_owned[NUM_OWNERS]; /* Owned resources */ unsignedint intr_running:1; /* Flag set in interrupt routine */ unsignedint ai_bipolar:1; /* Flag AI range is bipolar */ unsignedint ao_bipolar:1; /* Flag AO range is bipolar */ unsignedint ai_cmd_started:1; /* Flag AI command started */ unsignedint ao_cmd_started:1; /* Flag AO command started */
};
/* Read sample. */
data = inw(devpriv->daqio + PCI230_ADCDATA); /* * PCI230 is 12 bit - stored in upper bits of 16 bit register * (lower four bits reserved for expansion). PCI230+ is 16 bit AI. * * If a bipolar range was specified, mangle it * (twos complement->straight binary).
*/ if (devpriv->ai_bipolar)
data ^= 0x8000;
data >>= (16 - board->ai_bits); return data;
}
/* * PCI230 is 12 bit - stored in upper bits of 16 bit register (lower * four bits reserved for expansion). PCI230+ is also 12 bit AO.
*/
datum <<= (16 - board->ao_bits); /* * If a bipolar range was specified, mangle it * (straight binary->twos complement).
*/ if (devpriv->ao_bipolar)
datum ^= 0x8000; return datum;
}
div = ns;
rem = do_div(div, timebase); switch (flags & CMDF_ROUND_MASK) { default: case CMDF_ROUND_NEAREST:
div += DIV_ROUND_CLOSEST(rem, timebase); break; case CMDF_ROUND_DOWN: break; case CMDF_ROUND_UP:
div += DIV_ROUND_UP(rem, timebase); break;
} return div > UINT_MAX ? UINT_MAX : (unsignedint)div;
}
/* * Given desired period in ns, returns the required internal clock source * and gets the initial count.
*/ staticunsignedint pci230_choose_clk_count(u64 ns, unsignedint *count, unsignedint flags)
{ unsignedint clk_src, cnt;
/* Unpack channel and range. */
chan = CR_CHAN(insn->chanspec);
range = CR_RANGE(insn->chanspec);
aref = CR_AREF(insn->chanspec); if (aref == AREF_DIFF) { /* Differential. */ if (chan >= s->n_chan / 2) {
dev_dbg(dev->class_dev, "%s: differential channel number out of range 0 to %u\n",
__func__, (s->n_chan / 2) - 1); return -EINVAL;
}
}
/* * Use Z2-CT2 as a conversion trigger instead of the built-in * software trigger, as otherwise triggering of differential channels * doesn't work properly for some versions of PCI230/260. Also set * FIFO mode because the ADC busy bit only works for software triggers.
*/
adccon = PCI230_ADC_TRIG_Z2CT2 | PCI230_ADC_FIFO_EN; /* Set Z2-CT2 output low to avoid any false triggers. */
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
devpriv->ai_bipolar = comedi_range_is_bipolar(s, range); if (aref == AREF_DIFF) { /* Differential. */
gainshift = chan * 2; 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;
}
adccon |= PCI230_ADC_IM_DIF;
} else { /* Single ended. */
adcen = 1 << chan;
gainshift = chan & ~1;
adccon |= PCI230_ADC_IM_SE;
}
devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) |
(pci230_ai_gain[range] << gainshift); if (devpriv->ai_bipolar)
adccon |= PCI230_ADC_IR_BIP; else
adccon |= PCI230_ADC_IR_UNI;
/* * Enable only this channel in the scan list - otherwise by default * we'll get one sample from each channel.
*/
outw(adcen, devpriv->daqio + PCI230_ADCEN);
/* Set gain for channel. */
outw(devpriv->adcg, devpriv->daqio + PCI230_ADCG);
tmp = TRIG_TIMER | TRIG_INT; if (board->min_hwver > 0 && devpriv->hwver >= 2) { /* * For PCI230+ hardware version 2 onwards, allow external * trigger from EXTTRIG/EXTCONVCLK input (PCI230+ pin 25). * * FIXME: The permitted scan_begin_src values shouldn't depend * on devpriv->hwver (the detected card's actual hardware * version). They should only depend on board->min_hwver * (the static capabilities of the configured card). To fix * it, a new card model, e.g. "pci230+2" would have to be * defined with min_hwver set to 2. It doesn't seem worth it * for this alone. At the moment, please consider * scan_begin_src==TRIG_EXT support to be a bonus rather than a * guarantee!
*/
tmp |= TRIG_EXT;
}
err |= comedi_check_trigger_src(&cmd->scan_begin_src, tmp);
if (events == 0) { /* Check for FIFO underrun. */ if (dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) {
dev_err(dev->class_dev, "AO FIFO underrun\n");
events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
} /* * Check for buffer underrun if FIFO less than half full * (otherwise there will be loads of "DAC FIFO not half full" * interrupts).
*/ if (num_scans == 0 &&
(dacstat & PCI230P2_DAC_FIFO_HALF) == 0) {
dev_err(dev->class_dev, "AO buffer underrun\n");
events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
}
} if (events == 0) { /* Determine how much room is in the FIFO (in samples). */ if (dacstat & PCI230P2_DAC_FIFO_FULL)
room = PCI230P2_DAC_FIFOROOM_FULL; elseif (dacstat & PCI230P2_DAC_FIFO_HALF)
room = PCI230P2_DAC_FIFOROOM_HALFTOFULL; elseif (dacstat & PCI230P2_DAC_FIFO_EMPTY)
room = PCI230P2_DAC_FIFOROOM_EMPTY; else
room = PCI230P2_DAC_FIFOROOM_ONETOHALF; /* Convert room to number of scans that can be added. */
room /= cmd->chanlist_len; /* Determine number of scans to process. */ if (num_scans > room)
num_scans = room; /* Process scans. */ for (n = 0; n < num_scans; n++) { for (i = 0; i < cmd->chanlist_len; i++) { unsignedint chan = CR_CHAN(cmd->chanlist[i]); unsignedshort datum;
/* Get the command. */ struct comedi_cmd *cmd = &s->async->cmd;
if (cmd->scan_begin_src == TRIG_TIMER) { /* Claim Z2-CT1. */ if (!pci230_claim_shared(dev, RES_Z2CT1, OWNER_AOCMD)) return -EBUSY;
}
/* * Set range - see analogue output range table; 0 => unipolar 10V, * 1 => bipolar +/-10V range scale
*/
range = CR_RANGE(cmd->chanlist[0]);
devpriv->ao_bipolar = comedi_range_is_bipolar(s, range);
daccon = devpriv->ao_bipolar ? PCI230_DAC_OR_BIP : PCI230_DAC_OR_UNI; /* Use DAC FIFO for hardware version 2 onwards. */ if (devpriv->hwver >= 2) { unsignedshort dacen; unsignedint i;
dacen = 0; for (i = 0; i < cmd->chanlist_len; i++)
dacen |= 1 << CR_CHAN(cmd->chanlist[i]);
/* Set channel scan list. */
outw(dacen, devpriv->daqio + PCI230P2_DACEN); /* * Enable DAC FIFO. * Set DAC scan source to 'none'. * Set DAC FIFO interrupt trigger level to 'not half full'. * Reset DAC FIFO and clear underrun. * * N.B. DAC FIFO interrupts are currently disabled.
*/
daccon |= PCI230P2_DAC_FIFO_EN | PCI230P2_DAC_FIFO_RESET |
PCI230P2_DAC_FIFO_UNDERRUN_CLEAR |
PCI230P2_DAC_TRIG_NONE | PCI230P2_DAC_INT_FIFO_NHALF;
}
/* Set DACCON. */
outw(daccon, devpriv->daqio + PCI230_DACCON); /* Preserve most of DACCON apart from write-only, transient bits. */
devpriv->daccon = daccon & ~(PCI230P2_DAC_FIFO_RESET |
PCI230P2_DAC_FIFO_UNDERRUN_CLEAR);
if (cmd->scan_begin_src == TRIG_TIMER) { /* * Set the counter timer 1 to the specified scan frequency. * cmd->scan_begin_arg is sampling period in ns. * Gate it off for now.
*/
outb(pci230_gat_config(1, GAT_GND),
dev->iobase + PCI230_ZGAT_SCE);
pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3,
cmd->scan_begin_arg,
cmd->flags);
}
for (i = 0; i < cmd->chanlist_len; i++) { unsignedint chanspec = cmd->chanlist[i]; unsignedint chan = CR_CHAN(chanspec); unsignedint range = CR_RANGE(chanspec); unsignedint aref = CR_AREF(chanspec); bool bipolar = comedi_range_is_bipolar(s, range);
if (aref == AREF_DIFF && chan >= max_diff_chan) {
dev_dbg(dev->class_dev, "%s: differential channel number out of range 0 to %u\n",
__func__, max_diff_chan); return -EINVAL;
}
if (i > 0) { /* * Channel numbers must strictly increase or * subsequence must repeat exactly.
*/ if (chan <= prev_chan && subseq_len == 0)
subseq_len = i;
if (subseq_len > 0 &&
cmd->chanlist[i % subseq_len] != chanspec) {
dev_dbg(dev->class_dev, "%s: channel numbers must increase or sequence must repeat exactly\n",
__func__); return -EINVAL;
}
if (aref != prev_aref) {
dev_dbg(dev->class_dev, "%s: channel sequence analogue references must be all the same (single-ended or differential)\n",
__func__); return -EINVAL;
}
if (bipolar != prev_bipolar) {
dev_dbg(dev->class_dev, "%s: channel sequence ranges must be all bipolar or all unipolar\n",
__func__); return -EINVAL;
}
if (aref != AREF_DIFF && range != prev_range &&
((chan ^ prev_chan) & ~1) == 0) {
dev_dbg(dev->class_dev, "%s: single-ended channel pairs must have the same range\n",
__func__); return -EINVAL;
}
}
prev_chan = chan;
prev_range = range;
prev_aref = aref;
prev_bipolar = bipolar;
}
if (subseq_len == 0)
subseq_len = cmd->chanlist_len;
if (cmd->chanlist_len % subseq_len) {
dev_dbg(dev->class_dev, "%s: sequence must repeat exactly\n", __func__); return -EINVAL;
}
/* * Buggy PCI230+ or PCI260+ requires channel 0 to be (first) in the * sequence if the sequence contains more than one channel. Hardware * versions 1 and 2 have the bug. There is no hardware version 3. * * Actually, there are two firmwares that report themselves as * hardware version 1 (the boards have different ADC chips with * slightly different timing requirements, which was supposed to * be invisible to software). The first one doesn't seem to have * the bug, but the second one does, and we can't tell them apart!
*/ if (devpriv->hwver > 0 && devpriv->hwver < 4) { if (subseq_len > 1 && CR_CHAN(cmd->chanlist[0])) {
dev_info(dev->class_dev, "amplc_pci230: ai_cmdtest: Buggy PCI230+/260+ h/w version %u requires first channel of multi-channel sequence to be 0 (corrected in h/w version 4)\n",
devpriv->hwver); return -EINVAL;
}
}
tmp = TRIG_FOLLOW | TRIG_TIMER | TRIG_INT; if (board->have_dio || board->min_hwver > 0) { /* * Unfortunately, we cannot trigger a scan off an external * source on the PCI260 board, since it uses the PPIC0 (DIO) * input, which isn't present on the PCI260. For PCI260+ * we can use the EXTTRIG/EXTCONVCLK input on pin 17 instead.
*/
tmp |= TRIG_EXT;
}
err |= comedi_check_trigger_src(&cmd->scan_begin_src, tmp);
err |= comedi_check_trigger_src(&cmd->convert_src,
TRIG_TIMER | TRIG_INT | TRIG_EXT);
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err) return 1;
/* Step 2a : make sure trigger sources are unique */
/* * If scan_begin_src is not TRIG_FOLLOW, then a monostable will be * set up to generate a fixed number of timed conversion pulses.
*/ if (cmd->scan_begin_src != TRIG_FOLLOW &&
cmd->convert_src != TRIG_TIMER)
err |= -EINVAL;
if (err) return 2;
/* Step 3: check if arguments are trivially valid */
if (cmd->scan_begin_src == TRIG_EXT) { /* * external "trigger" to begin each scan: * scan_begin_arg==0 => use PPC0 input -> gate of CT0 -> gate * of CT2 (sample convert trigger is CT2)
*/ if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) {
cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
~CR_FLAGS_MASK);
err |= -EINVAL;
} /* The only flag allowed is CR_EDGE, which is ignored. */ if (cmd->scan_begin_arg & CR_FLAGS_MASK & ~CR_EDGE) {
cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
CR_FLAGS_MASK & ~CR_EDGE);
err |= -EINVAL;
}
} elseif (cmd->scan_begin_src == TRIG_TIMER) { /* N.B. cmd->convert_arg is also TRIG_TIMER */ if (!pci230_ai_check_scan_period(cmd))
err |= -EINVAL;
spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); if (!devpriv->ai_cmd_started) {
spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags); return 1;
} /* * Trigger conversion by toggling Z2-CT2 output. * Finish with output high.
*/
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1); /* * Delay. Should driver be responsible for this? An * alternative would be to wait until conversion is complete, * but we can't tell when it's complete because the ADC busy * bit has a different meaning when FIFO enabled (and when * FIFO not enabled, it only works for software triggers).
*/ if ((devpriv->adccon & PCI230_ADC_IM_MASK) == PCI230_ADC_IM_DIF &&
devpriv->hwver == 0) { /* PCI230/260 in differential mode */
delayus = 8;
} else { /* single-ended or PCI230+/260+ */
delayus = 4;
}
spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
udelay(delayus); return 1;
}
/* * Update conversion trigger source which is currently set * to CT2 output, which is currently stuck high.
*/ switch (cmd->convert_src) { default:
conv = PCI230_ADC_TRIG_NONE; break; case TRIG_TIMER: /* Using CT2 output. */
conv = PCI230_ADC_TRIG_Z2CT2; break; case TRIG_EXT: if (cmd->convert_arg & CR_EDGE) { if ((cmd->convert_arg & CR_INVERT) == 0) { /* Trigger on +ve edge. */
conv = PCI230_ADC_TRIG_EXTP;
} else { /* Trigger on -ve edge. */
conv = PCI230_ADC_TRIG_EXTN;
}
} else { /* Backwards compatibility. */ if (cmd->convert_arg) { /* Trigger on +ve edge. */
conv = PCI230_ADC_TRIG_EXTP;
} else { /* Trigger on -ve edge. */
conv = PCI230_ADC_TRIG_EXTN;
}
} break; case TRIG_INT: /* * Use CT2 output for software trigger due to problems * in differential mode on PCI230/260.
*/
conv = PCI230_ADC_TRIG_Z2CT2; break;
}
devpriv->adccon = (devpriv->adccon & ~PCI230_ADC_TRIG_MASK) | conv;
outw(devpriv->adccon, devpriv->daqio + PCI230_ADCCON); if (cmd->convert_src == TRIG_INT)
async->inttrig = pci230_ai_inttrig_convert;
/* * Update FIFO interrupt trigger level, which is currently * set to "full".
*/
pci230_ai_update_fifo_trigger_level(dev, s); if (cmd->convert_src == TRIG_TIMER) { /* Update timer gates. */ unsignedchar zgat;
if (cmd->scan_begin_src != TRIG_FOLLOW) { /* * Conversion timer CT2 needs to be gated by * inverted output of monostable CT2.
*/
zgat = pci230_gat_config(2, GAT_NOUTNM2);
} else { /* * Conversion timer CT2 needs to be gated on * continuously.
*/
zgat = pci230_gat_config(2, GAT_VCC);
}
outb(zgat, dev->iobase + PCI230_ZGAT_SCE); if (cmd->scan_begin_src != TRIG_FOLLOW) { /* Set monostable CT0 trigger source. */ switch (cmd->scan_begin_src) { default:
zgat = pci230_gat_config(0, GAT_VCC); break; case TRIG_EXT: /* * For CT0 on PCI230, the external trigger * (gate) signal comes from PPC0, which is * channel 16 of the DIO subdevice. The * application needs to configure this as an * input in order to use it as an external scan * trigger.
*/
zgat = pci230_gat_config(0, GAT_EXT); break; case TRIG_TIMER: /* * Monostable CT0 triggered by rising edge on * inverted output of CT1 (falling edge on CT1).
*/
zgat = pci230_gat_config(0, GAT_NOUTNM2); break; case TRIG_INT: /* * Monostable CT0 is triggered by inttrig * function waggling the CT0 gate source.
*/
zgat = pci230_gat_config(0, GAT_VCC); break;
}
outb(zgat, dev->iobase + PCI230_ZGAT_SCE); switch (cmd->scan_begin_src) { case TRIG_TIMER: /* * Scan period timer CT1 needs to be * gated on to start counting.
*/
zgat = pci230_gat_config(1, GAT_VCC);
outb(zgat, dev->iobase + PCI230_ZGAT_SCE); break; case TRIG_INT:
async->inttrig = pci230_ai_inttrig_scan_begin; break;
}
}
} elseif (cmd->convert_src != TRIG_INT) { /* No longer need Z2-CT2. */
pci230_release_shared(dev, RES_Z2CT2, OWNER_AICMD);
}
}
/* 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
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.