[go: nahoru, domu]

1/*
2 * comedi/drivers/adv_pci1710.c
3 *
4 * Author: Michal Dobes <dobes@tesnet.cz>
5 *
6 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
7 * for testing and informations.
8 *
9 *  hardware driver for Advantech cards:
10 *   card:   PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
11 *   driver: pci1710,  pci1710hg,  pci1711,  pci1713,  pci1720,  pci1731
12 *
13 * Options:
14 *  [0] - PCI bus number - if bus number and slot number are 0,
15 *                         then driver search for first unused card
16 *  [1] - PCI slot number
17 *
18*/
19/*
20Driver: adv_pci1710
21Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
22	     Advantech PCI-1720, PCI-1731
23Author: Michal Dobes <dobes@tesnet.cz>
24Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
25  PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
26  PCI-1731
27Status: works
28
29This driver supports AI, AO, DI and DO subdevices.
30AI subdevice supports cmd and insn interface,
31other subdevices support only insn interface.
32
33The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
34driver cannot distinguish between them, as would be normal for a
35PCI driver.
36
37Configuration options:
38  [0] - PCI bus of device (optional)
39  [1] - PCI slot of device (optional)
40	If bus/slot is not specified, the first available PCI
41	device will be used.
42*/
43
44#include <linux/module.h>
45#include <linux/pci.h>
46#include <linux/interrupt.h>
47
48#include "../comedidev.h"
49
50#include "comedi_fc.h"
51#include "8253.h"
52#include "amcc_s5933.h"
53
54/* hardware types of the cards */
55#define TYPE_PCI171X	0
56#define TYPE_PCI1713	2
57#define TYPE_PCI1720	3
58
59#define PCI171x_AD_DATA	 0	/* R:   A/D data */
60#define PCI171x_SOFTTRG	 0	/* W:   soft trigger for A/D */
61#define PCI171x_RANGE	 2	/* W:   A/D gain/range register */
62#define PCI171x_MUX	 4	/* W:   A/D multiplexor control */
63#define PCI171x_STATUS	 6	/* R:   status register */
64#define PCI171x_CONTROL	 6	/* W:   control register */
65#define PCI171x_CLRINT	 8	/* W:   clear interrupts request */
66#define PCI171x_CLRFIFO	 9	/* W:   clear FIFO */
67#define PCI171x_DA1	10	/* W:   D/A register */
68#define PCI171x_DA2	12	/* W:   D/A register */
69#define PCI171x_DAREF	14	/* W:   D/A reference control */
70#define PCI171x_DI	16	/* R:   digi inputs */
71#define PCI171x_DO	16	/* R:   digi inputs */
72
73#define PCI171X_TIMER_BASE	0x18
74
75#define PCI171x_CNT0	24	/* R/W: 8254 counter 0 */
76#define PCI171x_CNT1	26	/* R/W: 8254 counter 1 */
77#define PCI171x_CNT2	28	/* R/W: 8254 counter 2 */
78#define PCI171x_CNTCTRL	30	/* W:   8254 counter control */
79
80/* upper bits from status register (PCI171x_STATUS) (lower is same with control
81 * reg) */
82#define	Status_FE	0x0100	/* 1=FIFO is empty */
83#define Status_FH	0x0200	/* 1=FIFO is half full */
84#define Status_FF	0x0400	/* 1=FIFO is full, fatal error */
85#define Status_IRQ	0x0800	/* 1=IRQ occurred */
86/* bits from control register (PCI171x_CONTROL) */
87#define Control_CNT0	0x0040	/* 1=CNT0 have external source,
88				 * 0=have internal 100kHz source */
89#define Control_ONEFH	0x0020	/* 1=IRQ on FIFO is half full, 0=every sample */
90#define Control_IRQEN	0x0010	/* 1=enable IRQ */
91#define Control_GATE	0x0008	/* 1=enable external trigger GATE (8254?) */
92#define Control_EXT	0x0004	/* 1=external trigger source */
93#define Control_PACER	0x0002	/* 1=enable internal 8254 trigger source */
94#define Control_SW	0x0001	/* 1=enable software trigger source */
95/* bits from counter control register (PCI171x_CNTCTRL) */
96#define Counter_BCD     0x0001	/* 0 = binary counter, 1 = BCD counter */
97#define Counter_M0      0x0002	/* M0-M2 select modes 0-5 */
98#define Counter_M1      0x0004	/* 000 = mode 0, 010 = mode 2 ... */
99#define Counter_M2      0x0008
100#define Counter_RW0     0x0010	/* RW0/RW1 select read/write mode */
101#define Counter_RW1     0x0020
102#define Counter_SC0     0x0040	/* Select Counter. Only 00 or 11 may */
103#define Counter_SC1     0x0080	/* be used, 00 for CNT0,
104				 * 11 for read-back command */
105
106#define PCI1720_DA0	 0	/* W:   D/A register 0 */
107#define PCI1720_DA1	 2	/* W:   D/A register 1 */
108#define PCI1720_DA2	 4	/* W:   D/A register 2 */
109#define PCI1720_DA3	 6	/* W:   D/A register 3 */
110#define PCI1720_RANGE	 8	/* R/W: D/A range register */
111#define PCI1720_SYNCOUT	 9	/* W:   D/A synchronized output register */
112#define PCI1720_SYNCONT	15	/* R/W: D/A synchronized control */
113
114/* D/A synchronized control (PCI1720_SYNCONT) */
115#define Syncont_SC0	 1	/* set synchronous output mode */
116
117static const struct comedi_lrange range_pci1710_3 = {
118	9, {
119		BIP_RANGE(5),
120		BIP_RANGE(2.5),
121		BIP_RANGE(1.25),
122		BIP_RANGE(0.625),
123		BIP_RANGE(10),
124		UNI_RANGE(10),
125		UNI_RANGE(5),
126		UNI_RANGE(2.5),
127		UNI_RANGE(1.25)
128	}
129};
130
131static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
132					      0x10, 0x11, 0x12, 0x13 };
133
134static const struct comedi_lrange range_pci1710hg = {
135	12, {
136		BIP_RANGE(5),
137		BIP_RANGE(0.5),
138		BIP_RANGE(0.05),
139		BIP_RANGE(0.005),
140		BIP_RANGE(10),
141		BIP_RANGE(1),
142		BIP_RANGE(0.1),
143		BIP_RANGE(0.01),
144		UNI_RANGE(10),
145		UNI_RANGE(1),
146		UNI_RANGE(0.1),
147		UNI_RANGE(0.01)
148	}
149};
150
151static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
152					      0x05, 0x06, 0x07, 0x10, 0x11,
153					      0x12, 0x13 };
154
155static const struct comedi_lrange range_pci17x1 = {
156	5, {
157		BIP_RANGE(10),
158		BIP_RANGE(5),
159		BIP_RANGE(2.5),
160		BIP_RANGE(1.25),
161		BIP_RANGE(0.625)
162	}
163};
164
165static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
166
167static const struct comedi_lrange range_pci1720 = {
168	4, {
169		UNI_RANGE(5),
170		UNI_RANGE(10),
171		BIP_RANGE(5),
172		BIP_RANGE(10)
173	}
174};
175
176static const struct comedi_lrange range_pci171x_da = {
177	2, {
178		UNI_RANGE(5),
179		UNI_RANGE(10)
180	}
181};
182
183enum pci1710_boardid {
184	BOARD_PCI1710,
185	BOARD_PCI1710HG,
186	BOARD_PCI1711,
187	BOARD_PCI1713,
188	BOARD_PCI1720,
189	BOARD_PCI1731,
190};
191
192struct boardtype {
193	const char *name;	/*  board name */
194	char have_irq;		/*  1=card support IRQ */
195	char cardtype;		/*  0=1710& co. 2=1713, ... */
196	int n_aichan;		/*  num of A/D chans */
197	int n_aichand;		/*  num of A/D chans in diff mode */
198	int n_aochan;		/*  num of D/A chans */
199	int n_dichan;		/*  num of DI chans */
200	int n_dochan;		/*  num of DO chans */
201	int n_counter;		/*  num of counters */
202	int ai_maxdata;		/*  resolution of A/D */
203	int ao_maxdata;		/*  resolution of D/A */
204	const struct comedi_lrange *rangelist_ai;	/*  rangelist for A/D */
205	const char *rangecode_ai;	/*  range codes for programming */
206	const struct comedi_lrange *rangelist_ao;	/*  rangelist for D/A */
207	unsigned int ai_ns_min;	/*  max sample speed of card v ns */
208	unsigned int fifo_half_size;	/*  size of FIFO/2 */
209};
210
211static const struct boardtype boardtypes[] = {
212	[BOARD_PCI1710] = {
213		.name		= "pci1710",
214		.have_irq	= 1,
215		.cardtype	= TYPE_PCI171X,
216		.n_aichan	= 16,
217		.n_aichand	= 8,
218		.n_aochan	= 2,
219		.n_dichan	= 16,
220		.n_dochan	= 16,
221		.n_counter	= 1,
222		.ai_maxdata	= 0x0fff,
223		.ao_maxdata	= 0x0fff,
224		.rangelist_ai	= &range_pci1710_3,
225		.rangecode_ai	= range_codes_pci1710_3,
226		.rangelist_ao	= &range_pci171x_da,
227		.ai_ns_min	= 10000,
228		.fifo_half_size	= 2048,
229	},
230	[BOARD_PCI1710HG] = {
231		.name		= "pci1710hg",
232		.have_irq	= 1,
233		.cardtype	= TYPE_PCI171X,
234		.n_aichan	= 16,
235		.n_aichand	= 8,
236		.n_aochan	= 2,
237		.n_dichan	= 16,
238		.n_dochan	= 16,
239		.n_counter	= 1,
240		.ai_maxdata	= 0x0fff,
241		.ao_maxdata	= 0x0fff,
242		.rangelist_ai	= &range_pci1710hg,
243		.rangecode_ai	= range_codes_pci1710hg,
244		.rangelist_ao	= &range_pci171x_da,
245		.ai_ns_min	= 10000,
246		.fifo_half_size	= 2048,
247	},
248	[BOARD_PCI1711] = {
249		.name		= "pci1711",
250		.have_irq	= 1,
251		.cardtype	= TYPE_PCI171X,
252		.n_aichan	= 16,
253		.n_aochan	= 2,
254		.n_dichan	= 16,
255		.n_dochan	= 16,
256		.n_counter	= 1,
257		.ai_maxdata	= 0x0fff,
258		.ao_maxdata	= 0x0fff,
259		.rangelist_ai	= &range_pci17x1,
260		.rangecode_ai	= range_codes_pci17x1,
261		.rangelist_ao	= &range_pci171x_da,
262		.ai_ns_min	= 10000,
263		.fifo_half_size	= 512,
264	},
265	[BOARD_PCI1713] = {
266		.name		= "pci1713",
267		.have_irq	= 1,
268		.cardtype	= TYPE_PCI1713,
269		.n_aichan	= 32,
270		.n_aichand	= 16,
271		.ai_maxdata	= 0x0fff,
272		.rangelist_ai	= &range_pci1710_3,
273		.rangecode_ai	= range_codes_pci1710_3,
274		.ai_ns_min	= 10000,
275		.fifo_half_size	= 2048,
276	},
277	[BOARD_PCI1720] = {
278		.name		= "pci1720",
279		.cardtype	= TYPE_PCI1720,
280		.n_aochan	= 4,
281		.ao_maxdata	= 0x0fff,
282		.rangelist_ao	= &range_pci1720,
283	},
284	[BOARD_PCI1731] = {
285		.name		= "pci1731",
286		.have_irq	= 1,
287		.cardtype	= TYPE_PCI171X,
288		.n_aichan	= 16,
289		.n_dichan	= 16,
290		.n_dochan	= 16,
291		.ai_maxdata	= 0x0fff,
292		.rangelist_ai	= &range_pci17x1,
293		.rangecode_ai	= range_codes_pci17x1,
294		.ai_ns_min	= 10000,
295		.fifo_half_size	= 512,
296	},
297};
298
299struct pci1710_private {
300	unsigned int CntrlReg;	/*  Control register */
301	unsigned int ai_act_scan;	/*  how many scans we finished */
302	unsigned char ai_et;
303	unsigned int ai_et_CntrlReg;
304	unsigned int ai_et_MuxVal;
305	unsigned int next_divisor1;
306	unsigned int next_divisor2;
307	unsigned int divisor1;
308	unsigned int divisor2;
309	unsigned int act_chanlist[32];	/*  list of scanned channel */
310	unsigned char saved_seglen;	/* len of the non-repeating chanlist */
311	unsigned char da_ranges;	/*  copy of D/A outpit range register */
312	unsigned short ao_data[4];	/*  data output buffer */
313	unsigned int cnt0_write_wait;	/* after a write, wait for update of the
314					 * internal state */
315};
316
317/*  used for gain list programming */
318static const unsigned int muxonechan[] = {
319	0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
320	0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
321	0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
322	0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
323};
324
325static int pci171x_ai_dropout(struct comedi_device *dev,
326			      struct comedi_subdevice *s,
327			      unsigned int chan,
328			      unsigned int val)
329{
330	const struct boardtype *board = dev->board_ptr;
331	struct pci1710_private *devpriv = dev->private;
332
333	if (board->cardtype != TYPE_PCI1713) {
334		if ((val & 0xf000) != devpriv->act_chanlist[chan]) {
335			dev_err(dev->class_dev,
336				"A/D data droput: received from channel %d, expected %d\n",
337				(val >> 12) & 0xf,
338				(devpriv->act_chanlist[chan] >> 12) & 0xf);
339			return -ENODATA;
340		}
341	}
342	return 0;
343}
344
345static int pci171x_ai_check_chanlist(struct comedi_device *dev,
346				     struct comedi_subdevice *s,
347				     struct comedi_cmd *cmd)
348{
349	struct pci1710_private *devpriv = dev->private;
350	unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
351	unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
352	unsigned int next_chan = (chan0 + 1) % s->n_chan;
353	unsigned int chansegment[32];
354	unsigned int seglen;
355	int i;
356
357	if (cmd->chanlist_len == 1) {
358		devpriv->saved_seglen = cmd->chanlist_len;
359		return 0;
360	}
361
362	/* first channel is always ok */
363	chansegment[0] = cmd->chanlist[0];
364
365	for (i = 1; i < cmd->chanlist_len; i++) {
366		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
367		unsigned int aref = CR_AREF(cmd->chanlist[i]);
368
369		if (cmd->chanlist[0] == cmd->chanlist[i])
370			break;	/*  we detected a loop, stop */
371
372		if (aref == AREF_DIFF && (chan & 1)) {
373			dev_err(dev->class_dev,
374				"Odd channel cannot be differential input!\n");
375			return -EINVAL;
376		}
377
378		if (last_aref == AREF_DIFF)
379			next_chan = (next_chan + 1) % s->n_chan;
380		if (chan != next_chan) {
381			dev_err(dev->class_dev,
382				"channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
383				i, chan, next_chan, chan0);
384			return -EINVAL;
385		}
386
387		/* next correct channel in list */
388		chansegment[i] = cmd->chanlist[i];
389		last_aref = aref;
390	}
391	seglen = i;
392
393	for (i = 0; i < cmd->chanlist_len; i++) {
394		if (cmd->chanlist[i] != chansegment[i % seglen]) {
395			dev_err(dev->class_dev,
396				"bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
397				i, CR_CHAN(chansegment[i]),
398				CR_RANGE(chansegment[i]),
399				CR_AREF(chansegment[i]),
400				CR_CHAN(cmd->chanlist[i % seglen]),
401				CR_RANGE(cmd->chanlist[i % seglen]),
402				CR_AREF(chansegment[i % seglen]));
403			return -EINVAL;
404		}
405	}
406	devpriv->saved_seglen = seglen;
407
408	return 0;
409}
410
411static void setup_channel_list(struct comedi_device *dev,
412			       struct comedi_subdevice *s,
413			       unsigned int *chanlist, unsigned int n_chan,
414			       unsigned int seglen)
415{
416	const struct boardtype *this_board = dev->board_ptr;
417	struct pci1710_private *devpriv = dev->private;
418	unsigned int i, range, chanprog;
419
420	for (i = 0; i < seglen; i++) {	/*  store range list to card */
421		chanprog = muxonechan[CR_CHAN(chanlist[i])];
422		outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
423		range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
424		if (CR_AREF(chanlist[i]) == AREF_DIFF)
425			range |= 0x0020;
426		outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
427		devpriv->act_chanlist[i] =
428			(CR_CHAN(chanlist[i]) << 12) & 0xf000;
429	}
430	for ( ; i < n_chan; i++) { /* store remainder of channel list */
431		devpriv->act_chanlist[i] =
432			(CR_CHAN(chanlist[i]) << 12) & 0xf000;
433	}
434
435	devpriv->ai_et_MuxVal =
436		CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
437	/* select channel interval to scan */
438	outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
439}
440
441static int pci171x_ai_eoc(struct comedi_device *dev,
442			  struct comedi_subdevice *s,
443			  struct comedi_insn *insn,
444			  unsigned long context)
445{
446	unsigned int status;
447
448	status = inw(dev->iobase + PCI171x_STATUS);
449	if ((status & Status_FE) == 0)
450		return 0;
451	return -EBUSY;
452}
453
454static int pci171x_insn_read_ai(struct comedi_device *dev,
455				struct comedi_subdevice *s,
456				struct comedi_insn *insn, unsigned int *data)
457{
458	struct pci1710_private *devpriv = dev->private;
459	unsigned int chan = CR_CHAN(insn->chanspec);
460	int ret = 0;
461	int i;
462
463	devpriv->CntrlReg &= Control_CNT0;
464	devpriv->CntrlReg |= Control_SW;	/*  set software trigger */
465	outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
466	outb(0, dev->iobase + PCI171x_CLRFIFO);
467	outb(0, dev->iobase + PCI171x_CLRINT);
468
469	setup_channel_list(dev, s, &insn->chanspec, 1, 1);
470
471	for (i = 0; i < insn->n; i++) {
472		unsigned int val;
473
474		outw(0, dev->iobase + PCI171x_SOFTTRG);	/* start conversion */
475
476		ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
477		if (ret)
478			break;
479
480		val = inw(dev->iobase + PCI171x_AD_DATA);
481		ret = pci171x_ai_dropout(dev, s, chan, val);
482		if (ret)
483			break;
484
485		data[i] = val & s->maxdata;
486	}
487
488	outb(0, dev->iobase + PCI171x_CLRFIFO);
489	outb(0, dev->iobase + PCI171x_CLRINT);
490
491	return ret ? ret : insn->n;
492}
493
494/*
495==============================================================================
496*/
497static int pci171x_insn_write_ao(struct comedi_device *dev,
498				 struct comedi_subdevice *s,
499				 struct comedi_insn *insn, unsigned int *data)
500{
501	struct pci1710_private *devpriv = dev->private;
502	unsigned int val;
503	int n, chan, range, ofs;
504
505	chan = CR_CHAN(insn->chanspec);
506	range = CR_RANGE(insn->chanspec);
507	if (chan) {
508		devpriv->da_ranges &= 0xfb;
509		devpriv->da_ranges |= (range << 2);
510		outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
511		ofs = PCI171x_DA2;
512	} else {
513		devpriv->da_ranges &= 0xfe;
514		devpriv->da_ranges |= range;
515		outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
516		ofs = PCI171x_DA1;
517	}
518	val = devpriv->ao_data[chan];
519
520	for (n = 0; n < insn->n; n++) {
521		val = data[n];
522		outw(val, dev->iobase + ofs);
523	}
524
525	devpriv->ao_data[chan] = val;
526
527	return n;
528
529}
530
531/*
532==============================================================================
533*/
534static int pci171x_insn_read_ao(struct comedi_device *dev,
535				struct comedi_subdevice *s,
536				struct comedi_insn *insn, unsigned int *data)
537{
538	struct pci1710_private *devpriv = dev->private;
539	int n, chan;
540
541	chan = CR_CHAN(insn->chanspec);
542	for (n = 0; n < insn->n; n++)
543		data[n] = devpriv->ao_data[chan];
544
545	return n;
546}
547
548/*
549==============================================================================
550*/
551static int pci171x_insn_bits_di(struct comedi_device *dev,
552				struct comedi_subdevice *s,
553				struct comedi_insn *insn, unsigned int *data)
554{
555	data[1] = inw(dev->iobase + PCI171x_DI);
556
557	return insn->n;
558}
559
560static int pci171x_insn_bits_do(struct comedi_device *dev,
561				struct comedi_subdevice *s,
562				struct comedi_insn *insn,
563				unsigned int *data)
564{
565	if (comedi_dio_update_state(s, data))
566		outw(s->state, dev->iobase + PCI171x_DO);
567
568	data[1] = s->state;
569
570	return insn->n;
571}
572
573static void pci171x_start_pacer(struct comedi_device *dev,
574				bool load_counters)
575{
576	struct pci1710_private *devpriv = dev->private;
577	unsigned long timer_base = dev->iobase + PCI171X_TIMER_BASE;
578
579	i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
580	i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
581
582	if (load_counters) {
583		i8254_write(timer_base, 1, 2, devpriv->divisor2);
584		i8254_write(timer_base, 1, 1, devpriv->divisor1);
585	}
586}
587
588/*
589==============================================================================
590*/
591static int pci171x_insn_counter_read(struct comedi_device *dev,
592				     struct comedi_subdevice *s,
593				     struct comedi_insn *insn,
594				     unsigned int *data)
595{
596	unsigned int msb, lsb, ccntrl;
597	int i;
598
599	ccntrl = 0xD2;		/* count only */
600	for (i = 0; i < insn->n; i++) {
601		outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
602
603		lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
604		msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
605
606		data[0] = lsb | (msb << 8);
607	}
608
609	return insn->n;
610}
611
612/*
613==============================================================================
614*/
615static int pci171x_insn_counter_write(struct comedi_device *dev,
616				      struct comedi_subdevice *s,
617				      struct comedi_insn *insn,
618				      unsigned int *data)
619{
620	struct pci1710_private *devpriv = dev->private;
621	uint msb, lsb, ccntrl, status;
622
623	lsb = data[0] & 0x00FF;
624	msb = (data[0] & 0xFF00) >> 8;
625
626	/* write lsb, then msb */
627	outw(lsb, dev->iobase + PCI171x_CNT0);
628	outw(msb, dev->iobase + PCI171x_CNT0);
629
630	if (devpriv->cnt0_write_wait) {
631		/* wait for the new count to be loaded */
632		ccntrl = 0xE2;
633		do {
634			outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
635			status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
636		} while (status & 0x40);
637	}
638
639	return insn->n;
640}
641
642/*
643==============================================================================
644*/
645static int pci171x_insn_counter_config(struct comedi_device *dev,
646				       struct comedi_subdevice *s,
647				       struct comedi_insn *insn,
648				       unsigned int *data)
649{
650#ifdef unused
651	/* This doesn't work like a normal Comedi counter config */
652	struct pci1710_private *devpriv = dev->private;
653	uint ccntrl = 0;
654
655	devpriv->cnt0_write_wait = data[0] & 0x20;
656
657	/* internal or external clock? */
658	if (!(data[0] & 0x10)) {	/* internal */
659		devpriv->CntrlReg &= ~Control_CNT0;
660	} else {
661		devpriv->CntrlReg |= Control_CNT0;
662	}
663	outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
664
665	if (data[0] & 0x01)
666		ccntrl |= Counter_M0;
667	if (data[0] & 0x02)
668		ccntrl |= Counter_M1;
669	if (data[0] & 0x04)
670		ccntrl |= Counter_M2;
671	if (data[0] & 0x08)
672		ccntrl |= Counter_BCD;
673	ccntrl |= Counter_RW0;	/* set read/write mode */
674	ccntrl |= Counter_RW1;
675	outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
676#endif
677
678	return 1;
679}
680
681/*
682==============================================================================
683*/
684static int pci1720_insn_write_ao(struct comedi_device *dev,
685				 struct comedi_subdevice *s,
686				 struct comedi_insn *insn, unsigned int *data)
687{
688	struct pci1710_private *devpriv = dev->private;
689	unsigned int val;
690	int n, rangereg, chan;
691
692	chan = CR_CHAN(insn->chanspec);
693	rangereg = devpriv->da_ranges & (~(0x03 << (chan << 1)));
694	rangereg |= (CR_RANGE(insn->chanspec) << (chan << 1));
695	if (rangereg != devpriv->da_ranges) {
696		outb(rangereg, dev->iobase + PCI1720_RANGE);
697		devpriv->da_ranges = rangereg;
698	}
699	val = devpriv->ao_data[chan];
700
701	for (n = 0; n < insn->n; n++) {
702		val = data[n];
703		outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
704		outb(0, dev->iobase + PCI1720_SYNCOUT);	/*  update outputs */
705	}
706
707	devpriv->ao_data[chan] = val;
708
709	return n;
710}
711
712/*
713==============================================================================
714*/
715static int pci171x_ai_cancel(struct comedi_device *dev,
716			     struct comedi_subdevice *s)
717{
718	const struct boardtype *this_board = dev->board_ptr;
719	struct pci1710_private *devpriv = dev->private;
720
721	switch (this_board->cardtype) {
722	default:
723		devpriv->CntrlReg &= Control_CNT0;
724		devpriv->CntrlReg |= Control_SW;
725		/* reset any operations */
726		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
727		pci171x_start_pacer(dev, false);
728		outb(0, dev->iobase + PCI171x_CLRFIFO);
729		outb(0, dev->iobase + PCI171x_CLRINT);
730		break;
731	}
732
733	devpriv->ai_act_scan = 0;
734	s->async->cur_chan = 0;
735
736	return 0;
737}
738
739static void pci1710_handle_every_sample(struct comedi_device *dev,
740					struct comedi_subdevice *s)
741{
742	struct pci1710_private *devpriv = dev->private;
743	struct comedi_cmd *cmd = &s->async->cmd;
744	unsigned int status;
745	unsigned int val;
746	int ret;
747
748	status = inw(dev->iobase + PCI171x_STATUS);
749	if (status & Status_FE) {
750		dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
751		s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
752		cfc_handle_events(dev, s);
753		return;
754	}
755	if (status & Status_FF) {
756		dev_dbg(dev->class_dev,
757			"A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
758		s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
759		cfc_handle_events(dev, s);
760		return;
761	}
762
763	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear our INT request */
764
765	for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
766		val = inw(dev->iobase + PCI171x_AD_DATA);
767		ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
768		if (ret) {
769			s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
770			break;
771		}
772
773		comedi_buf_put(s, val & s->maxdata);
774
775		s->async->cur_chan++;
776		if (s->async->cur_chan >= cmd->chanlist_len)
777			s->async->cur_chan = 0;
778
779
780		if (s->async->cur_chan == 0) {	/*  one scan done */
781			devpriv->ai_act_scan++;
782			if (cmd->stop_src == TRIG_COUNT &&
783			    devpriv->ai_act_scan >= cmd->stop_arg) {
784				/*  all data sampled */
785				s->async->events |= COMEDI_CB_EOA;
786				break;
787			}
788		}
789	}
790
791	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear our INT request */
792
793	cfc_handle_events(dev, s);
794}
795
796/*
797==============================================================================
798*/
799static int move_block_from_fifo(struct comedi_device *dev,
800				struct comedi_subdevice *s, int n, int turn)
801{
802	struct pci1710_private *devpriv = dev->private;
803	struct comedi_cmd *cmd = &s->async->cmd;
804	unsigned int val;
805	int ret;
806	int i;
807
808	for (i = 0; i < n; i++) {
809		val = inw(dev->iobase + PCI171x_AD_DATA);
810
811		ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
812		if (ret) {
813			s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
814			return ret;
815		}
816
817		comedi_buf_put(s, val & s->maxdata);
818
819		s->async->cur_chan++;
820		if (s->async->cur_chan >= cmd->chanlist_len) {
821			s->async->cur_chan = 0;
822			devpriv->ai_act_scan++;
823		}
824	}
825	return 0;
826}
827
828static void pci1710_handle_fifo(struct comedi_device *dev,
829				struct comedi_subdevice *s)
830{
831	const struct boardtype *this_board = dev->board_ptr;
832	struct pci1710_private *devpriv = dev->private;
833	struct comedi_cmd *cmd = &s->async->cmd;
834	int m, samplesinbuf;
835
836	m = inw(dev->iobase + PCI171x_STATUS);
837	if (!(m & Status_FH)) {
838		dev_dbg(dev->class_dev, "A/D FIFO not half full! (%4x)\n", m);
839		s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
840		cfc_handle_events(dev, s);
841		return;
842	}
843	if (m & Status_FF) {
844		dev_dbg(dev->class_dev,
845			"A/D FIFO Full status (Fatal Error!) (%4x)\n", m);
846		s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
847		cfc_handle_events(dev, s);
848		return;
849	}
850
851	samplesinbuf = this_board->fifo_half_size;
852	if (samplesinbuf * sizeof(short) >= s->async->prealloc_bufsz) {
853		m = s->async->prealloc_bufsz / sizeof(short);
854		if (move_block_from_fifo(dev, s, m, 0))
855			return;
856		samplesinbuf -= m;
857	}
858
859	if (samplesinbuf) {
860		if (move_block_from_fifo(dev, s, samplesinbuf, 1))
861			return;
862	}
863
864	if (cmd->stop_src == TRIG_COUNT &&
865	    devpriv->ai_act_scan >= cmd->stop_arg) {
866		/* all data sampled */
867		s->async->events |= COMEDI_CB_EOA;
868		cfc_handle_events(dev, s);
869		return;
870	}
871	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear our INT request */
872
873	cfc_handle_events(dev, s);
874}
875
876/*
877==============================================================================
878*/
879static irqreturn_t interrupt_service_pci1710(int irq, void *d)
880{
881	struct comedi_device *dev = d;
882	struct pci1710_private *devpriv = dev->private;
883	struct comedi_subdevice *s;
884	struct comedi_cmd *cmd;
885
886	if (!dev->attached)	/*  is device attached? */
887		return IRQ_NONE;	/*  no, exit */
888
889	s = dev->read_subdev;
890	cmd = &s->async->cmd;
891
892	/*  is this interrupt from our board? */
893	if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
894		return IRQ_NONE;	/*  no, exit */
895
896	if (devpriv->ai_et) {	/*  Switch from initial TRIG_EXT to TRIG_xxx. */
897		devpriv->ai_et = 0;
898		devpriv->CntrlReg &= Control_CNT0;
899		devpriv->CntrlReg |= Control_SW; /* set software trigger */
900		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
901		devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
902		outb(0, dev->iobase + PCI171x_CLRFIFO);
903		outb(0, dev->iobase + PCI171x_CLRINT);
904		outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
905		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
906		pci171x_start_pacer(dev, true);
907		return IRQ_HANDLED;
908	}
909
910	if (cmd->flags & CMDF_WAKE_EOS)
911		pci1710_handle_every_sample(dev, s);
912	else
913		pci1710_handle_fifo(dev, s);
914
915	return IRQ_HANDLED;
916}
917
918static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
919{
920	struct pci1710_private *devpriv = dev->private;
921	struct comedi_cmd *cmd = &s->async->cmd;
922
923	pci171x_start_pacer(dev, false);
924
925	setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len,
926			   devpriv->saved_seglen);
927
928	outb(0, dev->iobase + PCI171x_CLRFIFO);
929	outb(0, dev->iobase + PCI171x_CLRINT);
930
931	devpriv->ai_act_scan = 0;
932	s->async->cur_chan = 0;
933
934	devpriv->CntrlReg &= Control_CNT0;
935	if ((cmd->flags & CMDF_WAKE_EOS) == 0)
936		devpriv->CntrlReg |= Control_ONEFH;
937
938	devpriv->divisor1 = devpriv->next_divisor1;
939	devpriv->divisor2 = devpriv->next_divisor2;
940
941	if (cmd->convert_src == TRIG_TIMER) {
942		devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
943		if (cmd->start_src == TRIG_EXT) {
944			devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
945			devpriv->CntrlReg &=
946			    ~(Control_PACER | Control_ONEFH | Control_GATE);
947			devpriv->CntrlReg |= Control_EXT;
948			devpriv->ai_et = 1;
949		} else {	/* TRIG_NOW */
950			devpriv->ai_et = 0;
951		}
952		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
953
954		if (cmd->start_src == TRIG_NOW)
955			pci171x_start_pacer(dev, true);
956	} else {	/* TRIG_EXT */
957		devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
958		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
959	}
960
961	return 0;
962}
963
964/*
965==============================================================================
966*/
967static int pci171x_ai_cmdtest(struct comedi_device *dev,
968			      struct comedi_subdevice *s,
969			      struct comedi_cmd *cmd)
970{
971	const struct boardtype *this_board = dev->board_ptr;
972	struct pci1710_private *devpriv = dev->private;
973	int err = 0;
974	unsigned int arg;
975
976	/* Step 1 : check if triggers are trivially valid */
977
978	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
979	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
980	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
981	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
982	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
983
984	if (err)
985		return 1;
986
987	/* step 2a: make sure trigger sources are unique */
988
989	err |= cfc_check_trigger_is_unique(cmd->start_src);
990	err |= cfc_check_trigger_is_unique(cmd->convert_src);
991	err |= cfc_check_trigger_is_unique(cmd->stop_src);
992
993	/* step 2b: and mutually compatible */
994
995	if (err)
996		return 2;
997
998	/* Step 3: check if arguments are trivially valid */
999
1000	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
1001	err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
1002
1003	if (cmd->convert_src == TRIG_TIMER)
1004		err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
1005						 this_board->ai_ns_min);
1006	else	/* TRIG_FOLLOW */
1007		err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
1008
1009	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
1010
1011	if (cmd->stop_src == TRIG_COUNT)
1012		err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
1013	else	/* TRIG_NONE */
1014		err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
1015
1016	if (err)
1017		return 3;
1018
1019	/* step 4: fix up any arguments */
1020
1021	if (cmd->convert_src == TRIG_TIMER) {
1022		arg = cmd->convert_arg;
1023		i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
1024					  &devpriv->next_divisor1,
1025					  &devpriv->next_divisor2,
1026					  &arg, cmd->flags);
1027		err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
1028	}
1029
1030	if (err)
1031		return 4;
1032
1033	/* Step 5: check channel list */
1034
1035	err |= pci171x_ai_check_chanlist(dev, s, cmd);
1036
1037	if (err)
1038		return 5;
1039
1040	return 0;
1041}
1042
1043/*
1044==============================================================================
1045*/
1046static int pci171x_reset(struct comedi_device *dev)
1047{
1048	const struct boardtype *this_board = dev->board_ptr;
1049	struct pci1710_private *devpriv = dev->private;
1050
1051	outw(0x30, dev->iobase + PCI171x_CNTCTRL);
1052	/* Software trigger, CNT0=external */
1053	devpriv->CntrlReg = Control_SW | Control_CNT0;
1054	/* reset any operations */
1055	outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
1056	outb(0, dev->iobase + PCI171x_CLRFIFO);	/*  clear FIFO */
1057	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear INT request */
1058	pci171x_start_pacer(dev, false);
1059	devpriv->da_ranges = 0;
1060	if (this_board->n_aochan) {
1061		/* set DACs to 0..5V */
1062		outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
1063		outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
1064		devpriv->ao_data[0] = 0x0000;
1065		if (this_board->n_aochan > 1) {
1066			outw(0, dev->iobase + PCI171x_DA2);
1067			devpriv->ao_data[1] = 0x0000;
1068		}
1069	}
1070	outw(0, dev->iobase + PCI171x_DO);	/*  digital outputs to 0 */
1071	outb(0, dev->iobase + PCI171x_CLRFIFO);	/*  clear FIFO */
1072	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear INT request */
1073
1074	return 0;
1075}
1076
1077/*
1078==============================================================================
1079*/
1080static int pci1720_reset(struct comedi_device *dev)
1081{
1082	struct pci1710_private *devpriv = dev->private;
1083	/* set synchronous output mode */
1084	outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
1085	devpriv->da_ranges = 0xAA;
1086	/* set all ranges to +/-5V */
1087	outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
1088	outw(0x0800, dev->iobase + PCI1720_DA0);	/*  set outputs to 0V */
1089	outw(0x0800, dev->iobase + PCI1720_DA1);
1090	outw(0x0800, dev->iobase + PCI1720_DA2);
1091	outw(0x0800, dev->iobase + PCI1720_DA3);
1092	outb(0, dev->iobase + PCI1720_SYNCOUT);	/*  update outputs */
1093	devpriv->ao_data[0] = 0x0800;
1094	devpriv->ao_data[1] = 0x0800;
1095	devpriv->ao_data[2] = 0x0800;
1096	devpriv->ao_data[3] = 0x0800;
1097	return 0;
1098}
1099
1100/*
1101==============================================================================
1102*/
1103static int pci1710_reset(struct comedi_device *dev)
1104{
1105	const struct boardtype *this_board = dev->board_ptr;
1106
1107	switch (this_board->cardtype) {
1108	case TYPE_PCI1720:
1109		return pci1720_reset(dev);
1110	default:
1111		return pci171x_reset(dev);
1112	}
1113}
1114
1115static int pci1710_auto_attach(struct comedi_device *dev,
1116			       unsigned long context)
1117{
1118	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1119	const struct boardtype *this_board = NULL;
1120	struct pci1710_private *devpriv;
1121	struct comedi_subdevice *s;
1122	int ret, subdev, n_subdevices;
1123
1124	if (context < ARRAY_SIZE(boardtypes))
1125		this_board = &boardtypes[context];
1126	if (!this_board)
1127		return -ENODEV;
1128	dev->board_ptr = this_board;
1129	dev->board_name = this_board->name;
1130
1131	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1132	if (!devpriv)
1133		return -ENOMEM;
1134
1135	ret = comedi_pci_enable(dev);
1136	if (ret)
1137		return ret;
1138	dev->iobase = pci_resource_start(pcidev, 2);
1139
1140	n_subdevices = 0;
1141	if (this_board->n_aichan)
1142		n_subdevices++;
1143	if (this_board->n_aochan)
1144		n_subdevices++;
1145	if (this_board->n_dichan)
1146		n_subdevices++;
1147	if (this_board->n_dochan)
1148		n_subdevices++;
1149	if (this_board->n_counter)
1150		n_subdevices++;
1151
1152	ret = comedi_alloc_subdevices(dev, n_subdevices);
1153	if (ret)
1154		return ret;
1155
1156	pci1710_reset(dev);
1157
1158	if (this_board->have_irq && pcidev->irq) {
1159		ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1160				  IRQF_SHARED, dev->board_name, dev);
1161		if (ret == 0)
1162			dev->irq = pcidev->irq;
1163	}
1164
1165	subdev = 0;
1166
1167	if (this_board->n_aichan) {
1168		s = &dev->subdevices[subdev];
1169		s->type = COMEDI_SUBD_AI;
1170		s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1171		if (this_board->n_aichand)
1172			s->subdev_flags |= SDF_DIFF;
1173		s->n_chan = this_board->n_aichan;
1174		s->maxdata = this_board->ai_maxdata;
1175		s->range_table = this_board->rangelist_ai;
1176		s->insn_read = pci171x_insn_read_ai;
1177		if (dev->irq) {
1178			dev->read_subdev = s;
1179			s->subdev_flags |= SDF_CMD_READ;
1180			s->len_chanlist = s->n_chan;
1181			s->do_cmdtest = pci171x_ai_cmdtest;
1182			s->do_cmd = pci171x_ai_cmd;
1183			s->cancel = pci171x_ai_cancel;
1184		}
1185		subdev++;
1186	}
1187
1188	if (this_board->n_aochan) {
1189		s = &dev->subdevices[subdev];
1190		s->type = COMEDI_SUBD_AO;
1191		s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1192		s->n_chan = this_board->n_aochan;
1193		s->maxdata = this_board->ao_maxdata;
1194		s->len_chanlist = this_board->n_aochan;
1195		s->range_table = this_board->rangelist_ao;
1196		switch (this_board->cardtype) {
1197		case TYPE_PCI1720:
1198			s->insn_write = pci1720_insn_write_ao;
1199			break;
1200		default:
1201			s->insn_write = pci171x_insn_write_ao;
1202			break;
1203		}
1204		s->insn_read = pci171x_insn_read_ao;
1205		subdev++;
1206	}
1207
1208	if (this_board->n_dichan) {
1209		s = &dev->subdevices[subdev];
1210		s->type = COMEDI_SUBD_DI;
1211		s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
1212		s->n_chan = this_board->n_dichan;
1213		s->maxdata = 1;
1214		s->len_chanlist = this_board->n_dichan;
1215		s->range_table = &range_digital;
1216		s->insn_bits = pci171x_insn_bits_di;
1217		subdev++;
1218	}
1219
1220	if (this_board->n_dochan) {
1221		s = &dev->subdevices[subdev];
1222		s->type = COMEDI_SUBD_DO;
1223		s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1224		s->n_chan = this_board->n_dochan;
1225		s->maxdata = 1;
1226		s->len_chanlist = this_board->n_dochan;
1227		s->range_table = &range_digital;
1228		s->insn_bits = pci171x_insn_bits_do;
1229		subdev++;
1230	}
1231
1232	if (this_board->n_counter) {
1233		s = &dev->subdevices[subdev];
1234		s->type = COMEDI_SUBD_COUNTER;
1235		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1236		s->n_chan = this_board->n_counter;
1237		s->len_chanlist = this_board->n_counter;
1238		s->maxdata = 0xffff;
1239		s->range_table = &range_unknown;
1240		s->insn_read = pci171x_insn_counter_read;
1241		s->insn_write = pci171x_insn_counter_write;
1242		s->insn_config = pci171x_insn_counter_config;
1243		subdev++;
1244	}
1245
1246	return 0;
1247}
1248
1249static void pci1710_detach(struct comedi_device *dev)
1250{
1251	if (dev->iobase)
1252		pci1710_reset(dev);
1253	comedi_pci_detach(dev);
1254}
1255
1256static struct comedi_driver adv_pci1710_driver = {
1257	.driver_name	= "adv_pci1710",
1258	.module		= THIS_MODULE,
1259	.auto_attach	= pci1710_auto_attach,
1260	.detach		= pci1710_detach,
1261};
1262
1263static int adv_pci1710_pci_probe(struct pci_dev *dev,
1264				 const struct pci_device_id *id)
1265{
1266	return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1267				      id->driver_data);
1268}
1269
1270static const struct pci_device_id adv_pci1710_pci_table[] = {
1271	{
1272		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1273			       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1274		.driver_data = BOARD_PCI1710,
1275	}, {
1276		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1277			       PCI_VENDOR_ID_ADVANTECH, 0x0000),
1278		.driver_data = BOARD_PCI1710,
1279	}, {
1280		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1281			       PCI_VENDOR_ID_ADVANTECH, 0xb100),
1282		.driver_data = BOARD_PCI1710,
1283	}, {
1284		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1285			       PCI_VENDOR_ID_ADVANTECH, 0xb200),
1286		.driver_data = BOARD_PCI1710,
1287	}, {
1288		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1289			       PCI_VENDOR_ID_ADVANTECH, 0xc100),
1290		.driver_data = BOARD_PCI1710,
1291	}, {
1292		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1293			       PCI_VENDOR_ID_ADVANTECH, 0xc200),
1294		.driver_data = BOARD_PCI1710,
1295	}, {
1296		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1297		.driver_data = BOARD_PCI1710,
1298	}, {
1299		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1300			       PCI_VENDOR_ID_ADVANTECH, 0x0002),
1301		.driver_data = BOARD_PCI1710HG,
1302	}, {
1303		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1304			       PCI_VENDOR_ID_ADVANTECH, 0xb102),
1305		.driver_data = BOARD_PCI1710HG,
1306	}, {
1307		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1308			       PCI_VENDOR_ID_ADVANTECH, 0xb202),
1309		.driver_data = BOARD_PCI1710HG,
1310	}, {
1311		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1312			       PCI_VENDOR_ID_ADVANTECH, 0xc102),
1313		.driver_data = BOARD_PCI1710HG,
1314	}, {
1315		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1316			       PCI_VENDOR_ID_ADVANTECH, 0xc202),
1317		.driver_data = BOARD_PCI1710HG,
1318	}, {
1319		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1320		.driver_data = BOARD_PCI1710HG,
1321	},
1322	{ PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1323	{ PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1324	{ PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1325	{ PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1326	{ 0 }
1327};
1328MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1329
1330static struct pci_driver adv_pci1710_pci_driver = {
1331	.name		= "adv_pci1710",
1332	.id_table	= adv_pci1710_pci_table,
1333	.probe		= adv_pci1710_pci_probe,
1334	.remove		= comedi_pci_auto_unconfig,
1335};
1336module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1337
1338MODULE_AUTHOR("Comedi http://www.comedi.org");
1339MODULE_DESCRIPTION("Comedi low-level driver");
1340MODULE_LICENSE("GPL");
1341