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