1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * adl_pci9111.c
4  * Hardware driver for PCI9111 ADLink cards: PCI-9111HR
5  * Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
6  */
7 
8 /*
9  * Driver: adl_pci9111
10  * Description: Adlink PCI-9111HR
11  * Devices: [ADLink] PCI-9111HR (adl_pci9111)
12  * Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
13  * Status: experimental
14  *
15  * Configuration options: not applicable, uses PCI auto config
16  *
17  * Supports:
18  * - ai_insn read
19  * - ao_insn read/write
20  * - di_insn read
21  * - do_insn read/write
22  * - ai_do_cmd mode with the following sources:
23  *	- start_src		TRIG_NOW
24  *	- scan_begin_src	TRIG_FOLLOW	TRIG_TIMER	TRIG_EXT
25  *	- convert_src				TRIG_TIMER	TRIG_EXT
26  *	- scan_end_src		TRIG_COUNT
27  *	- stop_src		TRIG_COUNT	TRIG_NONE
28  *
29  * The scanned channels must be consecutive and start from 0. They must
30  * all have the same range and aref.
31  */
32 
33 /*
34  * TODO:
35  * - Really test implemented functionality.
36  * - Add support for the PCI-9111DG with a probe routine to identify
37  *   the card type (perhaps with the help of the channel number readback
38  *   of the A/D Data register).
39  * - Add external multiplexer support.
40  */
41 
42 #include <linux/module.h>
43 #include <linux/delay.h>
44 #include <linux/interrupt.h>
45 
46 #include "../comedi_pci.h"
47 
48 #include "plx9052.h"
49 #include "comedi_8254.h"
50 
51 #define PCI9111_FIFO_HALF_SIZE	512
52 
53 #define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS	10000
54 
55 #define PCI9111_RANGE_SETTING_DELAY		10
56 #define PCI9111_AI_INSTANT_READ_UDELAY_US	2
57 
58 /*
59  * IO address map and bit defines
60  */
61 #define PCI9111_AI_FIFO_REG		0x00
62 #define PCI9111_AO_REG			0x00
63 #define PCI9111_DIO_REG			0x02
64 #define PCI9111_EDIO_REG		0x04
65 #define PCI9111_AI_CHANNEL_REG		0x06
66 #define PCI9111_AI_RANGE_STAT_REG	0x08
67 #define PCI9111_AI_STAT_AD_BUSY		BIT(7)
68 #define PCI9111_AI_STAT_FF_FF		BIT(6)
69 #define PCI9111_AI_STAT_FF_HF		BIT(5)
70 #define PCI9111_AI_STAT_FF_EF		BIT(4)
71 #define PCI9111_AI_RANGE(x)		(((x) & 0x7) << 0)
72 #define PCI9111_AI_RANGE_MASK		PCI9111_AI_RANGE(7)
73 #define PCI9111_AI_TRIG_CTRL_REG	0x0a
74 #define PCI9111_AI_TRIG_CTRL_TRGEVENT	BIT(5)
75 #define PCI9111_AI_TRIG_CTRL_POTRG	BIT(4)
76 #define PCI9111_AI_TRIG_CTRL_PTRG	BIT(3)
77 #define PCI9111_AI_TRIG_CTRL_ETIS	BIT(2)
78 #define PCI9111_AI_TRIG_CTRL_TPST	BIT(1)
79 #define PCI9111_AI_TRIG_CTRL_ASCAN	BIT(0)
80 #define PCI9111_INT_CTRL_REG		0x0c
81 #define PCI9111_INT_CTRL_ISC2		BIT(3)
82 #define PCI9111_INT_CTRL_FFEN		BIT(2)
83 #define PCI9111_INT_CTRL_ISC1		BIT(1)
84 #define PCI9111_INT_CTRL_ISC0		BIT(0)
85 #define PCI9111_SOFT_TRIG_REG		0x0e
86 #define PCI9111_8254_BASE_REG		0x40
87 #define PCI9111_INT_CLR_REG		0x48
88 
89 /* PLX 9052 Local Interrupt 1 enabled and active */
90 #define PCI9111_LI1_ACTIVE	(PLX9052_INTCSR_LI1ENAB |	\
91 				 PLX9052_INTCSR_LI1STAT)
92 
93 /* PLX 9052 Local Interrupt 2 enabled and active */
94 #define PCI9111_LI2_ACTIVE	(PLX9052_INTCSR_LI2ENAB |	\
95 				 PLX9052_INTCSR_LI2STAT)
96 
97 static const struct comedi_lrange pci9111_ai_range = {
98 	5, {
99 		BIP_RANGE(10),
100 		BIP_RANGE(5),
101 		BIP_RANGE(2.5),
102 		BIP_RANGE(1.25),
103 		BIP_RANGE(0.625)
104 	}
105 };
106 
107 struct pci9111_private_data {
108 	unsigned long lcr_io_base;
109 
110 	unsigned int scan_delay;
111 	unsigned int chunk_counter;
112 	unsigned int chunk_num_samples;
113 
114 	unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
115 };
116 
plx9050_interrupt_control(unsigned long io_base,bool int1_enable,bool int1_active_high,bool int2_enable,bool int2_active_high,bool interrupt_enable)117 static void plx9050_interrupt_control(unsigned long io_base,
118 				      bool int1_enable,
119 				      bool int1_active_high,
120 				      bool int2_enable,
121 				      bool int2_active_high,
122 				      bool interrupt_enable)
123 {
124 	int flags = 0;
125 
126 	if (int1_enable)
127 		flags |= PLX9052_INTCSR_LI1ENAB;
128 	if (int1_active_high)
129 		flags |= PLX9052_INTCSR_LI1POL;
130 	if (int2_enable)
131 		flags |= PLX9052_INTCSR_LI2ENAB;
132 	if (int2_active_high)
133 		flags |= PLX9052_INTCSR_LI2POL;
134 
135 	if (interrupt_enable)
136 		flags |= PLX9052_INTCSR_PCIENAB;
137 
138 	outb(flags, io_base + PLX9052_INTCSR);
139 }
140 
141 enum pci9111_ISC0_sources {
142 	irq_on_eoc,
143 	irq_on_fifo_half_full
144 };
145 
146 enum pci9111_ISC1_sources {
147 	irq_on_timer_tick,
148 	irq_on_external_trigger
149 };
150 
pci9111_interrupt_source_set(struct comedi_device * dev,enum pci9111_ISC0_sources irq_0_source,enum pci9111_ISC1_sources irq_1_source)151 static void pci9111_interrupt_source_set(struct comedi_device *dev,
152 					 enum pci9111_ISC0_sources irq_0_source,
153 					 enum pci9111_ISC1_sources irq_1_source)
154 {
155 	int flags;
156 
157 	/* Read the current interrupt control bits */
158 	flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
159 	/* Shift the bits so they are compatible with the write register */
160 	flags >>= 4;
161 	/* Mask off the ISCx bits */
162 	flags &= 0xc0;
163 
164 	/* Now set the new ISCx bits */
165 	if (irq_0_source == irq_on_fifo_half_full)
166 		flags |= PCI9111_INT_CTRL_ISC0;
167 
168 	if (irq_1_source == irq_on_external_trigger)
169 		flags |= PCI9111_INT_CTRL_ISC1;
170 
171 	outb(flags, dev->iobase + PCI9111_INT_CTRL_REG);
172 }
173 
pci9111_fifo_reset(struct comedi_device * dev)174 static void pci9111_fifo_reset(struct comedi_device *dev)
175 {
176 	unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG;
177 
178 	/* To reset the FIFO, set FFEN sequence as 0 -> 1 -> 0 */
179 	outb(0, int_ctrl_reg);
180 	outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg);
181 	outb(0, int_ctrl_reg);
182 }
183 
pci9111_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)184 static int pci9111_ai_cancel(struct comedi_device *dev,
185 			     struct comedi_subdevice *s)
186 {
187 	struct pci9111_private_data *dev_private = dev->private;
188 
189 	/*  Disable interrupts */
190 	plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
191 				  true, false);
192 
193 	/* disable A/D triggers (software trigger mode) and auto scan off */
194 	outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
195 
196 	pci9111_fifo_reset(dev);
197 
198 	return 0;
199 }
200 
pci9111_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)201 static int pci9111_ai_check_chanlist(struct comedi_device *dev,
202 				     struct comedi_subdevice *s,
203 				     struct comedi_cmd *cmd)
204 {
205 	unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
206 	unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
207 	int i;
208 
209 	for (i = 1; i < cmd->chanlist_len; i++) {
210 		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
211 		unsigned int range = CR_RANGE(cmd->chanlist[i]);
212 		unsigned int aref = CR_AREF(cmd->chanlist[i]);
213 
214 		if (chan != i) {
215 			dev_dbg(dev->class_dev,
216 				"entries in chanlist must be consecutive channels,counting upwards from 0\n");
217 			return -EINVAL;
218 		}
219 
220 		if (range != range0) {
221 			dev_dbg(dev->class_dev,
222 				"entries in chanlist must all have the same gain\n");
223 			return -EINVAL;
224 		}
225 
226 		if (aref != aref0) {
227 			dev_dbg(dev->class_dev,
228 				"entries in chanlist must all have the same reference\n");
229 			return -EINVAL;
230 		}
231 	}
232 
233 	return 0;
234 }
235 
pci9111_ai_do_cmd_test(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)236 static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
237 				  struct comedi_subdevice *s,
238 				  struct comedi_cmd *cmd)
239 {
240 	int err = 0;
241 	unsigned int arg;
242 
243 	/* Step 1 : check if triggers are trivially valid */
244 
245 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
246 	err |= comedi_check_trigger_src(&cmd->scan_begin_src,
247 					TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
248 	err |= comedi_check_trigger_src(&cmd->convert_src,
249 					TRIG_TIMER | TRIG_EXT);
250 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
251 	err |= comedi_check_trigger_src(&cmd->stop_src,
252 					TRIG_COUNT | TRIG_NONE);
253 
254 	if (err)
255 		return 1;
256 
257 	/* Step 2a : make sure trigger sources are unique */
258 
259 	err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
260 	err |= comedi_check_trigger_is_unique(cmd->convert_src);
261 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
262 
263 	/* Step 2b : and mutually compatible */
264 
265 	if (cmd->scan_begin_src != TRIG_FOLLOW) {
266 		if (cmd->scan_begin_src != cmd->convert_src)
267 			err |= -EINVAL;
268 	}
269 
270 	if (err)
271 		return 2;
272 
273 	/* Step 3: check if arguments are trivially valid */
274 
275 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
276 
277 	if (cmd->convert_src == TRIG_TIMER) {
278 		err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
279 					PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
280 	} else {	/* TRIG_EXT */
281 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
282 	}
283 
284 	if (cmd->scan_begin_src == TRIG_TIMER) {
285 		err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
286 					PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
287 	} else {	/* TRIG_FOLLOW || TRIG_EXT */
288 		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
289 	}
290 
291 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
292 					   cmd->chanlist_len);
293 
294 	if (cmd->stop_src == TRIG_COUNT)
295 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
296 	else	/* TRIG_NONE */
297 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
298 
299 	if (err)
300 		return 3;
301 
302 	/* Step 4: fix up any arguments */
303 
304 	if (cmd->convert_src == TRIG_TIMER) {
305 		arg = cmd->convert_arg;
306 		comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
307 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
308 	}
309 
310 	/*
311 	 * There's only one timer on this card, so the scan_begin timer
312 	 * must be a multiple of chanlist_len*convert_arg
313 	 */
314 	if (cmd->scan_begin_src == TRIG_TIMER) {
315 		arg = cmd->chanlist_len * cmd->convert_arg;
316 
317 		if (arg < cmd->scan_begin_arg)
318 			arg *= (cmd->scan_begin_arg / arg);
319 
320 		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
321 	}
322 
323 	if (err)
324 		return 4;
325 
326 	/* Step 5: check channel list if it exists */
327 	if (cmd->chanlist && cmd->chanlist_len > 0)
328 		err |= pci9111_ai_check_chanlist(dev, s, cmd);
329 
330 	if (err)
331 		return 5;
332 
333 	return 0;
334 }
335 
pci9111_ai_do_cmd(struct comedi_device * dev,struct comedi_subdevice * s)336 static int pci9111_ai_do_cmd(struct comedi_device *dev,
337 			     struct comedi_subdevice *s)
338 {
339 	struct pci9111_private_data *dev_private = dev->private;
340 	struct comedi_cmd *cmd = &s->async->cmd;
341 	unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
342 	unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
343 	unsigned int trig = 0;
344 
345 	/*  Set channel scan limit */
346 	/*  PCI9111 allows only scanning from channel 0 to channel n */
347 	/*  TODO: handle the case of an external multiplexer */
348 
349 	if (cmd->chanlist_len > 1)
350 		trig |= PCI9111_AI_TRIG_CTRL_ASCAN;
351 
352 	outb(last_chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
353 
354 	/*  Set gain - all channels use the same range */
355 	outb(PCI9111_AI_RANGE(range0), dev->iobase + PCI9111_AI_RANGE_STAT_REG);
356 
357 	/*  Set timer pacer */
358 	dev_private->scan_delay = 0;
359 	if (cmd->convert_src == TRIG_TIMER) {
360 		trig |= PCI9111_AI_TRIG_CTRL_TPST;
361 		comedi_8254_update_divisors(dev->pacer);
362 		comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
363 		pci9111_fifo_reset(dev);
364 		pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
365 					     irq_on_timer_tick);
366 		plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
367 					  false, true, true);
368 
369 		if (cmd->scan_begin_src == TRIG_TIMER) {
370 			dev_private->scan_delay = (cmd->scan_begin_arg /
371 				(cmd->convert_arg * cmd->chanlist_len)) - 1;
372 		}
373 	} else {	/* TRIG_EXT */
374 		trig |= PCI9111_AI_TRIG_CTRL_ETIS;
375 		pci9111_fifo_reset(dev);
376 		pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
377 					     irq_on_timer_tick);
378 		plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
379 					  false, true, true);
380 	}
381 	outb(trig, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
382 
383 	dev_private->chunk_counter = 0;
384 	dev_private->chunk_num_samples = cmd->chanlist_len *
385 					 (1 + dev_private->scan_delay);
386 
387 	return 0;
388 }
389 
pci9111_ai_munge(struct comedi_device * dev,struct comedi_subdevice * s,void * data,unsigned int num_bytes,unsigned int start_chan_index)390 static void pci9111_ai_munge(struct comedi_device *dev,
391 			     struct comedi_subdevice *s, void *data,
392 			     unsigned int num_bytes,
393 			     unsigned int start_chan_index)
394 {
395 	unsigned short *array = data;
396 	unsigned int maxdata = s->maxdata;
397 	unsigned int invert = (maxdata + 1) >> 1;
398 	unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
399 	unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
400 	unsigned int i;
401 
402 	for (i = 0; i < num_samples; i++)
403 		array[i] = ((array[i] >> shift) & maxdata) ^ invert;
404 }
405 
pci9111_handle_fifo_half_full(struct comedi_device * dev,struct comedi_subdevice * s)406 static void pci9111_handle_fifo_half_full(struct comedi_device *dev,
407 					  struct comedi_subdevice *s)
408 {
409 	struct pci9111_private_data *devpriv = dev->private;
410 	struct comedi_cmd *cmd = &s->async->cmd;
411 	unsigned short *buf = devpriv->ai_bounce_buffer;
412 	unsigned int samples;
413 
414 	samples = comedi_nsamples_left(s, PCI9111_FIFO_HALF_SIZE);
415 	insw(dev->iobase + PCI9111_AI_FIFO_REG, buf, samples);
416 
417 	if (devpriv->scan_delay < 1) {
418 		comedi_buf_write_samples(s, buf, samples);
419 	} else {
420 		unsigned int pos = 0;
421 		unsigned int to_read;
422 
423 		while (pos < samples) {
424 			if (devpriv->chunk_counter < cmd->chanlist_len) {
425 				to_read = cmd->chanlist_len -
426 					  devpriv->chunk_counter;
427 
428 				if (to_read > samples - pos)
429 					to_read = samples - pos;
430 
431 				comedi_buf_write_samples(s, buf + pos, to_read);
432 			} else {
433 				to_read = devpriv->chunk_num_samples -
434 					  devpriv->chunk_counter;
435 
436 				if (to_read > samples - pos)
437 					to_read = samples - pos;
438 			}
439 
440 			pos += to_read;
441 			devpriv->chunk_counter += to_read;
442 
443 			if (devpriv->chunk_counter >=
444 			    devpriv->chunk_num_samples)
445 				devpriv->chunk_counter = 0;
446 		}
447 	}
448 }
449 
pci9111_interrupt(int irq,void * p_device)450 static irqreturn_t pci9111_interrupt(int irq, void *p_device)
451 {
452 	struct comedi_device *dev = p_device;
453 	struct pci9111_private_data *dev_private = dev->private;
454 	struct comedi_subdevice *s = dev->read_subdev;
455 	struct comedi_async *async;
456 	struct comedi_cmd *cmd;
457 	unsigned int status;
458 	unsigned long irq_flags;
459 	unsigned char intcsr;
460 
461 	if (!dev->attached) {
462 		/*  Ignore interrupt before device fully attached. */
463 		/*  Might not even have allocated subdevices yet! */
464 		return IRQ_NONE;
465 	}
466 
467 	async = s->async;
468 	cmd = &async->cmd;
469 
470 	spin_lock_irqsave(&dev->spinlock, irq_flags);
471 
472 	/*  Check if we are source of interrupt */
473 	intcsr = inb(dev_private->lcr_io_base + PLX9052_INTCSR);
474 	if (!(((intcsr & PLX9052_INTCSR_PCIENAB) != 0) &&
475 	      (((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) ||
476 	       ((intcsr & PCI9111_LI2_ACTIVE) == PCI9111_LI2_ACTIVE)))) {
477 		/*  Not the source of the interrupt. */
478 		/*  (N.B. not using PLX9052_INTCSR_SOFTINT) */
479 		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
480 		return IRQ_NONE;
481 	}
482 
483 	if ((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) {
484 		/*  Interrupt comes from fifo_half-full signal */
485 
486 		status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
487 
488 		/* '0' means FIFO is full, data may have been lost */
489 		if (!(status & PCI9111_AI_STAT_FF_FF)) {
490 			spin_unlock_irqrestore(&dev->spinlock, irq_flags);
491 			dev_dbg(dev->class_dev, "fifo overflow\n");
492 			outb(0, dev->iobase + PCI9111_INT_CLR_REG);
493 			async->events |= COMEDI_CB_ERROR;
494 			comedi_handle_events(dev, s);
495 
496 			return IRQ_HANDLED;
497 		}
498 
499 		/* '0' means FIFO is half-full */
500 		if (!(status & PCI9111_AI_STAT_FF_HF))
501 			pci9111_handle_fifo_half_full(dev, s);
502 	}
503 
504 	if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
505 		async->events |= COMEDI_CB_EOA;
506 
507 	outb(0, dev->iobase + PCI9111_INT_CLR_REG);
508 
509 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
510 
511 	comedi_handle_events(dev, s);
512 
513 	return IRQ_HANDLED;
514 }
515 
pci9111_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)516 static int pci9111_ai_eoc(struct comedi_device *dev,
517 			  struct comedi_subdevice *s,
518 			  struct comedi_insn *insn,
519 			  unsigned long context)
520 {
521 	unsigned int status;
522 
523 	status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
524 	if (status & PCI9111_AI_STAT_FF_EF)
525 		return 0;
526 	return -EBUSY;
527 }
528 
pci9111_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)529 static int pci9111_ai_insn_read(struct comedi_device *dev,
530 				struct comedi_subdevice *s,
531 				struct comedi_insn *insn, unsigned int *data)
532 {
533 	unsigned int chan = CR_CHAN(insn->chanspec);
534 	unsigned int range = CR_RANGE(insn->chanspec);
535 	unsigned int maxdata = s->maxdata;
536 	unsigned int invert = (maxdata + 1) >> 1;
537 	unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
538 	unsigned int status;
539 	int ret;
540 	int i;
541 
542 	outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
543 
544 	status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
545 	if ((status & PCI9111_AI_RANGE_MASK) != range) {
546 		outb(PCI9111_AI_RANGE(range),
547 		     dev->iobase + PCI9111_AI_RANGE_STAT_REG);
548 	}
549 
550 	pci9111_fifo_reset(dev);
551 
552 	for (i = 0; i < insn->n; i++) {
553 		/* Generate a software trigger */
554 		outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG);
555 
556 		ret = comedi_timeout(dev, s, insn, pci9111_ai_eoc, 0);
557 		if (ret) {
558 			pci9111_fifo_reset(dev);
559 			return ret;
560 		}
561 
562 		data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
563 		data[i] = ((data[i] >> shift) & maxdata) ^ invert;
564 	}
565 
566 	return i;
567 }
568 
pci9111_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)569 static int pci9111_ao_insn_write(struct comedi_device *dev,
570 				 struct comedi_subdevice *s,
571 				 struct comedi_insn *insn,
572 				 unsigned int *data)
573 {
574 	unsigned int chan = CR_CHAN(insn->chanspec);
575 	unsigned int val = s->readback[chan];
576 	int i;
577 
578 	for (i = 0; i < insn->n; i++) {
579 		val = data[i];
580 		outw(val, dev->iobase + PCI9111_AO_REG);
581 	}
582 	s->readback[chan] = val;
583 
584 	return insn->n;
585 }
586 
pci9111_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)587 static int pci9111_di_insn_bits(struct comedi_device *dev,
588 				struct comedi_subdevice *s,
589 				struct comedi_insn *insn,
590 				unsigned int *data)
591 {
592 	data[1] = inw(dev->iobase + PCI9111_DIO_REG);
593 
594 	return insn->n;
595 }
596 
pci9111_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)597 static int pci9111_do_insn_bits(struct comedi_device *dev,
598 				struct comedi_subdevice *s,
599 				struct comedi_insn *insn,
600 				unsigned int *data)
601 {
602 	if (comedi_dio_update_state(s, data))
603 		outw(s->state, dev->iobase + PCI9111_DIO_REG);
604 
605 	data[1] = s->state;
606 
607 	return insn->n;
608 }
609 
pci9111_reset(struct comedi_device * dev)610 static int pci9111_reset(struct comedi_device *dev)
611 {
612 	struct pci9111_private_data *dev_private = dev->private;
613 
614 	/*  Set trigger source to software */
615 	plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
616 				  true, false);
617 
618 	/* disable A/D triggers (software trigger mode) and auto scan off */
619 	outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
620 
621 	return 0;
622 }
623 
pci9111_auto_attach(struct comedi_device * dev,unsigned long context_unused)624 static int pci9111_auto_attach(struct comedi_device *dev,
625 			       unsigned long context_unused)
626 {
627 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
628 	struct pci9111_private_data *dev_private;
629 	struct comedi_subdevice *s;
630 	int ret;
631 
632 	dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
633 	if (!dev_private)
634 		return -ENOMEM;
635 
636 	ret = comedi_pci_enable(dev);
637 	if (ret)
638 		return ret;
639 	dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
640 	dev->iobase = pci_resource_start(pcidev, 2);
641 
642 	pci9111_reset(dev);
643 
644 	if (pcidev->irq) {
645 		ret = request_irq(pcidev->irq, pci9111_interrupt,
646 				  IRQF_SHARED, dev->board_name, dev);
647 		if (ret == 0)
648 			dev->irq = pcidev->irq;
649 	}
650 
651 	dev->pacer = comedi_8254_init(dev->iobase + PCI9111_8254_BASE_REG,
652 				      I8254_OSC_BASE_2MHZ, I8254_IO16, 0);
653 	if (!dev->pacer)
654 		return -ENOMEM;
655 
656 	ret = comedi_alloc_subdevices(dev, 4);
657 	if (ret)
658 		return ret;
659 
660 	s = &dev->subdevices[0];
661 	s->type		= COMEDI_SUBD_AI;
662 	s->subdev_flags	= SDF_READABLE | SDF_COMMON;
663 	s->n_chan	= 16;
664 	s->maxdata	= 0xffff;
665 	s->range_table	= &pci9111_ai_range;
666 	s->insn_read	= pci9111_ai_insn_read;
667 	if (dev->irq) {
668 		dev->read_subdev = s;
669 		s->subdev_flags	|= SDF_CMD_READ;
670 		s->len_chanlist	= s->n_chan;
671 		s->do_cmdtest	= pci9111_ai_do_cmd_test;
672 		s->do_cmd	= pci9111_ai_do_cmd;
673 		s->cancel	= pci9111_ai_cancel;
674 		s->munge	= pci9111_ai_munge;
675 	}
676 
677 	s = &dev->subdevices[1];
678 	s->type		= COMEDI_SUBD_AO;
679 	s->subdev_flags	= SDF_WRITABLE | SDF_COMMON;
680 	s->n_chan	= 1;
681 	s->maxdata	= 0x0fff;
682 	s->len_chanlist	= 1;
683 	s->range_table	= &range_bipolar10;
684 	s->insn_write	= pci9111_ao_insn_write;
685 
686 	ret = comedi_alloc_subdev_readback(s);
687 	if (ret)
688 		return ret;
689 
690 	s = &dev->subdevices[2];
691 	s->type		= COMEDI_SUBD_DI;
692 	s->subdev_flags	= SDF_READABLE;
693 	s->n_chan	= 16;
694 	s->maxdata	= 1;
695 	s->range_table	= &range_digital;
696 	s->insn_bits	= pci9111_di_insn_bits;
697 
698 	s = &dev->subdevices[3];
699 	s->type		= COMEDI_SUBD_DO;
700 	s->subdev_flags	= SDF_WRITABLE;
701 	s->n_chan	= 16;
702 	s->maxdata	= 1;
703 	s->range_table	= &range_digital;
704 	s->insn_bits	= pci9111_do_insn_bits;
705 
706 	return 0;
707 }
708 
pci9111_detach(struct comedi_device * dev)709 static void pci9111_detach(struct comedi_device *dev)
710 {
711 	if (dev->iobase)
712 		pci9111_reset(dev);
713 	comedi_pci_detach(dev);
714 }
715 
716 static struct comedi_driver adl_pci9111_driver = {
717 	.driver_name	= "adl_pci9111",
718 	.module		= THIS_MODULE,
719 	.auto_attach	= pci9111_auto_attach,
720 	.detach		= pci9111_detach,
721 };
722 
pci9111_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)723 static int pci9111_pci_probe(struct pci_dev *dev,
724 			     const struct pci_device_id *id)
725 {
726 	return comedi_pci_auto_config(dev, &adl_pci9111_driver,
727 				      id->driver_data);
728 }
729 
730 static const struct pci_device_id pci9111_pci_table[] = {
731 	{ PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) },
732 	/* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */
733 	{ 0 }
734 };
735 MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
736 
737 static struct pci_driver adl_pci9111_pci_driver = {
738 	.name		= "adl_pci9111",
739 	.id_table	= pci9111_pci_table,
740 	.probe		= pci9111_pci_probe,
741 	.remove		= comedi_pci_auto_unconfig,
742 };
743 module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
744 
745 MODULE_AUTHOR("Comedi https://www.comedi.org");
746 MODULE_DESCRIPTION("Comedi low-level driver");
747 MODULE_LICENSE("GPL");
748