162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * adv_pci1710.c 462306a36Sopenharmony_ci * Comedi driver for Advantech PCI-1710 series boards 562306a36Sopenharmony_ci * Author: Michal Dobes <dobes@tesnet.cz> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn> 862306a36Sopenharmony_ci * for testing and information. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Driver: adv_pci1710 1362306a36Sopenharmony_ci * Description: Comedi driver for Advantech PCI-1710 series boards 1462306a36Sopenharmony_ci * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711, 1562306a36Sopenharmony_ci * PCI-1713, PCI-1731 1662306a36Sopenharmony_ci * Author: Michal Dobes <dobes@tesnet.cz> 1762306a36Sopenharmony_ci * Updated: Fri, 29 Oct 2015 17:19:35 -0700 1862306a36Sopenharmony_ci * Status: works 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Configuration options: not applicable, uses PCI auto config 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * This driver supports AI, AO, DI and DO subdevices. 2362306a36Sopenharmony_ci * AI subdevice supports cmd and insn interface, 2462306a36Sopenharmony_ci * other subdevices support only insn interface. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * The PCI-1710 and PCI-1710HG have the same PCI device ID, so the 2762306a36Sopenharmony_ci * driver cannot distinguish between them, as would be normal for a 2862306a36Sopenharmony_ci * PCI driver. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/module.h> 3262306a36Sopenharmony_ci#include <linux/interrupt.h> 3362306a36Sopenharmony_ci#include <linux/comedi/comedi_pci.h> 3462306a36Sopenharmony_ci#include <linux/comedi/comedi_8254.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "amcc_s5933.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * PCI BAR2 Register map (dev->iobase) 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define PCI171X_AD_DATA_REG 0x00 /* R: A/D data */ 4262306a36Sopenharmony_ci#define PCI171X_SOFTTRG_REG 0x00 /* W: soft trigger for A/D */ 4362306a36Sopenharmony_ci#define PCI171X_RANGE_REG 0x02 /* W: A/D gain/range register */ 4462306a36Sopenharmony_ci#define PCI171X_RANGE_DIFF BIT(5) 4562306a36Sopenharmony_ci#define PCI171X_RANGE_UNI BIT(4) 4662306a36Sopenharmony_ci#define PCI171X_RANGE_GAIN(x) (((x) & 0x7) << 0) 4762306a36Sopenharmony_ci#define PCI171X_MUX_REG 0x04 /* W: A/D multiplexor control */ 4862306a36Sopenharmony_ci#define PCI171X_MUX_CHANH(x) (((x) & 0xff) << 8) 4962306a36Sopenharmony_ci#define PCI171X_MUX_CHANL(x) (((x) & 0xff) << 0) 5062306a36Sopenharmony_ci#define PCI171X_MUX_CHAN(x) (PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x)) 5162306a36Sopenharmony_ci#define PCI171X_STATUS_REG 0x06 /* R: status register */ 5262306a36Sopenharmony_ci#define PCI171X_STATUS_IRQ BIT(11) /* 1=IRQ occurred */ 5362306a36Sopenharmony_ci#define PCI171X_STATUS_FF BIT(10) /* 1=FIFO is full, fatal error */ 5462306a36Sopenharmony_ci#define PCI171X_STATUS_FH BIT(9) /* 1=FIFO is half full */ 5562306a36Sopenharmony_ci#define PCI171X_STATUS_FE BIT(8) /* 1=FIFO is empty */ 5662306a36Sopenharmony_ci#define PCI171X_CTRL_REG 0x06 /* W: control register */ 5762306a36Sopenharmony_ci#define PCI171X_CTRL_CNT0 BIT(6) /* 1=ext. clk, 0=int. 100kHz clk */ 5862306a36Sopenharmony_ci#define PCI171X_CTRL_ONEFH BIT(5) /* 1=on FIFO half full, 0=on sample */ 5962306a36Sopenharmony_ci#define PCI171X_CTRL_IRQEN BIT(4) /* 1=enable IRQ */ 6062306a36Sopenharmony_ci#define PCI171X_CTRL_GATE BIT(3) /* 1=enable ext. trigger GATE (8254?) */ 6162306a36Sopenharmony_ci#define PCI171X_CTRL_EXT BIT(2) /* 1=enable ext. trigger source */ 6262306a36Sopenharmony_ci#define PCI171X_CTRL_PACER BIT(1) /* 1=enable int. 8254 trigger source */ 6362306a36Sopenharmony_ci#define PCI171X_CTRL_SW BIT(0) /* 1=enable software trigger source */ 6462306a36Sopenharmony_ci#define PCI171X_CLRINT_REG 0x08 /* W: clear interrupts request */ 6562306a36Sopenharmony_ci#define PCI171X_CLRFIFO_REG 0x09 /* W: clear FIFO */ 6662306a36Sopenharmony_ci#define PCI171X_DA_REG(x) (0x0a + ((x) * 2)) /* W: D/A register */ 6762306a36Sopenharmony_ci#define PCI171X_DAREF_REG 0x0e /* W: D/A reference control */ 6862306a36Sopenharmony_ci#define PCI171X_DAREF(c, r) (((r) & 0x3) << ((c) * 2)) 6962306a36Sopenharmony_ci#define PCI171X_DAREF_MASK(c) PCI171X_DAREF((c), 0x3) 7062306a36Sopenharmony_ci#define PCI171X_DI_REG 0x10 /* R: digital inputs */ 7162306a36Sopenharmony_ci#define PCI171X_DO_REG 0x10 /* W: digital outputs */ 7262306a36Sopenharmony_ci#define PCI171X_TIMER_BASE 0x18 /* R/W: 8254 timer */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic const struct comedi_lrange pci1710_ai_range = { 7562306a36Sopenharmony_ci 9, { 7662306a36Sopenharmony_ci BIP_RANGE(5), /* gain 1 (0x00) */ 7762306a36Sopenharmony_ci BIP_RANGE(2.5), /* gain 2 (0x01) */ 7862306a36Sopenharmony_ci BIP_RANGE(1.25), /* gain 4 (0x02) */ 7962306a36Sopenharmony_ci BIP_RANGE(0.625), /* gain 8 (0x03) */ 8062306a36Sopenharmony_ci BIP_RANGE(10), /* gain 0.5 (0x04) */ 8162306a36Sopenharmony_ci UNI_RANGE(10), /* gain 1 (0x00 | UNI) */ 8262306a36Sopenharmony_ci UNI_RANGE(5), /* gain 2 (0x01 | UNI) */ 8362306a36Sopenharmony_ci UNI_RANGE(2.5), /* gain 4 (0x02 | UNI) */ 8462306a36Sopenharmony_ci UNI_RANGE(1.25) /* gain 8 (0x03 | UNI) */ 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct comedi_lrange pci1710hg_ai_range = { 8962306a36Sopenharmony_ci 12, { 9062306a36Sopenharmony_ci BIP_RANGE(5), /* gain 1 (0x00) */ 9162306a36Sopenharmony_ci BIP_RANGE(0.5), /* gain 10 (0x01) */ 9262306a36Sopenharmony_ci BIP_RANGE(0.05), /* gain 100 (0x02) */ 9362306a36Sopenharmony_ci BIP_RANGE(0.005), /* gain 1000 (0x03) */ 9462306a36Sopenharmony_ci BIP_RANGE(10), /* gain 0.5 (0x04) */ 9562306a36Sopenharmony_ci BIP_RANGE(1), /* gain 5 (0x05) */ 9662306a36Sopenharmony_ci BIP_RANGE(0.1), /* gain 50 (0x06) */ 9762306a36Sopenharmony_ci BIP_RANGE(0.01), /* gain 500 (0x07) */ 9862306a36Sopenharmony_ci UNI_RANGE(10), /* gain 1 (0x00 | UNI) */ 9962306a36Sopenharmony_ci UNI_RANGE(1), /* gain 10 (0x01 | UNI) */ 10062306a36Sopenharmony_ci UNI_RANGE(0.1), /* gain 100 (0x02 | UNI) */ 10162306a36Sopenharmony_ci UNI_RANGE(0.01) /* gain 1000 (0x03 | UNI) */ 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const struct comedi_lrange pci1711_ai_range = { 10662306a36Sopenharmony_ci 5, { 10762306a36Sopenharmony_ci BIP_RANGE(10), /* gain 1 (0x00) */ 10862306a36Sopenharmony_ci BIP_RANGE(5), /* gain 2 (0x01) */ 10962306a36Sopenharmony_ci BIP_RANGE(2.5), /* gain 4 (0x02) */ 11062306a36Sopenharmony_ci BIP_RANGE(1.25), /* gain 8 (0x03) */ 11162306a36Sopenharmony_ci BIP_RANGE(0.625) /* gain 16 (0x04) */ 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct comedi_lrange pci171x_ao_range = { 11662306a36Sopenharmony_ci 3, { 11762306a36Sopenharmony_ci UNI_RANGE(5), /* internal -5V ref */ 11862306a36Sopenharmony_ci UNI_RANGE(10), /* internal -10V ref */ 11962306a36Sopenharmony_ci RANGE_ext(0, 1) /* external -Vref (+/-10V max) */ 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cienum pci1710_boardid { 12462306a36Sopenharmony_ci BOARD_PCI1710, 12562306a36Sopenharmony_ci BOARD_PCI1710HG, 12662306a36Sopenharmony_ci BOARD_PCI1711, 12762306a36Sopenharmony_ci BOARD_PCI1713, 12862306a36Sopenharmony_ci BOARD_PCI1731, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistruct boardtype { 13262306a36Sopenharmony_ci const char *name; 13362306a36Sopenharmony_ci const struct comedi_lrange *ai_range; 13462306a36Sopenharmony_ci unsigned int is_pci1711:1; 13562306a36Sopenharmony_ci unsigned int is_pci1713:1; 13662306a36Sopenharmony_ci unsigned int has_ao:1; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic const struct boardtype boardtypes[] = { 14062306a36Sopenharmony_ci [BOARD_PCI1710] = { 14162306a36Sopenharmony_ci .name = "pci1710", 14262306a36Sopenharmony_ci .ai_range = &pci1710_ai_range, 14362306a36Sopenharmony_ci .has_ao = 1, 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci [BOARD_PCI1710HG] = { 14662306a36Sopenharmony_ci .name = "pci1710hg", 14762306a36Sopenharmony_ci .ai_range = &pci1710hg_ai_range, 14862306a36Sopenharmony_ci .has_ao = 1, 14962306a36Sopenharmony_ci }, 15062306a36Sopenharmony_ci [BOARD_PCI1711] = { 15162306a36Sopenharmony_ci .name = "pci1711", 15262306a36Sopenharmony_ci .ai_range = &pci1711_ai_range, 15362306a36Sopenharmony_ci .is_pci1711 = 1, 15462306a36Sopenharmony_ci .has_ao = 1, 15562306a36Sopenharmony_ci }, 15662306a36Sopenharmony_ci [BOARD_PCI1713] = { 15762306a36Sopenharmony_ci .name = "pci1713", 15862306a36Sopenharmony_ci .ai_range = &pci1710_ai_range, 15962306a36Sopenharmony_ci .is_pci1713 = 1, 16062306a36Sopenharmony_ci }, 16162306a36Sopenharmony_ci [BOARD_PCI1731] = { 16262306a36Sopenharmony_ci .name = "pci1731", 16362306a36Sopenharmony_ci .ai_range = &pci1711_ai_range, 16462306a36Sopenharmony_ci .is_pci1711 = 1, 16562306a36Sopenharmony_ci }, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistruct pci1710_private { 16962306a36Sopenharmony_ci unsigned int max_samples; 17062306a36Sopenharmony_ci unsigned int ctrl; /* control register value */ 17162306a36Sopenharmony_ci unsigned int ctrl_ext; /* used to switch from TRIG_EXT to TRIG_xxx */ 17262306a36Sopenharmony_ci unsigned int mux_scan; /* used to set the channel interval to scan */ 17362306a36Sopenharmony_ci unsigned char ai_et; 17462306a36Sopenharmony_ci unsigned int act_chanlist[32]; /* list of scanned channel */ 17562306a36Sopenharmony_ci unsigned char saved_seglen; /* len of the non-repeating chanlist */ 17662306a36Sopenharmony_ci unsigned char da_ranges; /* copy of D/A outpit range register */ 17762306a36Sopenharmony_ci unsigned char unipolar_gain; /* adjust for unipolar gain codes */ 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int pci1710_ai_check_chanlist(struct comedi_device *dev, 18162306a36Sopenharmony_ci struct comedi_subdevice *s, 18262306a36Sopenharmony_ci struct comedi_cmd *cmd) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 18562306a36Sopenharmony_ci unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); 18662306a36Sopenharmony_ci unsigned int last_aref = CR_AREF(cmd->chanlist[0]); 18762306a36Sopenharmony_ci unsigned int next_chan = (chan0 + 1) % s->n_chan; 18862306a36Sopenharmony_ci unsigned int chansegment[32]; 18962306a36Sopenharmony_ci unsigned int seglen; 19062306a36Sopenharmony_ci int i; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (cmd->chanlist_len == 1) { 19362306a36Sopenharmony_ci devpriv->saved_seglen = cmd->chanlist_len; 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* first channel is always ok */ 19862306a36Sopenharmony_ci chansegment[0] = cmd->chanlist[0]; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci for (i = 1; i < cmd->chanlist_len; i++) { 20162306a36Sopenharmony_ci unsigned int chan = CR_CHAN(cmd->chanlist[i]); 20262306a36Sopenharmony_ci unsigned int aref = CR_AREF(cmd->chanlist[i]); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (cmd->chanlist[0] == cmd->chanlist[i]) 20562306a36Sopenharmony_ci break; /* we detected a loop, stop */ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (aref == AREF_DIFF && (chan & 1)) { 20862306a36Sopenharmony_ci dev_err(dev->class_dev, 20962306a36Sopenharmony_ci "Odd channel cannot be differential input!\n"); 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (last_aref == AREF_DIFF) 21462306a36Sopenharmony_ci next_chan = (next_chan + 1) % s->n_chan; 21562306a36Sopenharmony_ci if (chan != next_chan) { 21662306a36Sopenharmony_ci dev_err(dev->class_dev, 21762306a36Sopenharmony_ci "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n", 21862306a36Sopenharmony_ci i, chan, next_chan, chan0); 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* next correct channel in list */ 22362306a36Sopenharmony_ci chansegment[i] = cmd->chanlist[i]; 22462306a36Sopenharmony_ci last_aref = aref; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci seglen = i; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (i = 0; i < cmd->chanlist_len; i++) { 22962306a36Sopenharmony_ci if (cmd->chanlist[i] != chansegment[i % seglen]) { 23062306a36Sopenharmony_ci dev_err(dev->class_dev, 23162306a36Sopenharmony_ci "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n", 23262306a36Sopenharmony_ci i, CR_CHAN(chansegment[i]), 23362306a36Sopenharmony_ci CR_RANGE(chansegment[i]), 23462306a36Sopenharmony_ci CR_AREF(chansegment[i]), 23562306a36Sopenharmony_ci CR_CHAN(cmd->chanlist[i % seglen]), 23662306a36Sopenharmony_ci CR_RANGE(cmd->chanlist[i % seglen]), 23762306a36Sopenharmony_ci CR_AREF(chansegment[i % seglen])); 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci devpriv->saved_seglen = seglen; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void pci1710_ai_setup_chanlist(struct comedi_device *dev, 24762306a36Sopenharmony_ci struct comedi_subdevice *s, 24862306a36Sopenharmony_ci unsigned int *chanlist, 24962306a36Sopenharmony_ci unsigned int n_chan, 25062306a36Sopenharmony_ci unsigned int seglen) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 25362306a36Sopenharmony_ci unsigned int first_chan = CR_CHAN(chanlist[0]); 25462306a36Sopenharmony_ci unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]); 25562306a36Sopenharmony_ci unsigned int i; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci for (i = 0; i < seglen; i++) { /* store range list to card */ 25862306a36Sopenharmony_ci unsigned int chan = CR_CHAN(chanlist[i]); 25962306a36Sopenharmony_ci unsigned int range = CR_RANGE(chanlist[i]); 26062306a36Sopenharmony_ci unsigned int aref = CR_AREF(chanlist[i]); 26162306a36Sopenharmony_ci unsigned int rangeval = 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (aref == AREF_DIFF) 26462306a36Sopenharmony_ci rangeval |= PCI171X_RANGE_DIFF; 26562306a36Sopenharmony_ci if (comedi_range_is_unipolar(s, range)) { 26662306a36Sopenharmony_ci rangeval |= PCI171X_RANGE_UNI; 26762306a36Sopenharmony_ci range -= devpriv->unipolar_gain; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci rangeval |= PCI171X_RANGE_GAIN(range); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* select channel and set range */ 27262306a36Sopenharmony_ci outw(PCI171X_MUX_CHAN(chan), dev->iobase + PCI171X_MUX_REG); 27362306a36Sopenharmony_ci outw(rangeval, dev->iobase + PCI171X_RANGE_REG); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci devpriv->act_chanlist[i] = chan; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci for ( ; i < n_chan; i++) /* store remainder of channel list */ 27862306a36Sopenharmony_ci devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* select channel interval to scan */ 28162306a36Sopenharmony_ci devpriv->mux_scan = PCI171X_MUX_CHANL(first_chan) | 28262306a36Sopenharmony_ci PCI171X_MUX_CHANH(last_chan); 28362306a36Sopenharmony_ci outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int pci1710_ai_eoc(struct comedi_device *dev, 28762306a36Sopenharmony_ci struct comedi_subdevice *s, 28862306a36Sopenharmony_ci struct comedi_insn *insn, 28962306a36Sopenharmony_ci unsigned long context) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci unsigned int status; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci status = inw(dev->iobase + PCI171X_STATUS_REG); 29462306a36Sopenharmony_ci if ((status & PCI171X_STATUS_FE) == 0) 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci return -EBUSY; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int pci1710_ai_read_sample(struct comedi_device *dev, 30062306a36Sopenharmony_ci struct comedi_subdevice *s, 30162306a36Sopenharmony_ci unsigned int cur_chan, 30262306a36Sopenharmony_ci unsigned short *val) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci const struct boardtype *board = dev->board_ptr; 30562306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 30662306a36Sopenharmony_ci unsigned short sample; 30762306a36Sopenharmony_ci unsigned int chan; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci sample = inw(dev->iobase + PCI171X_AD_DATA_REG); 31062306a36Sopenharmony_ci if (!board->is_pci1713) { 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * The upper 4 bits of the 16-bit sample are the channel number 31362306a36Sopenharmony_ci * that the sample was acquired from. Verify that this channel 31462306a36Sopenharmony_ci * number matches the expected channel number. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci chan = sample >> 12; 31762306a36Sopenharmony_ci if (chan != devpriv->act_chanlist[cur_chan]) { 31862306a36Sopenharmony_ci dev_err(dev->class_dev, 31962306a36Sopenharmony_ci "A/D data dropout: received from channel %d, expected %d\n", 32062306a36Sopenharmony_ci chan, devpriv->act_chanlist[cur_chan]); 32162306a36Sopenharmony_ci return -ENODATA; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci *val = sample & s->maxdata; 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int pci1710_ai_insn_read(struct comedi_device *dev, 32962306a36Sopenharmony_ci struct comedi_subdevice *s, 33062306a36Sopenharmony_ci struct comedi_insn *insn, 33162306a36Sopenharmony_ci unsigned int *data) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 33462306a36Sopenharmony_ci int ret = 0; 33562306a36Sopenharmony_ci int i; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* enable software trigger */ 33862306a36Sopenharmony_ci devpriv->ctrl |= PCI171X_CTRL_SW; 33962306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRFIFO_REG); 34262306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci pci1710_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) { 34762306a36Sopenharmony_ci unsigned short val; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* start conversion */ 35062306a36Sopenharmony_ci outw(0, dev->iobase + PCI171X_SOFTTRG_REG); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ret = comedi_timeout(dev, s, insn, pci1710_ai_eoc, 0); 35362306a36Sopenharmony_ci if (ret) 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = pci1710_ai_read_sample(dev, s, 0, &val); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci data[i] = val; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* disable software trigger */ 36462306a36Sopenharmony_ci devpriv->ctrl &= ~PCI171X_CTRL_SW; 36562306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRFIFO_REG); 36862306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return ret ? ret : insn->n; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int pci1710_ai_cancel(struct comedi_device *dev, 37462306a36Sopenharmony_ci struct comedi_subdevice *s) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* disable A/D triggers and interrupt sources */ 37962306a36Sopenharmony_ci devpriv->ctrl &= PCI171X_CTRL_CNT0; /* preserve counter 0 clk src */ 38062306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* disable pacer */ 38362306a36Sopenharmony_ci comedi_8254_pacer_enable(dev->pacer, 1, 2, false); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* clear A/D FIFO and any pending interrutps */ 38662306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRFIFO_REG); 38762306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void pci1710_handle_every_sample(struct comedi_device *dev, 39362306a36Sopenharmony_ci struct comedi_subdevice *s) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct comedi_cmd *cmd = &s->async->cmd; 39662306a36Sopenharmony_ci unsigned int status; 39762306a36Sopenharmony_ci unsigned short val; 39862306a36Sopenharmony_ci int ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci status = inw(dev->iobase + PCI171X_STATUS_REG); 40162306a36Sopenharmony_ci if (status & PCI171X_STATUS_FE) { 40262306a36Sopenharmony_ci dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status); 40362306a36Sopenharmony_ci s->async->events |= COMEDI_CB_ERROR; 40462306a36Sopenharmony_ci return; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci if (status & PCI171X_STATUS_FF) { 40762306a36Sopenharmony_ci dev_dbg(dev->class_dev, 40862306a36Sopenharmony_ci "A/D FIFO Full status (Fatal Error!) (%4x)\n", status); 40962306a36Sopenharmony_ci s->async->events |= COMEDI_CB_ERROR; 41062306a36Sopenharmony_ci return; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci for (; !(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) { 41662306a36Sopenharmony_ci ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val); 41762306a36Sopenharmony_ci if (ret) { 41862306a36Sopenharmony_ci s->async->events |= COMEDI_CB_ERROR; 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci comedi_buf_write_samples(s, &val, 1); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (cmd->stop_src == TRIG_COUNT && 42562306a36Sopenharmony_ci s->async->scans_done >= cmd->stop_arg) { 42662306a36Sopenharmony_ci s->async->events |= COMEDI_CB_EOA; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void pci1710_handle_fifo(struct comedi_device *dev, 43562306a36Sopenharmony_ci struct comedi_subdevice *s) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 43862306a36Sopenharmony_ci struct comedi_async *async = s->async; 43962306a36Sopenharmony_ci struct comedi_cmd *cmd = &async->cmd; 44062306a36Sopenharmony_ci unsigned int status; 44162306a36Sopenharmony_ci int i; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci status = inw(dev->iobase + PCI171X_STATUS_REG); 44462306a36Sopenharmony_ci if (!(status & PCI171X_STATUS_FH)) { 44562306a36Sopenharmony_ci dev_dbg(dev->class_dev, "A/D FIFO not half full!\n"); 44662306a36Sopenharmony_ci async->events |= COMEDI_CB_ERROR; 44762306a36Sopenharmony_ci return; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci if (status & PCI171X_STATUS_FF) { 45062306a36Sopenharmony_ci dev_dbg(dev->class_dev, 45162306a36Sopenharmony_ci "A/D FIFO Full status (Fatal Error!)\n"); 45262306a36Sopenharmony_ci async->events |= COMEDI_CB_ERROR; 45362306a36Sopenharmony_ci return; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (i = 0; i < devpriv->max_samples; i++) { 45762306a36Sopenharmony_ci unsigned short val; 45862306a36Sopenharmony_ci int ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val); 46162306a36Sopenharmony_ci if (ret) { 46262306a36Sopenharmony_ci s->async->events |= COMEDI_CB_ERROR; 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!comedi_buf_write_samples(s, &val, 1)) 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (cmd->stop_src == TRIG_COUNT && 47062306a36Sopenharmony_ci async->scans_done >= cmd->stop_arg) { 47162306a36Sopenharmony_ci async->events |= COMEDI_CB_EOA; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic irqreturn_t pci1710_irq_handler(int irq, void *d) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct comedi_device *dev = d; 48262306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 48362306a36Sopenharmony_ci struct comedi_subdevice *s; 48462306a36Sopenharmony_ci struct comedi_cmd *cmd; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!dev->attached) /* is device attached? */ 48762306a36Sopenharmony_ci return IRQ_NONE; /* no, exit */ 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci s = dev->read_subdev; 49062306a36Sopenharmony_ci cmd = &s->async->cmd; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* is this interrupt from our board? */ 49362306a36Sopenharmony_ci if (!(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_IRQ)) 49462306a36Sopenharmony_ci return IRQ_NONE; /* no, exit */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */ 49762306a36Sopenharmony_ci devpriv->ai_et = 0; 49862306a36Sopenharmony_ci devpriv->ctrl &= PCI171X_CTRL_CNT0; 49962306a36Sopenharmony_ci devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */ 50062306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 50162306a36Sopenharmony_ci devpriv->ctrl = devpriv->ctrl_ext; 50262306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRFIFO_REG); 50362306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 50462306a36Sopenharmony_ci /* no sample on this interrupt; reset the channel interval */ 50562306a36Sopenharmony_ci outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG); 50662306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 50762306a36Sopenharmony_ci comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 50862306a36Sopenharmony_ci return IRQ_HANDLED; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (cmd->flags & CMDF_WAKE_EOS) 51262306a36Sopenharmony_ci pci1710_handle_every_sample(dev, s); 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci pci1710_handle_fifo(dev, s); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci comedi_handle_events(dev, s); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return IRQ_HANDLED; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int pci1710_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 52462306a36Sopenharmony_ci struct comedi_cmd *cmd = &s->async->cmd; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci pci1710_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, 52762306a36Sopenharmony_ci devpriv->saved_seglen); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRFIFO_REG); 53062306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci devpriv->ctrl &= PCI171X_CTRL_CNT0; 53362306a36Sopenharmony_ci if ((cmd->flags & CMDF_WAKE_EOS) == 0) 53462306a36Sopenharmony_ci devpriv->ctrl |= PCI171X_CTRL_ONEFH; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (cmd->convert_src == TRIG_TIMER) { 53762306a36Sopenharmony_ci comedi_8254_update_divisors(dev->pacer); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci devpriv->ctrl |= PCI171X_CTRL_PACER | PCI171X_CTRL_IRQEN; 54062306a36Sopenharmony_ci if (cmd->start_src == TRIG_EXT) { 54162306a36Sopenharmony_ci devpriv->ctrl_ext = devpriv->ctrl; 54262306a36Sopenharmony_ci devpriv->ctrl &= ~(PCI171X_CTRL_PACER | 54362306a36Sopenharmony_ci PCI171X_CTRL_ONEFH | 54462306a36Sopenharmony_ci PCI171X_CTRL_GATE); 54562306a36Sopenharmony_ci devpriv->ctrl |= PCI171X_CTRL_EXT; 54662306a36Sopenharmony_ci devpriv->ai_et = 1; 54762306a36Sopenharmony_ci } else { /* TRIG_NOW */ 54862306a36Sopenharmony_ci devpriv->ai_et = 0; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (cmd->start_src == TRIG_NOW) 55362306a36Sopenharmony_ci comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 55462306a36Sopenharmony_ci } else { /* TRIG_EXT */ 55562306a36Sopenharmony_ci devpriv->ctrl |= PCI171X_CTRL_EXT | PCI171X_CTRL_IRQEN; 55662306a36Sopenharmony_ci outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int pci1710_ai_cmdtest(struct comedi_device *dev, 56362306a36Sopenharmony_ci struct comedi_subdevice *s, 56462306a36Sopenharmony_ci struct comedi_cmd *cmd) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci int err = 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* Step 1 : check if triggers are trivially valid */ 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); 57162306a36Sopenharmony_ci err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 57262306a36Sopenharmony_ci err |= comedi_check_trigger_src(&cmd->convert_src, 57362306a36Sopenharmony_ci TRIG_TIMER | TRIG_EXT); 57462306a36Sopenharmony_ci err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 57562306a36Sopenharmony_ci err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (err) 57862306a36Sopenharmony_ci return 1; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* step 2a: make sure trigger sources are unique */ 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci err |= comedi_check_trigger_is_unique(cmd->start_src); 58362306a36Sopenharmony_ci err |= comedi_check_trigger_is_unique(cmd->convert_src); 58462306a36Sopenharmony_ci err |= comedi_check_trigger_is_unique(cmd->stop_src); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* step 2b: and mutually compatible */ 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (err) 58962306a36Sopenharmony_ci return 2; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Step 3: check if arguments are trivially valid */ 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 59462306a36Sopenharmony_ci err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (cmd->convert_src == TRIG_TIMER) 59762306a36Sopenharmony_ci err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); 59862306a36Sopenharmony_ci else /* TRIG_FOLLOW */ 59962306a36Sopenharmony_ci err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 60262306a36Sopenharmony_ci cmd->chanlist_len); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (cmd->stop_src == TRIG_COUNT) 60562306a36Sopenharmony_ci err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 60662306a36Sopenharmony_ci else /* TRIG_NONE */ 60762306a36Sopenharmony_ci err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (err) 61062306a36Sopenharmony_ci return 3; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* step 4: fix up any arguments */ 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (cmd->convert_src == TRIG_TIMER) { 61562306a36Sopenharmony_ci unsigned int arg = cmd->convert_arg; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); 61862306a36Sopenharmony_ci err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (err) 62262306a36Sopenharmony_ci return 4; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Step 5: check channel list */ 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci err |= pci1710_ai_check_chanlist(dev, s, cmd); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (err) 62962306a36Sopenharmony_ci return 5; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int pci1710_ao_insn_write(struct comedi_device *dev, 63562306a36Sopenharmony_ci struct comedi_subdevice *s, 63662306a36Sopenharmony_ci struct comedi_insn *insn, 63762306a36Sopenharmony_ci unsigned int *data) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 64062306a36Sopenharmony_ci unsigned int chan = CR_CHAN(insn->chanspec); 64162306a36Sopenharmony_ci unsigned int range = CR_RANGE(insn->chanspec); 64262306a36Sopenharmony_ci unsigned int val = s->readback[chan]; 64362306a36Sopenharmony_ci int i; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci devpriv->da_ranges &= ~PCI171X_DAREF_MASK(chan); 64662306a36Sopenharmony_ci devpriv->da_ranges |= PCI171X_DAREF(chan, range); 64762306a36Sopenharmony_ci outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci for (i = 0; i < insn->n; i++) { 65062306a36Sopenharmony_ci val = data[i]; 65162306a36Sopenharmony_ci outw(val, dev->iobase + PCI171X_DA_REG(chan)); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci s->readback[chan] = val; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return insn->n; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int pci1710_di_insn_bits(struct comedi_device *dev, 66062306a36Sopenharmony_ci struct comedi_subdevice *s, 66162306a36Sopenharmony_ci struct comedi_insn *insn, 66262306a36Sopenharmony_ci unsigned int *data) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci data[1] = inw(dev->iobase + PCI171X_DI_REG); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return insn->n; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int pci1710_do_insn_bits(struct comedi_device *dev, 67062306a36Sopenharmony_ci struct comedi_subdevice *s, 67162306a36Sopenharmony_ci struct comedi_insn *insn, 67262306a36Sopenharmony_ci unsigned int *data) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci if (comedi_dio_update_state(s, data)) 67562306a36Sopenharmony_ci outw(s->state, dev->iobase + PCI171X_DO_REG); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci data[1] = s->state; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return insn->n; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int pci1710_counter_insn_config(struct comedi_device *dev, 68362306a36Sopenharmony_ci struct comedi_subdevice *s, 68462306a36Sopenharmony_ci struct comedi_insn *insn, 68562306a36Sopenharmony_ci unsigned int *data) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct pci1710_private *devpriv = dev->private; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci switch (data[0]) { 69062306a36Sopenharmony_ci case INSN_CONFIG_SET_CLOCK_SRC: 69162306a36Sopenharmony_ci switch (data[1]) { 69262306a36Sopenharmony_ci case 0: /* internal */ 69362306a36Sopenharmony_ci devpriv->ctrl_ext &= ~PCI171X_CTRL_CNT0; 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci case 1: /* external */ 69662306a36Sopenharmony_ci devpriv->ctrl_ext |= PCI171X_CTRL_CNT0; 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci default: 69962306a36Sopenharmony_ci return -EINVAL; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci outw(devpriv->ctrl_ext, dev->iobase + PCI171X_CTRL_REG); 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci case INSN_CONFIG_GET_CLOCK_SRC: 70462306a36Sopenharmony_ci if (devpriv->ctrl_ext & PCI171X_CTRL_CNT0) { 70562306a36Sopenharmony_ci data[1] = 1; 70662306a36Sopenharmony_ci data[2] = 0; 70762306a36Sopenharmony_ci } else { 70862306a36Sopenharmony_ci data[1] = 0; 70962306a36Sopenharmony_ci data[2] = I8254_OSC_BASE_1MHZ; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci default: 71362306a36Sopenharmony_ci return -EINVAL; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return insn->n; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void pci1710_reset(struct comedi_device *dev) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci const struct boardtype *board = dev->board_ptr; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * Disable A/D triggers and interrupt sources, set counter 0 72562306a36Sopenharmony_ci * to use internal 1 MHz clock. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci outw(0, dev->iobase + PCI171X_CTRL_REG); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* clear A/D FIFO and any pending interrutps */ 73062306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRFIFO_REG); 73162306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_CLRINT_REG); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (board->has_ao) { 73462306a36Sopenharmony_ci /* set DACs to 0..5V and outputs to 0V */ 73562306a36Sopenharmony_ci outb(0, dev->iobase + PCI171X_DAREF_REG); 73662306a36Sopenharmony_ci outw(0, dev->iobase + PCI171X_DA_REG(0)); 73762306a36Sopenharmony_ci outw(0, dev->iobase + PCI171X_DA_REG(1)); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* set digital outputs to 0 */ 74162306a36Sopenharmony_ci outw(0, dev->iobase + PCI171X_DO_REG); 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int pci1710_auto_attach(struct comedi_device *dev, 74562306a36Sopenharmony_ci unsigned long context) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct pci_dev *pcidev = comedi_to_pci_dev(dev); 74862306a36Sopenharmony_ci const struct boardtype *board = NULL; 74962306a36Sopenharmony_ci struct pci1710_private *devpriv; 75062306a36Sopenharmony_ci struct comedi_subdevice *s; 75162306a36Sopenharmony_ci int ret, subdev, n_subdevices; 75262306a36Sopenharmony_ci int i; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (context < ARRAY_SIZE(boardtypes)) 75562306a36Sopenharmony_ci board = &boardtypes[context]; 75662306a36Sopenharmony_ci if (!board) 75762306a36Sopenharmony_ci return -ENODEV; 75862306a36Sopenharmony_ci dev->board_ptr = board; 75962306a36Sopenharmony_ci dev->board_name = board->name; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 76262306a36Sopenharmony_ci if (!devpriv) 76362306a36Sopenharmony_ci return -ENOMEM; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci ret = comedi_pci_enable(dev); 76662306a36Sopenharmony_ci if (ret) 76762306a36Sopenharmony_ci return ret; 76862306a36Sopenharmony_ci dev->iobase = pci_resource_start(pcidev, 2); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE, 77162306a36Sopenharmony_ci I8254_OSC_BASE_10MHZ, I8254_IO16, 0); 77262306a36Sopenharmony_ci if (!dev->pacer) 77362306a36Sopenharmony_ci return -ENOMEM; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci n_subdevices = 1; /* all boards have analog inputs */ 77662306a36Sopenharmony_ci if (board->has_ao) 77762306a36Sopenharmony_ci n_subdevices++; 77862306a36Sopenharmony_ci if (!board->is_pci1713) { 77962306a36Sopenharmony_ci /* 78062306a36Sopenharmony_ci * All other boards have digital inputs and outputs as 78162306a36Sopenharmony_ci * well as a user counter. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ci n_subdevices += 3; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = comedi_alloc_subdevices(dev, n_subdevices); 78762306a36Sopenharmony_ci if (ret) 78862306a36Sopenharmony_ci return ret; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci pci1710_reset(dev); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (pcidev->irq) { 79362306a36Sopenharmony_ci ret = request_irq(pcidev->irq, pci1710_irq_handler, 79462306a36Sopenharmony_ci IRQF_SHARED, dev->board_name, dev); 79562306a36Sopenharmony_ci if (ret == 0) 79662306a36Sopenharmony_ci dev->irq = pcidev->irq; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci subdev = 0; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Analog Input subdevice */ 80262306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 80362306a36Sopenharmony_ci s->type = COMEDI_SUBD_AI; 80462306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE | SDF_GROUND; 80562306a36Sopenharmony_ci if (!board->is_pci1711) 80662306a36Sopenharmony_ci s->subdev_flags |= SDF_DIFF; 80762306a36Sopenharmony_ci s->n_chan = board->is_pci1713 ? 32 : 16; 80862306a36Sopenharmony_ci s->maxdata = 0x0fff; 80962306a36Sopenharmony_ci s->range_table = board->ai_range; 81062306a36Sopenharmony_ci s->insn_read = pci1710_ai_insn_read; 81162306a36Sopenharmony_ci if (dev->irq) { 81262306a36Sopenharmony_ci dev->read_subdev = s; 81362306a36Sopenharmony_ci s->subdev_flags |= SDF_CMD_READ; 81462306a36Sopenharmony_ci s->len_chanlist = s->n_chan; 81562306a36Sopenharmony_ci s->do_cmdtest = pci1710_ai_cmdtest; 81662306a36Sopenharmony_ci s->do_cmd = pci1710_ai_cmd; 81762306a36Sopenharmony_ci s->cancel = pci1710_ai_cancel; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* find the value needed to adjust for unipolar gain codes */ 82162306a36Sopenharmony_ci for (i = 0; i < s->range_table->length; i++) { 82262306a36Sopenharmony_ci if (comedi_range_is_unipolar(s, i)) { 82362306a36Sopenharmony_ci devpriv->unipolar_gain = i; 82462306a36Sopenharmony_ci break; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (board->has_ao) { 82962306a36Sopenharmony_ci /* Analog Output subdevice */ 83062306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 83162306a36Sopenharmony_ci s->type = COMEDI_SUBD_AO; 83262306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 83362306a36Sopenharmony_ci s->n_chan = 2; 83462306a36Sopenharmony_ci s->maxdata = 0x0fff; 83562306a36Sopenharmony_ci s->range_table = &pci171x_ao_range; 83662306a36Sopenharmony_ci s->insn_write = pci1710_ao_insn_write; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci ret = comedi_alloc_subdev_readback(s); 83962306a36Sopenharmony_ci if (ret) 84062306a36Sopenharmony_ci return ret; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (!board->is_pci1713) { 84462306a36Sopenharmony_ci /* Digital Input subdevice */ 84562306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 84662306a36Sopenharmony_ci s->type = COMEDI_SUBD_DI; 84762306a36Sopenharmony_ci s->subdev_flags = SDF_READABLE; 84862306a36Sopenharmony_ci s->n_chan = 16; 84962306a36Sopenharmony_ci s->maxdata = 1; 85062306a36Sopenharmony_ci s->range_table = &range_digital; 85162306a36Sopenharmony_ci s->insn_bits = pci1710_di_insn_bits; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* Digital Output subdevice */ 85462306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 85562306a36Sopenharmony_ci s->type = COMEDI_SUBD_DO; 85662306a36Sopenharmony_ci s->subdev_flags = SDF_WRITABLE; 85762306a36Sopenharmony_ci s->n_chan = 16; 85862306a36Sopenharmony_ci s->maxdata = 1; 85962306a36Sopenharmony_ci s->range_table = &range_digital; 86062306a36Sopenharmony_ci s->insn_bits = pci1710_do_insn_bits; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* Counter subdevice (8254) */ 86362306a36Sopenharmony_ci s = &dev->subdevices[subdev++]; 86462306a36Sopenharmony_ci comedi_8254_subdevice_init(s, dev->pacer); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci dev->pacer->insn_config = pci1710_counter_insn_config; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* counters 1 and 2 are used internally for the pacer */ 86962306a36Sopenharmony_ci comedi_8254_set_busy(dev->pacer, 1, true); 87062306a36Sopenharmony_ci comedi_8254_set_busy(dev->pacer, 2, true); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* max_samples is half the FIFO size (2 bytes/sample) */ 87462306a36Sopenharmony_ci devpriv->max_samples = (board->is_pci1711) ? 512 : 2048; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return 0; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic struct comedi_driver adv_pci1710_driver = { 88062306a36Sopenharmony_ci .driver_name = "adv_pci1710", 88162306a36Sopenharmony_ci .module = THIS_MODULE, 88262306a36Sopenharmony_ci .auto_attach = pci1710_auto_attach, 88362306a36Sopenharmony_ci .detach = comedi_pci_detach, 88462306a36Sopenharmony_ci}; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic int adv_pci1710_pci_probe(struct pci_dev *dev, 88762306a36Sopenharmony_ci const struct pci_device_id *id) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci return comedi_pci_auto_config(dev, &adv_pci1710_driver, 89062306a36Sopenharmony_ci id->driver_data); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic const struct pci_device_id adv_pci1710_pci_table[] = { 89462306a36Sopenharmony_ci { 89562306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 89662306a36Sopenharmony_ci PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), 89762306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 89862306a36Sopenharmony_ci }, { 89962306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 90062306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0x0000), 90162306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 90262306a36Sopenharmony_ci }, { 90362306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 90462306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xb100), 90562306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 90662306a36Sopenharmony_ci }, { 90762306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 90862306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xb200), 90962306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 91062306a36Sopenharmony_ci }, { 91162306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 91262306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xc100), 91362306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 91462306a36Sopenharmony_ci }, { 91562306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 91662306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xc200), 91762306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 91862306a36Sopenharmony_ci }, { 91962306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100), 92062306a36Sopenharmony_ci .driver_data = BOARD_PCI1710, 92162306a36Sopenharmony_ci }, { 92262306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 92362306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0x0002), 92462306a36Sopenharmony_ci .driver_data = BOARD_PCI1710HG, 92562306a36Sopenharmony_ci }, { 92662306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 92762306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xb102), 92862306a36Sopenharmony_ci .driver_data = BOARD_PCI1710HG, 92962306a36Sopenharmony_ci }, { 93062306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 93162306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xb202), 93262306a36Sopenharmony_ci .driver_data = BOARD_PCI1710HG, 93362306a36Sopenharmony_ci }, { 93462306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 93562306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xc102), 93662306a36Sopenharmony_ci .driver_data = BOARD_PCI1710HG, 93762306a36Sopenharmony_ci }, { 93862306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 93962306a36Sopenharmony_ci PCI_VENDOR_ID_ADVANTECH, 0xc202), 94062306a36Sopenharmony_ci .driver_data = BOARD_PCI1710HG, 94162306a36Sopenharmony_ci }, { 94262306a36Sopenharmony_ci PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102), 94362306a36Sopenharmony_ci .driver_data = BOARD_PCI1710HG, 94462306a36Sopenharmony_ci }, 94562306a36Sopenharmony_ci { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 }, 94662306a36Sopenharmony_ci { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 }, 94762306a36Sopenharmony_ci { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 }, 94862306a36Sopenharmony_ci { 0 } 94962306a36Sopenharmony_ci}; 95062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic struct pci_driver adv_pci1710_pci_driver = { 95362306a36Sopenharmony_ci .name = "adv_pci1710", 95462306a36Sopenharmony_ci .id_table = adv_pci1710_pci_table, 95562306a36Sopenharmony_ci .probe = adv_pci1710_pci_probe, 95662306a36Sopenharmony_ci .remove = comedi_pci_auto_unconfig, 95762306a36Sopenharmony_ci}; 95862306a36Sopenharmony_cimodule_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ciMODULE_AUTHOR("Comedi https://www.comedi.org"); 96162306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards"); 96262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 963