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