icp_multi.c revision 932b3ee7e3ebdf3f6362aee7de52b8b4e9c4ff6a
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 DEVICE_ID	0x8000	/* Device ID */
62
63#define ICP_MULTI_EXTDEBUG
64
65/*  Hardware types of the cards */
66#define TYPE_ICP_MULTI	0
67
68#define IORANGE_ICP_MULTI 	32
69
70#define ICP_MULTI_ADC_CSR	0	/* R/W: ADC command/status register */
71#define ICP_MULTI_AI		2	/* R:   Analogue input data */
72#define ICP_MULTI_DAC_CSR	4	/* R/W: DAC command/status register */
73#define ICP_MULTI_AO		6	/* R/W: Analogue output data */
74#define ICP_MULTI_DI		8	/* R/W: Digital inouts */
75#define ICP_MULTI_DO		0x0A	/* R/W: Digital outputs */
76#define ICP_MULTI_INT_EN	0x0C	/* R/W: Interrupt enable register */
77#define ICP_MULTI_INT_STAT	0x0E	/* R/W: Interrupt status register */
78#define ICP_MULTI_CNTR0		0x10	/* R/W: Counter 0 */
79#define ICP_MULTI_CNTR1		0x12	/* R/W: counter 1 */
80#define ICP_MULTI_CNTR2		0x14	/* R/W: Counter 2 */
81#define ICP_MULTI_CNTR3		0x16	/* R/W: Counter 3 */
82
83#define ICP_MULTI_SIZE		0x20	/* 32 bytes */
84
85/*  Define bits from ADC command/status register */
86#define	ADC_ST		0x0001	/* Start ADC */
87#define	ADC_BSY		0x0001	/* ADC busy */
88#define ADC_BI		0x0010	/* Bipolar input range 1 = bipolar */
89#define ADC_RA		0x0020	/* Input range 0 = 5V, 1 = 10V */
90#define	ADC_DI		0x0040	/* Differential input mode 1 = differential */
91
92/*  Define bits from DAC command/status register */
93#define	DAC_ST		0x0001	/* Start DAC */
94#define DAC_BSY		0x0001	/* DAC busy */
95#define	DAC_BI		0x0010	/* Bipolar input range 1 = bipolar */
96#define	DAC_RA		0x0020	/* Input range 0 = 5V, 1 = 10V */
97
98/*  Define bits from interrupt enable/status registers */
99#define	ADC_READY	0x0001	/* A/d conversion ready interrupt */
100#define	DAC_READY	0x0002	/* D/a conversion ready interrupt */
101#define	DOUT_ERROR	0x0004	/* Digital output error interrupt */
102#define	DIN_STATUS	0x0008	/* Digital input status change interrupt */
103#define	CIE0		0x0010	/* Counter 0 overrun interrupt */
104#define	CIE1		0x0020	/* Counter 1 overrun interrupt */
105#define	CIE2		0x0040	/* Counter 2 overrun interrupt */
106#define	CIE3		0x0080	/* Counter 3 overrun interrupt */
107
108/*  Useful definitions */
109#define	Status_IRQ	0x00ff	/*  All interrupts */
110
111/*  Define analogue range */
112static const struct comedi_lrange range_analog = { 4, {
113						       UNI_RANGE(5),
114						       UNI_RANGE(10),
115						       BIP_RANGE(5),
116						       BIP_RANGE(10)
117						       }
118};
119
120static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
121
122/*
123==============================================================================
124	Forward declarations
125==============================================================================
126*/
127static int icp_multi_attach(struct comedi_device *dev,
128			    struct comedi_devconfig *it);
129static int icp_multi_detach(struct comedi_device *dev);
130
131/*
132==============================================================================
133	Data & Structure declarations
134==============================================================================
135*/
136static unsigned short pci_list_builded;	/*>0 list of card is known */
137
138struct boardtype {
139	const char *name;	/*  driver name */
140	int device_id;
141	int iorange;		/*  I/O range len */
142	char have_irq;		/*  1=card support IRQ */
143	char cardtype;		/*  0=ICP Multi */
144	int n_aichan;		/*  num of A/D chans */
145	int n_aichand;		/*  num of A/D chans in diff mode */
146	int n_aochan;		/*  num of D/A chans */
147	int n_dichan;		/*  num of DI chans */
148	int n_dochan;		/*  num of DO chans */
149	int n_ctrs;		/*  num of counters */
150	int ai_maxdata;		/*  resolution of A/D */
151	int ao_maxdata;		/*  resolution of D/A */
152	const struct comedi_lrange *rangelist_ai;	/*  rangelist for A/D */
153	const char *rangecode;	/*  range codes for programming */
154	const struct comedi_lrange *rangelist_ao;	/*  rangelist for D/A */
155};
156
157static const struct boardtype boardtypes[] = {
158	{"icp_multi",		/*  Driver name */
159	 DEVICE_ID,		/*  PCI device ID */
160	 IORANGE_ICP_MULTI,	/*  I/O range length */
161	 1,			/*  1=Card supports interrupts */
162	 TYPE_ICP_MULTI,	/*  Card type = ICP MULTI */
163	 16,			/*  Num of A/D channels */
164	 8,			/*  Num of A/D channels in diff mode */
165	 4,			/*  Num of D/A channels */
166	 16,			/*  Num of digital inputs */
167	 8,			/*  Num of digital outputs */
168	 4,			/*  Num of counters */
169	 0x0fff,		/*  Resolution of A/D */
170	 0x0fff,		/*  Resolution of D/A */
171	 &range_analog,		/*  Rangelist for A/D */
172	 range_codes_analog,	/*  Range codes for programming */
173	 &range_analog},	/*  Rangelist for D/A */
174};
175
176#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
177
178static struct comedi_driver driver_icp_multi = {
179driver_name: "icp_multi",
180module : THIS_MODULE,
181attach : icp_multi_attach,
182detach : icp_multi_detach,
183num_names : n_boardtypes,
184board_name : &boardtypes[0].name,
185offset : sizeof(struct boardtype),
186};
187
188COMEDI_INITCLEANUP(driver_icp_multi);
189
190struct icp_multi_private {
191	struct pcilst_struct *card;	/*  pointer to card */
192	char valid;		/*  card is usable */
193	void *io_addr;		/*  Pointer to mapped io address */
194	resource_size_t phys_iobase;	/*  Physical io address */
195	unsigned int AdcCmdStatus;	/*  ADC Command/Status register */
196	unsigned int DacCmdStatus;	/*  DAC Command/Status register */
197	unsigned int IntEnable;	/*  Interrupt Enable register */
198	unsigned int IntStatus;	/*  Interrupt Status register */
199	unsigned int act_chanlist[32];	/*  list of scaned channel */
200	unsigned char act_chanlist_len;	/*  len of scanlist */
201	unsigned char act_chanlist_pos;	/*  actual position in MUX list */
202	unsigned int *ai_chanlist;	/*  actaul chanlist */
203	short *ai_data;		/*  data buffer */
204	short ao_data[4];	/*  data output buffer */
205	short di_data;		/*  Digital input data */
206	unsigned int do_data;	/*  Remember digital output data */
207};
208
209#define devpriv ((struct icp_multi_private *)dev->private)
210#define this_board ((const struct boardtype *)dev->board_ptr)
211
212/*
213==============================================================================
214	More forward declarations
215==============================================================================
216*/
217
218#if 0
219static int check_channel_list(struct comedi_device *dev,
220			      struct comedi_subdevice *s,
221			      unsigned int *chanlist, unsigned int n_chan);
222#endif
223static void setup_channel_list(struct comedi_device *dev,
224			       struct comedi_subdevice *s,
225			       unsigned int *chanlist, unsigned int n_chan);
226static int icp_multi_reset(struct comedi_device *dev);
227
228/*
229==============================================================================
230	Functions
231==============================================================================
232*/
233
234/*
235==============================================================================
236
237Name:	icp_multi_insn_read_ai
238
239Description:
240	This function reads a single analogue input.
241
242Parameters:
243	struct comedi_device *dev	Pointer to current device structure
244	struct comedi_subdevice *s	Pointer to current subdevice structure
245	struct comedi_insn *insn	Pointer to current comedi instruction
246	unsigned int *data		Pointer to analogue input data
247
248Returns:int			Nmuber of instructions executed
249
250==============================================================================
251*/
252static int icp_multi_insn_read_ai(struct comedi_device *dev,
253				  struct comedi_subdevice *s,
254				  struct comedi_insn *insn, unsigned int *data)
255{
256	int n, timeout;
257
258#ifdef ICP_MULTI_EXTDEBUG
259	printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
260#endif
261	/*  Disable A/D conversion ready interrupt */
262	devpriv->IntEnable &= ~ADC_READY;
263	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
264
265	/*  Clear interrupt status */
266	devpriv->IntStatus |= ADC_READY;
267	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
268
269	/*  Set up appropriate channel, mode and range data, for specified ch */
270	setup_channel_list(dev, s, &insn->chanspec, 1);
271
272#ifdef ICP_MULTI_EXTDEBUG
273	printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n",
274	       readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
275	       devpriv->io_addr + ICP_MULTI_ADC_CSR);
276#endif
277
278	for (n = 0; n < insn->n; n++) {
279		/*  Set start ADC bit */
280		devpriv->AdcCmdStatus |= ADC_ST;
281		writew(devpriv->AdcCmdStatus,
282		       devpriv->io_addr + ICP_MULTI_ADC_CSR);
283		devpriv->AdcCmdStatus &= ~ADC_ST;
284
285#ifdef ICP_MULTI_EXTDEBUG
286		printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n,
287		       readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
288#endif
289
290		udelay(1);
291
292#ifdef ICP_MULTI_EXTDEBUG
293		printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n,
294		       readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
295#endif
296
297		/*  Wait for conversion to complete, or get fed up waiting */
298		timeout = 100;
299		while (timeout--) {
300			if (!(readw(devpriv->io_addr +
301				    ICP_MULTI_ADC_CSR) & ADC_BSY))
302				goto conv_finish;
303
304#ifdef ICP_MULTI_EXTDEBUG
305			if (!(timeout % 10))
306				printk(KERN_DEBUG
307				       "icp multi D n=%d tm=%d ST=%4x\n", n,
308				       timeout,
309				       readw(devpriv->io_addr +
310					     ICP_MULTI_ADC_CSR));
311#endif
312
313			udelay(1);
314		}
315
316		/*  If we reach here, a timeout has occurred */
317		comedi_error(dev, "A/D insn timeout");
318
319		/*  Disable interrupt */
320		devpriv->IntEnable &= ~ADC_READY;
321		writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
322
323		/*  Clear interrupt status */
324		devpriv->IntStatus |= ADC_READY;
325		writew(devpriv->IntStatus,
326		       devpriv->io_addr + ICP_MULTI_INT_STAT);
327
328		/*  Clear data received */
329		data[n] = 0;
330
331#ifdef ICP_MULTI_EXTDEBUG
332		printk(KERN_DEBUG
333		      "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
334		      n);
335#endif
336		return -ETIME;
337
338conv_finish:
339		data[n] =
340		    (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
341	}
342
343	/*  Disable interrupt */
344	devpriv->IntEnable &= ~ADC_READY;
345	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
346
347	/*  Clear interrupt status */
348	devpriv->IntStatus |= ADC_READY;
349	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
350
351#ifdef ICP_MULTI_EXTDEBUG
352	printk(KERN_DEBUG
353	       "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
354#endif
355	return n;
356}
357
358/*
359==============================================================================
360
361Name:	icp_multi_insn_write_ao
362
363Description:
364	This function writes a single analogue output.
365
366Parameters:
367	struct comedi_device *dev	Pointer to current device structure
368	struct comedi_subdevice *s	Pointer to current subdevice structure
369	struct comedi_insn *insn	Pointer to current comedi instruction
370	unsigned int *data		Pointer to analogue output data
371
372Returns:int			Nmuber of instructions executed
373
374==============================================================================
375*/
376static int icp_multi_insn_write_ao(struct comedi_device *dev,
377				   struct comedi_subdevice *s,
378				   struct comedi_insn *insn, unsigned int *data)
379{
380	int n, chan, range, timeout;
381
382#ifdef ICP_MULTI_EXTDEBUG
383	printk(KERN_DEBUG
384	       "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
385#endif
386	/*  Disable D/A conversion ready interrupt */
387	devpriv->IntEnable &= ~DAC_READY;
388	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
389
390	/*  Clear interrupt status */
391	devpriv->IntStatus |= DAC_READY;
392	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
393
394	/*  Get channel number and range */
395	chan = CR_CHAN(insn->chanspec);
396	range = CR_RANGE(insn->chanspec);
397
398	/*  Set up range and channel data */
399	/*  Bit 4 = 1 : Bipolar */
400	/*  Bit 5 = 0 : 5V */
401	/*  Bit 5 = 1 : 10V */
402	/*  Bits 8-9 : Channel number */
403	devpriv->DacCmdStatus &= 0xfccf;
404	devpriv->DacCmdStatus |= this_board->rangecode[range];
405	devpriv->DacCmdStatus |= (chan << 8);
406
407	writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
408
409	for (n = 0; n < insn->n; n++) {
410		/*  Wait for analogue output data register to be
411		 *  ready for new data, or get fed up waiting */
412		timeout = 100;
413		while (timeout--) {
414			if (!(readw(devpriv->io_addr +
415				    ICP_MULTI_DAC_CSR) & DAC_BSY))
416				goto dac_ready;
417
418#ifdef ICP_MULTI_EXTDEBUG
419			if (!(timeout % 10))
420				printk(KERN_DEBUG
421				       "icp multi A n=%d tm=%d ST=%4x\n", n,
422				       timeout,
423				       readw(devpriv->io_addr +
424					     ICP_MULTI_DAC_CSR));
425#endif
426
427			udelay(1);
428		}
429
430		/*  If we reach here, a timeout has occurred */
431		comedi_error(dev, "D/A insn timeout");
432
433		/*  Disable interrupt */
434		devpriv->IntEnable &= ~DAC_READY;
435		writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
436
437		/*  Clear interrupt status */
438		devpriv->IntStatus |= DAC_READY;
439		writew(devpriv->IntStatus,
440		       devpriv->io_addr + ICP_MULTI_INT_STAT);
441
442		/*  Clear data received */
443		devpriv->ao_data[chan] = 0;
444
445#ifdef ICP_MULTI_EXTDEBUG
446		printk(KERN_DEBUG
447		     "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
448		     n);
449#endif
450		return -ETIME;
451
452dac_ready:
453		/*  Write data to analogue output data register */
454		writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
455
456		/*  Set DAC_ST bit to write the data to selected channel */
457		devpriv->DacCmdStatus |= DAC_ST;
458		writew(devpriv->DacCmdStatus,
459		       devpriv->io_addr + ICP_MULTI_DAC_CSR);
460		devpriv->DacCmdStatus &= ~DAC_ST;
461
462		/*  Save analogue output data */
463		devpriv->ao_data[chan] = data[n];
464	}
465
466#ifdef ICP_MULTI_EXTDEBUG
467	printk(KERN_DEBUG
468	       "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
469#endif
470	return n;
471}
472
473/*
474==============================================================================
475
476Name:	icp_multi_insn_read_ao
477
478Description:
479	This function reads a single analogue output.
480
481Parameters:
482	struct comedi_device *dev	Pointer to current device structure
483	struct comedi_subdevice *s	Pointer to current subdevice structure
484	struct comedi_insn *insn	Pointer to current comedi instruction
485	unsigned int *data		Pointer to analogue output data
486
487Returns:int			Nmuber of instructions executed
488
489==============================================================================
490*/
491static int icp_multi_insn_read_ao(struct comedi_device *dev,
492				  struct comedi_subdevice *s,
493				  struct comedi_insn *insn, unsigned int *data)
494{
495	int n, chan;
496
497	/*  Get channel number */
498	chan = CR_CHAN(insn->chanspec);
499
500	/*  Read analogue outputs */
501	for (n = 0; n < insn->n; n++)
502		data[n] = devpriv->ao_data[chan];
503
504	return n;
505}
506
507/*
508==============================================================================
509
510Name:	icp_multi_insn_bits_di
511
512Description:
513	This function reads the digital inputs.
514
515Parameters:
516	struct comedi_device *dev	Pointer to current device structure
517	struct comedi_subdevice *s	Pointer to current subdevice structure
518	struct comedi_insn *insn	Pointer to current comedi instruction
519	unsigned int *data		Pointer to analogue output data
520
521Returns:int			Nmuber of instructions executed
522
523==============================================================================
524*/
525static int icp_multi_insn_bits_di(struct comedi_device *dev,
526				  struct comedi_subdevice *s,
527				  struct comedi_insn *insn, unsigned int *data)
528{
529	data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
530
531	return 2;
532}
533
534/*
535==============================================================================
536
537Name:	icp_multi_insn_bits_do
538
539Description:
540	This function writes the appropriate digital outputs.
541
542Parameters:
543	struct comedi_device *dev	Pointer to current device structure
544	struct comedi_subdevice *s	Pointer to current subdevice structure
545	struct comedi_insn *insn	Pointer to current comedi instruction
546	unsigned int *data		Pointer to analogue output data
547
548Returns:int			Nmuber of instructions executed
549
550==============================================================================
551*/
552static int icp_multi_insn_bits_do(struct comedi_device *dev,
553				  struct comedi_subdevice *s,
554				  struct comedi_insn *insn, unsigned int *data)
555{
556#ifdef ICP_MULTI_EXTDEBUG
557	printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
558#endif
559
560	if (data[0]) {
561		s->state &= ~data[0];
562		s->state |= (data[0] & data[1]);
563
564		printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
565
566		writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
567	}
568
569	data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
570
571#ifdef ICP_MULTI_EXTDEBUG
572	printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
573#endif
574	return 2;
575}
576
577/*
578==============================================================================
579
580Name:	icp_multi_insn_read_ctr
581
582Description:
583	This function reads the specified counter.
584
585Parameters:
586	struct comedi_device *dev	Pointer to current device structure
587	struct comedi_subdevice *s	Pointer to current subdevice structure
588	struct comedi_insn *insn	Pointer to current comedi instruction
589	unsigned int *data		Pointer to counter data
590
591Returns:int			Nmuber of instructions executed
592
593==============================================================================
594*/
595static int icp_multi_insn_read_ctr(struct comedi_device *dev,
596				   struct comedi_subdevice *s,
597				   struct comedi_insn *insn, unsigned int *data)
598{
599	return 0;
600}
601
602/*
603==============================================================================
604
605Name:	icp_multi_insn_write_ctr
606
607Description:
608	This function write to the specified counter.
609
610Parameters:
611	struct comedi_device *dev	Pointer to current device structure
612	struct comedi_subdevice *s	Pointer to current subdevice structure
613	struct comedi_insn *insn	Pointer to current comedi instruction
614	unsigned int *data		Pointer to counter data
615
616Returns:int			Nmuber of instructions executed
617
618==============================================================================
619*/
620static int icp_multi_insn_write_ctr(struct comedi_device *dev,
621				    struct comedi_subdevice *s,
622				    struct comedi_insn *insn,
623				    unsigned int *data)
624{
625	return 0;
626}
627
628/*
629==============================================================================
630
631Name:	interrupt_service_icp_multi
632
633Description:
634	This function is the interrupt service routine for all
635	interrupts generated by the icp multi board.
636
637Parameters:
638	int irq
639	void *d			Pointer to current device
640
641==============================================================================
642*/
643static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
644{
645	struct comedi_device *dev = d;
646	int int_no;
647
648#ifdef ICP_MULTI_EXTDEBUG
649	printk(KERN_DEBUG
650	       "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
651	       irq);
652#endif
653
654	/*  Is this interrupt from our board? */
655	int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
656	if (!int_no)
657		/*  No, exit */
658		return IRQ_NONE;
659
660#ifdef ICP_MULTI_EXTDEBUG
661	printk(KERN_DEBUG
662	       "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
663	       readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
664#endif
665
666	/*  Determine which interrupt is active & handle it */
667	switch (int_no) {
668	case ADC_READY:
669		break;
670	case DAC_READY:
671		break;
672	case DOUT_ERROR:
673		break;
674	case DIN_STATUS:
675		break;
676	case CIE0:
677		break;
678	case CIE1:
679		break;
680	case CIE2:
681		break;
682	case CIE3:
683		break;
684	default:
685		break;
686
687	}
688
689#ifdef ICP_MULTI_EXTDEBUG
690	printk(KERN_DEBUG
691	       "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
692#endif
693	return IRQ_HANDLED;
694}
695
696#if 0
697/*
698==============================================================================
699
700Name:	check_channel_list
701
702Description:
703	This function checks if the channel list, provided by user
704	is built correctly
705
706Parameters:
707	struct comedi_device *dev	Pointer to current sevice structure
708	struct comedi_subdevice *s	Pointer to current subdevice structure
709	unsigned int *chanlist	Pointer to packed channel list
710	unsigned int n_chan	Number of channels to scan
711
712Returns:int 0 = failure
713	    1 = success
714
715==============================================================================
716*/
717static int check_channel_list(struct comedi_device *dev,
718			      struct comedi_subdevice *s,
719			      unsigned int *chanlist, unsigned int n_chan)
720{
721	unsigned int i;
722
723#ifdef ICP_MULTI_EXTDEBUG
724	printk(KERN_DEBUG
725	       "icp multi EDBG:  check_channel_list(...,%d)\n", n_chan);
726#endif
727	/*  Check that we at least have one channel to check */
728	if (n_chan < 1) {
729		comedi_error(dev, "range/channel list is empty!");
730		return 0;
731	}
732	/*  Check all channels */
733	for (i = 0; i < n_chan; i++) {
734		/*  Check that channel number is < maximum */
735		if (CR_AREF(chanlist[i]) == AREF_DIFF) {
736			if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
737				comedi_error(dev,
738					     "Incorrect differential ai ch-nr");
739				return 0;
740			}
741		} else {
742			if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
743				comedi_error(dev,
744					     "Incorrect ai channel number");
745				return 0;
746			}
747		}
748	}
749	return 1;
750}
751#endif
752
753/*
754==============================================================================
755
756Name:	setup_channel_list
757
758Description:
759	This function sets the appropriate channel selection,
760	differential input mode and range bits in the ADC Command/
761	Status register.
762
763Parameters:
764	struct comedi_device *dev	Pointer to current sevice structure
765	struct comedi_subdevice *s	Pointer to current subdevice structure
766	unsigned int *chanlist	Pointer to packed channel list
767	unsigned int n_chan	Number of channels to scan
768
769Returns:Void
770
771==============================================================================
772*/
773static void setup_channel_list(struct comedi_device *dev,
774			       struct comedi_subdevice *s,
775			       unsigned int *chanlist, unsigned int n_chan)
776{
777	unsigned int i, range, chanprog;
778	unsigned int diff;
779
780#ifdef ICP_MULTI_EXTDEBUG
781	printk(KERN_DEBUG
782	       "icp multi EDBG:  setup_channel_list(...,%d)\n", n_chan);
783#endif
784	devpriv->act_chanlist_len = n_chan;
785	devpriv->act_chanlist_pos = 0;
786
787	for (i = 0; i < n_chan; i++) {
788		/*  Get channel */
789		chanprog = CR_CHAN(chanlist[i]);
790
791		/*  Determine if it is a differential channel (Bit 15  = 1) */
792		if (CR_AREF(chanlist[i]) == AREF_DIFF) {
793			diff = 1;
794			chanprog &= 0x0007;
795		} else {
796			diff = 0;
797			chanprog &= 0x000f;
798		}
799
800		/*  Clear channel, range and input mode bits
801		 *  in A/D command/status register */
802		devpriv->AdcCmdStatus &= 0xf00f;
803
804		/*  Set channel number and differential mode status bit */
805		if (diff) {
806			/*  Set channel number, bits 9-11 & mode, bit 6 */
807			devpriv->AdcCmdStatus |= (chanprog << 9);
808			devpriv->AdcCmdStatus |= ADC_DI;
809		} else
810			/*  Set channel number, bits 8-11 */
811			devpriv->AdcCmdStatus |= (chanprog << 8);
812
813		/*  Get range for current channel */
814		range = this_board->rangecode[CR_RANGE(chanlist[i])];
815		/*  Set range. bits 4-5 */
816		devpriv->AdcCmdStatus |= range;
817
818		/* Output channel, range, mode to ICP Multi */
819		writew(devpriv->AdcCmdStatus,
820		       devpriv->io_addr + ICP_MULTI_ADC_CSR);
821
822#ifdef ICP_MULTI_EXTDEBUG
823		printk(KERN_DEBUG
824		       "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
825		       devpriv->act_chanlist[i]);
826#endif
827	}
828
829}
830
831/*
832==============================================================================
833
834Name:	icp_multi_reset
835
836Description:
837	This function resets the icp multi device to a 'safe' state
838
839Parameters:
840	struct comedi_device *dev	Pointer to current sevice structure
841
842Returns:int	0 = success
843
844==============================================================================
845*/
846static int icp_multi_reset(struct comedi_device *dev)
847{
848	unsigned int i;
849
850#ifdef ICP_MULTI_EXTDEBUG
851	printk(KERN_DEBUG
852	       "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
853#endif
854	/*  Clear INT enables and requests */
855	writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
856	writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
857
858	if (this_board->n_aochan)
859		/*  Set DACs to 0..5V range and 0V output */
860		for (i = 0; i < this_board->n_aochan; i++) {
861			devpriv->DacCmdStatus &= 0xfcce;
862
863			/*  Set channel number */
864			devpriv->DacCmdStatus |= (i << 8);
865
866			/*  Output 0V */
867			writew(0, devpriv->io_addr + ICP_MULTI_AO);
868
869			/*  Set start conversion bit */
870			devpriv->DacCmdStatus |= DAC_ST;
871
872			/*  Output to command / status register */
873			writew(devpriv->DacCmdStatus,
874			       devpriv->io_addr + ICP_MULTI_DAC_CSR);
875
876			/*  Delay to allow DAC time to recover */
877			udelay(1);
878		}
879	/*  Digital outputs to 0 */
880	writew(0, devpriv->io_addr + ICP_MULTI_DO);
881
882#ifdef ICP_MULTI_EXTDEBUG
883	printk(KERN_DEBUG
884	       "icp multi EDBG: END: icp_multi_reset(...)\n");
885#endif
886	return 0;
887}
888
889/*
890==============================================================================
891
892Name:	icp_multi_attach
893
894Description:
895	This function sets up all the appropriate data for the current
896	device.
897
898Parameters:
899	struct comedi_device *dev	Pointer to current device structure
900	struct comedi_devconfig *it	Pointer to current device configuration
901
902Returns:int	0 = success
903
904==============================================================================
905*/
906static int icp_multi_attach(struct comedi_device *dev,
907			    struct comedi_devconfig *it)
908{
909	struct comedi_subdevice *s;
910	int ret, subdev, n_subdevices;
911	unsigned int irq;
912	struct pcilst_struct *card = NULL;
913	resource_size_t io_addr[5], iobase;
914	unsigned char pci_bus, pci_slot, pci_func;
915
916	printk(KERN_WARNING
917	       "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
918
919	/*  Alocate private data storage space */
920	ret = alloc_private(dev, sizeof(struct icp_multi_private));
921	if (ret < 0)
922		return ret;
923
924	/*  Initialise list of PCI cards in system, if not already done so */
925	if (pci_list_builded++ == 0) {
926		pci_card_list_init(PCI_VENDOR_ID_ICP,
927#ifdef ICP_MULTI_EXTDEBUG
928				   1
929#else
930				   0
931#endif
932		    );
933	}
934
935	printk(KERN_WARNING
936	       "Anne's comedi%d: icp_multi: board=%s", dev->minor,
937	       this_board->name);
938
939	card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
940					 this_board->device_id, it->options[0],
941					 it->options[1]);
942
943	if (card == NULL)
944		return -EIO;
945
946	devpriv->card = card;
947
948	if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
949			   &irq)) < 0) {
950		printk(KERN_WARNING " - Can't get configuration data!\n");
951		return -EIO;
952	}
953
954	iobase = io_addr[2];
955	devpriv->phys_iobase = iobase;
956
957	printk(KERN_WARNING
958	       ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
959	       (unsigned long long)iobase);
960
961	devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
962
963	if (devpriv->io_addr == NULL) {
964		printk(KERN_WARNING "ioremap failed.\n");
965		return -ENOMEM;
966	}
967#ifdef ICP_MULTI_EXTDEBUG
968	printk(KERN_DEBUG
969	       "0x%08llx mapped to %p, ", (unsigned long long)iobase,
970	       devpriv->io_addr);
971#endif
972
973	dev->board_name = this_board->name;
974
975	n_subdevices = 0;
976	if (this_board->n_aichan)
977		n_subdevices++;
978	if (this_board->n_aochan)
979		n_subdevices++;
980	if (this_board->n_dichan)
981		n_subdevices++;
982	if (this_board->n_dochan)
983		n_subdevices++;
984	if (this_board->n_ctrs)
985		n_subdevices++;
986
987	ret = alloc_subdevices(dev, n_subdevices);
988	if (ret < 0)
989		return ret;
990
991	icp_multi_reset(dev);
992
993	if (this_board->have_irq) {
994		if (irq) {
995			if (request_irq(irq, interrupt_service_icp_multi,
996					IRQF_SHARED, "Inova Icp Multi", dev)) {
997				printk(KERN_WARNING
998				    "unable to allocate IRQ %u, DISABLING IT",
999				     irq);
1000				irq = 0;	/* Can't use IRQ */
1001			} else
1002				printk(KERN_WARNING ", irq=%u", irq);
1003		} else
1004			printk(KERN_WARNING ", IRQ disabled");
1005	} else
1006		irq = 0;
1007
1008	dev->irq = irq;
1009
1010	printk(KERN_WARNING ".\n");
1011
1012	subdev = 0;
1013
1014	if (this_board->n_aichan) {
1015		s = dev->subdevices + subdev;
1016		dev->read_subdev = s;
1017		s->type = COMEDI_SUBD_AI;
1018		s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1019		if (this_board->n_aichand)
1020			s->subdev_flags |= SDF_DIFF;
1021		s->n_chan = this_board->n_aichan;
1022		s->maxdata = this_board->ai_maxdata;
1023		s->len_chanlist = this_board->n_aichan;
1024		s->range_table = this_board->rangelist_ai;
1025		s->insn_read = icp_multi_insn_read_ai;
1026		subdev++;
1027	}
1028
1029	if (this_board->n_aochan) {
1030		s = dev->subdevices + subdev;
1031		s->type = COMEDI_SUBD_AO;
1032		s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1033		s->n_chan = this_board->n_aochan;
1034		s->maxdata = this_board->ao_maxdata;
1035		s->len_chanlist = this_board->n_aochan;
1036		s->range_table = this_board->rangelist_ao;
1037		s->insn_write = icp_multi_insn_write_ao;
1038		s->insn_read = icp_multi_insn_read_ao;
1039		subdev++;
1040	}
1041
1042	if (this_board->n_dichan) {
1043		s = dev->subdevices + subdev;
1044		s->type = COMEDI_SUBD_DI;
1045		s->subdev_flags = SDF_READABLE;
1046		s->n_chan = this_board->n_dichan;
1047		s->maxdata = 1;
1048		s->len_chanlist = this_board->n_dichan;
1049		s->range_table = &range_digital;
1050		s->io_bits = 0;
1051		s->insn_bits = icp_multi_insn_bits_di;
1052		subdev++;
1053	}
1054
1055	if (this_board->n_dochan) {
1056		s = dev->subdevices + subdev;
1057		s->type = COMEDI_SUBD_DO;
1058		s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1059		s->n_chan = this_board->n_dochan;
1060		s->maxdata = 1;
1061		s->len_chanlist = this_board->n_dochan;
1062		s->range_table = &range_digital;
1063		s->io_bits = (1 << this_board->n_dochan) - 1;
1064		s->state = 0;
1065		s->insn_bits = icp_multi_insn_bits_do;
1066		subdev++;
1067	}
1068
1069	if (this_board->n_ctrs) {
1070		s = dev->subdevices + subdev;
1071		s->type = COMEDI_SUBD_COUNTER;
1072		s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1073		s->n_chan = this_board->n_ctrs;
1074		s->maxdata = 0xffff;
1075		s->len_chanlist = this_board->n_ctrs;
1076		s->state = 0;
1077		s->insn_read = icp_multi_insn_read_ctr;
1078		s->insn_write = icp_multi_insn_write_ctr;
1079		subdev++;
1080	}
1081
1082	devpriv->valid = 1;
1083
1084#ifdef ICP_MULTI_EXTDEBUG
1085	printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
1086#endif
1087
1088	return 0;
1089}
1090
1091/*
1092==============================================================================
1093
1094Name:	icp_multi_detach
1095
1096Description:
1097	This function releases all the resources used by the current
1098	device.
1099
1100Parameters:
1101	struct comedi_device *dev	Pointer to current device structure
1102
1103Returns:int	0 = success
1104
1105==============================================================================
1106*/
1107static int icp_multi_detach(struct comedi_device *dev)
1108{
1109
1110	if (dev->private)
1111		if (devpriv->valid)
1112			icp_multi_reset(dev);
1113
1114	if (dev->irq)
1115		free_irq(dev->irq, dev);
1116
1117	if (dev->private && devpriv->io_addr)
1118		iounmap(devpriv->io_addr);
1119
1120	if (dev->private && devpriv->card)
1121		pci_card_free(devpriv->card);
1122
1123	if (--pci_list_builded == 0)
1124		pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1125
1126	return 0;
1127}
1128