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