addi_apci_3xxx.c revision 1867add98e44d4bec01572b4cfd4becd11297e5d
1#include <linux/pci.h>
2#include <linux/interrupt.h>
3#include <linux/sched.h>
4
5#include "../comedidev.h"
6#include "comedi_fc.h"
7#include "amcc_s5933.h"
8
9#ifndef COMEDI_SUBD_TTLIO
10#define COMEDI_SUBD_TTLIO   11	/* Digital Input Output But TTL */
11#endif
12
13static const struct comedi_lrange apci3xxx_ai_range = {
14	8, {
15		BIP_RANGE(10),
16		BIP_RANGE(5),
17		BIP_RANGE(2),
18		BIP_RANGE(1),
19		UNI_RANGE(10),
20		UNI_RANGE(5),
21		UNI_RANGE(2),
22		UNI_RANGE(1)
23	}
24};
25
26static const struct comedi_lrange apci3xxx_ao_range = {
27	2, {
28		BIP_RANGE(10),
29		UNI_RANGE(10)
30	}
31};
32
33enum apci3xxx_boardid {
34	BOARD_APCI3000_16,
35	BOARD_APCI3000_8,
36	BOARD_APCI3000_4,
37	BOARD_APCI3006_16,
38	BOARD_APCI3006_8,
39	BOARD_APCI3006_4,
40	BOARD_APCI3010_16,
41	BOARD_APCI3010_8,
42	BOARD_APCI3010_4,
43	BOARD_APCI3016_16,
44	BOARD_APCI3016_8,
45	BOARD_APCI3016_4,
46	BOARD_APCI3100_16_4,
47	BOARD_APCI3100_8_4,
48	BOARD_APCI3106_16_4,
49	BOARD_APCI3106_8_4,
50	BOARD_APCI3110_16_4,
51	BOARD_APCI3110_8_4,
52	BOARD_APCI3116_16_4,
53	BOARD_APCI3116_8_4,
54	BOARD_APCI3003,
55	BOARD_APCI3002_16,
56	BOARD_APCI3002_8,
57	BOARD_APCI3002_4,
58	BOARD_APCI3500,
59};
60
61struct apci3xxx_boardinfo {
62	const char *pc_DriverName;
63	int i_IorangeBase1;
64	int i_NbrAiChannel;
65	int i_NbrAiChannelDiff;
66	int i_AiChannelList;
67	int i_NbrAoChannel;
68	int i_AiMaxdata;
69	int i_AoMaxdata;
70	int i_NbrDiChannel;
71	int i_NbrDoChannel;
72	int i_NbrTTLChannel;
73	unsigned char b_AvailableConvertUnit;
74	unsigned int ui_MinAcquisitiontimeNs;
75};
76
77static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
78	[BOARD_APCI3000_16] = {
79		.pc_DriverName		= "apci3000-16",
80		.i_IorangeBase1		= 256,
81		.i_NbrAiChannel		= 16,
82		.i_NbrAiChannelDiff	= 8,
83		.i_AiChannelList	= 16,
84		.i_AiMaxdata		= 4095,
85		.i_NbrTTLChannel	= 24,
86		.b_AvailableConvertUnit	= 6,
87		.ui_MinAcquisitiontimeNs = 10000,
88	},
89	[BOARD_APCI3000_8] = {
90		.pc_DriverName		= "apci3000-8",
91		.i_IorangeBase1		= 256,
92		.i_NbrAiChannel		= 8,
93		.i_NbrAiChannelDiff	= 4,
94		.i_AiChannelList	= 8,
95		.i_AiMaxdata		= 4095,
96		.i_NbrTTLChannel	= 24,
97		.b_AvailableConvertUnit	= 6,
98		.ui_MinAcquisitiontimeNs = 10000,
99	},
100	[BOARD_APCI3000_4] = {
101		.pc_DriverName		= "apci3000-4",
102		.i_IorangeBase1		= 256,
103		.i_NbrAiChannel		= 4,
104		.i_NbrAiChannelDiff	= 2,
105		.i_AiChannelList	= 4,
106		.i_AiMaxdata		= 4095,
107		.i_NbrTTLChannel	= 24,
108		.b_AvailableConvertUnit	= 6,
109		.ui_MinAcquisitiontimeNs = 10000,
110	},
111	[BOARD_APCI3006_16] = {
112		.pc_DriverName		= "apci3006-16",
113		.i_IorangeBase1		= 256,
114		.i_NbrAiChannel		= 16,
115		.i_NbrAiChannelDiff	= 8,
116		.i_AiChannelList	= 16,
117		.i_AiMaxdata		= 65535,
118		.i_NbrTTLChannel	= 24,
119		.b_AvailableConvertUnit	= 6,
120		.ui_MinAcquisitiontimeNs = 10000,
121	},
122	[BOARD_APCI3006_8] = {
123		.pc_DriverName		= "apci3006-8",
124		.i_IorangeBase1		= 256,
125		.i_NbrAiChannel		= 8,
126		.i_NbrAiChannelDiff	= 4,
127		.i_AiChannelList	= 8,
128		.i_AiMaxdata		= 65535,
129		.i_NbrTTLChannel	= 24,
130		.b_AvailableConvertUnit	= 6,
131		.ui_MinAcquisitiontimeNs = 10000,
132	},
133	[BOARD_APCI3006_4] = {
134		.pc_DriverName		= "apci3006-4",
135		.i_IorangeBase1		= 256,
136		.i_NbrAiChannel		= 4,
137		.i_NbrAiChannelDiff	= 2,
138		.i_AiChannelList	= 4,
139		.i_AiMaxdata		= 65535,
140		.i_NbrTTLChannel	= 24,
141		.b_AvailableConvertUnit	= 6,
142		.ui_MinAcquisitiontimeNs = 10000,
143	},
144	[BOARD_APCI3010_16] = {
145		.pc_DriverName		= "apci3010-16",
146		.i_IorangeBase1		= 256,
147		.i_NbrAiChannel		= 16,
148		.i_NbrAiChannelDiff	= 8,
149		.i_AiChannelList	= 16,
150		.i_AiMaxdata		= 4095,
151		.i_NbrDiChannel		= 4,
152		.i_NbrDoChannel		= 4,
153		.i_NbrTTLChannel	= 24,
154		.b_AvailableConvertUnit	= 6,
155		.ui_MinAcquisitiontimeNs = 5000,
156	},
157	[BOARD_APCI3010_8] = {
158		.pc_DriverName		= "apci3010-8",
159		.i_IorangeBase1		= 256,
160		.i_NbrAiChannel		= 8,
161		.i_NbrAiChannelDiff	= 4,
162		.i_AiChannelList	= 8,
163		.i_AiMaxdata		= 4095,
164		.i_NbrDiChannel		= 4,
165		.i_NbrDoChannel		= 4,
166		.i_NbrTTLChannel	= 24,
167		.b_AvailableConvertUnit	= 6,
168		.ui_MinAcquisitiontimeNs = 5000,
169	},
170	[BOARD_APCI3010_4] = {
171		.pc_DriverName		= "apci3010-4",
172		.i_IorangeBase1		= 256,
173		.i_NbrAiChannel		= 4,
174		.i_NbrAiChannelDiff	= 2,
175		.i_AiChannelList	= 4,
176		.i_AiMaxdata		= 4095,
177		.i_NbrDiChannel		= 4,
178		.i_NbrDoChannel		= 4,
179		.i_NbrTTLChannel	= 24,
180		.b_AvailableConvertUnit	= 6,
181		.ui_MinAcquisitiontimeNs = 5000,
182	},
183	[BOARD_APCI3016_16] = {
184		.pc_DriverName		= "apci3016-16",
185		.i_IorangeBase1		= 256,
186		.i_NbrAiChannel		= 16,
187		.i_NbrAiChannelDiff	= 8,
188		.i_AiChannelList	= 16,
189		.i_AiMaxdata		= 65535,
190		.i_NbrDiChannel		= 4,
191		.i_NbrDoChannel		= 4,
192		.i_NbrTTLChannel	= 24,
193		.b_AvailableConvertUnit	= 6,
194		.ui_MinAcquisitiontimeNs = 5000,
195	},
196	[BOARD_APCI3016_8] = {
197		.pc_DriverName		= "apci3016-8",
198		.i_IorangeBase1		= 256,
199		.i_NbrAiChannel		= 8,
200		.i_NbrAiChannelDiff	= 4,
201		.i_AiChannelList	= 8,
202		.i_AiMaxdata		= 65535,
203		.i_NbrDiChannel		= 4,
204		.i_NbrDoChannel		= 4,
205		.i_NbrTTLChannel	= 24,
206		.b_AvailableConvertUnit	= 6,
207		.ui_MinAcquisitiontimeNs = 5000,
208	},
209	[BOARD_APCI3016_4] = {
210		.pc_DriverName		= "apci3016-4",
211		.i_IorangeBase1		= 256,
212		.i_NbrAiChannel		= 4,
213		.i_NbrAiChannelDiff	= 2,
214		.i_AiChannelList	= 4,
215		.i_AiMaxdata		= 65535,
216		.i_NbrDiChannel		= 4,
217		.i_NbrDoChannel		= 4,
218		.i_NbrTTLChannel	= 24,
219		.b_AvailableConvertUnit	= 6,
220		.ui_MinAcquisitiontimeNs = 5000,
221	},
222	[BOARD_APCI3100_16_4] = {
223		.pc_DriverName		= "apci3100-16-4",
224		.i_IorangeBase1		= 256,
225		.i_NbrAiChannel		= 16,
226		.i_NbrAiChannelDiff	= 8,
227		.i_AiChannelList	= 16,
228		.i_NbrAoChannel		= 4,
229		.i_AiMaxdata		= 4095,
230		.i_AoMaxdata		= 4095,
231		.i_NbrTTLChannel	= 24,
232		.b_AvailableConvertUnit	= 6,
233		.ui_MinAcquisitiontimeNs = 10000,
234	},
235	[BOARD_APCI3100_8_4] = {
236		.pc_DriverName		= "apci3100-8-4",
237		.i_IorangeBase1		= 256,
238		.i_NbrAiChannel		= 8,
239		.i_NbrAiChannelDiff	= 4,
240		.i_AiChannelList	= 8,
241		.i_NbrAoChannel		= 4,
242		.i_AiMaxdata		= 4095,
243		.i_AoMaxdata		= 4095,
244		.i_NbrTTLChannel	= 24,
245		.b_AvailableConvertUnit	= 6,
246		.ui_MinAcquisitiontimeNs = 10000,
247	},
248	[BOARD_APCI3106_16_4] = {
249		.pc_DriverName		= "apci3106-16-4",
250		.i_IorangeBase1		= 256,
251		.i_NbrAiChannel		= 16,
252		.i_NbrAiChannelDiff	= 8,
253		.i_AiChannelList	= 16,
254		.i_NbrAoChannel		= 4,
255		.i_AiMaxdata		= 65535,
256		.i_AoMaxdata		= 4095,
257		.i_NbrTTLChannel	= 24,
258		.b_AvailableConvertUnit	= 6,
259		.ui_MinAcquisitiontimeNs = 10000,
260	},
261	[BOARD_APCI3106_8_4] = {
262		.pc_DriverName		= "apci3106-8-4",
263		.i_IorangeBase1		= 256,
264		.i_NbrAiChannel		= 8,
265		.i_NbrAiChannelDiff	= 4,
266		.i_AiChannelList	= 8,
267		.i_NbrAoChannel		= 4,
268		.i_AiMaxdata		= 65535,
269		.i_AoMaxdata		= 4095,
270		.i_NbrTTLChannel	= 24,
271		.b_AvailableConvertUnit	= 6,
272		.ui_MinAcquisitiontimeNs = 10000,
273	},
274	[BOARD_APCI3110_16_4] = {
275		.pc_DriverName		= "apci3110-16-4",
276		.i_IorangeBase1		= 256,
277		.i_NbrAiChannel		= 16,
278		.i_NbrAiChannelDiff	= 8,
279		.i_AiChannelList	= 16,
280		.i_NbrAoChannel		= 4,
281		.i_AiMaxdata		= 4095,
282		.i_AoMaxdata		= 4095,
283		.i_NbrDiChannel		= 4,
284		.i_NbrDoChannel		= 4,
285		.i_NbrTTLChannel	= 24,
286		.b_AvailableConvertUnit	= 6,
287		.ui_MinAcquisitiontimeNs = 5000,
288	},
289	[BOARD_APCI3110_8_4] = {
290		.pc_DriverName		= "apci3110-8-4",
291		.i_IorangeBase1		= 256,
292		.i_NbrAiChannel		= 8,
293		.i_NbrAiChannelDiff	= 4,
294		.i_AiChannelList	= 8,
295		.i_NbrAoChannel		= 4,
296		.i_AiMaxdata		= 4095,
297		.i_AoMaxdata		= 4095,
298		.i_NbrDiChannel		= 4,
299		.i_NbrDoChannel		= 4,
300		.i_NbrTTLChannel	= 24,
301		.b_AvailableConvertUnit	= 6,
302		.ui_MinAcquisitiontimeNs = 5000,
303	},
304	[BOARD_APCI3116_16_4] = {
305		.pc_DriverName		= "apci3116-16-4",
306		.i_IorangeBase1		= 256,
307		.i_NbrAiChannel		= 16,
308		.i_NbrAiChannelDiff	= 8,
309		.i_AiChannelList	= 16,
310		.i_NbrAoChannel		= 4,
311		.i_AiMaxdata		= 65535,
312		.i_AoMaxdata		= 4095,
313		.i_NbrDiChannel		= 4,
314		.i_NbrDoChannel		= 4,
315		.i_NbrTTLChannel	= 24,
316		.b_AvailableConvertUnit	= 6,
317		.ui_MinAcquisitiontimeNs = 5000,
318	},
319	[BOARD_APCI3116_8_4] = {
320		.pc_DriverName		= "apci3116-8-4",
321		.i_IorangeBase1		= 256,
322		.i_NbrAiChannel		= 8,
323		.i_NbrAiChannelDiff	= 4,
324		.i_AiChannelList	= 8,
325		.i_NbrAoChannel		= 4,
326		.i_AiMaxdata		= 65535,
327		.i_AoMaxdata		= 4095,
328		.i_NbrDiChannel		= 4,
329		.i_NbrDoChannel		= 4,
330		.i_NbrTTLChannel	= 24,
331		.b_AvailableConvertUnit	= 6,
332		.ui_MinAcquisitiontimeNs = 5000,
333	},
334	[BOARD_APCI3003] = {
335		.pc_DriverName		= "apci3003",
336		.i_IorangeBase1		= 256,
337		.i_NbrAiChannelDiff	= 4,
338		.i_AiChannelList	= 4,
339		.i_AiMaxdata		= 65535,
340		.i_NbrDiChannel		= 4,
341		.i_NbrDoChannel		= 4,
342		.b_AvailableConvertUnit	= 7,
343		.ui_MinAcquisitiontimeNs = 2500,
344	},
345	[BOARD_APCI3002_16] = {
346		.pc_DriverName		= "apci3002-16",
347		.i_IorangeBase1		= 256,
348		.i_NbrAiChannelDiff	= 16,
349		.i_AiChannelList	= 16,
350		.i_AiMaxdata		= 65535,
351		.i_NbrDiChannel		= 4,
352		.i_NbrDoChannel		= 4,
353		.b_AvailableConvertUnit	= 6,
354		.ui_MinAcquisitiontimeNs = 5000,
355	},
356	[BOARD_APCI3002_8] = {
357		.pc_DriverName		= "apci3002-8",
358		.i_IorangeBase1		= 256,
359		.i_NbrAiChannelDiff	= 8,
360		.i_AiChannelList	= 8,
361		.i_AiMaxdata		= 65535,
362		.i_NbrDiChannel		= 4,
363		.i_NbrDoChannel		= 4,
364		.b_AvailableConvertUnit	= 6,
365		.ui_MinAcquisitiontimeNs = 5000,
366	},
367	[BOARD_APCI3002_4] = {
368		.pc_DriverName		= "apci3002-4",
369		.i_IorangeBase1		= 256,
370		.i_NbrAiChannelDiff	= 4,
371		.i_AiChannelList	= 4,
372		.i_AiMaxdata		= 65535,
373		.i_NbrDiChannel		= 4,
374		.i_NbrDoChannel		= 4,
375		.b_AvailableConvertUnit	= 6,
376		.ui_MinAcquisitiontimeNs = 5000,
377	},
378	[BOARD_APCI3500] = {
379		.pc_DriverName		= "apci3500",
380		.i_IorangeBase1		= 256,
381		.i_NbrAoChannel		= 4,
382		.i_AoMaxdata		= 4095,
383		.i_NbrTTLChannel	= 24,
384	},
385};
386
387struct apci3xxx_private {
388	int iobase;
389	int i_IobaseReserved;
390	void __iomem *dw_AiBase;
391	unsigned char b_AiInitialisation;
392	unsigned int ui_AiNbrofChannels;	/*  how many channels is measured */
393	unsigned int ui_AiReadData[32];
394	unsigned char b_EocEosInterrupt;
395	unsigned int ui_EocEosConversionTime;
396	unsigned char b_EocEosConversionTimeBase;
397	unsigned char b_SingelDiff;
398	struct task_struct *tsk_Current;
399	unsigned int ul_TTLPortConfiguration[10];
400};
401
402#include "addi-data/hwdrv_apci3xxx.c"
403
404static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
405{
406	struct comedi_device *dev = d;
407	struct apci3xxx_private *devpriv = dev->private;
408	unsigned int status;
409	int i;
410
411	/* Test if interrupt occur */
412	status = readl(devpriv->dw_AiBase + 16);
413	if ((status & 0x2) == 0x2) {
414		/* Reset the interrupt */
415		writel(status, devpriv->dw_AiBase + 16);
416
417		/* Test if interrupt enabled */
418		if (devpriv->b_EocEosInterrupt == 1) {
419			/* Read all analog inputs value */
420			for (i = 0; i < devpriv->ui_AiNbrofChannels; i++) {
421				unsigned int val;
422
423				val = readl(devpriv->dw_AiBase + 28);
424				devpriv->ui_AiReadData[i] = val;
425			}
426
427			/* Set the interrupt flag */
428			devpriv->b_EocEosInterrupt = 2;
429
430			/* Send a signal to from kernel to user space */
431			send_sig(SIGIO, devpriv->tsk_Current, 0);
432		}
433	}
434	return IRQ_RETVAL(1);
435}
436
437static int apci3xxx_di_insn_bits(struct comedi_device *dev,
438				 struct comedi_subdevice *s,
439				 struct comedi_insn *insn,
440				 unsigned int *data)
441{
442	struct apci3xxx_private *devpriv = dev->private;
443
444	data[1] = inl(devpriv->iobase + 32) & 0xf;
445
446	return insn->n;
447}
448
449static int apci3xxx_do_insn_bits(struct comedi_device *dev,
450				 struct comedi_subdevice *s,
451				 struct comedi_insn *insn,
452				 unsigned int *data)
453{
454	struct apci3xxx_private *devpriv = dev->private;
455	unsigned int mask = data[0];
456	unsigned int bits = data[1];
457
458	s->state = inl(devpriv->iobase + 48) & 0xf;
459	if (mask) {
460		s->state &= ~mask;
461		s->state |= (bits & mask);
462
463		outl(s->state, devpriv->iobase + 48);
464	}
465
466	data[1] = s->state;
467
468	return insn->n;
469}
470
471static int apci3xxx_reset(struct comedi_device *dev)
472{
473	struct apci3xxx_private *devpriv = dev->private;
474	unsigned int val;
475	int i;
476
477	/* Disable the interrupt */
478	disable_irq(dev->irq);
479
480	/* Reset the interrupt flag */
481	devpriv->b_EocEosInterrupt = 0;
482
483	/* Clear the start command */
484	writel(0, devpriv->dw_AiBase + 8);
485
486	/* Reset the interrupt flags */
487	val = readl(devpriv->dw_AiBase + 16);
488	writel(val, devpriv->dw_AiBase + 16);
489
490	/* clear the EOS */
491	readl(devpriv->dw_AiBase + 20);
492
493	/* Clear the FIFO */
494	for (i = 0; i < 16; i++)
495		val = readl(devpriv->dw_AiBase + 28);
496
497	/* Enable the interrupt */
498	enable_irq(dev->irq);
499
500	return 0;
501}
502
503static int apci3xxx_auto_attach(struct comedi_device *dev,
504				unsigned long context)
505{
506	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
507	const struct apci3xxx_boardinfo *board = NULL;
508	struct apci3xxx_private *devpriv;
509	struct comedi_subdevice *s;
510	int ret, n_subdevices;
511
512	if (context < ARRAY_SIZE(apci3xxx_boardtypes))
513		board = &apci3xxx_boardtypes[context];
514	if (!board)
515		return -ENODEV;
516	dev->board_ptr = board;
517	dev->board_name = board->pc_DriverName;
518
519	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
520	if (!devpriv)
521		return -ENOMEM;
522	dev->private = devpriv;
523
524	ret = comedi_pci_enable(dev);
525	if (ret)
526		return ret;
527
528	/* board has an ADDIDATA_9054 eeprom */
529	dev->iobase = pci_resource_start(pcidev, 2);
530	devpriv->iobase = pci_resource_start(pcidev, 2);
531	devpriv->dw_AiBase = pci_ioremap_bar(pcidev, 3);
532	devpriv->i_IobaseReserved = pci_resource_start(pcidev, 3);
533
534	if (pcidev->irq > 0) {
535		ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
536				  IRQF_SHARED, dev->board_name, dev);
537		if (ret == 0)
538			dev->irq = pcidev->irq;
539	}
540
541	n_subdevices = 7;
542	ret = comedi_alloc_subdevices(dev, n_subdevices);
543	if (ret)
544		return ret;
545
546	/*  Allocate and Initialise AI Subdevice Structures */
547	s = &dev->subdevices[0];
548	if (board->i_NbrAiChannel || board->i_NbrAiChannelDiff) {
549		dev->read_subdev = s;
550		s->type = COMEDI_SUBD_AI;
551		s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND |
552				  SDF_DIFF;
553		if (board->i_NbrAiChannel) {
554			s->n_chan = board->i_NbrAiChannel;
555			devpriv->b_SingelDiff = 0;
556		} else {
557			s->n_chan = board->i_NbrAiChannelDiff;
558			devpriv->b_SingelDiff = 1;
559		}
560		s->maxdata = board->i_AiMaxdata;
561		s->len_chanlist = board->i_AiChannelList;
562		s->range_table = &apci3xxx_ai_range;
563
564		/* Set the initialisation flag */
565		devpriv->b_AiInitialisation = 1;
566
567		s->insn_config = i_APCI3XXX_InsnConfigAnalogInput;
568		s->insn_read = i_APCI3XXX_InsnReadAnalogInput;
569
570	} else {
571		s->type = COMEDI_SUBD_UNUSED;
572	}
573
574	/*  Allocate and Initialise AO Subdevice Structures */
575	s = &dev->subdevices[1];
576	if (board->i_NbrAoChannel) {
577		s->type = COMEDI_SUBD_AO;
578		s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
579		s->n_chan = board->i_NbrAoChannel;
580		s->maxdata = board->i_AoMaxdata;
581		s->range_table = &apci3xxx_ao_range;
582		s->insn_write = i_APCI3XXX_InsnWriteAnalogOutput;
583	} else {
584		s->type = COMEDI_SUBD_UNUSED;
585	}
586	/*  Allocate and Initialise DI Subdevice Structures */
587	s = &dev->subdevices[2];
588	if (board->i_NbrDiChannel) {
589		s->type = COMEDI_SUBD_DI;
590		s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
591		s->n_chan = board->i_NbrDiChannel;
592		s->maxdata = 1;
593		s->range_table = &range_digital;
594		s->io_bits = 0;	/* all bits input */
595		s->insn_bits = apci3xxx_di_insn_bits;
596	} else {
597		s->type = COMEDI_SUBD_UNUSED;
598	}
599	/*  Allocate and Initialise DO Subdevice Structures */
600	s = &dev->subdevices[3];
601	if (board->i_NbrDoChannel) {
602		s->type = COMEDI_SUBD_DO;
603		s->subdev_flags =
604			SDF_READABLE | SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
605		s->n_chan = board->i_NbrDoChannel;
606		s->maxdata = 1;
607		s->range_table = &range_digital;
608		s->io_bits = 0xf;	/* all bits output */
609		s->insn_bits = apci3xxx_do_insn_bits;
610	} else {
611		s->type = COMEDI_SUBD_UNUSED;
612	}
613
614	/*  Allocate and Initialise Timer Subdevice Structures */
615	s = &dev->subdevices[4];
616	s->type = COMEDI_SUBD_UNUSED;
617
618	/*  Allocate and Initialise TTL */
619	s = &dev->subdevices[5];
620	if (board->i_NbrTTLChannel) {
621		s->type = COMEDI_SUBD_TTLIO;
622		s->subdev_flags =
623			SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
624		s->n_chan = board->i_NbrTTLChannel;
625		s->maxdata = 1;
626		s->io_bits = 0;	/* all bits input */
627		s->len_chanlist = board->i_NbrTTLChannel;
628		s->range_table = &range_digital;
629		s->insn_config = i_APCI3XXX_InsnConfigInitTTLIO;
630		s->insn_bits = i_APCI3XXX_InsnBitsTTLIO;
631		s->insn_read = i_APCI3XXX_InsnReadTTLIO;
632		s->insn_write = i_APCI3XXX_InsnWriteTTLIO;
633	} else {
634		s->type = COMEDI_SUBD_UNUSED;
635	}
636
637	/* EEPROM */
638	s = &dev->subdevices[6];
639	s->type = COMEDI_SUBD_UNUSED;
640
641	apci3xxx_reset(dev);
642	return 0;
643}
644
645static void apci3xxx_detach(struct comedi_device *dev)
646{
647	struct apci3xxx_private *devpriv = dev->private;
648
649	if (devpriv) {
650		if (dev->iobase)
651			apci3xxx_reset(dev);
652		if (dev->irq)
653			free_irq(dev->irq, dev);
654		if (devpriv->dw_AiBase)
655			iounmap(devpriv->dw_AiBase);
656	}
657	comedi_pci_disable(dev);
658}
659
660static struct comedi_driver apci3xxx_driver = {
661	.driver_name	= "addi_apci_3xxx",
662	.module		= THIS_MODULE,
663	.auto_attach	= apci3xxx_auto_attach,
664	.detach		= apci3xxx_detach,
665};
666
667static int apci3xxx_pci_probe(struct pci_dev *dev,
668			      const struct pci_device_id *id)
669{
670	return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
671}
672
673static DEFINE_PCI_DEVICE_TABLE(apci3xxx_pci_table) = {
674	{ PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
675	{ PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
676	{ PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
677	{ PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
678	{ PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
679	{ PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
680	{ PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
681	{ PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
682	{ PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
683	{ PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
684	{ PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
685	{ PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
686	{ PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
687	{ PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
688	{ PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
689	{ PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
690	{ PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
691	{ PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
692	{ PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
693	{ PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
694	{ PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
695	{ PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
696	{ PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
697	{ PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
698	{ PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
699	{ 0 }
700};
701MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
702
703static struct pci_driver apci3xxx_pci_driver = {
704	.name		= "addi_apci_3xxx",
705	.id_table	= apci3xxx_pci_table,
706	.probe		= apci3xxx_pci_probe,
707	.remove		= comedi_pci_auto_unconfig,
708};
709module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
710
711MODULE_AUTHOR("Comedi http://www.comedi.org");
712MODULE_DESCRIPTION("Comedi low-level driver");
713MODULE_LICENSE("GPL");
714