icp_multi.c revision a622afcb7e4c7d24bb29b07e82fd8b7fcd81c0c0
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;	/*>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("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("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("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("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("icp multi D n=%d tm=%d ST=%4x\n", n,
307				       timeout,
308				       readw(devpriv->io_addr +
309					     ICP_MULTI_ADC_CSR));
310#endif
311
312			udelay(1);
313		}
314
315		/*  If we reach here, a timeout has occurred */
316		comedi_error(dev, "A/D insn timeout");
317
318		/*  Disable interrupt */
319		devpriv->IntEnable &= ~ADC_READY;
320		writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
321
322		/*  Clear interrupt status */
323		devpriv->IntStatus |= ADC_READY;
324		writew(devpriv->IntStatus,
325		       devpriv->io_addr + ICP_MULTI_INT_STAT);
326
327		/*  Clear data received */
328		data[n] = 0;
329
330#ifdef ICP_MULTI_EXTDEBUG
331		printk
332		    ("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
333		     n);
334#endif
335		return -ETIME;
336
337conv_finish:
338		data[n] =
339		    (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
340	}
341
342	/*  Disable interrupt */
343	devpriv->IntEnable &= ~ADC_READY;
344	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
345
346	/*  Clear interrupt status */
347	devpriv->IntStatus |= ADC_READY;
348	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
349
350#ifdef ICP_MULTI_EXTDEBUG
351	printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
352#endif
353	return n;
354}
355
356/*
357==============================================================================
358
359Name:	icp_multi_insn_write_ao
360
361Description:
362	This function writes a single analogue output.
363
364Parameters:
365	struct comedi_device *dev	Pointer to current device structure
366	struct comedi_subdevice *s	Pointer to current subdevice structure
367	struct comedi_insn *insn	Pointer to current comedi instruction
368	unsigned int *data		Pointer to analogue output data
369
370Returns:int			Nmuber of instructions executed
371
372==============================================================================
373*/
374static int icp_multi_insn_write_ao(struct comedi_device *dev,
375				   struct comedi_subdevice *s,
376				   struct comedi_insn *insn, unsigned int *data)
377{
378	int n, chan, range, timeout;
379
380#ifdef ICP_MULTI_EXTDEBUG
381	printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
382#endif
383	/*  Disable D/A conversion ready interrupt */
384	devpriv->IntEnable &= ~DAC_READY;
385	writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
386
387	/*  Clear interrupt status */
388	devpriv->IntStatus |= DAC_READY;
389	writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
390
391	/*  Get channel number and range */
392	chan = CR_CHAN(insn->chanspec);
393	range = CR_RANGE(insn->chanspec);
394
395	/*  Set up range and channel data */
396	/*  Bit 4 = 1 : Bipolar */
397	/*  Bit 5 = 0 : 5V */
398	/*  Bit 5 = 1 : 10V */
399	/*  Bits 8-9 : Channel number */
400	devpriv->DacCmdStatus &= 0xfccf;
401	devpriv->DacCmdStatus |= this_board->rangecode[range];
402	devpriv->DacCmdStatus |= (chan << 8);
403
404	writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
405
406	for (n = 0; n < insn->n; n++) {
407		/*  Wait for analogue output data register to be
408		 *  ready for new data, or get fed up waiting */
409		timeout = 100;
410		while (timeout--) {
411			if (!(readw(devpriv->io_addr +
412				    ICP_MULTI_DAC_CSR) & DAC_BSY))
413				goto dac_ready;
414
415#ifdef ICP_MULTI_EXTDEBUG
416			if (!(timeout % 10))
417				printk("icp multi A n=%d tm=%d ST=%4x\n", n,
418				       timeout,
419				       readw(devpriv->io_addr +
420					     ICP_MULTI_DAC_CSR));
421#endif
422
423			udelay(1);
424		}
425
426		/*  If we reach here, a timeout has occurred */
427		comedi_error(dev, "D/A insn timeout");
428
429		/*  Disable interrupt */
430		devpriv->IntEnable &= ~DAC_READY;
431		writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
432
433		/*  Clear interrupt status */
434		devpriv->IntStatus |= DAC_READY;
435		writew(devpriv->IntStatus,
436		       devpriv->io_addr + ICP_MULTI_INT_STAT);
437
438		/*  Clear data received */
439		devpriv->ao_data[chan] = 0;
440
441#ifdef ICP_MULTI_EXTDEBUG
442		printk
443		    ("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
444		     n);
445#endif
446		return -ETIME;
447
448dac_ready:
449		/*  Write data to analogue output data register */
450		writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
451
452		/*  Set DAC_ST bit to write the data to selected channel */
453		devpriv->DacCmdStatus |= DAC_ST;
454		writew(devpriv->DacCmdStatus,
455		       devpriv->io_addr + ICP_MULTI_DAC_CSR);
456		devpriv->DacCmdStatus &= ~DAC_ST;
457
458		/*  Save analogue output data */
459		devpriv->ao_data[chan] = data[n];
460	}
461
462#ifdef ICP_MULTI_EXTDEBUG
463	printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
464#endif
465	return n;
466}
467
468/*
469==============================================================================
470
471Name:	icp_multi_insn_read_ao
472
473Description:
474	This function reads a single analogue output.
475
476Parameters:
477	struct comedi_device *dev	Pointer to current device structure
478	struct comedi_subdevice *s	Pointer to current subdevice structure
479	struct comedi_insn *insn	Pointer to current comedi instruction
480	unsigned int *data		Pointer to analogue output data
481
482Returns:int			Nmuber of instructions executed
483
484==============================================================================
485*/
486static int icp_multi_insn_read_ao(struct comedi_device *dev,
487				  struct comedi_subdevice *s,
488				  struct comedi_insn *insn, unsigned int *data)
489{
490	int n, chan;
491
492	/*  Get channel number */
493	chan = CR_CHAN(insn->chanspec);
494
495	/*  Read analogue outputs */
496	for (n = 0; n < insn->n; n++)
497		data[n] = devpriv->ao_data[chan];
498
499	return n;
500}
501
502/*
503==============================================================================
504
505Name:	icp_multi_insn_bits_di
506
507Description:
508	This function reads the digital inputs.
509
510Parameters:
511	struct comedi_device *dev	Pointer to current device structure
512	struct comedi_subdevice *s	Pointer to current subdevice structure
513	struct comedi_insn *insn	Pointer to current comedi instruction
514	unsigned int *data		Pointer to analogue output data
515
516Returns:int			Nmuber of instructions executed
517
518==============================================================================
519*/
520static int icp_multi_insn_bits_di(struct comedi_device *dev,
521				  struct comedi_subdevice *s,
522				  struct comedi_insn *insn, unsigned int *data)
523{
524	data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
525
526	return 2;
527}
528
529/*
530==============================================================================
531
532Name:	icp_multi_insn_bits_do
533
534Description:
535	This function writes the appropriate digital outputs.
536
537Parameters:
538	struct comedi_device *dev	Pointer to current device structure
539	struct comedi_subdevice *s	Pointer to current subdevice structure
540	struct comedi_insn *insn	Pointer to current comedi instruction
541	unsigned int *data		Pointer to analogue output data
542
543Returns:int			Nmuber of instructions executed
544
545==============================================================================
546*/
547static int icp_multi_insn_bits_do(struct comedi_device *dev,
548				  struct comedi_subdevice *s,
549				  struct comedi_insn *insn, unsigned int *data)
550{
551#ifdef ICP_MULTI_EXTDEBUG
552	printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
553#endif
554
555	if (data[0]) {
556		s->state &= ~data[0];
557		s->state |= (data[0] & data[1]);
558
559		printk("Digital outputs = %4x \n", s->state);
560
561		writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
562	}
563
564	data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
565
566#ifdef ICP_MULTI_EXTDEBUG
567	printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
568#endif
569	return 2;
570}
571
572/*
573==============================================================================
574
575Name:	icp_multi_insn_read_ctr
576
577Description:
578	This function reads the specified counter.
579
580Parameters:
581	struct comedi_device *dev	Pointer to current device structure
582	struct comedi_subdevice *s	Pointer to current subdevice structure
583	struct comedi_insn *insn	Pointer to current comedi instruction
584	unsigned int *data		Pointer to counter data
585
586Returns:int			Nmuber of instructions executed
587
588==============================================================================
589*/
590static int icp_multi_insn_read_ctr(struct comedi_device *dev,
591				   struct comedi_subdevice *s,
592				   struct comedi_insn *insn, unsigned int *data)
593{
594	return 0;
595}
596
597/*
598==============================================================================
599
600Name:	icp_multi_insn_write_ctr
601
602Description:
603	This function write to the specified counter.
604
605Parameters:
606	struct comedi_device *dev	Pointer to current device structure
607	struct comedi_subdevice *s	Pointer to current subdevice structure
608	struct comedi_insn *insn	Pointer to current comedi instruction
609	unsigned int *data		Pointer to counter data
610
611Returns:int			Nmuber of instructions executed
612
613==============================================================================
614*/
615static int icp_multi_insn_write_ctr(struct comedi_device *dev,
616				    struct comedi_subdevice *s,
617				    struct comedi_insn *insn,
618				    unsigned int *data)
619{
620	return 0;
621}
622
623/*
624==============================================================================
625
626Name:	interrupt_service_icp_multi
627
628Description:
629	This function is the interrupt service routine for all
630	interrupts generated by the icp multi board.
631
632Parameters:
633	int irq
634	void *d			Pointer to current device
635
636==============================================================================
637*/
638static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
639{
640	struct comedi_device *dev = d;
641	int int_no;
642
643#ifdef ICP_MULTI_EXTDEBUG
644	printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
645	       irq);
646#endif
647
648	/*  Is this interrupt from our board? */
649	int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
650	if (!int_no)
651		/*  No, exit */
652		return IRQ_NONE;
653
654#ifdef ICP_MULTI_EXTDEBUG
655	printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
656	       readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
657#endif
658
659	/*  Determine which interrupt is active & handle it */
660	switch (int_no) {
661	case ADC_READY:
662		break;
663	case DAC_READY:
664		break;
665	case DOUT_ERROR:
666		break;
667	case DIN_STATUS:
668		break;
669	case CIE0:
670		break;
671	case CIE1:
672		break;
673	case CIE2:
674		break;
675	case CIE3:
676		break;
677	default:
678		break;
679
680	}
681
682#ifdef ICP_MULTI_EXTDEBUG
683	printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
684#endif
685	return IRQ_HANDLED;
686}
687
688#if 0
689/*
690==============================================================================
691
692Name:	check_channel_list
693
694Description:
695	This function checks if the channel list, provided by user
696	is built correctly
697
698Parameters:
699	struct comedi_device *dev	Pointer to current sevice structure
700	struct comedi_subdevice *s	Pointer to current subdevice structure
701	unsigned int *chanlist	Pointer to packed channel list
702	unsigned int n_chan	Number of channels to scan
703
704Returns:int 0 = failure
705	    1 = success
706
707==============================================================================
708*/
709static int check_channel_list(struct comedi_device *dev,
710			      struct comedi_subdevice *s,
711			      unsigned int *chanlist, unsigned int n_chan)
712{
713	unsigned int i;
714
715#ifdef ICP_MULTI_EXTDEBUG
716	printk("icp multi EDBG:  check_channel_list(...,%d)\n", n_chan);
717#endif
718	/*  Check that we at least have one channel to check */
719	if (n_chan < 1) {
720		comedi_error(dev, "range/channel list is empty!");
721		return 0;
722	}
723	/*  Check all channels */
724	for (i = 0; i < n_chan; i++) {
725		/*  Check that channel number is < maximum */
726		if (CR_AREF(chanlist[i]) == AREF_DIFF) {
727			if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
728				comedi_error(dev,
729					     "Incorrect differential ai channel number");
730				return 0;
731			}
732		} else {
733			if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
734				comedi_error(dev,
735					     "Incorrect ai channel number");
736				return 0;
737			}
738		}
739	}
740	return 1;
741}
742#endif
743
744/*
745==============================================================================
746
747Name:	setup_channel_list
748
749Description:
750	This function sets the appropriate channel selection,
751	differential input mode and range bits in the ADC Command/
752	Status register.
753
754Parameters:
755	struct comedi_device *dev	Pointer to current sevice structure
756	struct comedi_subdevice *s	Pointer to current subdevice structure
757	unsigned int *chanlist	Pointer to packed channel list
758	unsigned int n_chan	Number of channels to scan
759
760Returns:Void
761
762==============================================================================
763*/
764static void setup_channel_list(struct comedi_device *dev,
765			       struct comedi_subdevice *s,
766			       unsigned int *chanlist, unsigned int n_chan)
767{
768	unsigned int i, range, chanprog;
769	unsigned int diff;
770
771#ifdef ICP_MULTI_EXTDEBUG
772	printk("icp multi EDBG:  setup_channel_list(...,%d)\n", n_chan);
773#endif
774	devpriv->act_chanlist_len = n_chan;
775	devpriv->act_chanlist_pos = 0;
776
777	for (i = 0; i < n_chan; i++) {
778		/*  Get channel */
779		chanprog = CR_CHAN(chanlist[i]);
780
781		/*  Determine if it is a differential channel (Bit 15  = 1) */
782		if (CR_AREF(chanlist[i]) == AREF_DIFF) {
783			diff = 1;
784			chanprog &= 0x0007;
785		} else {
786			diff = 0;
787			chanprog &= 0x000f;
788		}
789
790		/*  Clear channel, range and input mode bits
791		 *  in A/D command/status register */
792		devpriv->AdcCmdStatus &= 0xf00f;
793
794		/*  Set channel number and differential mode status bit */
795		if (diff) {
796			/*  Set channel number, bits 9-11 & mode, bit 6 */
797			devpriv->AdcCmdStatus |= (chanprog << 9);
798			devpriv->AdcCmdStatus |= ADC_DI;
799		} else
800			/*  Set channel number, bits 8-11 */
801			devpriv->AdcCmdStatus |= (chanprog << 8);
802
803		/*  Get range for current channel */
804		range = this_board->rangecode[CR_RANGE(chanlist[i])];
805		/*  Set range. bits 4-5 */
806		devpriv->AdcCmdStatus |= range;
807
808		/* Output channel, range, mode to ICP Multi */
809		writew(devpriv->AdcCmdStatus,
810		       devpriv->io_addr + ICP_MULTI_ADC_CSR);
811
812#ifdef ICP_MULTI_EXTDEBUG
813		printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
814		       devpriv->act_chanlist[i]);
815#endif
816	}
817
818}
819
820/*
821==============================================================================
822
823Name:	icp_multi_reset
824
825Description:
826	This function resets the icp multi device to a 'safe' state
827
828Parameters:
829	struct comedi_device *dev	Pointer to current sevice structure
830
831Returns:int	0 = success
832
833==============================================================================
834*/
835static int icp_multi_reset(struct comedi_device *dev)
836{
837	unsigned int i;
838
839#ifdef ICP_MULTI_EXTDEBUG
840	printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
841#endif
842	/*  Clear INT enables and requests */
843	writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
844	writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
845
846	if (this_board->n_aochan)
847		/*  Set DACs to 0..5V range and 0V output */
848		for (i = 0; i < this_board->n_aochan; i++) {
849			devpriv->DacCmdStatus &= 0xfcce;
850
851			/*  Set channel number */
852			devpriv->DacCmdStatus |= (i << 8);
853
854			/*  Output 0V */
855			writew(0, devpriv->io_addr + ICP_MULTI_AO);
856
857			/*  Set start conversion bit */
858			devpriv->DacCmdStatus |= DAC_ST;
859
860			/*  Output to command / status register */
861			writew(devpriv->DacCmdStatus,
862			       devpriv->io_addr + ICP_MULTI_DAC_CSR);
863
864			/*  Delay to allow DAC time to recover */
865			udelay(1);
866		}
867	/*  Digital outputs to 0 */
868	writew(0, devpriv->io_addr + ICP_MULTI_DO);
869
870#ifdef ICP_MULTI_EXTDEBUG
871	printk("icp multi EDBG: END: icp_multi_reset(...)\n");
872#endif
873	return 0;
874}
875
876/*
877==============================================================================
878
879Name:	icp_multi_attach
880
881Description:
882	This function sets up all the appropriate data for the current
883	device.
884
885Parameters:
886	struct comedi_device *dev	Pointer to current device structure
887	struct comedi_devconfig *it	Pointer to current device configuration
888
889Returns:int	0 = success
890
891==============================================================================
892*/
893static int icp_multi_attach(struct comedi_device *dev,
894			    struct comedi_devconfig *it)
895{
896	struct comedi_subdevice *s;
897	int ret, subdev, n_subdevices;
898	unsigned int irq;
899	struct pcilst_struct *card = NULL;
900	resource_size_t io_addr[5], iobase;
901	unsigned char pci_bus, pci_slot, pci_func;
902
903	printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
904
905	/*  Alocate private data storage space */
906	ret = alloc_private(dev, sizeof(struct icp_multi_private));
907	if (ret < 0)
908		return ret;
909
910	/*  Initialise list of PCI cards in system, if not already done so */
911	if (pci_list_builded++ == 0) {
912		pci_card_list_init(PCI_VENDOR_ID_ICP,
913#ifdef ICP_MULTI_EXTDEBUG
914				   1
915#else
916				   0
917#endif
918		    );
919	}
920
921	printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
922	       this_board->name);
923
924	card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
925					 this_board->device_id, it->options[0],
926					 it->options[1]);
927
928	if (card == NULL)
929		return -EIO;
930
931	devpriv->card = card;
932
933	if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
934			   &irq)) < 0) {
935		printk(" - Can't get configuration data!\n");
936		return -EIO;
937	}
938
939	iobase = io_addr[2];
940	devpriv->phys_iobase = iobase;
941
942	printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
943	       (unsigned long long)iobase);
944
945	devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
946
947	if (devpriv->io_addr == NULL) {
948		printk("ioremap failed.\n");
949		return -ENOMEM;
950	}
951#ifdef ICP_MULTI_EXTDEBUG
952	printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
953	       devpriv->io_addr);
954#endif
955
956	dev->board_name = this_board->name;
957
958	n_subdevices = 0;
959	if (this_board->n_aichan)
960		n_subdevices++;
961	if (this_board->n_aochan)
962		n_subdevices++;
963	if (this_board->n_dichan)
964		n_subdevices++;
965	if (this_board->n_dochan)
966		n_subdevices++;
967	if (this_board->n_ctrs)
968		n_subdevices++;
969
970	ret = alloc_subdevices(dev, n_subdevices);
971	if (ret < 0)
972		return ret;
973
974	icp_multi_reset(dev);
975
976	if (this_board->have_irq) {
977		if (irq) {
978			if (request_irq(irq, interrupt_service_icp_multi,
979					IRQF_SHARED, "Inova Icp Multi", dev)) {
980				printk
981				    (", unable to allocate IRQ %u, DISABLING IT",
982				     irq);
983				irq = 0;	/* Can't use IRQ */
984			} else
985				printk(", irq=%u", irq);
986		} else
987			printk(", IRQ disabled");
988	} else
989		irq = 0;
990
991	dev->irq = irq;
992
993	printk(".\n");
994
995	subdev = 0;
996
997	if (this_board->n_aichan) {
998		s = dev->subdevices + subdev;
999		dev->read_subdev = s;
1000		s->type = COMEDI_SUBD_AI;
1001		s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1002		if (this_board->n_aichand)
1003			s->subdev_flags |= SDF_DIFF;
1004		s->n_chan = this_board->n_aichan;
1005		s->maxdata = this_board->ai_maxdata;
1006		s->len_chanlist = this_board->n_aichan;
1007		s->range_table = this_board->rangelist_ai;
1008		s->insn_read = icp_multi_insn_read_ai;
1009		subdev++;
1010	}
1011
1012	if (this_board->n_aochan) {
1013		s = dev->subdevices + subdev;
1014		s->type = COMEDI_SUBD_AO;
1015		s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1016		s->n_chan = this_board->n_aochan;
1017		s->maxdata = this_board->ao_maxdata;
1018		s->len_chanlist = this_board->n_aochan;
1019		s->range_table = this_board->rangelist_ao;
1020		s->insn_write = icp_multi_insn_write_ao;
1021		s->insn_read = icp_multi_insn_read_ao;
1022		subdev++;
1023	}
1024
1025	if (this_board->n_dichan) {
1026		s = dev->subdevices + subdev;
1027		s->type = COMEDI_SUBD_DI;
1028		s->subdev_flags = SDF_READABLE;
1029		s->n_chan = this_board->n_dichan;
1030		s->maxdata = 1;
1031		s->len_chanlist = this_board->n_dichan;
1032		s->range_table = &range_digital;
1033		s->io_bits = 0;
1034		s->insn_bits = icp_multi_insn_bits_di;
1035		subdev++;
1036	}
1037
1038	if (this_board->n_dochan) {
1039		s = dev->subdevices + subdev;
1040		s->type = COMEDI_SUBD_DO;
1041		s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1042		s->n_chan = this_board->n_dochan;
1043		s->maxdata = 1;
1044		s->len_chanlist = this_board->n_dochan;
1045		s->range_table = &range_digital;
1046		s->io_bits = (1 << this_board->n_dochan) - 1;
1047		s->state = 0;
1048		s->insn_bits = icp_multi_insn_bits_do;
1049		subdev++;
1050	}
1051
1052	if (this_board->n_ctrs) {
1053		s = dev->subdevices + subdev;
1054		s->type = COMEDI_SUBD_COUNTER;
1055		s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1056		s->n_chan = this_board->n_ctrs;
1057		s->maxdata = 0xffff;
1058		s->len_chanlist = this_board->n_ctrs;
1059		s->state = 0;
1060		s->insn_read = icp_multi_insn_read_ctr;
1061		s->insn_write = icp_multi_insn_write_ctr;
1062		subdev++;
1063	}
1064
1065	devpriv->valid = 1;
1066
1067#ifdef ICP_MULTI_EXTDEBUG
1068	printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1069#endif
1070
1071	return 0;
1072}
1073
1074/*
1075==============================================================================
1076
1077Name:	icp_multi_detach
1078
1079Description:
1080	This function releases all the resources used by the current
1081	device.
1082
1083Parameters:
1084	struct comedi_device *dev	Pointer to current device structure
1085
1086Returns:int	0 = success
1087
1088==============================================================================
1089*/
1090static int icp_multi_detach(struct comedi_device *dev)
1091{
1092
1093	if (dev->private)
1094		if (devpriv->valid)
1095			icp_multi_reset(dev);
1096
1097	if (dev->irq)
1098		free_irq(dev->irq, dev);
1099
1100	if (dev->private && devpriv->io_addr)
1101		iounmap(devpriv->io_addr);
1102
1103	if (dev->private && devpriv->card)
1104		pci_card_free(devpriv->card);
1105
1106	if (--pci_list_builded == 0)
1107		pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1108
1109	return 0;
1110}
1111