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