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