icp_multi.c revision 798cdd05e00f2ffb2aa763f5bc2605cd9c08a265
1/*
2    comedi/drivers/icp_multi.c
3
4    COMEDI - Linux Control and Measurement Device Interface
5    Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21*/
22
23/*
24Driver: icp_multi
25Description: Inova ICP_MULTI
26Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27Devices: [Inova] ICP_MULTI (icp_multi)
28Status: works
29
30The driver works for analog input and output and digital input and output.
31It does not work with interrupts or with the counters.  Currently no support
32for DMA.
33
34It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.  Input
36ranges can be individually programmed for each channel.  Voltage or current
37measurement is selected by jumper.
38
39There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
40
4116 x Digital Inputs, 24V
42
438 x Digital Outputs, 24V, 1A
44
454 x 16-bit counters
46
47Options:
48 [0] - PCI bus number - if bus number and slot number are 0,
49			then driver search for first unused card
50 [1] - PCI slot number
51*/
52
53#include <linux/interrupt.h>
54#include "../comedidev.h"
55
56#include <linux/delay.h>
57#include <linux/pci.h>
58
59#include "icp_multi.h"
60
61#define PCI_DEVICE_ID_ICP_MULTI	0x8000
62
63#define ICP_MULTI_ADC_CSR	0	/* R/W: ADC command/status register */
64#define ICP_MULTI_AI		2	/* R:   Analogue input data */
65#define ICP_MULTI_DAC_CSR	4	/* R/W: DAC command/status register */
66#define ICP_MULTI_AO		6	/* R/W: Analogue output data */
67#define ICP_MULTI_DI		8	/* R/W: Digital inouts */
68#define ICP_MULTI_DO		0x0A	/* R/W: Digital outputs */
69#define ICP_MULTI_INT_EN	0x0C	/* R/W: Interrupt enable register */
70#define ICP_MULTI_INT_STAT	0x0E	/* R/W: Interrupt status register */
71#define ICP_MULTI_CNTR0		0x10	/* R/W: Counter 0 */
72#define ICP_MULTI_CNTR1		0x12	/* R/W: counter 1 */
73#define ICP_MULTI_CNTR2		0x14	/* R/W: Counter 2 */
74#define ICP_MULTI_CNTR3		0x16	/* R/W: Counter 3 */
75
76#define ICP_MULTI_SIZE		0x20	/* 32 bytes */
77
78/*  Define bits from ADC command/status register */
79#define	ADC_ST		0x0001	/* Start ADC */
80#define	ADC_BSY		0x0001	/* ADC busy */
81#define ADC_BI		0x0010	/* Bipolar input range 1 = bipolar */
82#define ADC_RA		0x0020	/* Input range 0 = 5V, 1 = 10V */
83#define	ADC_DI		0x0040	/* Differential input mode 1 = differential */
84
85/*  Define bits from DAC command/status register */
86#define	DAC_ST		0x0001	/* Start DAC */
87#define DAC_BSY		0x0001	/* DAC busy */
88#define	DAC_BI		0x0010	/* Bipolar input range 1 = bipolar */
89#define	DAC_RA		0x0020	/* Input range 0 = 5V, 1 = 10V */
90
91/*  Define bits from interrupt enable/status registers */
92#define	ADC_READY	0x0001	/* A/d conversion ready interrupt */
93#define	DAC_READY	0x0002	/* D/a conversion ready interrupt */
94#define	DOUT_ERROR	0x0004	/* Digital output error interrupt */
95#define	DIN_STATUS	0x0008	/* Digital input status change interrupt */
96#define	CIE0		0x0010	/* Counter 0 overrun interrupt */
97#define	CIE1		0x0020	/* Counter 1 overrun interrupt */
98#define	CIE2		0x0040	/* Counter 2 overrun interrupt */
99#define	CIE3		0x0080	/* Counter 3 overrun interrupt */
100
101/*  Useful definitions */
102#define	Status_IRQ	0x00ff	/*  All interrupts */
103
104/*  Define analogue range */
105static const struct comedi_lrange range_analog = { 4, {
106						       UNI_RANGE(5),
107						       UNI_RANGE(10),
108						       BIP_RANGE(5),
109						       BIP_RANGE(10)
110						       }
111};
112
113static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
114
115/*
116==============================================================================
117	Data & Structure declarations
118==============================================================================
119*/
120static unsigned short pci_list_builded;	/*>0 list of card is known */
121
122struct boardtype {
123	const char *name;	/*  driver name */
124	int device_id;
125};
126
127struct icp_multi_private {
128	struct pcilst_struct *card;	/*  pointer to card */
129	char valid;		/*  card is usable */
130	void __iomem *io_addr;		/*  Pointer to mapped io address */
131	resource_size_t phys_iobase;	/*  Physical io address */
132	unsigned int AdcCmdStatus;	/*  ADC Command/Status register */
133	unsigned int DacCmdStatus;	/*  DAC Command/Status register */
134	unsigned int IntEnable;	/*  Interrupt Enable register */
135	unsigned int IntStatus;	/*  Interrupt Status register */
136	unsigned int act_chanlist[32];	/*  list of scaned channel */
137	unsigned char act_chanlist_len;	/*  len of scanlist */
138	unsigned char act_chanlist_pos;	/*  actual position in MUX list */
139	unsigned int *ai_chanlist;	/*  actaul chanlist */
140	short *ai_data;		/*  data buffer */
141	short ao_data[4];	/*  data output buffer */
142	short di_data;		/*  Digital input data */
143	unsigned int do_data;	/*  Remember digital output data */
144};
145
146#define devpriv ((struct icp_multi_private *)dev->private)
147#define this_board ((const struct boardtype *)dev->board_ptr)
148
149/*
150==============================================================================
151
152Name:	setup_channel_list
153
154Description:
155	This function sets the appropriate channel selection,
156	differential input mode and range bits in the ADC Command/
157	Status register.
158
159Parameters:
160	struct comedi_device *dev	Pointer to current service structure
161	struct comedi_subdevice *s	Pointer to current subdevice structure
162	unsigned int *chanlist	Pointer to packed channel list
163	unsigned int n_chan	Number of channels to scan
164
165Returns:Void
166
167==============================================================================
168*/
169static void setup_channel_list(struct comedi_device *dev,
170			       struct comedi_subdevice *s,
171			       unsigned int *chanlist, unsigned int n_chan)
172{
173	unsigned int i, range, chanprog;
174	unsigned int diff;
175
176	devpriv->act_chanlist_len = n_chan;
177	devpriv->act_chanlist_pos = 0;
178
179	for (i = 0; i < n_chan; i++) {
180		/*  Get channel */
181		chanprog = CR_CHAN(chanlist[i]);
182
183		/*  Determine if it is a differential channel (Bit 15  = 1) */
184		if (CR_AREF(chanlist[i]) == AREF_DIFF) {
185			diff = 1;
186			chanprog &= 0x0007;
187		} else {
188			diff = 0;
189			chanprog &= 0x000f;
190		}
191
192		/*  Clear channel, range and input mode bits
193		 *  in A/D command/status register */
194		devpriv->AdcCmdStatus &= 0xf00f;
195
196		/*  Set channel number and differential mode status bit */
197		if (diff) {
198			/*  Set channel number, bits 9-11 & mode, bit 6 */
199			devpriv->AdcCmdStatus |= (chanprog << 9);
200			devpriv->AdcCmdStatus |= ADC_DI;
201		} else
202			/*  Set channel number, bits 8-11 */
203			devpriv->AdcCmdStatus |= (chanprog << 8);
204
205		/*  Get range for current channel */
206		range = range_codes_analog[CR_RANGE(chanlist[i])];
207		/*  Set range. bits 4-5 */
208		devpriv->AdcCmdStatus |= range;
209
210		/* Output channel, range, mode to ICP Multi */
211		writew(devpriv->AdcCmdStatus,
212		       devpriv->io_addr + ICP_MULTI_ADC_CSR);
213	}
214}
215
216/*
217==============================================================================
218
219Name:	icp_multi_insn_read_ai
220
221Description:
222	This function reads a single analogue input.
223
224Parameters:
225	struct comedi_device *dev	Pointer to current device structure
226	struct comedi_subdevice *s	Pointer to current subdevice structure
227	struct comedi_insn *insn	Pointer to current comedi instruction
228	unsigned int *data		Pointer to analogue input data
229
230Returns:int			Nmuber of instructions executed
231
232==============================================================================
233*/
234static int icp_multi_insn_read_ai(struct comedi_device *dev,
235				  struct comedi_subdevice *s,
236				  struct comedi_insn *insn, unsigned int *data)
237{
238	int n, timeout;
239
240	/*  Disable A/D conversion ready interrupt */
241	devpriv->IntEnable &= ~ADC_READY;
242	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
243
244	/*  Clear interrupt status */
245	devpriv->IntStatus |= ADC_READY;
246	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
247
248	/*  Set up appropriate channel, mode and range data, for specified ch */
249	setup_channel_list(dev, s, &insn->chanspec, 1);
250
251	for (n = 0; n < insn->n; n++) {
252		/*  Set start ADC bit */
253		devpriv->AdcCmdStatus |= ADC_ST;
254		writew(devpriv->AdcCmdStatus,
255		       devpriv->io_addr + ICP_MULTI_ADC_CSR);
256		devpriv->AdcCmdStatus &= ~ADC_ST;
257
258		udelay(1);
259
260		/*  Wait for conversion to complete, or get fed up waiting */
261		timeout = 100;
262		while (timeout--) {
263			if (!(readw(devpriv->io_addr +
264				    ICP_MULTI_ADC_CSR) & ADC_BSY))
265				goto conv_finish;
266
267			udelay(1);
268		}
269
270		/*  If we reach here, a timeout has occurred */
271		comedi_error(dev, "A/D insn timeout");
272
273		/*  Disable interrupt */
274		devpriv->IntEnable &= ~ADC_READY;
275		writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
276
277		/*  Clear interrupt status */
278		devpriv->IntStatus |= ADC_READY;
279		writew(devpriv->IntStatus,
280		       devpriv->io_addr + ICP_MULTI_INT_STAT);
281
282		/*  Clear data received */
283		data[n] = 0;
284
285		return -ETIME;
286
287conv_finish:
288		data[n] =
289		    (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
290	}
291
292	/*  Disable interrupt */
293	devpriv->IntEnable &= ~ADC_READY;
294	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
295
296	/*  Clear interrupt status */
297	devpriv->IntStatus |= ADC_READY;
298	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
299
300	return n;
301}
302
303/*
304==============================================================================
305
306Name:	icp_multi_insn_write_ao
307
308Description:
309	This function writes a single analogue output.
310
311Parameters:
312	struct comedi_device *dev	Pointer to current device structure
313	struct comedi_subdevice *s	Pointer to current subdevice structure
314	struct comedi_insn *insn	Pointer to current comedi instruction
315	unsigned int *data		Pointer to analogue output data
316
317Returns:int			Nmuber of instructions executed
318
319==============================================================================
320*/
321static int icp_multi_insn_write_ao(struct comedi_device *dev,
322				   struct comedi_subdevice *s,
323				   struct comedi_insn *insn, unsigned int *data)
324{
325	int n, chan, range, timeout;
326
327	/*  Disable D/A conversion ready interrupt */
328	devpriv->IntEnable &= ~DAC_READY;
329	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
330
331	/*  Clear interrupt status */
332	devpriv->IntStatus |= DAC_READY;
333	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
334
335	/*  Get channel number and range */
336	chan = CR_CHAN(insn->chanspec);
337	range = CR_RANGE(insn->chanspec);
338
339	/*  Set up range and channel data */
340	/*  Bit 4 = 1 : Bipolar */
341	/*  Bit 5 = 0 : 5V */
342	/*  Bit 5 = 1 : 10V */
343	/*  Bits 8-9 : Channel number */
344	devpriv->DacCmdStatus &= 0xfccf;
345	devpriv->DacCmdStatus |= range_codes_analog[range];
346	devpriv->DacCmdStatus |= (chan << 8);
347
348	writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
349
350	for (n = 0; n < insn->n; n++) {
351		/*  Wait for analogue output data register to be
352		 *  ready for new data, or get fed up waiting */
353		timeout = 100;
354		while (timeout--) {
355			if (!(readw(devpriv->io_addr +
356				    ICP_MULTI_DAC_CSR) & DAC_BSY))
357				goto dac_ready;
358
359			udelay(1);
360		}
361
362		/*  If we reach here, a timeout has occurred */
363		comedi_error(dev, "D/A insn timeout");
364
365		/*  Disable interrupt */
366		devpriv->IntEnable &= ~DAC_READY;
367		writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
368
369		/*  Clear interrupt status */
370		devpriv->IntStatus |= DAC_READY;
371		writew(devpriv->IntStatus,
372		       devpriv->io_addr + ICP_MULTI_INT_STAT);
373
374		/*  Clear data received */
375		devpriv->ao_data[chan] = 0;
376
377		return -ETIME;
378
379dac_ready:
380		/*  Write data to analogue output data register */
381		writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
382
383		/*  Set DAC_ST bit to write the data to selected channel */
384		devpriv->DacCmdStatus |= DAC_ST;
385		writew(devpriv->DacCmdStatus,
386		       devpriv->io_addr + ICP_MULTI_DAC_CSR);
387		devpriv->DacCmdStatus &= ~DAC_ST;
388
389		/*  Save analogue output data */
390		devpriv->ao_data[chan] = data[n];
391	}
392
393	return n;
394}
395
396/*
397==============================================================================
398
399Name:	icp_multi_insn_read_ao
400
401Description:
402	This function reads a single analogue output.
403
404Parameters:
405	struct comedi_device *dev	Pointer to current device structure
406	struct comedi_subdevice *s	Pointer to current subdevice structure
407	struct comedi_insn *insn	Pointer to current comedi instruction
408	unsigned int *data		Pointer to analogue output data
409
410Returns:int			Nmuber of instructions executed
411
412==============================================================================
413*/
414static int icp_multi_insn_read_ao(struct comedi_device *dev,
415				  struct comedi_subdevice *s,
416				  struct comedi_insn *insn, unsigned int *data)
417{
418	int n, chan;
419
420	/*  Get channel number */
421	chan = CR_CHAN(insn->chanspec);
422
423	/*  Read analogue outputs */
424	for (n = 0; n < insn->n; n++)
425		data[n] = devpriv->ao_data[chan];
426
427	return n;
428}
429
430/*
431==============================================================================
432
433Name:	icp_multi_insn_bits_di
434
435Description:
436	This function reads the digital inputs.
437
438Parameters:
439	struct comedi_device *dev	Pointer to current device structure
440	struct comedi_subdevice *s	Pointer to current subdevice structure
441	struct comedi_insn *insn	Pointer to current comedi instruction
442	unsigned int *data		Pointer to analogue output data
443
444Returns:int			Nmuber of instructions executed
445
446==============================================================================
447*/
448static int icp_multi_insn_bits_di(struct comedi_device *dev,
449				  struct comedi_subdevice *s,
450				  struct comedi_insn *insn, unsigned int *data)
451{
452	data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
453
454	return insn->n;
455}
456
457/*
458==============================================================================
459
460Name:	icp_multi_insn_bits_do
461
462Description:
463	This function writes the appropriate digital outputs.
464
465Parameters:
466	struct comedi_device *dev	Pointer to current device structure
467	struct comedi_subdevice *s	Pointer to current subdevice structure
468	struct comedi_insn *insn	Pointer to current comedi instruction
469	unsigned int *data		Pointer to analogue output data
470
471Returns:int			Nmuber of instructions executed
472
473==============================================================================
474*/
475static int icp_multi_insn_bits_do(struct comedi_device *dev,
476				  struct comedi_subdevice *s,
477				  struct comedi_insn *insn, unsigned int *data)
478{
479	if (data[0]) {
480		s->state &= ~data[0];
481		s->state |= (data[0] & data[1]);
482
483		printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
484
485		writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
486	}
487
488	data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
489
490	return insn->n;
491}
492
493/*
494==============================================================================
495
496Name:	icp_multi_insn_read_ctr
497
498Description:
499	This function reads the specified counter.
500
501Parameters:
502	struct comedi_device *dev	Pointer to current device structure
503	struct comedi_subdevice *s	Pointer to current subdevice structure
504	struct comedi_insn *insn	Pointer to current comedi instruction
505	unsigned int *data		Pointer to counter data
506
507Returns:int			Nmuber of instructions executed
508
509==============================================================================
510*/
511static int icp_multi_insn_read_ctr(struct comedi_device *dev,
512				   struct comedi_subdevice *s,
513				   struct comedi_insn *insn, unsigned int *data)
514{
515	return 0;
516}
517
518/*
519==============================================================================
520
521Name:	icp_multi_insn_write_ctr
522
523Description:
524	This function write to the specified counter.
525
526Parameters:
527	struct comedi_device *dev	Pointer to current device structure
528	struct comedi_subdevice *s	Pointer to current subdevice structure
529	struct comedi_insn *insn	Pointer to current comedi instruction
530	unsigned int *data		Pointer to counter data
531
532Returns:int			Nmuber of instructions executed
533
534==============================================================================
535*/
536static int icp_multi_insn_write_ctr(struct comedi_device *dev,
537				    struct comedi_subdevice *s,
538				    struct comedi_insn *insn,
539				    unsigned int *data)
540{
541	return 0;
542}
543
544/*
545==============================================================================
546
547Name:	interrupt_service_icp_multi
548
549Description:
550	This function is the interrupt service routine for all
551	interrupts generated by the icp multi board.
552
553Parameters:
554	int irq
555	void *d			Pointer to current device
556
557==============================================================================
558*/
559static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
560{
561	struct comedi_device *dev = d;
562	int int_no;
563
564	/*  Is this interrupt from our board? */
565	int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
566	if (!int_no)
567		/*  No, exit */
568		return IRQ_NONE;
569
570	/*  Determine which interrupt is active & handle it */
571	switch (int_no) {
572	case ADC_READY:
573		break;
574	case DAC_READY:
575		break;
576	case DOUT_ERROR:
577		break;
578	case DIN_STATUS:
579		break;
580	case CIE0:
581		break;
582	case CIE1:
583		break;
584	case CIE2:
585		break;
586	case CIE3:
587		break;
588	default:
589		break;
590
591	}
592
593	return IRQ_HANDLED;
594}
595
596#if 0
597/*
598==============================================================================
599
600Name:	check_channel_list
601
602Description:
603	This function checks if the channel list, provided by user
604	is built correctly
605
606Parameters:
607	struct comedi_device *dev	Pointer to current service structure
608	struct comedi_subdevice *s	Pointer to current subdevice structure
609	unsigned int *chanlist	Pointer to packed channel list
610	unsigned int n_chan	Number of channels to scan
611
612Returns:int 0 = failure
613	    1 = success
614
615==============================================================================
616*/
617static int check_channel_list(struct comedi_device *dev,
618			      struct comedi_subdevice *s,
619			      unsigned int *chanlist, unsigned int n_chan)
620{
621	unsigned int i;
622
623	/*  Check that we at least have one channel to check */
624	if (n_chan < 1) {
625		comedi_error(dev, "range/channel list is empty!");
626		return 0;
627	}
628	/*  Check all channels */
629	for (i = 0; i < n_chan; i++) {
630		/*  Check that channel number is < maximum */
631		if (CR_AREF(chanlist[i]) == AREF_DIFF) {
632			if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
633				comedi_error(dev,
634					     "Incorrect differential ai ch-nr");
635				return 0;
636			}
637		} else {
638			if (CR_CHAN(chanlist[i]) > s->n_chan) {
639				comedi_error(dev,
640					     "Incorrect ai channel number");
641				return 0;
642			}
643		}
644	}
645	return 1;
646}
647#endif
648
649/*
650==============================================================================
651
652Name:	icp_multi_reset
653
654Description:
655	This function resets the icp multi device to a 'safe' state
656
657Parameters:
658	struct comedi_device *dev	Pointer to current service structure
659
660Returns:int	0 = success
661
662==============================================================================
663*/
664static int icp_multi_reset(struct comedi_device *dev)
665{
666	unsigned int i;
667
668	/*  Clear INT enables and requests */
669	writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
670	writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
671
672	/* Set DACs to 0..5V range and 0V output */
673	for (i = 0; i < 4; i++) {
674		devpriv->DacCmdStatus &= 0xfcce;
675
676		/*  Set channel number */
677		devpriv->DacCmdStatus |= (i << 8);
678
679		/*  Output 0V */
680		writew(0, devpriv->io_addr + ICP_MULTI_AO);
681
682		/*  Set start conversion bit */
683		devpriv->DacCmdStatus |= DAC_ST;
684
685		/*  Output to command / status register */
686		writew(devpriv->DacCmdStatus,
687			devpriv->io_addr + ICP_MULTI_DAC_CSR);
688
689		/*  Delay to allow DAC time to recover */
690		udelay(1);
691	}
692
693	/* Digital outputs to 0 */
694	writew(0, devpriv->io_addr + ICP_MULTI_DO);
695
696	return 0;
697}
698
699static int icp_multi_attach(struct comedi_device *dev,
700			    struct comedi_devconfig *it)
701{
702	struct comedi_subdevice *s;
703	int ret;
704	unsigned int irq;
705	struct pcilst_struct *card = NULL;
706	resource_size_t io_addr[5], iobase;
707	unsigned char pci_bus, pci_slot, pci_func;
708
709	ret = alloc_private(dev, sizeof(struct icp_multi_private));
710	if (ret < 0)
711		return ret;
712
713	/*  Initialise list of PCI cards in system, if not already done so */
714	if (pci_list_builded++ == 0)
715		pci_card_list_init(PCI_VENDOR_ID_ICP, 0);
716
717	card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
718					 this_board->device_id, it->options[0],
719					 it->options[1]);
720
721	if (card == NULL)
722		return -EIO;
723
724	devpriv->card = card;
725
726	if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
727			   &irq)) < 0)
728		return -EIO;
729
730	iobase = io_addr[2];
731	devpriv->phys_iobase = iobase;
732	devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
733	if (devpriv->io_addr == NULL)
734		return -ENOMEM;
735
736	dev->board_name = this_board->name;
737
738	ret = comedi_alloc_subdevices(dev, 5);
739	if (ret)
740		return ret;
741
742	icp_multi_reset(dev);
743
744	if (irq) {
745		if (request_irq(irq, interrupt_service_icp_multi,
746				IRQF_SHARED, "Inova Icp Multi", dev)) {
747			irq = 0;	/* Can't use IRQ */
748		}
749	}
750
751	dev->irq = irq;
752
753	s = &dev->subdevices[0];
754	dev->read_subdev = s;
755	s->type = COMEDI_SUBD_AI;
756	s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
757	s->n_chan = 16;
758	s->maxdata = 0x0fff;
759	s->len_chanlist = 16;
760	s->range_table = &range_analog;
761	s->insn_read = icp_multi_insn_read_ai;
762
763	s = &dev->subdevices[1];
764	s->type = COMEDI_SUBD_AO;
765	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
766	s->n_chan = 4;
767	s->maxdata = 0x0fff;
768	s->len_chanlist = 4;
769	s->range_table = &range_analog;
770	s->insn_write = icp_multi_insn_write_ao;
771	s->insn_read = icp_multi_insn_read_ao;
772
773	s = &dev->subdevices[2];
774	s->type = COMEDI_SUBD_DI;
775	s->subdev_flags = SDF_READABLE;
776	s->n_chan = 16;
777	s->maxdata = 1;
778	s->len_chanlist = 16;
779	s->range_table = &range_digital;
780	s->io_bits = 0;
781	s->insn_bits = icp_multi_insn_bits_di;
782
783	s = &dev->subdevices[3];
784	s->type = COMEDI_SUBD_DO;
785	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
786	s->n_chan = 8;
787	s->maxdata = 1;
788	s->len_chanlist = 8;
789	s->range_table = &range_digital;
790	s->io_bits = 0xff;
791	s->state = 0;
792	s->insn_bits = icp_multi_insn_bits_do;
793
794	s = &dev->subdevices[4];
795	s->type = COMEDI_SUBD_COUNTER;
796	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
797	s->n_chan = 4;
798	s->maxdata = 0xffff;
799	s->len_chanlist = 4;
800	s->state = 0;
801	s->insn_read = icp_multi_insn_read_ctr;
802	s->insn_write = icp_multi_insn_write_ctr;
803
804	devpriv->valid = 1;
805
806	dev_info(dev->class_dev, "%s attached, irq %sabled\n",
807		dev->board_name, dev->irq ? "en" : "dis");
808
809	return 0;
810}
811
812static void icp_multi_detach(struct comedi_device *dev)
813{
814	if (dev->private)
815		if (devpriv->valid)
816			icp_multi_reset(dev);
817	if (dev->irq)
818		free_irq(dev->irq, dev);
819	if (dev->private && devpriv->io_addr)
820		iounmap(devpriv->io_addr);
821	if (dev->private && devpriv->card)
822		pci_card_free(devpriv->card);
823	if (--pci_list_builded == 0)
824		pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
825}
826
827static const struct boardtype boardtypes[] = {
828	{
829		.name		= "icp_multi",
830		.device_id	= PCI_DEVICE_ID_ICP_MULTI,
831	},
832};
833
834static struct comedi_driver icp_multi_driver = {
835	.driver_name	= "icp_multi",
836	.module		= THIS_MODULE,
837	.attach		= icp_multi_attach,
838	.detach		= icp_multi_detach,
839	.num_names	= ARRAY_SIZE(boardtypes),
840	.board_name	= &boardtypes[0].name,
841	.offset		= sizeof(struct boardtype),
842};
843
844static int __devinit icp_multi_pci_probe(struct pci_dev *dev,
845					   const struct pci_device_id *ent)
846{
847	return comedi_pci_auto_config(dev, &icp_multi_driver);
848}
849
850static void __devexit icp_multi_pci_remove(struct pci_dev *dev)
851{
852	comedi_pci_auto_unconfig(dev);
853}
854
855static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
856	{ PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
857	{ 0 }
858};
859MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
860
861static struct pci_driver icp_multi_pci_driver = {
862	.name		= "icp_multi",
863	.id_table	= icp_multi_pci_table,
864	.probe		= icp_multi_pci_probe,
865	.remove		= __devexit_p(icp_multi_pci_remove),
866};
867module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
868
869MODULE_AUTHOR("Comedi http://www.comedi.org");
870MODULE_DESCRIPTION("Comedi low-level driver");
871MODULE_LICENSE("GPL");
872