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