1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * addi_apci_3xxx.c
4  * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
5  * Project manager: S. Weber
6  *
7  *	ADDI-DATA GmbH
8  *	Dieselstrasse 3
9  *	D-77833 Ottersweier
10  *	Tel: +19(0)7223/9493-0
11  *	Fax: +49(0)7223/9493-92
12  *	http://www.addi-data.com
13  *	info@addi-data.com
14  */
15 
16 #include <linux/module.h>
17 #include <linux/interrupt.h>
18 #include <linux/comedi/comedi_pci.h>
19 
20 #define CONV_UNIT_NS		BIT(0)
21 #define CONV_UNIT_US		BIT(1)
22 #define CONV_UNIT_MS		BIT(2)
23 
24 static const struct comedi_lrange apci3xxx_ai_range = {
25 	8, {
26 		BIP_RANGE(10),
27 		BIP_RANGE(5),
28 		BIP_RANGE(2),
29 		BIP_RANGE(1),
30 		UNI_RANGE(10),
31 		UNI_RANGE(5),
32 		UNI_RANGE(2),
33 		UNI_RANGE(1)
34 	}
35 };
36 
37 static const struct comedi_lrange apci3xxx_ao_range = {
38 	2, {
39 		BIP_RANGE(10),
40 		UNI_RANGE(10)
41 	}
42 };
43 
44 enum apci3xxx_boardid {
45 	BOARD_APCI3000_16,
46 	BOARD_APCI3000_8,
47 	BOARD_APCI3000_4,
48 	BOARD_APCI3006_16,
49 	BOARD_APCI3006_8,
50 	BOARD_APCI3006_4,
51 	BOARD_APCI3010_16,
52 	BOARD_APCI3010_8,
53 	BOARD_APCI3010_4,
54 	BOARD_APCI3016_16,
55 	BOARD_APCI3016_8,
56 	BOARD_APCI3016_4,
57 	BOARD_APCI3100_16_4,
58 	BOARD_APCI3100_8_4,
59 	BOARD_APCI3106_16_4,
60 	BOARD_APCI3106_8_4,
61 	BOARD_APCI3110_16_4,
62 	BOARD_APCI3110_8_4,
63 	BOARD_APCI3116_16_4,
64 	BOARD_APCI3116_8_4,
65 	BOARD_APCI3003,
66 	BOARD_APCI3002_16,
67 	BOARD_APCI3002_8,
68 	BOARD_APCI3002_4,
69 	BOARD_APCI3500,
70 };
71 
72 struct apci3xxx_boardinfo {
73 	const char *name;
74 	int ai_subdev_flags;
75 	int ai_n_chan;
76 	unsigned int ai_maxdata;
77 	unsigned char ai_conv_units;
78 	unsigned int ai_min_acq_ns;
79 	unsigned int has_ao:1;
80 	unsigned int has_dig_in:1;
81 	unsigned int has_dig_out:1;
82 	unsigned int has_ttl_io:1;
83 };
84 
85 static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
86 	[BOARD_APCI3000_16] = {
87 		.name			= "apci3000-16",
88 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
89 		.ai_n_chan		= 16,
90 		.ai_maxdata		= 0x0fff,
91 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
92 		.ai_min_acq_ns		= 10000,
93 		.has_ttl_io		= 1,
94 	},
95 	[BOARD_APCI3000_8] = {
96 		.name			= "apci3000-8",
97 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
98 		.ai_n_chan		= 8,
99 		.ai_maxdata		= 0x0fff,
100 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
101 		.ai_min_acq_ns		= 10000,
102 		.has_ttl_io		= 1,
103 	},
104 	[BOARD_APCI3000_4] = {
105 		.name			= "apci3000-4",
106 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
107 		.ai_n_chan		= 4,
108 		.ai_maxdata		= 0x0fff,
109 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
110 		.ai_min_acq_ns		= 10000,
111 		.has_ttl_io		= 1,
112 	},
113 	[BOARD_APCI3006_16] = {
114 		.name			= "apci3006-16",
115 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
116 		.ai_n_chan		= 16,
117 		.ai_maxdata		= 0xffff,
118 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
119 		.ai_min_acq_ns		= 10000,
120 		.has_ttl_io		= 1,
121 	},
122 	[BOARD_APCI3006_8] = {
123 		.name			= "apci3006-8",
124 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
125 		.ai_n_chan		= 8,
126 		.ai_maxdata		= 0xffff,
127 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
128 		.ai_min_acq_ns		= 10000,
129 		.has_ttl_io		= 1,
130 	},
131 	[BOARD_APCI3006_4] = {
132 		.name			= "apci3006-4",
133 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
134 		.ai_n_chan		= 4,
135 		.ai_maxdata		= 0xffff,
136 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
137 		.ai_min_acq_ns		= 10000,
138 		.has_ttl_io		= 1,
139 	},
140 	[BOARD_APCI3010_16] = {
141 		.name			= "apci3010-16",
142 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
143 		.ai_n_chan		= 16,
144 		.ai_maxdata		= 0x0fff,
145 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
146 		.ai_min_acq_ns		= 5000,
147 		.has_dig_in		= 1,
148 		.has_dig_out		= 1,
149 		.has_ttl_io		= 1,
150 	},
151 	[BOARD_APCI3010_8] = {
152 		.name			= "apci3010-8",
153 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
154 		.ai_n_chan		= 8,
155 		.ai_maxdata		= 0x0fff,
156 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
157 		.ai_min_acq_ns		= 5000,
158 		.has_dig_in		= 1,
159 		.has_dig_out		= 1,
160 		.has_ttl_io		= 1,
161 	},
162 	[BOARD_APCI3010_4] = {
163 		.name			= "apci3010-4",
164 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
165 		.ai_n_chan		= 4,
166 		.ai_maxdata		= 0x0fff,
167 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
168 		.ai_min_acq_ns		= 5000,
169 		.has_dig_in		= 1,
170 		.has_dig_out		= 1,
171 		.has_ttl_io		= 1,
172 	},
173 	[BOARD_APCI3016_16] = {
174 		.name			= "apci3016-16",
175 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
176 		.ai_n_chan		= 16,
177 		.ai_maxdata		= 0xffff,
178 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
179 		.ai_min_acq_ns		= 5000,
180 		.has_dig_in		= 1,
181 		.has_dig_out		= 1,
182 		.has_ttl_io		= 1,
183 	},
184 	[BOARD_APCI3016_8] = {
185 		.name			= "apci3016-8",
186 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
187 		.ai_n_chan		= 8,
188 		.ai_maxdata		= 0xffff,
189 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
190 		.ai_min_acq_ns		= 5000,
191 		.has_dig_in		= 1,
192 		.has_dig_out		= 1,
193 		.has_ttl_io		= 1,
194 	},
195 	[BOARD_APCI3016_4] = {
196 		.name			= "apci3016-4",
197 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
198 		.ai_n_chan		= 4,
199 		.ai_maxdata		= 0xffff,
200 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
201 		.ai_min_acq_ns		= 5000,
202 		.has_dig_in		= 1,
203 		.has_dig_out		= 1,
204 		.has_ttl_io		= 1,
205 	},
206 	[BOARD_APCI3100_16_4] = {
207 		.name			= "apci3100-16-4",
208 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
209 		.ai_n_chan		= 16,
210 		.ai_maxdata		= 0x0fff,
211 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
212 		.ai_min_acq_ns		= 10000,
213 		.has_ao			= 1,
214 		.has_ttl_io		= 1,
215 	},
216 	[BOARD_APCI3100_8_4] = {
217 		.name			= "apci3100-8-4",
218 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
219 		.ai_n_chan		= 8,
220 		.ai_maxdata		= 0x0fff,
221 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
222 		.ai_min_acq_ns		= 10000,
223 		.has_ao			= 1,
224 		.has_ttl_io		= 1,
225 	},
226 	[BOARD_APCI3106_16_4] = {
227 		.name			= "apci3106-16-4",
228 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
229 		.ai_n_chan		= 16,
230 		.ai_maxdata		= 0xffff,
231 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
232 		.ai_min_acq_ns		= 10000,
233 		.has_ao			= 1,
234 		.has_ttl_io		= 1,
235 	},
236 	[BOARD_APCI3106_8_4] = {
237 		.name			= "apci3106-8-4",
238 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
239 		.ai_n_chan		= 8,
240 		.ai_maxdata		= 0xffff,
241 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
242 		.ai_min_acq_ns		= 10000,
243 		.has_ao			= 1,
244 		.has_ttl_io		= 1,
245 	},
246 	[BOARD_APCI3110_16_4] = {
247 		.name			= "apci3110-16-4",
248 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
249 		.ai_n_chan		= 16,
250 		.ai_maxdata		= 0x0fff,
251 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
252 		.ai_min_acq_ns		= 5000,
253 		.has_ao			= 1,
254 		.has_dig_in		= 1,
255 		.has_dig_out		= 1,
256 		.has_ttl_io		= 1,
257 	},
258 	[BOARD_APCI3110_8_4] = {
259 		.name			= "apci3110-8-4",
260 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
261 		.ai_n_chan		= 8,
262 		.ai_maxdata		= 0x0fff,
263 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
264 		.ai_min_acq_ns		= 5000,
265 		.has_ao			= 1,
266 		.has_dig_in		= 1,
267 		.has_dig_out		= 1,
268 		.has_ttl_io		= 1,
269 	},
270 	[BOARD_APCI3116_16_4] = {
271 		.name			= "apci3116-16-4",
272 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
273 		.ai_n_chan		= 16,
274 		.ai_maxdata		= 0xffff,
275 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
276 		.ai_min_acq_ns		= 5000,
277 		.has_ao			= 1,
278 		.has_dig_in		= 1,
279 		.has_dig_out		= 1,
280 		.has_ttl_io		= 1,
281 	},
282 	[BOARD_APCI3116_8_4] = {
283 		.name			= "apci3116-8-4",
284 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
285 		.ai_n_chan		= 8,
286 		.ai_maxdata		= 0xffff,
287 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
288 		.ai_min_acq_ns		= 5000,
289 		.has_ao			= 1,
290 		.has_dig_in		= 1,
291 		.has_dig_out		= 1,
292 		.has_ttl_io		= 1,
293 	},
294 	[BOARD_APCI3003] = {
295 		.name			= "apci3003",
296 		.ai_subdev_flags	= SDF_DIFF,
297 		.ai_n_chan		= 4,
298 		.ai_maxdata		= 0xffff,
299 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US |
300 					  CONV_UNIT_NS,
301 		.ai_min_acq_ns		= 2500,
302 		.has_dig_in		= 1,
303 		.has_dig_out		= 1,
304 	},
305 	[BOARD_APCI3002_16] = {
306 		.name			= "apci3002-16",
307 		.ai_subdev_flags	= SDF_DIFF,
308 		.ai_n_chan		= 16,
309 		.ai_maxdata		= 0xffff,
310 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
311 		.ai_min_acq_ns		= 5000,
312 		.has_dig_in		= 1,
313 		.has_dig_out		= 1,
314 	},
315 	[BOARD_APCI3002_8] = {
316 		.name			= "apci3002-8",
317 		.ai_subdev_flags	= SDF_DIFF,
318 		.ai_n_chan		= 8,
319 		.ai_maxdata		= 0xffff,
320 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
321 		.ai_min_acq_ns		= 5000,
322 		.has_dig_in		= 1,
323 		.has_dig_out		= 1,
324 	},
325 	[BOARD_APCI3002_4] = {
326 		.name			= "apci3002-4",
327 		.ai_subdev_flags	= SDF_DIFF,
328 		.ai_n_chan		= 4,
329 		.ai_maxdata		= 0xffff,
330 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
331 		.ai_min_acq_ns		= 5000,
332 		.has_dig_in		= 1,
333 		.has_dig_out		= 1,
334 	},
335 	[BOARD_APCI3500] = {
336 		.name			= "apci3500",
337 		.has_ao			= 1,
338 		.has_ttl_io		= 1,
339 	},
340 };
341 
342 struct apci3xxx_private {
343 	unsigned int ai_timer;
344 	unsigned char ai_time_base;
345 };
346 
apci3xxx_irq_handler(int irq,void * d)347 static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
348 {
349 	struct comedi_device *dev = d;
350 	struct comedi_subdevice *s = dev->read_subdev;
351 	unsigned int status;
352 	unsigned int val;
353 
354 	/* Test if interrupt occur */
355 	status = readl(dev->mmio + 16);
356 	if ((status & 0x2) == 0x2) {
357 		/* Reset the interrupt */
358 		writel(status, dev->mmio + 16);
359 
360 		val = readl(dev->mmio + 28);
361 		comedi_buf_write_samples(s, &val, 1);
362 
363 		s->async->events |= COMEDI_CB_EOA;
364 		comedi_handle_events(dev, s);
365 
366 		return IRQ_HANDLED;
367 	}
368 	return IRQ_NONE;
369 }
370 
apci3xxx_ai_started(struct comedi_device * dev)371 static int apci3xxx_ai_started(struct comedi_device *dev)
372 {
373 	if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
374 		return 1;
375 
376 	return 0;
377 }
378 
apci3xxx_ai_setup(struct comedi_device * dev,unsigned int chanspec)379 static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
380 {
381 	unsigned int chan = CR_CHAN(chanspec);
382 	unsigned int range = CR_RANGE(chanspec);
383 	unsigned int aref = CR_AREF(chanspec);
384 	unsigned int delay_mode;
385 	unsigned int val;
386 
387 	if (apci3xxx_ai_started(dev))
388 		return -EBUSY;
389 
390 	/* Clear the FIFO */
391 	writel(0x10000, dev->mmio + 12);
392 
393 	/* Get and save the delay mode */
394 	delay_mode = readl(dev->mmio + 4);
395 	delay_mode &= 0xfffffef0;
396 
397 	/* Channel configuration selection */
398 	writel(delay_mode, dev->mmio + 4);
399 
400 	/* Make the configuration */
401 	val = (range & 3) | ((range >> 2) << 6) |
402 	      ((aref == AREF_DIFF) << 7);
403 	writel(val, dev->mmio + 0);
404 
405 	/* Channel selection */
406 	writel(delay_mode | 0x100, dev->mmio + 4);
407 	writel(chan, dev->mmio + 0);
408 
409 	/* Restore delay mode */
410 	writel(delay_mode, dev->mmio + 4);
411 
412 	/* Set the number of sequence to 1 */
413 	writel(1, dev->mmio + 48);
414 
415 	return 0;
416 }
417 
apci3xxx_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)418 static int apci3xxx_ai_eoc(struct comedi_device *dev,
419 			   struct comedi_subdevice *s,
420 			   struct comedi_insn *insn,
421 			   unsigned long context)
422 {
423 	unsigned int status;
424 
425 	status = readl(dev->mmio + 20);
426 	if (status & 0x1)
427 		return 0;
428 	return -EBUSY;
429 }
430 
apci3xxx_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)431 static int apci3xxx_ai_insn_read(struct comedi_device *dev,
432 				 struct comedi_subdevice *s,
433 				 struct comedi_insn *insn,
434 				 unsigned int *data)
435 {
436 	int ret;
437 	int i;
438 
439 	ret = apci3xxx_ai_setup(dev, insn->chanspec);
440 	if (ret)
441 		return ret;
442 
443 	for (i = 0; i < insn->n; i++) {
444 		/* Start the conversion */
445 		writel(0x80000, dev->mmio + 8);
446 
447 		/* Wait the EOS */
448 		ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
449 		if (ret)
450 			return ret;
451 
452 		/* Read the analog value */
453 		data[i] = readl(dev->mmio + 28);
454 	}
455 
456 	return insn->n;
457 }
458 
apci3xxx_ai_ns_to_timer(struct comedi_device * dev,unsigned int * ns,unsigned int flags)459 static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
460 				   unsigned int *ns, unsigned int flags)
461 {
462 	const struct apci3xxx_boardinfo *board = dev->board_ptr;
463 	struct apci3xxx_private *devpriv = dev->private;
464 	unsigned int base;
465 	unsigned int timer;
466 	int time_base;
467 
468 	/* time_base: 0 = ns, 1 = us, 2 = ms */
469 	for (time_base = 0; time_base < 3; time_base++) {
470 		/* skip unsupported time bases */
471 		if (!(board->ai_conv_units & (1 << time_base)))
472 			continue;
473 
474 		switch (time_base) {
475 		case 0:
476 			base = 1;
477 			break;
478 		case 1:
479 			base = 1000;
480 			break;
481 		case 2:
482 			base = 1000000;
483 			break;
484 		}
485 
486 		switch (flags & CMDF_ROUND_MASK) {
487 		case CMDF_ROUND_NEAREST:
488 		default:
489 			timer = DIV_ROUND_CLOSEST(*ns, base);
490 			break;
491 		case CMDF_ROUND_DOWN:
492 			timer = *ns / base;
493 			break;
494 		case CMDF_ROUND_UP:
495 			timer = DIV_ROUND_UP(*ns, base);
496 			break;
497 		}
498 
499 		if (timer < 0x10000) {
500 			devpriv->ai_time_base = time_base;
501 			devpriv->ai_timer = timer;
502 			*ns = timer * time_base;
503 			return 0;
504 		}
505 	}
506 	return -EINVAL;
507 }
508 
apci3xxx_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)509 static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
510 			       struct comedi_subdevice *s,
511 			       struct comedi_cmd *cmd)
512 {
513 	const struct apci3xxx_boardinfo *board = dev->board_ptr;
514 	int err = 0;
515 	unsigned int arg;
516 
517 	/* Step 1 : check if triggers are trivially valid */
518 
519 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
520 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
521 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
522 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
523 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
524 
525 	if (err)
526 		return 1;
527 
528 	/* Step 2a : make sure trigger sources are unique */
529 
530 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
531 
532 	/* Step 2b : and mutually compatible */
533 
534 	if (err)
535 		return 2;
536 
537 	/* Step 3: check if arguments are trivially valid */
538 
539 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
540 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
541 	err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
542 					    board->ai_min_acq_ns);
543 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
544 					   cmd->chanlist_len);
545 
546 	if (cmd->stop_src == TRIG_COUNT)
547 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
548 	else	/* TRIG_NONE */
549 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
550 
551 	if (err)
552 		return 3;
553 
554 	/* step 4: fix up any arguments */
555 
556 	arg = cmd->convert_arg;
557 	err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
558 	err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
559 
560 	if (err)
561 		return 4;
562 
563 	return 0;
564 }
565 
apci3xxx_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)566 static int apci3xxx_ai_cmd(struct comedi_device *dev,
567 			   struct comedi_subdevice *s)
568 {
569 	struct apci3xxx_private *devpriv = dev->private;
570 	struct comedi_cmd *cmd = &s->async->cmd;
571 	int ret;
572 
573 	ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
574 	if (ret)
575 		return ret;
576 
577 	/* Set the convert timing unit */
578 	writel(devpriv->ai_time_base, dev->mmio + 36);
579 
580 	/* Set the convert timing */
581 	writel(devpriv->ai_timer, dev->mmio + 32);
582 
583 	/* Start the conversion */
584 	writel(0x180000, dev->mmio + 8);
585 
586 	return 0;
587 }
588 
apci3xxx_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)589 static int apci3xxx_ai_cancel(struct comedi_device *dev,
590 			      struct comedi_subdevice *s)
591 {
592 	return 0;
593 }
594 
apci3xxx_ao_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)595 static int apci3xxx_ao_eoc(struct comedi_device *dev,
596 			   struct comedi_subdevice *s,
597 			   struct comedi_insn *insn,
598 			   unsigned long context)
599 {
600 	unsigned int status;
601 
602 	status = readl(dev->mmio + 96);
603 	if (status & 0x100)
604 		return 0;
605 	return -EBUSY;
606 }
607 
apci3xxx_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)608 static int apci3xxx_ao_insn_write(struct comedi_device *dev,
609 				  struct comedi_subdevice *s,
610 				  struct comedi_insn *insn,
611 				  unsigned int *data)
612 {
613 	unsigned int chan = CR_CHAN(insn->chanspec);
614 	unsigned int range = CR_RANGE(insn->chanspec);
615 	int ret;
616 	int i;
617 
618 	for (i = 0; i < insn->n; i++) {
619 		unsigned int val = data[i];
620 
621 		/* Set the range selection */
622 		writel(range, dev->mmio + 96);
623 
624 		/* Write the analog value to the selected channel */
625 		writel((val << 8) | chan, dev->mmio + 100);
626 
627 		/* Wait the end of transfer */
628 		ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
629 		if (ret)
630 			return ret;
631 
632 		s->readback[chan] = val;
633 	}
634 
635 	return insn->n;
636 }
637 
apci3xxx_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)638 static int apci3xxx_di_insn_bits(struct comedi_device *dev,
639 				 struct comedi_subdevice *s,
640 				 struct comedi_insn *insn,
641 				 unsigned int *data)
642 {
643 	data[1] = inl(dev->iobase + 32) & 0xf;
644 
645 	return insn->n;
646 }
647 
apci3xxx_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)648 static int apci3xxx_do_insn_bits(struct comedi_device *dev,
649 				 struct comedi_subdevice *s,
650 				 struct comedi_insn *insn,
651 				 unsigned int *data)
652 {
653 	s->state = inl(dev->iobase + 48) & 0xf;
654 
655 	if (comedi_dio_update_state(s, data))
656 		outl(s->state, dev->iobase + 48);
657 
658 	data[1] = s->state;
659 
660 	return insn->n;
661 }
662 
apci3xxx_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)663 static int apci3xxx_dio_insn_config(struct comedi_device *dev,
664 				    struct comedi_subdevice *s,
665 				    struct comedi_insn *insn,
666 				    unsigned int *data)
667 {
668 	unsigned int chan = CR_CHAN(insn->chanspec);
669 	unsigned int mask = 0;
670 	int ret;
671 
672 	/*
673 	 * Port 0 (channels 0-7) are always inputs
674 	 * Port 1 (channels 8-15) are always outputs
675 	 * Port 2 (channels 16-23) are programmable i/o
676 	 */
677 	if (data[0] != INSN_CONFIG_DIO_QUERY) {
678 		/* ignore all other instructions for ports 0 and 1 */
679 		if (chan < 16)
680 			return -EINVAL;
681 
682 		/* changing any channel in port 2 changes the entire port */
683 		mask = 0xff0000;
684 	}
685 
686 	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
687 	if (ret)
688 		return ret;
689 
690 	/* update port 2 configuration */
691 	outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
692 
693 	return insn->n;
694 }
695 
apci3xxx_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)696 static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
697 				  struct comedi_subdevice *s,
698 				  struct comedi_insn *insn,
699 				  unsigned int *data)
700 {
701 	unsigned int mask;
702 	unsigned int val;
703 
704 	mask = comedi_dio_update_state(s, data);
705 	if (mask) {
706 		if (mask & 0xff)
707 			outl(s->state & 0xff, dev->iobase + 80);
708 		if (mask & 0xff0000)
709 			outl((s->state >> 16) & 0xff, dev->iobase + 112);
710 	}
711 
712 	val = inl(dev->iobase + 80);
713 	val |= (inl(dev->iobase + 64) << 8);
714 	if (s->io_bits & 0xff0000)
715 		val |= (inl(dev->iobase + 112) << 16);
716 	else
717 		val |= (inl(dev->iobase + 96) << 16);
718 
719 	data[1] = val;
720 
721 	return insn->n;
722 }
723 
apci3xxx_reset(struct comedi_device * dev)724 static int apci3xxx_reset(struct comedi_device *dev)
725 {
726 	unsigned int val;
727 	int i;
728 
729 	/* Disable the interrupt */
730 	disable_irq(dev->irq);
731 
732 	/* Clear the start command */
733 	writel(0, dev->mmio + 8);
734 
735 	/* Reset the interrupt flags */
736 	val = readl(dev->mmio + 16);
737 	writel(val, dev->mmio + 16);
738 
739 	/* clear the EOS */
740 	readl(dev->mmio + 20);
741 
742 	/* Clear the FIFO */
743 	for (i = 0; i < 16; i++)
744 		val = readl(dev->mmio + 28);
745 
746 	/* Enable the interrupt */
747 	enable_irq(dev->irq);
748 
749 	return 0;
750 }
751 
apci3xxx_auto_attach(struct comedi_device * dev,unsigned long context)752 static int apci3xxx_auto_attach(struct comedi_device *dev,
753 				unsigned long context)
754 {
755 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
756 	const struct apci3xxx_boardinfo *board = NULL;
757 	struct apci3xxx_private *devpriv;
758 	struct comedi_subdevice *s;
759 	int n_subdevices;
760 	int subdev;
761 	int ret;
762 
763 	if (context < ARRAY_SIZE(apci3xxx_boardtypes))
764 		board = &apci3xxx_boardtypes[context];
765 	if (!board)
766 		return -ENODEV;
767 	dev->board_ptr = board;
768 	dev->board_name = board->name;
769 
770 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
771 	if (!devpriv)
772 		return -ENOMEM;
773 
774 	ret = comedi_pci_enable(dev);
775 	if (ret)
776 		return ret;
777 
778 	dev->iobase = pci_resource_start(pcidev, 2);
779 	dev->mmio = pci_ioremap_bar(pcidev, 3);
780 	if (!dev->mmio)
781 		return -ENOMEM;
782 
783 	if (pcidev->irq > 0) {
784 		ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
785 				  IRQF_SHARED, dev->board_name, dev);
786 		if (ret == 0)
787 			dev->irq = pcidev->irq;
788 	}
789 
790 	n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
791 		       board->has_dig_in + board->has_dig_out +
792 		       board->has_ttl_io;
793 	ret = comedi_alloc_subdevices(dev, n_subdevices);
794 	if (ret)
795 		return ret;
796 
797 	subdev = 0;
798 
799 	/* Analog Input subdevice */
800 	if (board->ai_n_chan) {
801 		s = &dev->subdevices[subdev];
802 		s->type		= COMEDI_SUBD_AI;
803 		s->subdev_flags	= SDF_READABLE | board->ai_subdev_flags;
804 		s->n_chan	= board->ai_n_chan;
805 		s->maxdata	= board->ai_maxdata;
806 		s->range_table	= &apci3xxx_ai_range;
807 		s->insn_read	= apci3xxx_ai_insn_read;
808 		if (dev->irq) {
809 			/*
810 			 * FIXME: The hardware supports multiple scan modes
811 			 * but the original addi-data driver only supported
812 			 * reading a single channel with interrupts. Need a
813 			 * proper datasheet to fix this.
814 			 *
815 			 * The following scan modes are supported by the
816 			 * hardware:
817 			 *   1) Single software scan
818 			 *   2) Single hardware triggered scan
819 			 *   3) Continuous software scan
820 			 *   4) Continuous software scan with timer delay
821 			 *   5) Continuous hardware triggered scan
822 			 *   6) Continuous hardware triggered scan with timer
823 			 *      delay
824 			 *
825 			 * For now, limit the chanlist to a single channel.
826 			 */
827 			dev->read_subdev = s;
828 			s->subdev_flags	|= SDF_CMD_READ;
829 			s->len_chanlist	= 1;
830 			s->do_cmdtest	= apci3xxx_ai_cmdtest;
831 			s->do_cmd	= apci3xxx_ai_cmd;
832 			s->cancel	= apci3xxx_ai_cancel;
833 		}
834 
835 		subdev++;
836 	}
837 
838 	/* Analog Output subdevice */
839 	if (board->has_ao) {
840 		s = &dev->subdevices[subdev];
841 		s->type		= COMEDI_SUBD_AO;
842 		s->subdev_flags	= SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
843 		s->n_chan	= 4;
844 		s->maxdata	= 0x0fff;
845 		s->range_table	= &apci3xxx_ao_range;
846 		s->insn_write	= apci3xxx_ao_insn_write;
847 
848 		ret = comedi_alloc_subdev_readback(s);
849 		if (ret)
850 			return ret;
851 
852 		subdev++;
853 	}
854 
855 	/* Digital Input subdevice */
856 	if (board->has_dig_in) {
857 		s = &dev->subdevices[subdev];
858 		s->type		= COMEDI_SUBD_DI;
859 		s->subdev_flags	= SDF_READABLE;
860 		s->n_chan	= 4;
861 		s->maxdata	= 1;
862 		s->range_table	= &range_digital;
863 		s->insn_bits	= apci3xxx_di_insn_bits;
864 
865 		subdev++;
866 	}
867 
868 	/* Digital Output subdevice */
869 	if (board->has_dig_out) {
870 		s = &dev->subdevices[subdev];
871 		s->type		= COMEDI_SUBD_DO;
872 		s->subdev_flags	= SDF_WRITABLE;
873 		s->n_chan	= 4;
874 		s->maxdata	= 1;
875 		s->range_table	= &range_digital;
876 		s->insn_bits	= apci3xxx_do_insn_bits;
877 
878 		subdev++;
879 	}
880 
881 	/* TTL Digital I/O subdevice */
882 	if (board->has_ttl_io) {
883 		s = &dev->subdevices[subdev];
884 		s->type		= COMEDI_SUBD_DIO;
885 		s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
886 		s->n_chan	= 24;
887 		s->maxdata	= 1;
888 		s->io_bits	= 0xff;	/* channels 0-7 are always outputs */
889 		s->range_table	= &range_digital;
890 		s->insn_config	= apci3xxx_dio_insn_config;
891 		s->insn_bits	= apci3xxx_dio_insn_bits;
892 
893 		subdev++;
894 	}
895 
896 	apci3xxx_reset(dev);
897 	return 0;
898 }
899 
apci3xxx_detach(struct comedi_device * dev)900 static void apci3xxx_detach(struct comedi_device *dev)
901 {
902 	if (dev->iobase)
903 		apci3xxx_reset(dev);
904 	comedi_pci_detach(dev);
905 }
906 
907 static struct comedi_driver apci3xxx_driver = {
908 	.driver_name	= "addi_apci_3xxx",
909 	.module		= THIS_MODULE,
910 	.auto_attach	= apci3xxx_auto_attach,
911 	.detach		= apci3xxx_detach,
912 };
913 
apci3xxx_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)914 static int apci3xxx_pci_probe(struct pci_dev *dev,
915 			      const struct pci_device_id *id)
916 {
917 	return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
918 }
919 
920 static const struct pci_device_id apci3xxx_pci_table[] = {
921 	{ PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
922 	{ PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
923 	{ PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
924 	{ PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
925 	{ PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
926 	{ PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
927 	{ PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
928 	{ PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
929 	{ PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
930 	{ PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
931 	{ PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
932 	{ PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
933 	{ PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
934 	{ PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
935 	{ PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
936 	{ PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
937 	{ PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
938 	{ PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
939 	{ PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
940 	{ PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
941 	{ PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
942 	{ PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
943 	{ PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
944 	{ PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
945 	{ PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
946 	{ 0 }
947 };
948 MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
949 
950 static struct pci_driver apci3xxx_pci_driver = {
951 	.name		= "addi_apci_3xxx",
952 	.id_table	= apci3xxx_pci_table,
953 	.probe		= apci3xxx_pci_probe,
954 	.remove		= comedi_pci_auto_unconfig,
955 };
956 module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
957 
958 MODULE_AUTHOR("Comedi https://www.comedi.org");
959 MODULE_DESCRIPTION("Comedi low-level driver");
960 MODULE_LICENSE("GPL");
961