xref: /linux/drivers/comedi/drivers/adv_pci1710.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman  * adv_pci1710.c
48ffdff6aSGreg Kroah-Hartman  * Comedi driver for Advantech PCI-1710 series boards
58ffdff6aSGreg Kroah-Hartman  * Author: Michal Dobes <dobes@tesnet.cz>
68ffdff6aSGreg Kroah-Hartman  *
78ffdff6aSGreg Kroah-Hartman  * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
88ffdff6aSGreg Kroah-Hartman  * for testing and information.
98ffdff6aSGreg Kroah-Hartman  */
108ffdff6aSGreg Kroah-Hartman 
118ffdff6aSGreg Kroah-Hartman /*
128ffdff6aSGreg Kroah-Hartman  * Driver: adv_pci1710
138ffdff6aSGreg Kroah-Hartman  * Description: Comedi driver for Advantech PCI-1710 series boards
148ffdff6aSGreg Kroah-Hartman  * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
158ffdff6aSGreg Kroah-Hartman  *   PCI-1713, PCI-1731
168ffdff6aSGreg Kroah-Hartman  * Author: Michal Dobes <dobes@tesnet.cz>
178ffdff6aSGreg Kroah-Hartman  * Updated: Fri, 29 Oct 2015 17:19:35 -0700
188ffdff6aSGreg Kroah-Hartman  * Status: works
198ffdff6aSGreg Kroah-Hartman  *
208ffdff6aSGreg Kroah-Hartman  * Configuration options: not applicable, uses PCI auto config
218ffdff6aSGreg Kroah-Hartman  *
228ffdff6aSGreg Kroah-Hartman  * This driver supports AI, AO, DI and DO subdevices.
238ffdff6aSGreg Kroah-Hartman  * AI subdevice supports cmd and insn interface,
248ffdff6aSGreg Kroah-Hartman  * other subdevices support only insn interface.
258ffdff6aSGreg Kroah-Hartman  *
268ffdff6aSGreg Kroah-Hartman  * The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
278ffdff6aSGreg Kroah-Hartman  * driver cannot distinguish between them, as would be normal for a
288ffdff6aSGreg Kroah-Hartman  * PCI driver.
298ffdff6aSGreg Kroah-Hartman  */
308ffdff6aSGreg Kroah-Hartman 
318ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
328ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
33df0e68c1SIan Abbott #include <linux/comedi/comedi_pci.h>
3444fb7affSIan Abbott #include <linux/comedi/comedi_8254.h>
358ffdff6aSGreg Kroah-Hartman 
368ffdff6aSGreg Kroah-Hartman #include "amcc_s5933.h"
378ffdff6aSGreg Kroah-Hartman 
388ffdff6aSGreg Kroah-Hartman /*
398ffdff6aSGreg Kroah-Hartman  * PCI BAR2 Register map (dev->iobase)
408ffdff6aSGreg Kroah-Hartman  */
418ffdff6aSGreg Kroah-Hartman #define PCI171X_AD_DATA_REG	0x00	/* R:   A/D data */
428ffdff6aSGreg Kroah-Hartman #define PCI171X_SOFTTRG_REG	0x00	/* W:   soft trigger for A/D */
438ffdff6aSGreg Kroah-Hartman #define PCI171X_RANGE_REG	0x02	/* W:   A/D gain/range register */
448ffdff6aSGreg Kroah-Hartman #define PCI171X_RANGE_DIFF	BIT(5)
458ffdff6aSGreg Kroah-Hartman #define PCI171X_RANGE_UNI	BIT(4)
468ffdff6aSGreg Kroah-Hartman #define PCI171X_RANGE_GAIN(x)	(((x) & 0x7) << 0)
478ffdff6aSGreg Kroah-Hartman #define PCI171X_MUX_REG		0x04	/* W:   A/D multiplexor control */
488ffdff6aSGreg Kroah-Hartman #define PCI171X_MUX_CHANH(x)	(((x) & 0xff) << 8)
498ffdff6aSGreg Kroah-Hartman #define PCI171X_MUX_CHANL(x)	(((x) & 0xff) << 0)
508ffdff6aSGreg Kroah-Hartman #define PCI171X_MUX_CHAN(x)	(PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x))
518ffdff6aSGreg Kroah-Hartman #define PCI171X_STATUS_REG	0x06	/* R:   status register */
528ffdff6aSGreg Kroah-Hartman #define PCI171X_STATUS_IRQ	BIT(11)	/* 1=IRQ occurred */
538ffdff6aSGreg Kroah-Hartman #define PCI171X_STATUS_FF	BIT(10)	/* 1=FIFO is full, fatal error */
548ffdff6aSGreg Kroah-Hartman #define PCI171X_STATUS_FH	BIT(9)	/* 1=FIFO is half full */
558ffdff6aSGreg Kroah-Hartman #define PCI171X_STATUS_FE	BIT(8)	/* 1=FIFO is empty */
568ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_REG	0x06	/* W:   control register */
578ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_CNT0	BIT(6)	/* 1=ext. clk, 0=int. 100kHz clk */
588ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_ONEFH	BIT(5)	/* 1=on FIFO half full, 0=on sample */
598ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_IRQEN	BIT(4)	/* 1=enable IRQ */
608ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_GATE	BIT(3)	/* 1=enable ext. trigger GATE (8254?) */
618ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_EXT	BIT(2)	/* 1=enable ext. trigger source */
628ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_PACER	BIT(1)	/* 1=enable int. 8254 trigger source */
638ffdff6aSGreg Kroah-Hartman #define PCI171X_CTRL_SW		BIT(0)	/* 1=enable software trigger source */
648ffdff6aSGreg Kroah-Hartman #define PCI171X_CLRINT_REG	0x08	/* W:   clear interrupts request */
658ffdff6aSGreg Kroah-Hartman #define PCI171X_CLRFIFO_REG	0x09	/* W:   clear FIFO */
668ffdff6aSGreg Kroah-Hartman #define PCI171X_DA_REG(x)	(0x0a + ((x) * 2)) /* W:   D/A register */
678ffdff6aSGreg Kroah-Hartman #define PCI171X_DAREF_REG	0x0e	/* W:   D/A reference control */
688ffdff6aSGreg Kroah-Hartman #define PCI171X_DAREF(c, r)	(((r) & 0x3) << ((c) * 2))
698ffdff6aSGreg Kroah-Hartman #define PCI171X_DAREF_MASK(c)	PCI171X_DAREF((c), 0x3)
708ffdff6aSGreg Kroah-Hartman #define PCI171X_DI_REG		0x10	/* R:   digital inputs */
718ffdff6aSGreg Kroah-Hartman #define PCI171X_DO_REG		0x10	/* W:   digital outputs */
728ffdff6aSGreg Kroah-Hartman #define PCI171X_TIMER_BASE	0x18	/* R/W: 8254 timer */
738ffdff6aSGreg Kroah-Hartman 
748ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange pci1710_ai_range = {
758ffdff6aSGreg Kroah-Hartman 	9, {
768ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(5),		/* gain 1   (0x00) */
778ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(2.5),		/* gain 2   (0x01) */
788ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(1.25),	/* gain 4   (0x02) */
798ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.625),	/* gain 8   (0x03) */
808ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),		/* gain 0.5 (0x04) */
818ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(10),		/* gain 1   (0x00 | UNI) */
828ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(5),		/* gain 2   (0x01 | UNI) */
838ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(2.5),		/* gain 4   (0x02 | UNI) */
848ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(1.25)		/* gain 8   (0x03 | UNI) */
858ffdff6aSGreg Kroah-Hartman 	}
868ffdff6aSGreg Kroah-Hartman };
878ffdff6aSGreg Kroah-Hartman 
888ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange pci1710hg_ai_range = {
898ffdff6aSGreg Kroah-Hartman 	12, {
908ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(5),		/* gain 1    (0x00) */
918ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.5),		/* gain 10   (0x01) */
928ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.05),	/* gain 100  (0x02) */
938ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.005),	/* gain 1000 (0x03) */
948ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),		/* gain 0.5  (0x04) */
958ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(1),		/* gain 5    (0x05) */
968ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.1),		/* gain 50   (0x06) */
978ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.01),	/* gain 500  (0x07) */
988ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(10),		/* gain 1    (0x00 | UNI) */
998ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(1),		/* gain 10   (0x01 | UNI) */
1008ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(0.1),		/* gain 100  (0x02 | UNI) */
1018ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(0.01)		/* gain 1000 (0x03 | UNI) */
1028ffdff6aSGreg Kroah-Hartman 	}
1038ffdff6aSGreg Kroah-Hartman };
1048ffdff6aSGreg Kroah-Hartman 
1058ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange pci1711_ai_range = {
1068ffdff6aSGreg Kroah-Hartman 	5, {
1078ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),		/* gain 1  (0x00) */
1088ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(5),		/* gain 2  (0x01) */
1098ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(2.5),		/* gain 4  (0x02) */
1108ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(1.25),	/* gain 8  (0x03) */
1118ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.625)	/* gain 16 (0x04) */
1128ffdff6aSGreg Kroah-Hartman 	}
1138ffdff6aSGreg Kroah-Hartman };
1148ffdff6aSGreg Kroah-Hartman 
1158ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange pci171x_ao_range = {
1168ffdff6aSGreg Kroah-Hartman 	3, {
1178ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(5),		/* internal -5V ref */
1188ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(10),		/* internal -10V ref */
1198ffdff6aSGreg Kroah-Hartman 		RANGE_ext(0, 1)		/* external -Vref (+/-10V max) */
1208ffdff6aSGreg Kroah-Hartman 	}
1218ffdff6aSGreg Kroah-Hartman };
1228ffdff6aSGreg Kroah-Hartman 
1238ffdff6aSGreg Kroah-Hartman enum pci1710_boardid {
1248ffdff6aSGreg Kroah-Hartman 	BOARD_PCI1710,
1258ffdff6aSGreg Kroah-Hartman 	BOARD_PCI1710HG,
1268ffdff6aSGreg Kroah-Hartman 	BOARD_PCI1711,
1278ffdff6aSGreg Kroah-Hartman 	BOARD_PCI1713,
1288ffdff6aSGreg Kroah-Hartman 	BOARD_PCI1731,
1298ffdff6aSGreg Kroah-Hartman };
1308ffdff6aSGreg Kroah-Hartman 
1318ffdff6aSGreg Kroah-Hartman struct boardtype {
1328ffdff6aSGreg Kroah-Hartman 	const char *name;
1338ffdff6aSGreg Kroah-Hartman 	const struct comedi_lrange *ai_range;
1348ffdff6aSGreg Kroah-Hartman 	unsigned int is_pci1711:1;
1358ffdff6aSGreg Kroah-Hartman 	unsigned int is_pci1713:1;
1368ffdff6aSGreg Kroah-Hartman 	unsigned int has_ao:1;
1378ffdff6aSGreg Kroah-Hartman };
1388ffdff6aSGreg Kroah-Hartman 
1398ffdff6aSGreg Kroah-Hartman static const struct boardtype boardtypes[] = {
1408ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI1710] = {
1418ffdff6aSGreg Kroah-Hartman 		.name		= "pci1710",
1428ffdff6aSGreg Kroah-Hartman 		.ai_range	= &pci1710_ai_range,
1438ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
1448ffdff6aSGreg Kroah-Hartman 	},
1458ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI1710HG] = {
1468ffdff6aSGreg Kroah-Hartman 		.name		= "pci1710hg",
1478ffdff6aSGreg Kroah-Hartman 		.ai_range	= &pci1710hg_ai_range,
1488ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
1498ffdff6aSGreg Kroah-Hartman 	},
1508ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI1711] = {
1518ffdff6aSGreg Kroah-Hartman 		.name		= "pci1711",
1528ffdff6aSGreg Kroah-Hartman 		.ai_range	= &pci1711_ai_range,
1538ffdff6aSGreg Kroah-Hartman 		.is_pci1711	= 1,
1548ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
1558ffdff6aSGreg Kroah-Hartman 	},
1568ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI1713] = {
1578ffdff6aSGreg Kroah-Hartman 		.name		= "pci1713",
1588ffdff6aSGreg Kroah-Hartman 		.ai_range	= &pci1710_ai_range,
1598ffdff6aSGreg Kroah-Hartman 		.is_pci1713	= 1,
1608ffdff6aSGreg Kroah-Hartman 	},
1618ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI1731] = {
1628ffdff6aSGreg Kroah-Hartman 		.name		= "pci1731",
1638ffdff6aSGreg Kroah-Hartman 		.ai_range	= &pci1711_ai_range,
1648ffdff6aSGreg Kroah-Hartman 		.is_pci1711	= 1,
1658ffdff6aSGreg Kroah-Hartman 	},
1668ffdff6aSGreg Kroah-Hartman };
1678ffdff6aSGreg Kroah-Hartman 
1688ffdff6aSGreg Kroah-Hartman struct pci1710_private {
1698ffdff6aSGreg Kroah-Hartman 	unsigned int max_samples;
1708ffdff6aSGreg Kroah-Hartman 	unsigned int ctrl;	/* control register value */
1718ffdff6aSGreg Kroah-Hartman 	unsigned int ctrl_ext;	/* used to switch from TRIG_EXT to TRIG_xxx */
1728ffdff6aSGreg Kroah-Hartman 	unsigned int mux_scan;	/* used to set the channel interval to scan */
1738ffdff6aSGreg Kroah-Hartman 	unsigned char ai_et;
1748ffdff6aSGreg Kroah-Hartman 	unsigned int act_chanlist[32];	/*  list of scanned channel */
1758ffdff6aSGreg Kroah-Hartman 	unsigned char saved_seglen;	/* len of the non-repeating chanlist */
1768ffdff6aSGreg Kroah-Hartman 	unsigned char da_ranges;	/*  copy of D/A outpit range register */
1778ffdff6aSGreg Kroah-Hartman 	unsigned char unipolar_gain;	/* adjust for unipolar gain codes */
1788ffdff6aSGreg Kroah-Hartman };
1798ffdff6aSGreg Kroah-Hartman 
pci1710_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)1808ffdff6aSGreg Kroah-Hartman static int pci1710_ai_check_chanlist(struct comedi_device *dev,
1818ffdff6aSGreg Kroah-Hartman 				     struct comedi_subdevice *s,
1828ffdff6aSGreg Kroah-Hartman 				     struct comedi_cmd *cmd)
1838ffdff6aSGreg Kroah-Hartman {
1848ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
1858ffdff6aSGreg Kroah-Hartman 	unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
1868ffdff6aSGreg Kroah-Hartman 	unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
1878ffdff6aSGreg Kroah-Hartman 	unsigned int next_chan = (chan0 + 1) % s->n_chan;
1888ffdff6aSGreg Kroah-Hartman 	unsigned int chansegment[32];
1898ffdff6aSGreg Kroah-Hartman 	unsigned int seglen;
1908ffdff6aSGreg Kroah-Hartman 	int i;
1918ffdff6aSGreg Kroah-Hartman 
1928ffdff6aSGreg Kroah-Hartman 	if (cmd->chanlist_len == 1) {
1938ffdff6aSGreg Kroah-Hartman 		devpriv->saved_seglen = cmd->chanlist_len;
1948ffdff6aSGreg Kroah-Hartman 		return 0;
1958ffdff6aSGreg Kroah-Hartman 	}
1968ffdff6aSGreg Kroah-Hartman 
1978ffdff6aSGreg Kroah-Hartman 	/* first channel is always ok */
1988ffdff6aSGreg Kroah-Hartman 	chansegment[0] = cmd->chanlist[0];
1998ffdff6aSGreg Kroah-Hartman 
2008ffdff6aSGreg Kroah-Hartman 	for (i = 1; i < cmd->chanlist_len; i++) {
2018ffdff6aSGreg Kroah-Hartman 		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
2028ffdff6aSGreg Kroah-Hartman 		unsigned int aref = CR_AREF(cmd->chanlist[i]);
2038ffdff6aSGreg Kroah-Hartman 
2048ffdff6aSGreg Kroah-Hartman 		if (cmd->chanlist[0] == cmd->chanlist[i])
2058ffdff6aSGreg Kroah-Hartman 			break;	/*  we detected a loop, stop */
2068ffdff6aSGreg Kroah-Hartman 
2078ffdff6aSGreg Kroah-Hartman 		if (aref == AREF_DIFF && (chan & 1)) {
2088ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev,
2098ffdff6aSGreg Kroah-Hartman 				"Odd channel cannot be differential input!\n");
2108ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
2118ffdff6aSGreg Kroah-Hartman 		}
2128ffdff6aSGreg Kroah-Hartman 
2138ffdff6aSGreg Kroah-Hartman 		if (last_aref == AREF_DIFF)
2148ffdff6aSGreg Kroah-Hartman 			next_chan = (next_chan + 1) % s->n_chan;
2158ffdff6aSGreg Kroah-Hartman 		if (chan != next_chan) {
2168ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev,
2178ffdff6aSGreg Kroah-Hartman 				"channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
2188ffdff6aSGreg Kroah-Hartman 				i, chan, next_chan, chan0);
2198ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
2208ffdff6aSGreg Kroah-Hartman 		}
2218ffdff6aSGreg Kroah-Hartman 
2228ffdff6aSGreg Kroah-Hartman 		/* next correct channel in list */
2238ffdff6aSGreg Kroah-Hartman 		chansegment[i] = cmd->chanlist[i];
2248ffdff6aSGreg Kroah-Hartman 		last_aref = aref;
2258ffdff6aSGreg Kroah-Hartman 	}
2268ffdff6aSGreg Kroah-Hartman 	seglen = i;
2278ffdff6aSGreg Kroah-Hartman 
2288ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < cmd->chanlist_len; i++) {
2298ffdff6aSGreg Kroah-Hartman 		if (cmd->chanlist[i] != chansegment[i % seglen]) {
2308ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev,
2318ffdff6aSGreg Kroah-Hartman 				"bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
2328ffdff6aSGreg Kroah-Hartman 				i, CR_CHAN(chansegment[i]),
2338ffdff6aSGreg Kroah-Hartman 				CR_RANGE(chansegment[i]),
2348ffdff6aSGreg Kroah-Hartman 				CR_AREF(chansegment[i]),
2358ffdff6aSGreg Kroah-Hartman 				CR_CHAN(cmd->chanlist[i % seglen]),
2368ffdff6aSGreg Kroah-Hartman 				CR_RANGE(cmd->chanlist[i % seglen]),
2378ffdff6aSGreg Kroah-Hartman 				CR_AREF(chansegment[i % seglen]));
2388ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
2398ffdff6aSGreg Kroah-Hartman 		}
2408ffdff6aSGreg Kroah-Hartman 	}
2418ffdff6aSGreg Kroah-Hartman 	devpriv->saved_seglen = seglen;
2428ffdff6aSGreg Kroah-Hartman 
2438ffdff6aSGreg Kroah-Hartman 	return 0;
2448ffdff6aSGreg Kroah-Hartman }
2458ffdff6aSGreg Kroah-Hartman 
pci1710_ai_setup_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int * chanlist,unsigned int n_chan,unsigned int seglen)2468ffdff6aSGreg Kroah-Hartman static void pci1710_ai_setup_chanlist(struct comedi_device *dev,
2478ffdff6aSGreg Kroah-Hartman 				      struct comedi_subdevice *s,
2488ffdff6aSGreg Kroah-Hartman 				      unsigned int *chanlist,
2498ffdff6aSGreg Kroah-Hartman 				      unsigned int n_chan,
2508ffdff6aSGreg Kroah-Hartman 				      unsigned int seglen)
2518ffdff6aSGreg Kroah-Hartman {
2528ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
2538ffdff6aSGreg Kroah-Hartman 	unsigned int first_chan = CR_CHAN(chanlist[0]);
2548ffdff6aSGreg Kroah-Hartman 	unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
2558ffdff6aSGreg Kroah-Hartman 	unsigned int i;
2568ffdff6aSGreg Kroah-Hartman 
2578ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < seglen; i++) {	/*  store range list to card */
2588ffdff6aSGreg Kroah-Hartman 		unsigned int chan = CR_CHAN(chanlist[i]);
2598ffdff6aSGreg Kroah-Hartman 		unsigned int range = CR_RANGE(chanlist[i]);
2608ffdff6aSGreg Kroah-Hartman 		unsigned int aref = CR_AREF(chanlist[i]);
2618ffdff6aSGreg Kroah-Hartman 		unsigned int rangeval = 0;
2628ffdff6aSGreg Kroah-Hartman 
2638ffdff6aSGreg Kroah-Hartman 		if (aref == AREF_DIFF)
2648ffdff6aSGreg Kroah-Hartman 			rangeval |= PCI171X_RANGE_DIFF;
2658ffdff6aSGreg Kroah-Hartman 		if (comedi_range_is_unipolar(s, range)) {
2668ffdff6aSGreg Kroah-Hartman 			rangeval |= PCI171X_RANGE_UNI;
2678ffdff6aSGreg Kroah-Hartman 			range -= devpriv->unipolar_gain;
2688ffdff6aSGreg Kroah-Hartman 		}
2698ffdff6aSGreg Kroah-Hartman 		rangeval |= PCI171X_RANGE_GAIN(range);
2708ffdff6aSGreg Kroah-Hartman 
2718ffdff6aSGreg Kroah-Hartman 		/* select channel and set range */
2728ffdff6aSGreg Kroah-Hartman 		outw(PCI171X_MUX_CHAN(chan), dev->iobase + PCI171X_MUX_REG);
2738ffdff6aSGreg Kroah-Hartman 		outw(rangeval, dev->iobase + PCI171X_RANGE_REG);
2748ffdff6aSGreg Kroah-Hartman 
2758ffdff6aSGreg Kroah-Hartman 		devpriv->act_chanlist[i] = chan;
2768ffdff6aSGreg Kroah-Hartman 	}
2778ffdff6aSGreg Kroah-Hartman 	for ( ; i < n_chan; i++)	/* store remainder of channel list */
2788ffdff6aSGreg Kroah-Hartman 		devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
2798ffdff6aSGreg Kroah-Hartman 
2808ffdff6aSGreg Kroah-Hartman 	/* select channel interval to scan */
2818ffdff6aSGreg Kroah-Hartman 	devpriv->mux_scan = PCI171X_MUX_CHANL(first_chan) |
2828ffdff6aSGreg Kroah-Hartman 			    PCI171X_MUX_CHANH(last_chan);
2838ffdff6aSGreg Kroah-Hartman 	outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG);
2848ffdff6aSGreg Kroah-Hartman }
2858ffdff6aSGreg Kroah-Hartman 
pci1710_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)2868ffdff6aSGreg Kroah-Hartman static int pci1710_ai_eoc(struct comedi_device *dev,
2878ffdff6aSGreg Kroah-Hartman 			  struct comedi_subdevice *s,
2888ffdff6aSGreg Kroah-Hartman 			  struct comedi_insn *insn,
2898ffdff6aSGreg Kroah-Hartman 			  unsigned long context)
2908ffdff6aSGreg Kroah-Hartman {
2918ffdff6aSGreg Kroah-Hartman 	unsigned int status;
2928ffdff6aSGreg Kroah-Hartman 
2938ffdff6aSGreg Kroah-Hartman 	status = inw(dev->iobase + PCI171X_STATUS_REG);
2948ffdff6aSGreg Kroah-Hartman 	if ((status & PCI171X_STATUS_FE) == 0)
2958ffdff6aSGreg Kroah-Hartman 		return 0;
2968ffdff6aSGreg Kroah-Hartman 	return -EBUSY;
2978ffdff6aSGreg Kroah-Hartman }
2988ffdff6aSGreg Kroah-Hartman 
pci1710_ai_read_sample(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int cur_chan,unsigned short * val)2998ffdff6aSGreg Kroah-Hartman static int pci1710_ai_read_sample(struct comedi_device *dev,
3008ffdff6aSGreg Kroah-Hartman 				  struct comedi_subdevice *s,
3018ffdff6aSGreg Kroah-Hartman 				  unsigned int cur_chan,
3028ffdff6aSGreg Kroah-Hartman 				  unsigned short *val)
3038ffdff6aSGreg Kroah-Hartman {
3048ffdff6aSGreg Kroah-Hartman 	const struct boardtype *board = dev->board_ptr;
3058ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
3068ffdff6aSGreg Kroah-Hartman 	unsigned short sample;
3078ffdff6aSGreg Kroah-Hartman 	unsigned int chan;
3088ffdff6aSGreg Kroah-Hartman 
3098ffdff6aSGreg Kroah-Hartman 	sample = inw(dev->iobase + PCI171X_AD_DATA_REG);
3108ffdff6aSGreg Kroah-Hartman 	if (!board->is_pci1713) {
3118ffdff6aSGreg Kroah-Hartman 		/*
3128ffdff6aSGreg Kroah-Hartman 		 * The upper 4 bits of the 16-bit sample are the channel number
3138ffdff6aSGreg Kroah-Hartman 		 * that the sample was acquired from. Verify that this channel
3148ffdff6aSGreg Kroah-Hartman 		 * number matches the expected channel number.
3158ffdff6aSGreg Kroah-Hartman 		 */
3168ffdff6aSGreg Kroah-Hartman 		chan = sample >> 12;
3178ffdff6aSGreg Kroah-Hartman 		if (chan != devpriv->act_chanlist[cur_chan]) {
3188ffdff6aSGreg Kroah-Hartman 			dev_err(dev->class_dev,
3198ffdff6aSGreg Kroah-Hartman 				"A/D data dropout: received from channel %d, expected %d\n",
3208ffdff6aSGreg Kroah-Hartman 				chan, devpriv->act_chanlist[cur_chan]);
3218ffdff6aSGreg Kroah-Hartman 			return -ENODATA;
3228ffdff6aSGreg Kroah-Hartman 		}
3238ffdff6aSGreg Kroah-Hartman 	}
3248ffdff6aSGreg Kroah-Hartman 	*val = sample & s->maxdata;
3258ffdff6aSGreg Kroah-Hartman 	return 0;
3268ffdff6aSGreg Kroah-Hartman }
3278ffdff6aSGreg Kroah-Hartman 
pci1710_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)3288ffdff6aSGreg Kroah-Hartman static int pci1710_ai_insn_read(struct comedi_device *dev,
3298ffdff6aSGreg Kroah-Hartman 				struct comedi_subdevice *s,
3308ffdff6aSGreg Kroah-Hartman 				struct comedi_insn *insn,
3318ffdff6aSGreg Kroah-Hartman 				unsigned int *data)
3328ffdff6aSGreg Kroah-Hartman {
3338ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
3348ffdff6aSGreg Kroah-Hartman 	int ret = 0;
3358ffdff6aSGreg Kroah-Hartman 	int i;
3368ffdff6aSGreg Kroah-Hartman 
3378ffdff6aSGreg Kroah-Hartman 	/* enable software trigger */
3388ffdff6aSGreg Kroah-Hartman 	devpriv->ctrl |= PCI171X_CTRL_SW;
3398ffdff6aSGreg Kroah-Hartman 	outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
3408ffdff6aSGreg Kroah-Hartman 
3418ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
3428ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
3438ffdff6aSGreg Kroah-Hartman 
3448ffdff6aSGreg Kroah-Hartman 	pci1710_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
3458ffdff6aSGreg Kroah-Hartman 
3468ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++) {
3478ffdff6aSGreg Kroah-Hartman 		unsigned short val;
3488ffdff6aSGreg Kroah-Hartman 
3498ffdff6aSGreg Kroah-Hartman 		/* start conversion */
3508ffdff6aSGreg Kroah-Hartman 		outw(0, dev->iobase + PCI171X_SOFTTRG_REG);
3518ffdff6aSGreg Kroah-Hartman 
3528ffdff6aSGreg Kroah-Hartman 		ret = comedi_timeout(dev, s, insn, pci1710_ai_eoc, 0);
3538ffdff6aSGreg Kroah-Hartman 		if (ret)
3548ffdff6aSGreg Kroah-Hartman 			break;
3558ffdff6aSGreg Kroah-Hartman 
3568ffdff6aSGreg Kroah-Hartman 		ret = pci1710_ai_read_sample(dev, s, 0, &val);
3578ffdff6aSGreg Kroah-Hartman 		if (ret)
3588ffdff6aSGreg Kroah-Hartman 			break;
3598ffdff6aSGreg Kroah-Hartman 
3608ffdff6aSGreg Kroah-Hartman 		data[i] = val;
3618ffdff6aSGreg Kroah-Hartman 	}
3628ffdff6aSGreg Kroah-Hartman 
3638ffdff6aSGreg Kroah-Hartman 	/* disable software trigger */
3648ffdff6aSGreg Kroah-Hartman 	devpriv->ctrl &= ~PCI171X_CTRL_SW;
3658ffdff6aSGreg Kroah-Hartman 	outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
3668ffdff6aSGreg Kroah-Hartman 
3678ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
3688ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
3698ffdff6aSGreg Kroah-Hartman 
3708ffdff6aSGreg Kroah-Hartman 	return ret ? ret : insn->n;
3718ffdff6aSGreg Kroah-Hartman }
3728ffdff6aSGreg Kroah-Hartman 
pci1710_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)3738ffdff6aSGreg Kroah-Hartman static int pci1710_ai_cancel(struct comedi_device *dev,
3748ffdff6aSGreg Kroah-Hartman 			     struct comedi_subdevice *s)
3758ffdff6aSGreg Kroah-Hartman {
3768ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
3778ffdff6aSGreg Kroah-Hartman 
3788ffdff6aSGreg Kroah-Hartman 	/* disable A/D triggers and interrupt sources */
3798ffdff6aSGreg Kroah-Hartman 	devpriv->ctrl &= PCI171X_CTRL_CNT0;	/* preserve counter 0 clk src */
3808ffdff6aSGreg Kroah-Hartman 	outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
3818ffdff6aSGreg Kroah-Hartman 
3828ffdff6aSGreg Kroah-Hartman 	/* disable pacer */
3838ffdff6aSGreg Kroah-Hartman 	comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
3848ffdff6aSGreg Kroah-Hartman 
3858ffdff6aSGreg Kroah-Hartman 	/* clear A/D FIFO and any pending interrutps */
3868ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
3878ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
3888ffdff6aSGreg Kroah-Hartman 
3898ffdff6aSGreg Kroah-Hartman 	return 0;
3908ffdff6aSGreg Kroah-Hartman }
3918ffdff6aSGreg Kroah-Hartman 
pci1710_handle_every_sample(struct comedi_device * dev,struct comedi_subdevice * s)3928ffdff6aSGreg Kroah-Hartman static void pci1710_handle_every_sample(struct comedi_device *dev,
3938ffdff6aSGreg Kroah-Hartman 					struct comedi_subdevice *s)
3948ffdff6aSGreg Kroah-Hartman {
3958ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
3968ffdff6aSGreg Kroah-Hartman 	unsigned int status;
3978ffdff6aSGreg Kroah-Hartman 	unsigned short val;
3988ffdff6aSGreg Kroah-Hartman 	int ret;
3998ffdff6aSGreg Kroah-Hartman 
4008ffdff6aSGreg Kroah-Hartman 	status = inw(dev->iobase + PCI171X_STATUS_REG);
4018ffdff6aSGreg Kroah-Hartman 	if (status & PCI171X_STATUS_FE) {
4028ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
4038ffdff6aSGreg Kroah-Hartman 		s->async->events |= COMEDI_CB_ERROR;
4048ffdff6aSGreg Kroah-Hartman 		return;
4058ffdff6aSGreg Kroah-Hartman 	}
4068ffdff6aSGreg Kroah-Hartman 	if (status & PCI171X_STATUS_FF) {
4078ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev,
4088ffdff6aSGreg Kroah-Hartman 			"A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
4098ffdff6aSGreg Kroah-Hartman 		s->async->events |= COMEDI_CB_ERROR;
4108ffdff6aSGreg Kroah-Hartman 		return;
4118ffdff6aSGreg Kroah-Hartman 	}
4128ffdff6aSGreg Kroah-Hartman 
4138ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
4148ffdff6aSGreg Kroah-Hartman 
4158ffdff6aSGreg Kroah-Hartman 	for (; !(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) {
4168ffdff6aSGreg Kroah-Hartman 		ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val);
4178ffdff6aSGreg Kroah-Hartman 		if (ret) {
4188ffdff6aSGreg Kroah-Hartman 			s->async->events |= COMEDI_CB_ERROR;
4198ffdff6aSGreg Kroah-Hartman 			break;
4208ffdff6aSGreg Kroah-Hartman 		}
4218ffdff6aSGreg Kroah-Hartman 
4228ffdff6aSGreg Kroah-Hartman 		comedi_buf_write_samples(s, &val, 1);
4238ffdff6aSGreg Kroah-Hartman 
4248ffdff6aSGreg Kroah-Hartman 		if (cmd->stop_src == TRIG_COUNT &&
4258ffdff6aSGreg Kroah-Hartman 		    s->async->scans_done >= cmd->stop_arg) {
4268ffdff6aSGreg Kroah-Hartman 			s->async->events |= COMEDI_CB_EOA;
4278ffdff6aSGreg Kroah-Hartman 			break;
4288ffdff6aSGreg Kroah-Hartman 		}
4298ffdff6aSGreg Kroah-Hartman 	}
4308ffdff6aSGreg Kroah-Hartman 
4318ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
4328ffdff6aSGreg Kroah-Hartman }
4338ffdff6aSGreg Kroah-Hartman 
pci1710_handle_fifo(struct comedi_device * dev,struct comedi_subdevice * s)4348ffdff6aSGreg Kroah-Hartman static void pci1710_handle_fifo(struct comedi_device *dev,
4358ffdff6aSGreg Kroah-Hartman 				struct comedi_subdevice *s)
4368ffdff6aSGreg Kroah-Hartman {
4378ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
4388ffdff6aSGreg Kroah-Hartman 	struct comedi_async *async = s->async;
4398ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &async->cmd;
4408ffdff6aSGreg Kroah-Hartman 	unsigned int status;
4418ffdff6aSGreg Kroah-Hartman 	int i;
4428ffdff6aSGreg Kroah-Hartman 
4438ffdff6aSGreg Kroah-Hartman 	status = inw(dev->iobase + PCI171X_STATUS_REG);
4448ffdff6aSGreg Kroah-Hartman 	if (!(status & PCI171X_STATUS_FH)) {
4458ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
4468ffdff6aSGreg Kroah-Hartman 		async->events |= COMEDI_CB_ERROR;
4478ffdff6aSGreg Kroah-Hartman 		return;
4488ffdff6aSGreg Kroah-Hartman 	}
4498ffdff6aSGreg Kroah-Hartman 	if (status & PCI171X_STATUS_FF) {
4508ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev,
4518ffdff6aSGreg Kroah-Hartman 			"A/D FIFO Full status (Fatal Error!)\n");
4528ffdff6aSGreg Kroah-Hartman 		async->events |= COMEDI_CB_ERROR;
4538ffdff6aSGreg Kroah-Hartman 		return;
4548ffdff6aSGreg Kroah-Hartman 	}
4558ffdff6aSGreg Kroah-Hartman 
4568ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < devpriv->max_samples; i++) {
4578ffdff6aSGreg Kroah-Hartman 		unsigned short val;
4588ffdff6aSGreg Kroah-Hartman 		int ret;
4598ffdff6aSGreg Kroah-Hartman 
4608ffdff6aSGreg Kroah-Hartman 		ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val);
4618ffdff6aSGreg Kroah-Hartman 		if (ret) {
4628ffdff6aSGreg Kroah-Hartman 			s->async->events |= COMEDI_CB_ERROR;
4638ffdff6aSGreg Kroah-Hartman 			break;
4648ffdff6aSGreg Kroah-Hartman 		}
4658ffdff6aSGreg Kroah-Hartman 
4668ffdff6aSGreg Kroah-Hartman 		if (!comedi_buf_write_samples(s, &val, 1))
4678ffdff6aSGreg Kroah-Hartman 			break;
4688ffdff6aSGreg Kroah-Hartman 
4698ffdff6aSGreg Kroah-Hartman 		if (cmd->stop_src == TRIG_COUNT &&
4708ffdff6aSGreg Kroah-Hartman 		    async->scans_done >= cmd->stop_arg) {
4718ffdff6aSGreg Kroah-Hartman 			async->events |= COMEDI_CB_EOA;
4728ffdff6aSGreg Kroah-Hartman 			break;
4738ffdff6aSGreg Kroah-Hartman 		}
4748ffdff6aSGreg Kroah-Hartman 	}
4758ffdff6aSGreg Kroah-Hartman 
4768ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
4778ffdff6aSGreg Kroah-Hartman }
4788ffdff6aSGreg Kroah-Hartman 
pci1710_irq_handler(int irq,void * d)4798ffdff6aSGreg Kroah-Hartman static irqreturn_t pci1710_irq_handler(int irq, void *d)
4808ffdff6aSGreg Kroah-Hartman {
4818ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
4828ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
4838ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
4848ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd;
4858ffdff6aSGreg Kroah-Hartman 
4868ffdff6aSGreg Kroah-Hartman 	if (!dev->attached)	/*  is device attached? */
4878ffdff6aSGreg Kroah-Hartman 		return IRQ_NONE;	/*  no, exit */
4888ffdff6aSGreg Kroah-Hartman 
4898ffdff6aSGreg Kroah-Hartman 	s = dev->read_subdev;
4908ffdff6aSGreg Kroah-Hartman 	cmd = &s->async->cmd;
4918ffdff6aSGreg Kroah-Hartman 
4928ffdff6aSGreg Kroah-Hartman 	/*  is this interrupt from our board? */
4938ffdff6aSGreg Kroah-Hartman 	if (!(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_IRQ))
4948ffdff6aSGreg Kroah-Hartman 		return IRQ_NONE;	/*  no, exit */
4958ffdff6aSGreg Kroah-Hartman 
4968ffdff6aSGreg Kroah-Hartman 	if (devpriv->ai_et) {	/*  Switch from initial TRIG_EXT to TRIG_xxx. */
4978ffdff6aSGreg Kroah-Hartman 		devpriv->ai_et = 0;
4988ffdff6aSGreg Kroah-Hartman 		devpriv->ctrl &= PCI171X_CTRL_CNT0;
4998ffdff6aSGreg Kroah-Hartman 		devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */
5008ffdff6aSGreg Kroah-Hartman 		outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
5018ffdff6aSGreg Kroah-Hartman 		devpriv->ctrl = devpriv->ctrl_ext;
5028ffdff6aSGreg Kroah-Hartman 		outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
5038ffdff6aSGreg Kroah-Hartman 		outb(0, dev->iobase + PCI171X_CLRINT_REG);
5048ffdff6aSGreg Kroah-Hartman 		/* no sample on this interrupt; reset the channel interval */
5058ffdff6aSGreg Kroah-Hartman 		outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG);
5068ffdff6aSGreg Kroah-Hartman 		outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
5078ffdff6aSGreg Kroah-Hartman 		comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
5088ffdff6aSGreg Kroah-Hartman 		return IRQ_HANDLED;
5098ffdff6aSGreg Kroah-Hartman 	}
5108ffdff6aSGreg Kroah-Hartman 
5118ffdff6aSGreg Kroah-Hartman 	if (cmd->flags & CMDF_WAKE_EOS)
5128ffdff6aSGreg Kroah-Hartman 		pci1710_handle_every_sample(dev, s);
5138ffdff6aSGreg Kroah-Hartman 	else
5148ffdff6aSGreg Kroah-Hartman 		pci1710_handle_fifo(dev, s);
5158ffdff6aSGreg Kroah-Hartman 
5168ffdff6aSGreg Kroah-Hartman 	comedi_handle_events(dev, s);
5178ffdff6aSGreg Kroah-Hartman 
5188ffdff6aSGreg Kroah-Hartman 	return IRQ_HANDLED;
5198ffdff6aSGreg Kroah-Hartman }
5208ffdff6aSGreg Kroah-Hartman 
pci1710_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)5218ffdff6aSGreg Kroah-Hartman static int pci1710_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
5228ffdff6aSGreg Kroah-Hartman {
5238ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
5248ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
5258ffdff6aSGreg Kroah-Hartman 
5268ffdff6aSGreg Kroah-Hartman 	pci1710_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
5278ffdff6aSGreg Kroah-Hartman 				  devpriv->saved_seglen);
5288ffdff6aSGreg Kroah-Hartman 
5298ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
5308ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
5318ffdff6aSGreg Kroah-Hartman 
5328ffdff6aSGreg Kroah-Hartman 	devpriv->ctrl &= PCI171X_CTRL_CNT0;
5338ffdff6aSGreg Kroah-Hartman 	if ((cmd->flags & CMDF_WAKE_EOS) == 0)
5348ffdff6aSGreg Kroah-Hartman 		devpriv->ctrl |= PCI171X_CTRL_ONEFH;
5358ffdff6aSGreg Kroah-Hartman 
5368ffdff6aSGreg Kroah-Hartman 	if (cmd->convert_src == TRIG_TIMER) {
5378ffdff6aSGreg Kroah-Hartman 		comedi_8254_update_divisors(dev->pacer);
5388ffdff6aSGreg Kroah-Hartman 
5398ffdff6aSGreg Kroah-Hartman 		devpriv->ctrl |= PCI171X_CTRL_PACER | PCI171X_CTRL_IRQEN;
5408ffdff6aSGreg Kroah-Hartman 		if (cmd->start_src == TRIG_EXT) {
5418ffdff6aSGreg Kroah-Hartman 			devpriv->ctrl_ext = devpriv->ctrl;
5428ffdff6aSGreg Kroah-Hartman 			devpriv->ctrl &= ~(PCI171X_CTRL_PACER |
5438ffdff6aSGreg Kroah-Hartman 					   PCI171X_CTRL_ONEFH |
5448ffdff6aSGreg Kroah-Hartman 					   PCI171X_CTRL_GATE);
5458ffdff6aSGreg Kroah-Hartman 			devpriv->ctrl |= PCI171X_CTRL_EXT;
5468ffdff6aSGreg Kroah-Hartman 			devpriv->ai_et = 1;
5478ffdff6aSGreg Kroah-Hartman 		} else {	/* TRIG_NOW */
5488ffdff6aSGreg Kroah-Hartman 			devpriv->ai_et = 0;
5498ffdff6aSGreg Kroah-Hartman 		}
5508ffdff6aSGreg Kroah-Hartman 		outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
5518ffdff6aSGreg Kroah-Hartman 
5528ffdff6aSGreg Kroah-Hartman 		if (cmd->start_src == TRIG_NOW)
5538ffdff6aSGreg Kroah-Hartman 			comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
5548ffdff6aSGreg Kroah-Hartman 	} else {	/* TRIG_EXT */
5558ffdff6aSGreg Kroah-Hartman 		devpriv->ctrl |= PCI171X_CTRL_EXT | PCI171X_CTRL_IRQEN;
5568ffdff6aSGreg Kroah-Hartman 		outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
5578ffdff6aSGreg Kroah-Hartman 	}
5588ffdff6aSGreg Kroah-Hartman 
5598ffdff6aSGreg Kroah-Hartman 	return 0;
5608ffdff6aSGreg Kroah-Hartman }
5618ffdff6aSGreg Kroah-Hartman 
pci1710_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)5628ffdff6aSGreg Kroah-Hartman static int pci1710_ai_cmdtest(struct comedi_device *dev,
5638ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s,
5648ffdff6aSGreg Kroah-Hartman 			      struct comedi_cmd *cmd)
5658ffdff6aSGreg Kroah-Hartman {
5668ffdff6aSGreg Kroah-Hartman 	int err = 0;
5678ffdff6aSGreg Kroah-Hartman 
5688ffdff6aSGreg Kroah-Hartman 	/* Step 1 : check if triggers are trivially valid */
5698ffdff6aSGreg Kroah-Hartman 
5708ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
5718ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
5728ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->convert_src,
5738ffdff6aSGreg Kroah-Hartman 					TRIG_TIMER | TRIG_EXT);
5748ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
5758ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
5768ffdff6aSGreg Kroah-Hartman 
5778ffdff6aSGreg Kroah-Hartman 	if (err)
5788ffdff6aSGreg Kroah-Hartman 		return 1;
5798ffdff6aSGreg Kroah-Hartman 
5808ffdff6aSGreg Kroah-Hartman 	/* step 2a: make sure trigger sources are unique */
5818ffdff6aSGreg Kroah-Hartman 
5828ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->start_src);
5838ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->convert_src);
5848ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
5858ffdff6aSGreg Kroah-Hartman 
5868ffdff6aSGreg Kroah-Hartman 	/* step 2b: and mutually compatible */
5878ffdff6aSGreg Kroah-Hartman 
5888ffdff6aSGreg Kroah-Hartman 	if (err)
5898ffdff6aSGreg Kroah-Hartman 		return 2;
5908ffdff6aSGreg Kroah-Hartman 
5918ffdff6aSGreg Kroah-Hartman 	/* Step 3: check if arguments are trivially valid */
5928ffdff6aSGreg Kroah-Hartman 
5938ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
5948ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
5958ffdff6aSGreg Kroah-Hartman 
5968ffdff6aSGreg Kroah-Hartman 	if (cmd->convert_src == TRIG_TIMER)
5978ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
5988ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_FOLLOW */
5998ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
6008ffdff6aSGreg Kroah-Hartman 
6018ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
6028ffdff6aSGreg Kroah-Hartman 					   cmd->chanlist_len);
6038ffdff6aSGreg Kroah-Hartman 
6048ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT)
6058ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
6068ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NONE */
6078ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
6088ffdff6aSGreg Kroah-Hartman 
6098ffdff6aSGreg Kroah-Hartman 	if (err)
6108ffdff6aSGreg Kroah-Hartman 		return 3;
6118ffdff6aSGreg Kroah-Hartman 
6128ffdff6aSGreg Kroah-Hartman 	/* step 4: fix up any arguments */
6138ffdff6aSGreg Kroah-Hartman 
6148ffdff6aSGreg Kroah-Hartman 	if (cmd->convert_src == TRIG_TIMER) {
6158ffdff6aSGreg Kroah-Hartman 		unsigned int arg = cmd->convert_arg;
6168ffdff6aSGreg Kroah-Hartman 
6178ffdff6aSGreg Kroah-Hartman 		comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
6188ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
6198ffdff6aSGreg Kroah-Hartman 	}
6208ffdff6aSGreg Kroah-Hartman 
6218ffdff6aSGreg Kroah-Hartman 	if (err)
6228ffdff6aSGreg Kroah-Hartman 		return 4;
6238ffdff6aSGreg Kroah-Hartman 
6248ffdff6aSGreg Kroah-Hartman 	/* Step 5: check channel list */
6258ffdff6aSGreg Kroah-Hartman 
6268ffdff6aSGreg Kroah-Hartman 	err |= pci1710_ai_check_chanlist(dev, s, cmd);
6278ffdff6aSGreg Kroah-Hartman 
6288ffdff6aSGreg Kroah-Hartman 	if (err)
6298ffdff6aSGreg Kroah-Hartman 		return 5;
6308ffdff6aSGreg Kroah-Hartman 
6318ffdff6aSGreg Kroah-Hartman 	return 0;
6328ffdff6aSGreg Kroah-Hartman }
6338ffdff6aSGreg Kroah-Hartman 
pci1710_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6348ffdff6aSGreg Kroah-Hartman static int pci1710_ao_insn_write(struct comedi_device *dev,
6358ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
6368ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
6378ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
6388ffdff6aSGreg Kroah-Hartman {
6398ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
6408ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
6418ffdff6aSGreg Kroah-Hartman 	unsigned int range = CR_RANGE(insn->chanspec);
6428ffdff6aSGreg Kroah-Hartman 	unsigned int val = s->readback[chan];
6438ffdff6aSGreg Kroah-Hartman 	int i;
6448ffdff6aSGreg Kroah-Hartman 
6458ffdff6aSGreg Kroah-Hartman 	devpriv->da_ranges &= ~PCI171X_DAREF_MASK(chan);
6468ffdff6aSGreg Kroah-Hartman 	devpriv->da_ranges |= PCI171X_DAREF(chan, range);
6478ffdff6aSGreg Kroah-Hartman 	outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
6488ffdff6aSGreg Kroah-Hartman 
6498ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++) {
6508ffdff6aSGreg Kroah-Hartman 		val = data[i];
6518ffdff6aSGreg Kroah-Hartman 		outw(val, dev->iobase + PCI171X_DA_REG(chan));
6528ffdff6aSGreg Kroah-Hartman 	}
6538ffdff6aSGreg Kroah-Hartman 
6548ffdff6aSGreg Kroah-Hartman 	s->readback[chan] = val;
6558ffdff6aSGreg Kroah-Hartman 
6568ffdff6aSGreg Kroah-Hartman 	return insn->n;
6578ffdff6aSGreg Kroah-Hartman }
6588ffdff6aSGreg Kroah-Hartman 
pci1710_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6598ffdff6aSGreg Kroah-Hartman static int pci1710_di_insn_bits(struct comedi_device *dev,
6608ffdff6aSGreg Kroah-Hartman 				struct comedi_subdevice *s,
6618ffdff6aSGreg Kroah-Hartman 				struct comedi_insn *insn,
6628ffdff6aSGreg Kroah-Hartman 				unsigned int *data)
6638ffdff6aSGreg Kroah-Hartman {
6648ffdff6aSGreg Kroah-Hartman 	data[1] = inw(dev->iobase + PCI171X_DI_REG);
6658ffdff6aSGreg Kroah-Hartman 
6668ffdff6aSGreg Kroah-Hartman 	return insn->n;
6678ffdff6aSGreg Kroah-Hartman }
6688ffdff6aSGreg Kroah-Hartman 
pci1710_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6698ffdff6aSGreg Kroah-Hartman static int pci1710_do_insn_bits(struct comedi_device *dev,
6708ffdff6aSGreg Kroah-Hartman 				struct comedi_subdevice *s,
6718ffdff6aSGreg Kroah-Hartman 				struct comedi_insn *insn,
6728ffdff6aSGreg Kroah-Hartman 				unsigned int *data)
6738ffdff6aSGreg Kroah-Hartman {
6748ffdff6aSGreg Kroah-Hartman 	if (comedi_dio_update_state(s, data))
6758ffdff6aSGreg Kroah-Hartman 		outw(s->state, dev->iobase + PCI171X_DO_REG);
6768ffdff6aSGreg Kroah-Hartman 
6778ffdff6aSGreg Kroah-Hartman 	data[1] = s->state;
6788ffdff6aSGreg Kroah-Hartman 
6798ffdff6aSGreg Kroah-Hartman 	return insn->n;
6808ffdff6aSGreg Kroah-Hartman }
6818ffdff6aSGreg Kroah-Hartman 
pci1710_counter_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6828ffdff6aSGreg Kroah-Hartman static int pci1710_counter_insn_config(struct comedi_device *dev,
6838ffdff6aSGreg Kroah-Hartman 				       struct comedi_subdevice *s,
6848ffdff6aSGreg Kroah-Hartman 				       struct comedi_insn *insn,
6858ffdff6aSGreg Kroah-Hartman 				       unsigned int *data)
6868ffdff6aSGreg Kroah-Hartman {
6878ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv = dev->private;
6888ffdff6aSGreg Kroah-Hartman 
6898ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
6908ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_SET_CLOCK_SRC:
6918ffdff6aSGreg Kroah-Hartman 		switch (data[1]) {
6928ffdff6aSGreg Kroah-Hartman 		case 0:	/* internal */
6938ffdff6aSGreg Kroah-Hartman 			devpriv->ctrl_ext &= ~PCI171X_CTRL_CNT0;
6948ffdff6aSGreg Kroah-Hartman 			break;
6958ffdff6aSGreg Kroah-Hartman 		case 1:	/* external */
6968ffdff6aSGreg Kroah-Hartman 			devpriv->ctrl_ext |= PCI171X_CTRL_CNT0;
6978ffdff6aSGreg Kroah-Hartman 			break;
6988ffdff6aSGreg Kroah-Hartman 		default:
6998ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
7008ffdff6aSGreg Kroah-Hartman 		}
7018ffdff6aSGreg Kroah-Hartman 		outw(devpriv->ctrl_ext, dev->iobase + PCI171X_CTRL_REG);
7028ffdff6aSGreg Kroah-Hartman 		break;
7038ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_GET_CLOCK_SRC:
7048ffdff6aSGreg Kroah-Hartman 		if (devpriv->ctrl_ext & PCI171X_CTRL_CNT0) {
7058ffdff6aSGreg Kroah-Hartman 			data[1] = 1;
7068ffdff6aSGreg Kroah-Hartman 			data[2] = 0;
7078ffdff6aSGreg Kroah-Hartman 		} else {
7088ffdff6aSGreg Kroah-Hartman 			data[1] = 0;
7098ffdff6aSGreg Kroah-Hartman 			data[2] = I8254_OSC_BASE_1MHZ;
7108ffdff6aSGreg Kroah-Hartman 		}
7118ffdff6aSGreg Kroah-Hartman 		break;
7128ffdff6aSGreg Kroah-Hartman 	default:
7138ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
7148ffdff6aSGreg Kroah-Hartman 	}
7158ffdff6aSGreg Kroah-Hartman 
7168ffdff6aSGreg Kroah-Hartman 	return insn->n;
7178ffdff6aSGreg Kroah-Hartman }
7188ffdff6aSGreg Kroah-Hartman 
pci1710_reset(struct comedi_device * dev)7198ffdff6aSGreg Kroah-Hartman static void pci1710_reset(struct comedi_device *dev)
7208ffdff6aSGreg Kroah-Hartman {
7218ffdff6aSGreg Kroah-Hartman 	const struct boardtype *board = dev->board_ptr;
7228ffdff6aSGreg Kroah-Hartman 
7238ffdff6aSGreg Kroah-Hartman 	/*
7248ffdff6aSGreg Kroah-Hartman 	 * Disable A/D triggers and interrupt sources, set counter 0
7258ffdff6aSGreg Kroah-Hartman 	 * to use internal 1 MHz clock.
7268ffdff6aSGreg Kroah-Hartman 	 */
7278ffdff6aSGreg Kroah-Hartman 	outw(0, dev->iobase + PCI171X_CTRL_REG);
7288ffdff6aSGreg Kroah-Hartman 
7298ffdff6aSGreg Kroah-Hartman 	/* clear A/D FIFO and any pending interrutps */
7308ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
7318ffdff6aSGreg Kroah-Hartman 	outb(0, dev->iobase + PCI171X_CLRINT_REG);
7328ffdff6aSGreg Kroah-Hartman 
7338ffdff6aSGreg Kroah-Hartman 	if (board->has_ao) {
7348ffdff6aSGreg Kroah-Hartman 		/* set DACs to 0..5V and outputs to 0V */
7358ffdff6aSGreg Kroah-Hartman 		outb(0, dev->iobase + PCI171X_DAREF_REG);
7368ffdff6aSGreg Kroah-Hartman 		outw(0, dev->iobase + PCI171X_DA_REG(0));
7378ffdff6aSGreg Kroah-Hartman 		outw(0, dev->iobase + PCI171X_DA_REG(1));
7388ffdff6aSGreg Kroah-Hartman 	}
7398ffdff6aSGreg Kroah-Hartman 
7408ffdff6aSGreg Kroah-Hartman 	/* set digital outputs to 0 */
7418ffdff6aSGreg Kroah-Hartman 	outw(0, dev->iobase + PCI171X_DO_REG);
7428ffdff6aSGreg Kroah-Hartman }
7438ffdff6aSGreg Kroah-Hartman 
pci1710_auto_attach(struct comedi_device * dev,unsigned long context)7448ffdff6aSGreg Kroah-Hartman static int pci1710_auto_attach(struct comedi_device *dev,
7458ffdff6aSGreg Kroah-Hartman 			       unsigned long context)
7468ffdff6aSGreg Kroah-Hartman {
7478ffdff6aSGreg Kroah-Hartman 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
7488ffdff6aSGreg Kroah-Hartman 	const struct boardtype *board = NULL;
7498ffdff6aSGreg Kroah-Hartman 	struct pci1710_private *devpriv;
7508ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
7518ffdff6aSGreg Kroah-Hartman 	int ret, subdev, n_subdevices;
7528ffdff6aSGreg Kroah-Hartman 	int i;
7538ffdff6aSGreg Kroah-Hartman 
7548ffdff6aSGreg Kroah-Hartman 	if (context < ARRAY_SIZE(boardtypes))
7558ffdff6aSGreg Kroah-Hartman 		board = &boardtypes[context];
7568ffdff6aSGreg Kroah-Hartman 	if (!board)
7578ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
7588ffdff6aSGreg Kroah-Hartman 	dev->board_ptr = board;
7598ffdff6aSGreg Kroah-Hartman 	dev->board_name = board->name;
7608ffdff6aSGreg Kroah-Hartman 
7618ffdff6aSGreg Kroah-Hartman 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
7628ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
7638ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
7648ffdff6aSGreg Kroah-Hartman 
7658ffdff6aSGreg Kroah-Hartman 	ret = comedi_pci_enable(dev);
7668ffdff6aSGreg Kroah-Hartman 	if (ret)
7678ffdff6aSGreg Kroah-Hartman 		return ret;
7688ffdff6aSGreg Kroah-Hartman 	dev->iobase = pci_resource_start(pcidev, 2);
7698ffdff6aSGreg Kroah-Hartman 
770*fade5e5bSIan Abbott 	dev->pacer = comedi_8254_io_alloc(dev->iobase + PCI171X_TIMER_BASE,
7718ffdff6aSGreg Kroah-Hartman 					  I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
772*fade5e5bSIan Abbott 	if (IS_ERR(dev->pacer))
773*fade5e5bSIan Abbott 		return PTR_ERR(dev->pacer);
7748ffdff6aSGreg Kroah-Hartman 
7758ffdff6aSGreg Kroah-Hartman 	n_subdevices = 1;	/* all boards have analog inputs */
7768ffdff6aSGreg Kroah-Hartman 	if (board->has_ao)
7778ffdff6aSGreg Kroah-Hartman 		n_subdevices++;
7788ffdff6aSGreg Kroah-Hartman 	if (!board->is_pci1713) {
7798ffdff6aSGreg Kroah-Hartman 		/*
7808ffdff6aSGreg Kroah-Hartman 		 * All other boards have digital inputs and outputs as
7818ffdff6aSGreg Kroah-Hartman 		 * well as a user counter.
7828ffdff6aSGreg Kroah-Hartman 		 */
7838ffdff6aSGreg Kroah-Hartman 		n_subdevices += 3;
7848ffdff6aSGreg Kroah-Hartman 	}
7858ffdff6aSGreg Kroah-Hartman 
7868ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, n_subdevices);
7878ffdff6aSGreg Kroah-Hartman 	if (ret)
7888ffdff6aSGreg Kroah-Hartman 		return ret;
7898ffdff6aSGreg Kroah-Hartman 
7908ffdff6aSGreg Kroah-Hartman 	pci1710_reset(dev);
7918ffdff6aSGreg Kroah-Hartman 
7928ffdff6aSGreg Kroah-Hartman 	if (pcidev->irq) {
7938ffdff6aSGreg Kroah-Hartman 		ret = request_irq(pcidev->irq, pci1710_irq_handler,
7948ffdff6aSGreg Kroah-Hartman 				  IRQF_SHARED, dev->board_name, dev);
7958ffdff6aSGreg Kroah-Hartman 		if (ret == 0)
7968ffdff6aSGreg Kroah-Hartman 			dev->irq = pcidev->irq;
7978ffdff6aSGreg Kroah-Hartman 	}
7988ffdff6aSGreg Kroah-Hartman 
7998ffdff6aSGreg Kroah-Hartman 	subdev = 0;
8008ffdff6aSGreg Kroah-Hartman 
8018ffdff6aSGreg Kroah-Hartman 	/* Analog Input subdevice */
8028ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[subdev++];
8038ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_AI;
8048ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE | SDF_GROUND;
8058ffdff6aSGreg Kroah-Hartman 	if (!board->is_pci1711)
8068ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	|= SDF_DIFF;
8078ffdff6aSGreg Kroah-Hartman 	s->n_chan	= board->is_pci1713 ? 32 : 16;
8088ffdff6aSGreg Kroah-Hartman 	s->maxdata	= 0x0fff;
8098ffdff6aSGreg Kroah-Hartman 	s->range_table	= board->ai_range;
8108ffdff6aSGreg Kroah-Hartman 	s->insn_read	= pci1710_ai_insn_read;
8118ffdff6aSGreg Kroah-Hartman 	if (dev->irq) {
8128ffdff6aSGreg Kroah-Hartman 		dev->read_subdev = s;
8138ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	|= SDF_CMD_READ;
8148ffdff6aSGreg Kroah-Hartman 		s->len_chanlist	= s->n_chan;
8158ffdff6aSGreg Kroah-Hartman 		s->do_cmdtest	= pci1710_ai_cmdtest;
8168ffdff6aSGreg Kroah-Hartman 		s->do_cmd	= pci1710_ai_cmd;
8178ffdff6aSGreg Kroah-Hartman 		s->cancel	= pci1710_ai_cancel;
8188ffdff6aSGreg Kroah-Hartman 	}
8198ffdff6aSGreg Kroah-Hartman 
8208ffdff6aSGreg Kroah-Hartman 	/* find the value needed to adjust for unipolar gain codes */
8218ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < s->range_table->length; i++) {
8228ffdff6aSGreg Kroah-Hartman 		if (comedi_range_is_unipolar(s, i)) {
8238ffdff6aSGreg Kroah-Hartman 			devpriv->unipolar_gain = i;
8248ffdff6aSGreg Kroah-Hartman 			break;
8258ffdff6aSGreg Kroah-Hartman 		}
8268ffdff6aSGreg Kroah-Hartman 	}
8278ffdff6aSGreg Kroah-Hartman 
8288ffdff6aSGreg Kroah-Hartman 	if (board->has_ao) {
8298ffdff6aSGreg Kroah-Hartman 		/* Analog Output subdevice */
8308ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev++];
8318ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_AO;
8328ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_WRITABLE | SDF_GROUND;
8338ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 2;
8348ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 0x0fff;
8358ffdff6aSGreg Kroah-Hartman 		s->range_table	= &pci171x_ao_range;
8368ffdff6aSGreg Kroah-Hartman 		s->insn_write	= pci1710_ao_insn_write;
8378ffdff6aSGreg Kroah-Hartman 
8388ffdff6aSGreg Kroah-Hartman 		ret = comedi_alloc_subdev_readback(s);
8398ffdff6aSGreg Kroah-Hartman 		if (ret)
8408ffdff6aSGreg Kroah-Hartman 			return ret;
8418ffdff6aSGreg Kroah-Hartman 	}
8428ffdff6aSGreg Kroah-Hartman 
8438ffdff6aSGreg Kroah-Hartman 	if (!board->is_pci1713) {
8448ffdff6aSGreg Kroah-Hartman 		/* Digital Input subdevice */
8458ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev++];
8468ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_DI;
8478ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_READABLE;
8488ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 16;
8498ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 1;
8508ffdff6aSGreg Kroah-Hartman 		s->range_table	= &range_digital;
8518ffdff6aSGreg Kroah-Hartman 		s->insn_bits	= pci1710_di_insn_bits;
8528ffdff6aSGreg Kroah-Hartman 
8538ffdff6aSGreg Kroah-Hartman 		/* Digital Output subdevice */
8548ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev++];
8558ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_DO;
8568ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_WRITABLE;
8578ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 16;
8588ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 1;
8598ffdff6aSGreg Kroah-Hartman 		s->range_table	= &range_digital;
8608ffdff6aSGreg Kroah-Hartman 		s->insn_bits	= pci1710_do_insn_bits;
8618ffdff6aSGreg Kroah-Hartman 
8628ffdff6aSGreg Kroah-Hartman 		/* Counter subdevice (8254) */
8638ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev++];
8648ffdff6aSGreg Kroah-Hartman 		comedi_8254_subdevice_init(s, dev->pacer);
8658ffdff6aSGreg Kroah-Hartman 
8668ffdff6aSGreg Kroah-Hartman 		dev->pacer->insn_config = pci1710_counter_insn_config;
8678ffdff6aSGreg Kroah-Hartman 
8688ffdff6aSGreg Kroah-Hartman 		/* counters 1 and 2 are used internally for the pacer */
8698ffdff6aSGreg Kroah-Hartman 		comedi_8254_set_busy(dev->pacer, 1, true);
8708ffdff6aSGreg Kroah-Hartman 		comedi_8254_set_busy(dev->pacer, 2, true);
8718ffdff6aSGreg Kroah-Hartman 	}
8728ffdff6aSGreg Kroah-Hartman 
8738ffdff6aSGreg Kroah-Hartman 	/* max_samples is half the FIFO size (2 bytes/sample) */
8748ffdff6aSGreg Kroah-Hartman 	devpriv->max_samples = (board->is_pci1711) ? 512 : 2048;
8758ffdff6aSGreg Kroah-Hartman 
8768ffdff6aSGreg Kroah-Hartman 	return 0;
8778ffdff6aSGreg Kroah-Hartman }
8788ffdff6aSGreg Kroah-Hartman 
8798ffdff6aSGreg Kroah-Hartman static struct comedi_driver adv_pci1710_driver = {
8808ffdff6aSGreg Kroah-Hartman 	.driver_name	= "adv_pci1710",
8818ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
8828ffdff6aSGreg Kroah-Hartman 	.auto_attach	= pci1710_auto_attach,
8838ffdff6aSGreg Kroah-Hartman 	.detach		= comedi_pci_detach,
8848ffdff6aSGreg Kroah-Hartman };
8858ffdff6aSGreg Kroah-Hartman 
adv_pci1710_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)8868ffdff6aSGreg Kroah-Hartman static int adv_pci1710_pci_probe(struct pci_dev *dev,
8878ffdff6aSGreg Kroah-Hartman 				 const struct pci_device_id *id)
8888ffdff6aSGreg Kroah-Hartman {
8898ffdff6aSGreg Kroah-Hartman 	return comedi_pci_auto_config(dev, &adv_pci1710_driver,
8908ffdff6aSGreg Kroah-Hartman 				      id->driver_data);
8918ffdff6aSGreg Kroah-Hartman }
8928ffdff6aSGreg Kroah-Hartman 
8938ffdff6aSGreg Kroah-Hartman static const struct pci_device_id adv_pci1710_pci_table[] = {
8948ffdff6aSGreg Kroah-Hartman 	{
8958ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
8968ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
8978ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
8988ffdff6aSGreg Kroah-Hartman 	}, {
8998ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9008ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0x0000),
9018ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
9028ffdff6aSGreg Kroah-Hartman 	}, {
9038ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9048ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xb100),
9058ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
9068ffdff6aSGreg Kroah-Hartman 	}, {
9078ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9088ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xb200),
9098ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
9108ffdff6aSGreg Kroah-Hartman 	}, {
9118ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9128ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xc100),
9138ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
9148ffdff6aSGreg Kroah-Hartman 	}, {
9158ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9168ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xc200),
9178ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
9188ffdff6aSGreg Kroah-Hartman 	}, {
9198ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
9208ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710,
9218ffdff6aSGreg Kroah-Hartman 	}, {
9228ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9238ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0x0002),
9248ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710HG,
9258ffdff6aSGreg Kroah-Hartman 	}, {
9268ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9278ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xb102),
9288ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710HG,
9298ffdff6aSGreg Kroah-Hartman 	}, {
9308ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9318ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xb202),
9328ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710HG,
9338ffdff6aSGreg Kroah-Hartman 	}, {
9348ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9358ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xc102),
9368ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710HG,
9378ffdff6aSGreg Kroah-Hartman 	}, {
9388ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
9398ffdff6aSGreg Kroah-Hartman 			       PCI_VENDOR_ID_ADVANTECH, 0xc202),
9408ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710HG,
9418ffdff6aSGreg Kroah-Hartman 	}, {
9428ffdff6aSGreg Kroah-Hartman 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
9438ffdff6aSGreg Kroah-Hartman 		.driver_data = BOARD_PCI1710HG,
9448ffdff6aSGreg Kroah-Hartman 	},
9458ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
9468ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
9478ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
9488ffdff6aSGreg Kroah-Hartman 	{ 0 }
9498ffdff6aSGreg Kroah-Hartman };
9508ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
9518ffdff6aSGreg Kroah-Hartman 
9528ffdff6aSGreg Kroah-Hartman static struct pci_driver adv_pci1710_pci_driver = {
9538ffdff6aSGreg Kroah-Hartman 	.name		= "adv_pci1710",
9548ffdff6aSGreg Kroah-Hartman 	.id_table	= adv_pci1710_pci_table,
9558ffdff6aSGreg Kroah-Hartman 	.probe		= adv_pci1710_pci_probe,
9568ffdff6aSGreg Kroah-Hartman 	.remove		= comedi_pci_auto_unconfig,
9578ffdff6aSGreg Kroah-Hartman };
9588ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
9598ffdff6aSGreg Kroah-Hartman 
9608ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
9618ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
9628ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
963