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