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 information.
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 /*
20 Driver: adv_pci1710
21 Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
22 	     Advantech PCI-1720, PCI-1731
23 Author: Michal Dobes <dobes@tesnet.cz>
24 Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
25   PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
26   PCI-1731
27 Status: works
28 
29 This driver supports AI, AO, DI and DO subdevices.
30 AI subdevice supports cmd and insn interface,
31 other subdevices support only insn interface.
32 
33 The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
34 driver cannot distinguish between them, as would be normal for a
35 PCI driver.
36 
37 Configuration 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/interrupt.h>
46 
47 #include "../comedi_pci.h"
48 
49 #include "comedi_8254.h"
50 #include "amcc_s5933.h"
51 
52 #define PCI171x_AD_DATA	 0	/* R:   A/D data */
53 #define PCI171x_SOFTTRG	 0	/* W:   soft trigger for A/D */
54 #define PCI171x_RANGE	 2	/* W:   A/D gain/range register */
55 #define PCI171x_MUX	 4	/* W:   A/D multiplexor control */
56 #define PCI171x_STATUS	 6	/* R:   status register */
57 #define PCI171x_CONTROL	 6	/* W:   control register */
58 #define PCI171x_CLRINT	 8	/* W:   clear interrupts request */
59 #define PCI171x_CLRFIFO	 9	/* W:   clear FIFO */
60 #define PCI171x_DA1	10	/* W:   D/A register */
61 #define PCI171x_DA2	12	/* W:   D/A register */
62 #define PCI171x_DAREF	14	/* W:   D/A reference control */
63 #define PCI171x_DI	16	/* R:   digi inputs */
64 #define PCI171x_DO	16	/* R:   digi inputs */
65 
66 #define PCI171X_TIMER_BASE	0x18
67 
68 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
69  * reg) */
70 #define	Status_FE	0x0100	/* 1=FIFO is empty */
71 #define Status_FH	0x0200	/* 1=FIFO is half full */
72 #define Status_FF	0x0400	/* 1=FIFO is full, fatal error */
73 #define Status_IRQ	0x0800	/* 1=IRQ occurred */
74 /* bits from control register (PCI171x_CONTROL) */
75 #define Control_CNT0	0x0040	/* 1=CNT0 have external source,
76 				 * 0=have internal 100kHz source */
77 #define Control_ONEFH	0x0020	/* 1=IRQ on FIFO is half full, 0=every sample */
78 #define Control_IRQEN	0x0010	/* 1=enable IRQ */
79 #define Control_GATE	0x0008	/* 1=enable external trigger GATE (8254?) */
80 #define Control_EXT	0x0004	/* 1=external trigger source */
81 #define Control_PACER	0x0002	/* 1=enable internal 8254 trigger source */
82 #define Control_SW	0x0001	/* 1=enable software trigger source */
83 
84 #define PCI1720_DA0	 0	/* W:   D/A register 0 */
85 #define PCI1720_DA1	 2	/* W:   D/A register 1 */
86 #define PCI1720_DA2	 4	/* W:   D/A register 2 */
87 #define PCI1720_DA3	 6	/* W:   D/A register 3 */
88 #define PCI1720_RANGE	 8	/* R/W: D/A range register */
89 #define PCI1720_SYNCOUT	 9	/* W:   D/A synchronized output register */
90 #define PCI1720_SYNCONT	15	/* R/W: D/A synchronized control */
91 
92 /* D/A synchronized control (PCI1720_SYNCONT) */
93 #define Syncont_SC0	 1	/* set synchronous output mode */
94 
95 static const struct comedi_lrange range_pci1710_3 = {
96 	9, {
97 		BIP_RANGE(5),
98 		BIP_RANGE(2.5),
99 		BIP_RANGE(1.25),
100 		BIP_RANGE(0.625),
101 		BIP_RANGE(10),
102 		UNI_RANGE(10),
103 		UNI_RANGE(5),
104 		UNI_RANGE(2.5),
105 		UNI_RANGE(1.25)
106 	}
107 };
108 
109 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
110 					      0x10, 0x11, 0x12, 0x13 };
111 
112 static const struct comedi_lrange range_pci1710hg = {
113 	12, {
114 		BIP_RANGE(5),
115 		BIP_RANGE(0.5),
116 		BIP_RANGE(0.05),
117 		BIP_RANGE(0.005),
118 		BIP_RANGE(10),
119 		BIP_RANGE(1),
120 		BIP_RANGE(0.1),
121 		BIP_RANGE(0.01),
122 		UNI_RANGE(10),
123 		UNI_RANGE(1),
124 		UNI_RANGE(0.1),
125 		UNI_RANGE(0.01)
126 	}
127 };
128 
129 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
130 					      0x05, 0x06, 0x07, 0x10, 0x11,
131 					      0x12, 0x13 };
132 
133 static const struct comedi_lrange range_pci17x1 = {
134 	5, {
135 		BIP_RANGE(10),
136 		BIP_RANGE(5),
137 		BIP_RANGE(2.5),
138 		BIP_RANGE(1.25),
139 		BIP_RANGE(0.625)
140 	}
141 };
142 
143 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
144 
145 static const struct comedi_lrange pci1720_ao_range = {
146 	4, {
147 		UNI_RANGE(5),
148 		UNI_RANGE(10),
149 		BIP_RANGE(5),
150 		BIP_RANGE(10)
151 	}
152 };
153 
154 static const struct comedi_lrange pci171x_ao_range = {
155 	2, {
156 		UNI_RANGE(5),
157 		UNI_RANGE(10)
158 	}
159 };
160 
161 enum pci1710_boardid {
162 	BOARD_PCI1710,
163 	BOARD_PCI1710HG,
164 	BOARD_PCI1711,
165 	BOARD_PCI1713,
166 	BOARD_PCI1720,
167 	BOARD_PCI1731,
168 };
169 
170 struct boardtype {
171 	const char *name;	/*  board name */
172 	int n_aichan;		/*  num of A/D chans */
173 	const struct comedi_lrange *rangelist_ai;	/*  rangelist for A/D */
174 	const char *rangecode_ai;	/*  range codes for programming */
175 	unsigned int is_pci1713:1;
176 	unsigned int is_pci1720:1;
177 	unsigned int has_irq:1;
178 	unsigned int has_large_fifo:1;	/* 4K or 1K FIFO */
179 	unsigned int has_diff_ai:1;
180 	unsigned int has_ao:1;
181 	unsigned int has_di_do:1;
182 	unsigned int has_counter:1;
183 };
184 
185 static const struct boardtype boardtypes[] = {
186 	[BOARD_PCI1710] = {
187 		.name		= "pci1710",
188 		.n_aichan	= 16,
189 		.rangelist_ai	= &range_pci1710_3,
190 		.rangecode_ai	= range_codes_pci1710_3,
191 		.has_irq	= 1,
192 		.has_large_fifo	= 1,
193 		.has_diff_ai	= 1,
194 		.has_ao		= 1,
195 		.has_di_do	= 1,
196 		.has_counter	= 1,
197 	},
198 	[BOARD_PCI1710HG] = {
199 		.name		= "pci1710hg",
200 		.n_aichan	= 16,
201 		.rangelist_ai	= &range_pci1710hg,
202 		.rangecode_ai	= range_codes_pci1710hg,
203 		.has_irq	= 1,
204 		.has_large_fifo	= 1,
205 		.has_diff_ai	= 1,
206 		.has_ao		= 1,
207 		.has_di_do	= 1,
208 		.has_counter	= 1,
209 	},
210 	[BOARD_PCI1711] = {
211 		.name		= "pci1711",
212 		.n_aichan	= 16,
213 		.rangelist_ai	= &range_pci17x1,
214 		.rangecode_ai	= range_codes_pci17x1,
215 		.has_irq	= 1,
216 		.has_ao		= 1,
217 		.has_di_do	= 1,
218 		.has_counter	= 1,
219 	},
220 	[BOARD_PCI1713] = {
221 		.name		= "pci1713",
222 		.n_aichan	= 32,
223 		.rangelist_ai	= &range_pci1710_3,
224 		.rangecode_ai	= range_codes_pci1710_3,
225 		.is_pci1713	= 1,
226 		.has_irq	= 1,
227 		.has_large_fifo	= 1,
228 		.has_diff_ai	= 1,
229 	},
230 	[BOARD_PCI1720] = {
231 		.name		= "pci1720",
232 		.is_pci1720	= 1,
233 		.has_ao		= 1,
234 	},
235 	[BOARD_PCI1731] = {
236 		.name		= "pci1731",
237 		.n_aichan	= 16,
238 		.rangelist_ai	= &range_pci17x1,
239 		.rangecode_ai	= range_codes_pci17x1,
240 		.has_irq	= 1,
241 		.has_di_do	= 1,
242 	},
243 };
244 
245 struct pci1710_private {
246 	unsigned int max_samples;
247 	unsigned int CntrlReg;	/*  Control register */
248 	unsigned char ai_et;
249 	unsigned int ai_et_CntrlReg;
250 	unsigned int ai_et_MuxVal;
251 	unsigned int act_chanlist[32];	/*  list of scanned channel */
252 	unsigned char saved_seglen;	/* len of the non-repeating chanlist */
253 	unsigned char da_ranges;	/*  copy of D/A outpit range register */
254 };
255 
pci171x_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)256 static int pci171x_ai_check_chanlist(struct comedi_device *dev,
257 				     struct comedi_subdevice *s,
258 				     struct comedi_cmd *cmd)
259 {
260 	struct pci1710_private *devpriv = dev->private;
261 	unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
262 	unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
263 	unsigned int next_chan = (chan0 + 1) % s->n_chan;
264 	unsigned int chansegment[32];
265 	unsigned int seglen;
266 	int i;
267 
268 	if (cmd->chanlist_len == 1) {
269 		devpriv->saved_seglen = cmd->chanlist_len;
270 		return 0;
271 	}
272 
273 	/* first channel is always ok */
274 	chansegment[0] = cmd->chanlist[0];
275 
276 	for (i = 1; i < cmd->chanlist_len; i++) {
277 		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
278 		unsigned int aref = CR_AREF(cmd->chanlist[i]);
279 
280 		if (cmd->chanlist[0] == cmd->chanlist[i])
281 			break;	/*  we detected a loop, stop */
282 
283 		if (aref == AREF_DIFF && (chan & 1)) {
284 			dev_err(dev->class_dev,
285 				"Odd channel cannot be differential input!\n");
286 			return -EINVAL;
287 		}
288 
289 		if (last_aref == AREF_DIFF)
290 			next_chan = (next_chan + 1) % s->n_chan;
291 		if (chan != next_chan) {
292 			dev_err(dev->class_dev,
293 				"channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
294 				i, chan, next_chan, chan0);
295 			return -EINVAL;
296 		}
297 
298 		/* next correct channel in list */
299 		chansegment[i] = cmd->chanlist[i];
300 		last_aref = aref;
301 	}
302 	seglen = i;
303 
304 	for (i = 0; i < cmd->chanlist_len; i++) {
305 		if (cmd->chanlist[i] != chansegment[i % seglen]) {
306 			dev_err(dev->class_dev,
307 				"bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
308 				i, CR_CHAN(chansegment[i]),
309 				CR_RANGE(chansegment[i]),
310 				CR_AREF(chansegment[i]),
311 				CR_CHAN(cmd->chanlist[i % seglen]),
312 				CR_RANGE(cmd->chanlist[i % seglen]),
313 				CR_AREF(chansegment[i % seglen]));
314 			return -EINVAL;
315 		}
316 	}
317 	devpriv->saved_seglen = seglen;
318 
319 	return 0;
320 }
321 
pci171x_ai_setup_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int * chanlist,unsigned int n_chan,unsigned int seglen)322 static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
323 				      struct comedi_subdevice *s,
324 				      unsigned int *chanlist,
325 				      unsigned int n_chan,
326 				      unsigned int seglen)
327 {
328 	const struct boardtype *board = dev->board_ptr;
329 	struct pci1710_private *devpriv = dev->private;
330 	unsigned int first_chan = CR_CHAN(chanlist[0]);
331 	unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
332 	unsigned int i;
333 
334 	for (i = 0; i < seglen; i++) {	/*  store range list to card */
335 		unsigned int chan = CR_CHAN(chanlist[i]);
336 		unsigned int range = CR_RANGE(chanlist[i]);
337 		unsigned int aref = CR_AREF(chanlist[i]);
338 		unsigned int rangeval;
339 
340 		rangeval = board->rangecode_ai[range];
341 		if (aref == AREF_DIFF)
342 			rangeval |= 0x0020;
343 
344 		/* select channel and set range */
345 		outw(chan | (chan << 8), dev->iobase + PCI171x_MUX);
346 		outw(rangeval, dev->iobase + PCI171x_RANGE);
347 
348 		devpriv->act_chanlist[i] = chan;
349 	}
350 	for ( ; i < n_chan; i++)	/* store remainder of channel list */
351 		devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
352 
353 	/* select channel interval to scan */
354 	devpriv->ai_et_MuxVal = first_chan | (last_chan << 8);
355 	outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
356 }
357 
pci171x_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)358 static int pci171x_ai_eoc(struct comedi_device *dev,
359 			  struct comedi_subdevice *s,
360 			  struct comedi_insn *insn,
361 			  unsigned long context)
362 {
363 	unsigned int status;
364 
365 	status = inw(dev->iobase + PCI171x_STATUS);
366 	if ((status & Status_FE) == 0)
367 		return 0;
368 	return -EBUSY;
369 }
370 
pci171x_ai_read_sample(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int cur_chan,unsigned int * val)371 static int pci171x_ai_read_sample(struct comedi_device *dev,
372 				  struct comedi_subdevice *s,
373 				  unsigned int cur_chan,
374 				  unsigned int *val)
375 {
376 	const struct boardtype *board = dev->board_ptr;
377 	struct pci1710_private *devpriv = dev->private;
378 	unsigned int sample;
379 	unsigned int chan;
380 
381 	sample = inw(dev->iobase + PCI171x_AD_DATA);
382 	if (!board->is_pci1713) {
383 		/*
384 		 * The upper 4 bits of the 16-bit sample are the channel number
385 		 * that the sample was acquired from. Verify that this channel
386 		 * number matches the expected channel number.
387 		 */
388 		chan = sample >> 12;
389 		if (chan != devpriv->act_chanlist[cur_chan]) {
390 			dev_err(dev->class_dev,
391 				"A/D data droput: received from channel %d, expected %d\n",
392 				chan, devpriv->act_chanlist[cur_chan]);
393 			return -ENODATA;
394 		}
395 	}
396 	*val = sample & s->maxdata;
397 	return 0;
398 }
399 
pci171x_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)400 static int pci171x_ai_insn_read(struct comedi_device *dev,
401 				struct comedi_subdevice *s,
402 				struct comedi_insn *insn,
403 				unsigned int *data)
404 {
405 	struct pci1710_private *devpriv = dev->private;
406 	int ret = 0;
407 	int i;
408 
409 	devpriv->CntrlReg &= Control_CNT0;
410 	devpriv->CntrlReg |= Control_SW;	/*  set software trigger */
411 	outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
412 	outb(0, dev->iobase + PCI171x_CLRFIFO);
413 	outb(0, dev->iobase + PCI171x_CLRINT);
414 
415 	pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
416 
417 	for (i = 0; i < insn->n; i++) {
418 		unsigned int val;
419 
420 		outw(0, dev->iobase + PCI171x_SOFTTRG);	/* start conversion */
421 
422 		ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
423 		if (ret)
424 			break;
425 
426 		ret = pci171x_ai_read_sample(dev, s, 0, &val);
427 		if (ret)
428 			break;
429 
430 		data[i] = val;
431 	}
432 
433 	outb(0, dev->iobase + PCI171x_CLRFIFO);
434 	outb(0, dev->iobase + PCI171x_CLRINT);
435 
436 	return ret ? ret : insn->n;
437 }
438 
pci171x_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)439 static int pci171x_ao_insn_write(struct comedi_device *dev,
440 				 struct comedi_subdevice *s,
441 				 struct comedi_insn *insn,
442 				 unsigned int *data)
443 {
444 	struct pci1710_private *devpriv = dev->private;
445 	unsigned int chan = CR_CHAN(insn->chanspec);
446 	unsigned int range = CR_RANGE(insn->chanspec);
447 	unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
448 	unsigned int val = s->readback[chan];
449 	int i;
450 
451 	devpriv->da_ranges &= ~(1 << (chan << 1));
452 	devpriv->da_ranges |= (range << (chan << 1));
453 	outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
454 
455 	for (i = 0; i < insn->n; i++) {
456 		val = data[i];
457 		outw(val, dev->iobase + reg);
458 	}
459 
460 	s->readback[chan] = val;
461 
462 	return insn->n;
463 }
464 
pci171x_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)465 static int pci171x_di_insn_bits(struct comedi_device *dev,
466 				struct comedi_subdevice *s,
467 				struct comedi_insn *insn,
468 				unsigned int *data)
469 {
470 	data[1] = inw(dev->iobase + PCI171x_DI);
471 
472 	return insn->n;
473 }
474 
pci171x_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)475 static int pci171x_do_insn_bits(struct comedi_device *dev,
476 				struct comedi_subdevice *s,
477 				struct comedi_insn *insn,
478 				unsigned int *data)
479 {
480 	if (comedi_dio_update_state(s, data))
481 		outw(s->state, dev->iobase + PCI171x_DO);
482 
483 	data[1] = s->state;
484 
485 	return insn->n;
486 }
487 
pci1720_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)488 static int pci1720_ao_insn_write(struct comedi_device *dev,
489 				 struct comedi_subdevice *s,
490 				 struct comedi_insn *insn,
491 				 unsigned int *data)
492 {
493 	struct pci1710_private *devpriv = dev->private;
494 	unsigned int chan = CR_CHAN(insn->chanspec);
495 	unsigned int range = CR_RANGE(insn->chanspec);
496 	unsigned int val;
497 	int i;
498 
499 	val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
500 	val |= (range << (chan << 1));
501 	if (val != devpriv->da_ranges) {
502 		outb(val, dev->iobase + PCI1720_RANGE);
503 		devpriv->da_ranges = val;
504 	}
505 
506 	val = s->readback[chan];
507 	for (i = 0; i < insn->n; i++) {
508 		val = data[i];
509 		outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
510 		outb(0, dev->iobase + PCI1720_SYNCOUT);	/* update outputs */
511 	}
512 
513 	s->readback[chan] = val;
514 
515 	return insn->n;
516 }
517 
pci171x_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)518 static int pci171x_ai_cancel(struct comedi_device *dev,
519 			     struct comedi_subdevice *s)
520 {
521 	struct pci1710_private *devpriv = dev->private;
522 
523 	devpriv->CntrlReg &= Control_CNT0;
524 	devpriv->CntrlReg |= Control_SW;
525 	/* reset any operations */
526 	outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
527 	comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
528 	outb(0, dev->iobase + PCI171x_CLRFIFO);
529 	outb(0, dev->iobase + PCI171x_CLRINT);
530 
531 	return 0;
532 }
533 
pci1710_handle_every_sample(struct comedi_device * dev,struct comedi_subdevice * s)534 static void pci1710_handle_every_sample(struct comedi_device *dev,
535 					struct comedi_subdevice *s)
536 {
537 	struct comedi_cmd *cmd = &s->async->cmd;
538 	unsigned int status;
539 	unsigned int val;
540 	int ret;
541 
542 	status = inw(dev->iobase + PCI171x_STATUS);
543 	if (status & Status_FE) {
544 		dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
545 		s->async->events |= COMEDI_CB_ERROR;
546 		return;
547 	}
548 	if (status & Status_FF) {
549 		dev_dbg(dev->class_dev,
550 			"A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
551 		s->async->events |= COMEDI_CB_ERROR;
552 		return;
553 	}
554 
555 	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear our INT request */
556 
557 	for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
558 		ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
559 		if (ret) {
560 			s->async->events |= COMEDI_CB_ERROR;
561 			break;
562 		}
563 
564 		comedi_buf_write_samples(s, &val, 1);
565 
566 		if (cmd->stop_src == TRIG_COUNT &&
567 		    s->async->scans_done >= cmd->stop_arg) {
568 			s->async->events |= COMEDI_CB_EOA;
569 			break;
570 		}
571 	}
572 
573 	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear our INT request */
574 }
575 
pci1710_handle_fifo(struct comedi_device * dev,struct comedi_subdevice * s)576 static void pci1710_handle_fifo(struct comedi_device *dev,
577 				struct comedi_subdevice *s)
578 {
579 	struct pci1710_private *devpriv = dev->private;
580 	struct comedi_async *async = s->async;
581 	struct comedi_cmd *cmd = &async->cmd;
582 	unsigned int status;
583 	int i;
584 
585 	status = inw(dev->iobase + PCI171x_STATUS);
586 	if (!(status & Status_FH)) {
587 		dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
588 		async->events |= COMEDI_CB_ERROR;
589 		return;
590 	}
591 	if (status & Status_FF) {
592 		dev_dbg(dev->class_dev,
593 			"A/D FIFO Full status (Fatal Error!)\n");
594 		async->events |= COMEDI_CB_ERROR;
595 		return;
596 	}
597 
598 	for (i = 0; i < devpriv->max_samples; i++) {
599 		unsigned int val;
600 		int ret;
601 
602 		ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
603 		if (ret) {
604 			s->async->events |= COMEDI_CB_ERROR;
605 			break;
606 		}
607 
608 		if (!comedi_buf_write_samples(s, &val, 1))
609 			break;
610 
611 		if (cmd->stop_src == TRIG_COUNT &&
612 		    async->scans_done >= cmd->stop_arg) {
613 			async->events |= COMEDI_CB_EOA;
614 			break;
615 		}
616 	}
617 
618 	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear our INT request */
619 }
620 
interrupt_service_pci1710(int irq,void * d)621 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
622 {
623 	struct comedi_device *dev = d;
624 	struct pci1710_private *devpriv = dev->private;
625 	struct comedi_subdevice *s;
626 	struct comedi_cmd *cmd;
627 
628 	if (!dev->attached)	/*  is device attached? */
629 		return IRQ_NONE;	/*  no, exit */
630 
631 	s = dev->read_subdev;
632 	cmd = &s->async->cmd;
633 
634 	/*  is this interrupt from our board? */
635 	if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
636 		return IRQ_NONE;	/*  no, exit */
637 
638 	if (devpriv->ai_et) {	/*  Switch from initial TRIG_EXT to TRIG_xxx. */
639 		devpriv->ai_et = 0;
640 		devpriv->CntrlReg &= Control_CNT0;
641 		devpriv->CntrlReg |= Control_SW; /* set software trigger */
642 		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
643 		devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
644 		outb(0, dev->iobase + PCI171x_CLRFIFO);
645 		outb(0, dev->iobase + PCI171x_CLRINT);
646 		outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
647 		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
648 		comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
649 		return IRQ_HANDLED;
650 	}
651 
652 	if (cmd->flags & CMDF_WAKE_EOS)
653 		pci1710_handle_every_sample(dev, s);
654 	else
655 		pci1710_handle_fifo(dev, s);
656 
657 	comedi_handle_events(dev, s);
658 
659 	return IRQ_HANDLED;
660 }
661 
pci171x_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)662 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
663 {
664 	struct pci1710_private *devpriv = dev->private;
665 	struct comedi_cmd *cmd = &s->async->cmd;
666 
667 	pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
668 				  devpriv->saved_seglen);
669 
670 	outb(0, dev->iobase + PCI171x_CLRFIFO);
671 	outb(0, dev->iobase + PCI171x_CLRINT);
672 
673 	devpriv->CntrlReg &= Control_CNT0;
674 	if ((cmd->flags & CMDF_WAKE_EOS) == 0)
675 		devpriv->CntrlReg |= Control_ONEFH;
676 
677 	if (cmd->convert_src == TRIG_TIMER) {
678 		comedi_8254_update_divisors(dev->pacer);
679 
680 		devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
681 		if (cmd->start_src == TRIG_EXT) {
682 			devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
683 			devpriv->CntrlReg &=
684 			    ~(Control_PACER | Control_ONEFH | Control_GATE);
685 			devpriv->CntrlReg |= Control_EXT;
686 			devpriv->ai_et = 1;
687 		} else {	/* TRIG_NOW */
688 			devpriv->ai_et = 0;
689 		}
690 		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
691 
692 		if (cmd->start_src == TRIG_NOW)
693 			comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
694 	} else {	/* TRIG_EXT */
695 		devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
696 		outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
697 	}
698 
699 	return 0;
700 }
701 
pci171x_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)702 static int pci171x_ai_cmdtest(struct comedi_device *dev,
703 			      struct comedi_subdevice *s,
704 			      struct comedi_cmd *cmd)
705 {
706 	int err = 0;
707 
708 	/* Step 1 : check if triggers are trivially valid */
709 
710 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
711 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
712 	err |= comedi_check_trigger_src(&cmd->convert_src,
713 					TRIG_TIMER | TRIG_EXT);
714 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
715 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
716 
717 	if (err)
718 		return 1;
719 
720 	/* step 2a: make sure trigger sources are unique */
721 
722 	err |= comedi_check_trigger_is_unique(cmd->start_src);
723 	err |= comedi_check_trigger_is_unique(cmd->convert_src);
724 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
725 
726 	/* step 2b: and mutually compatible */
727 
728 	if (err)
729 		return 2;
730 
731 	/* Step 3: check if arguments are trivially valid */
732 
733 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
734 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
735 
736 	if (cmd->convert_src == TRIG_TIMER)
737 		err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
738 	else	/* TRIG_FOLLOW */
739 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
740 
741 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
742 					   cmd->chanlist_len);
743 
744 	if (cmd->stop_src == TRIG_COUNT)
745 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
746 	else	/* TRIG_NONE */
747 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
748 
749 	if (err)
750 		return 3;
751 
752 	/* step 4: fix up any arguments */
753 
754 	if (cmd->convert_src == TRIG_TIMER) {
755 		unsigned int arg = cmd->convert_arg;
756 
757 		comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
758 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
759 	}
760 
761 	if (err)
762 		return 4;
763 
764 	/* Step 5: check channel list */
765 
766 	err |= pci171x_ai_check_chanlist(dev, s, cmd);
767 
768 	if (err)
769 		return 5;
770 
771 	return 0;
772 }
773 
pci171x_insn_counter_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)774 static int pci171x_insn_counter_config(struct comedi_device *dev,
775 				       struct comedi_subdevice *s,
776 				       struct comedi_insn *insn,
777 				       unsigned int *data)
778 {
779 	struct pci1710_private *devpriv = dev->private;
780 
781 	switch (data[0]) {
782 	case INSN_CONFIG_SET_CLOCK_SRC:
783 		switch (data[1]) {
784 		case 0:	/* internal */
785 			devpriv->ai_et_CntrlReg &= ~Control_CNT0;
786 			break;
787 		case 1:	/* external */
788 			devpriv->ai_et_CntrlReg |= Control_CNT0;
789 			break;
790 		default:
791 			return -EINVAL;
792 		}
793 		outw(devpriv->ai_et_CntrlReg, dev->iobase + PCI171x_CONTROL);
794 		break;
795 	case INSN_CONFIG_GET_CLOCK_SRC:
796 		if (devpriv->ai_et_CntrlReg & Control_CNT0) {
797 			data[1] = 1;
798 			data[2] = 0;
799 		} else {
800 			data[1] = 0;
801 			data[2] = I8254_OSC_BASE_10MHZ;
802 		}
803 		break;
804 	default:
805 		return -EINVAL;
806 	}
807 
808 	return insn->n;
809 }
810 
pci171x_reset(struct comedi_device * dev)811 static int pci171x_reset(struct comedi_device *dev)
812 {
813 	const struct boardtype *board = dev->board_ptr;
814 	struct pci1710_private *devpriv = dev->private;
815 
816 	/* Software trigger, CNT0=external */
817 	devpriv->CntrlReg = Control_SW | Control_CNT0;
818 	/* reset any operations */
819 	outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
820 	outb(0, dev->iobase + PCI171x_CLRFIFO);	/*  clear FIFO */
821 	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear INT request */
822 	devpriv->da_ranges = 0;
823 	if (board->has_ao) {
824 		/* set DACs to 0..5V */
825 		outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
826 		outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
827 		outw(0, dev->iobase + PCI171x_DA2);
828 	}
829 	outw(0, dev->iobase + PCI171x_DO);	/*  digital outputs to 0 */
830 	outb(0, dev->iobase + PCI171x_CLRFIFO);	/*  clear FIFO */
831 	outb(0, dev->iobase + PCI171x_CLRINT);	/*  clear INT request */
832 
833 	return 0;
834 }
835 
pci1720_reset(struct comedi_device * dev)836 static int pci1720_reset(struct comedi_device *dev)
837 {
838 	struct pci1710_private *devpriv = dev->private;
839 	/* set synchronous output mode */
840 	outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
841 	devpriv->da_ranges = 0xAA;
842 	/* set all ranges to +/-5V */
843 	outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
844 	outw(0x0800, dev->iobase + PCI1720_DA0);	/*  set outputs to 0V */
845 	outw(0x0800, dev->iobase + PCI1720_DA1);
846 	outw(0x0800, dev->iobase + PCI1720_DA2);
847 	outw(0x0800, dev->iobase + PCI1720_DA3);
848 	outb(0, dev->iobase + PCI1720_SYNCOUT);	/*  update outputs */
849 
850 	return 0;
851 }
852 
pci1710_reset(struct comedi_device * dev)853 static int pci1710_reset(struct comedi_device *dev)
854 {
855 	const struct boardtype *board = dev->board_ptr;
856 
857 	if (board->is_pci1720)
858 		return pci1720_reset(dev);
859 
860 	return pci171x_reset(dev);
861 }
862 
pci1710_auto_attach(struct comedi_device * dev,unsigned long context)863 static int pci1710_auto_attach(struct comedi_device *dev,
864 			       unsigned long context)
865 {
866 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
867 	const struct boardtype *board = NULL;
868 	struct pci1710_private *devpriv;
869 	struct comedi_subdevice *s;
870 	int ret, subdev, n_subdevices;
871 
872 	if (context < ARRAY_SIZE(boardtypes))
873 		board = &boardtypes[context];
874 	if (!board)
875 		return -ENODEV;
876 	dev->board_ptr = board;
877 	dev->board_name = board->name;
878 
879 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
880 	if (!devpriv)
881 		return -ENOMEM;
882 
883 	ret = comedi_pci_enable(dev);
884 	if (ret)
885 		return ret;
886 	dev->iobase = pci_resource_start(pcidev, 2);
887 
888 	dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE,
889 				      I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
890 	if (!dev->pacer)
891 		return -ENOMEM;
892 
893 	n_subdevices = 0;
894 	if (board->n_aichan)
895 		n_subdevices++;
896 	if (board->has_ao)
897 		n_subdevices++;
898 	if (board->has_di_do)
899 		n_subdevices += 2;
900 	if (board->has_counter)
901 		n_subdevices++;
902 
903 	ret = comedi_alloc_subdevices(dev, n_subdevices);
904 	if (ret)
905 		return ret;
906 
907 	pci1710_reset(dev);
908 
909 	if (board->has_irq && pcidev->irq) {
910 		ret = request_irq(pcidev->irq, interrupt_service_pci1710,
911 				  IRQF_SHARED, dev->board_name, dev);
912 		if (ret == 0)
913 			dev->irq = pcidev->irq;
914 	}
915 
916 	subdev = 0;
917 
918 	if (board->n_aichan) {
919 		s = &dev->subdevices[subdev];
920 		s->type		= COMEDI_SUBD_AI;
921 		s->subdev_flags	= SDF_READABLE | SDF_COMMON | SDF_GROUND;
922 		if (board->has_diff_ai)
923 			s->subdev_flags	|= SDF_DIFF;
924 		s->n_chan	= board->n_aichan;
925 		s->maxdata	= 0x0fff;
926 		s->range_table	= board->rangelist_ai;
927 		s->insn_read	= pci171x_ai_insn_read;
928 		if (dev->irq) {
929 			dev->read_subdev = s;
930 			s->subdev_flags	|= SDF_CMD_READ;
931 			s->len_chanlist	= s->n_chan;
932 			s->do_cmdtest	= pci171x_ai_cmdtest;
933 			s->do_cmd	= pci171x_ai_cmd;
934 			s->cancel	= pci171x_ai_cancel;
935 		}
936 		subdev++;
937 	}
938 
939 	if (board->has_ao) {
940 		s = &dev->subdevices[subdev];
941 		s->type		= COMEDI_SUBD_AO;
942 		s->subdev_flags	= SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
943 		s->maxdata	= 0x0fff;
944 		if (board->is_pci1720) {
945 			s->n_chan	= 4;
946 			s->range_table	= &pci1720_ao_range;
947 			s->insn_write	= pci1720_ao_insn_write;
948 		} else {
949 			s->n_chan	= 2;
950 			s->range_table	= &pci171x_ao_range;
951 			s->insn_write	= pci171x_ao_insn_write;
952 		}
953 
954 		ret = comedi_alloc_subdev_readback(s);
955 		if (ret)
956 			return ret;
957 
958 		/* initialize the readback values to match the board reset */
959 		if (board->is_pci1720) {
960 			int i;
961 
962 			for (i = 0; i < s->n_chan; i++)
963 				s->readback[i] = 0x0800;
964 		}
965 
966 		subdev++;
967 	}
968 
969 	if (board->has_di_do) {
970 		s = &dev->subdevices[subdev];
971 		s->type		= COMEDI_SUBD_DI;
972 		s->subdev_flags	= SDF_READABLE;
973 		s->n_chan	= 16;
974 		s->maxdata	= 1;
975 		s->range_table	= &range_digital;
976 		s->insn_bits	= pci171x_di_insn_bits;
977 		subdev++;
978 
979 		s = &dev->subdevices[subdev];
980 		s->type		= COMEDI_SUBD_DO;
981 		s->subdev_flags	= SDF_WRITABLE;
982 		s->n_chan	= 16;
983 		s->maxdata	= 1;
984 		s->range_table	= &range_digital;
985 		s->insn_bits	= pci171x_do_insn_bits;
986 		subdev++;
987 	}
988 
989 	/* Counter subdevice (8254) */
990 	if (board->has_counter) {
991 		s = &dev->subdevices[subdev];
992 		comedi_8254_subdevice_init(s, dev->pacer);
993 
994 		dev->pacer->insn_config = pci171x_insn_counter_config;
995 
996 		/* counters 1 and 2 are used internally for the pacer */
997 		comedi_8254_set_busy(dev->pacer, 1, true);
998 		comedi_8254_set_busy(dev->pacer, 2, true);
999 
1000 		subdev++;
1001 	}
1002 
1003 	/* max_samples is half the FIFO size (2 bytes/sample) */
1004 	devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
1005 
1006 	return 0;
1007 }
1008 
pci1710_detach(struct comedi_device * dev)1009 static void pci1710_detach(struct comedi_device *dev)
1010 {
1011 	if (dev->iobase)
1012 		pci1710_reset(dev);
1013 	comedi_pci_detach(dev);
1014 }
1015 
1016 static struct comedi_driver adv_pci1710_driver = {
1017 	.driver_name	= "adv_pci1710",
1018 	.module		= THIS_MODULE,
1019 	.auto_attach	= pci1710_auto_attach,
1020 	.detach		= pci1710_detach,
1021 };
1022 
adv_pci1710_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)1023 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1024 				 const struct pci_device_id *id)
1025 {
1026 	return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1027 				      id->driver_data);
1028 }
1029 
1030 static const struct pci_device_id adv_pci1710_pci_table[] = {
1031 	{
1032 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1033 			       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1034 		.driver_data = BOARD_PCI1710,
1035 	}, {
1036 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1037 			       PCI_VENDOR_ID_ADVANTECH, 0x0000),
1038 		.driver_data = BOARD_PCI1710,
1039 	}, {
1040 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1041 			       PCI_VENDOR_ID_ADVANTECH, 0xb100),
1042 		.driver_data = BOARD_PCI1710,
1043 	}, {
1044 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1045 			       PCI_VENDOR_ID_ADVANTECH, 0xb200),
1046 		.driver_data = BOARD_PCI1710,
1047 	}, {
1048 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1049 			       PCI_VENDOR_ID_ADVANTECH, 0xc100),
1050 		.driver_data = BOARD_PCI1710,
1051 	}, {
1052 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1053 			       PCI_VENDOR_ID_ADVANTECH, 0xc200),
1054 		.driver_data = BOARD_PCI1710,
1055 	}, {
1056 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1057 		.driver_data = BOARD_PCI1710,
1058 	}, {
1059 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1060 			       PCI_VENDOR_ID_ADVANTECH, 0x0002),
1061 		.driver_data = BOARD_PCI1710HG,
1062 	}, {
1063 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1064 			       PCI_VENDOR_ID_ADVANTECH, 0xb102),
1065 		.driver_data = BOARD_PCI1710HG,
1066 	}, {
1067 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1068 			       PCI_VENDOR_ID_ADVANTECH, 0xb202),
1069 		.driver_data = BOARD_PCI1710HG,
1070 	}, {
1071 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1072 			       PCI_VENDOR_ID_ADVANTECH, 0xc102),
1073 		.driver_data = BOARD_PCI1710HG,
1074 	}, {
1075 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1076 			       PCI_VENDOR_ID_ADVANTECH, 0xc202),
1077 		.driver_data = BOARD_PCI1710HG,
1078 	}, {
1079 		PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1080 		.driver_data = BOARD_PCI1710HG,
1081 	},
1082 	{ PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1083 	{ PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1084 	{ PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1085 	{ PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1086 	{ 0 }
1087 };
1088 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1089 
1090 static struct pci_driver adv_pci1710_pci_driver = {
1091 	.name		= "adv_pci1710",
1092 	.id_table	= adv_pci1710_pci_table,
1093 	.probe		= adv_pci1710_pci_probe,
1094 	.remove		= comedi_pci_auto_unconfig,
1095 };
1096 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1097 
1098 MODULE_AUTHOR("Comedi http://www.comedi.org");
1099 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
1100 MODULE_LICENSE("GPL");
1101