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