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 /*
24 Driver: icp_multi
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
28 Status: works
29 
30 The driver works for analog input and output and digital input and output.
31 It does not work with interrupts or with the counters.  Currently no support
32 for DMA.
33 
34 It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35 resolution.  Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA.  Input
36 ranges can be individually programmed for each channel.  Voltage or current
37 measurement is selected by jumper.
38 
39 There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
40 
41 16 x Digital Inputs, 24V
42 
43 8 x Digital Outputs, 24V, 1A
44 
45 4 x 16-bit counters
46 
47 Options:
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 */
112 static 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 
120 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
121 
122 /*
123 ==============================================================================
124 	Forward declarations
125 ==============================================================================
126 */
127 static int icp_multi_attach(struct comedi_device *dev,
128 			    struct comedi_devconfig *it);
129 static int icp_multi_detach(struct comedi_device *dev);
130 
131 /*
132 ==============================================================================
133 	Data & Structure declarations
134 ==============================================================================
135 */
136 static unsigned short pci_list_builded;	/*>0 list of card is known */
137 
138 struct 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 
157 static 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 
178 static struct comedi_driver driver_icp_multi = {
179 	.driver_name = "icp_multi",
180 	.module = THIS_MODULE,
181 	.attach = icp_multi_attach,
182 	.detach = icp_multi_detach,
183 	.num_names = n_boardtypes,
184 	.board_name = &boardtypes[0].name,
185 	.offset = sizeof(struct boardtype),
186 };
187 
driver_icp_multi_init_module(void)188 static int __init driver_icp_multi_init_module(void)
189 {
190 	return comedi_driver_register(&driver_icp_multi);
191 }
192 
driver_icp_multi_cleanup_module(void)193 static void __exit driver_icp_multi_cleanup_module(void)
194 {
195 	comedi_driver_unregister(&driver_icp_multi);
196 }
197 
198 module_init(driver_icp_multi_init_module);
199 module_exit(driver_icp_multi_cleanup_module);
200 
201 struct 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
230 static int check_channel_list(struct comedi_device *dev,
231 			      struct comedi_subdevice *s,
232 			      unsigned int *chanlist, unsigned int n_chan);
233 #endif
234 static void setup_channel_list(struct comedi_device *dev,
235 			       struct comedi_subdevice *s,
236 			       unsigned int *chanlist, unsigned int n_chan);
237 static int icp_multi_reset(struct comedi_device *dev);
238 
239 /*
240 ==============================================================================
241 	Functions
242 ==============================================================================
243 */
244 
245 /*
246 ==============================================================================
247 
248 Name:	icp_multi_insn_read_ai
249 
250 Description:
251 	This function reads a single analogue input.
252 
253 Parameters:
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 
259 Returns:int			Nmuber of instructions executed
260 
261 ==============================================================================
262 */
icp_multi_insn_read_ai(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)263 static 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 
349 conv_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 
372 Name:	icp_multi_insn_write_ao
373 
374 Description:
375 	This function writes a single analogue output.
376 
377 Parameters:
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 
383 Returns:int			Nmuber of instructions executed
384 
385 ==============================================================================
386 */
icp_multi_insn_write_ao(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)387 static 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 
463 dac_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 
487 Name:	icp_multi_insn_read_ao
488 
489 Description:
490 	This function reads a single analogue output.
491 
492 Parameters:
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 
498 Returns:int			Nmuber of instructions executed
499 
500 ==============================================================================
501 */
icp_multi_insn_read_ao(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)502 static 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 
521 Name:	icp_multi_insn_bits_di
522 
523 Description:
524 	This function reads the digital inputs.
525 
526 Parameters:
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 
532 Returns:int			Nmuber of instructions executed
533 
534 ==============================================================================
535 */
icp_multi_insn_bits_di(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)536 static 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 
548 Name:	icp_multi_insn_bits_do
549 
550 Description:
551 	This function writes the appropriate digital outputs.
552 
553 Parameters:
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 
559 Returns:int			Nmuber of instructions executed
560 
561 ==============================================================================
562 */
icp_multi_insn_bits_do(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)563 static 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 
591 Name:	icp_multi_insn_read_ctr
592 
593 Description:
594 	This function reads the specified counter.
595 
596 Parameters:
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 
602 Returns:int			Nmuber of instructions executed
603 
604 ==============================================================================
605 */
icp_multi_insn_read_ctr(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)606 static 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 
616 Name:	icp_multi_insn_write_ctr
617 
618 Description:
619 	This function write to the specified counter.
620 
621 Parameters:
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 
627 Returns:int			Nmuber of instructions executed
628 
629 ==============================================================================
630 */
icp_multi_insn_write_ctr(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)631 static 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 
642 Name:	interrupt_service_icp_multi
643 
644 Description:
645 	This function is the interrupt service routine for all
646 	interrupts generated by the icp multi board.
647 
648 Parameters:
649 	int irq
650 	void *d			Pointer to current device
651 
652 ==============================================================================
653 */
interrupt_service_icp_multi(int irq,void * d)654 static 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 
711 Name:	check_channel_list
712 
713 Description:
714 	This function checks if the channel list, provided by user
715 	is built correctly
716 
717 Parameters:
718 	struct comedi_device *dev	Pointer to current service 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 
723 Returns:int 0 = failure
724 	    1 = success
725 
726 ==============================================================================
727 */
728 static 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 
767 Name:	setup_channel_list
768 
769 Description:
770 	This function sets the appropriate channel selection,
771 	differential input mode and range bits in the ADC Command/
772 	Status register.
773 
774 Parameters:
775 	struct comedi_device *dev	Pointer to current service 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 
780 Returns:Void
781 
782 ==============================================================================
783 */
setup_channel_list(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int * chanlist,unsigned int n_chan)784 static 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 
845 Name:	icp_multi_reset
846 
847 Description:
848 	This function resets the icp multi device to a 'safe' state
849 
850 Parameters:
851 	struct comedi_device *dev	Pointer to current service structure
852 
853 Returns:int	0 = success
854 
855 ==============================================================================
856 */
icp_multi_reset(struct comedi_device * dev)857 static 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 
903 Name:	icp_multi_attach
904 
905 Description:
906 	This function sets up all the appropriate data for the current
907 	device.
908 
909 Parameters:
910 	struct comedi_device *dev	Pointer to current device structure
911 	struct comedi_devconfig *it	Pointer to current device configuration
912 
913 Returns:int	0 = success
914 
915 ==============================================================================
916 */
icp_multi_attach(struct comedi_device * dev,struct comedi_devconfig * it)917 static 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 
1105 Name:	icp_multi_detach
1106 
1107 Description:
1108 	This function releases all the resources used by the current
1109 	device.
1110 
1111 Parameters:
1112 	struct comedi_device *dev	Pointer to current device structure
1113 
1114 Returns:int	0 = success
1115 
1116 ==============================================================================
1117 */
icp_multi_detach(struct comedi_device * dev)1118 static 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 
1140 MODULE_AUTHOR("Comedi http://www.comedi.org");
1141 MODULE_DESCRIPTION("Comedi low-level driver");
1142 MODULE_LICENSE("GPL");
1143