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
24static 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
37static const struct comedi_lrange apci3xxx_ao_range = {
38	2, {
39		BIP_RANGE(10),
40		UNI_RANGE(10)
41	}
42};
43
44enum 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
72struct 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
85static 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
342struct apci3xxx_private {
343	unsigned int ai_timer;
344	unsigned char ai_time_base;
345};
346
347static 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
371static 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
379static 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
418static 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
431static 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
459static 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
509static 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
566static 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
589static int apci3xxx_ai_cancel(struct comedi_device *dev,
590			      struct comedi_subdevice *s)
591{
592	return 0;
593}
594
595static 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
608static 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
638static 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
648static 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
663static 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
696static 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
724static 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
752static 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
900static void apci3xxx_detach(struct comedi_device *dev)
901{
902	if (dev->iobase)
903		apci3xxx_reset(dev);
904	comedi_pci_detach(dev);
905}
906
907static 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
914static 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
920static 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};
948MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
949
950static 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};
956module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
957
958MODULE_AUTHOR("Comedi https://www.comedi.org");
959MODULE_DESCRIPTION("Comedi low-level driver");
960MODULE_LICENSE("GPL");
961