GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / drivers / staging / comedi / drivers / icp_multi.c
blob5f23fbc47d90e1cd2bdfb4aff6fc2820f2d048a5
1 /*
2 comedi/drivers/icp_multi.c
4 COMEDI - Linux Control and Measurement Device Interface
5 Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
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.
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.
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.
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
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.
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.
39 There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
41 16 x Digital Inputs, 24V
43 8 x Digital Outputs, 24V, 1A
45 4 x 16-bit counters
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
53 #include <linux/interrupt.h>
54 #include "../comedidev.h"
56 #include <linux/delay.h>
57 #include <linux/pci.h>
59 #include "icp_multi.h"
61 #define DEVICE_ID 0x8000 /* Device ID */
63 #define ICP_MULTI_EXTDEBUG
65 /* Hardware types of the cards */
66 #define TYPE_ICP_MULTI 0
68 #define IORANGE_ICP_MULTI 32
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 */
83 #define ICP_MULTI_SIZE 0x20 /* 32 bytes */
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 */
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 */
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 */
108 /* Useful definitions */
109 #define Status_IRQ 0x00ff /* All interrupts */
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)
120 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
123 ==============================================================================
124 Forward declarations
125 ==============================================================================
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);
132 ==============================================================================
133 Data & Structure declarations
134 ==============================================================================
136 static unsigned short pci_list_builded; /*>0 list of card is known */
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 */
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 */
176 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
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),
188 static int __init driver_icp_multi_init_module(void)
190 return comedi_driver_register(&driver_icp_multi);
193 static void __exit driver_icp_multi_cleanup_module(void)
195 comedi_driver_unregister(&driver_icp_multi);
198 module_init(driver_icp_multi_init_module);
199 module_exit(driver_icp_multi_cleanup_module);
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 */
220 #define devpriv ((struct icp_multi_private *)dev->private)
221 #define this_board ((const struct boardtype *)dev->board_ptr)
224 ==============================================================================
225 More forward declarations
226 ==============================================================================
229 static void setup_channel_list(struct comedi_device *dev,
230 struct comedi_subdevice *s,
231 unsigned int *chanlist, unsigned int n_chan);
232 static int icp_multi_reset(struct comedi_device *dev);
235 ==============================================================================
236 Functions
237 ==============================================================================
241 ==============================================================================
243 Name: icp_multi_insn_read_ai
245 Description:
246 This function reads a single analogue input.
248 Parameters:
249 struct comedi_device *dev Pointer to current device structure
250 struct comedi_subdevice *s Pointer to current subdevice structure
251 struct comedi_insn *insn Pointer to current comedi instruction
252 unsigned int *data Pointer to analogue input data
254 Returns:int Nmuber of instructions executed
256 ==============================================================================
258 static int icp_multi_insn_read_ai(struct comedi_device *dev,
259 struct comedi_subdevice *s,
260 struct comedi_insn *insn, unsigned int *data)
262 int n, timeout;
264 #ifdef ICP_MULTI_EXTDEBUG
265 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
266 #endif
267 /* Disable A/D conversion ready interrupt */
268 devpriv->IntEnable &= ~ADC_READY;
269 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
271 /* Clear interrupt status */
272 devpriv->IntStatus |= ADC_READY;
273 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
275 /* Set up appropriate channel, mode and range data, for specified ch */
276 setup_channel_list(dev, s, &insn->chanspec, 1);
278 #ifdef ICP_MULTI_EXTDEBUG
279 printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n",
280 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
281 devpriv->io_addr + ICP_MULTI_ADC_CSR);
282 #endif
284 for (n = 0; n < insn->n; n++) {
285 /* Set start ADC bit */
286 devpriv->AdcCmdStatus |= ADC_ST;
287 writew(devpriv->AdcCmdStatus,
288 devpriv->io_addr + ICP_MULTI_ADC_CSR);
289 devpriv->AdcCmdStatus &= ~ADC_ST;
291 #ifdef ICP_MULTI_EXTDEBUG
292 printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n,
293 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
294 #endif
296 udelay(1);
298 #ifdef ICP_MULTI_EXTDEBUG
299 printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n,
300 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
301 #endif
303 /* Wait for conversion to complete, or get fed up waiting */
304 timeout = 100;
305 while (timeout--) {
306 if (!(readw(devpriv->io_addr +
307 ICP_MULTI_ADC_CSR) & ADC_BSY))
308 goto conv_finish;
310 #ifdef ICP_MULTI_EXTDEBUG
311 if (!(timeout % 10))
312 printk(KERN_DEBUG
313 "icp multi D n=%d tm=%d ST=%4x\n", n,
314 timeout,
315 readw(devpriv->io_addr +
316 ICP_MULTI_ADC_CSR));
317 #endif
319 udelay(1);
322 /* If we reach here, a timeout has occurred */
323 comedi_error(dev, "A/D insn timeout");
325 /* Disable interrupt */
326 devpriv->IntEnable &= ~ADC_READY;
327 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
329 /* Clear interrupt status */
330 devpriv->IntStatus |= ADC_READY;
331 writew(devpriv->IntStatus,
332 devpriv->io_addr + ICP_MULTI_INT_STAT);
334 /* Clear data received */
335 data[n] = 0;
337 #ifdef ICP_MULTI_EXTDEBUG
338 printk(KERN_DEBUG
339 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
341 #endif
342 return -ETIME;
344 conv_finish:
345 data[n] =
346 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
349 /* Disable interrupt */
350 devpriv->IntEnable &= ~ADC_READY;
351 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
353 /* Clear interrupt status */
354 devpriv->IntStatus |= ADC_READY;
355 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
357 #ifdef ICP_MULTI_EXTDEBUG
358 printk(KERN_DEBUG
359 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
360 #endif
361 return n;
365 ==============================================================================
367 Name: icp_multi_insn_write_ao
369 Description:
370 This function writes a single analogue output.
372 Parameters:
373 struct comedi_device *dev Pointer to current device structure
374 struct comedi_subdevice *s Pointer to current subdevice structure
375 struct comedi_insn *insn Pointer to current comedi instruction
376 unsigned int *data Pointer to analogue output data
378 Returns:int Nmuber of instructions executed
380 ==============================================================================
382 static int icp_multi_insn_write_ao(struct comedi_device *dev,
383 struct comedi_subdevice *s,
384 struct comedi_insn *insn, unsigned int *data)
386 int n, chan, range, timeout;
388 #ifdef ICP_MULTI_EXTDEBUG
389 printk(KERN_DEBUG
390 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
391 #endif
392 /* Disable D/A conversion ready interrupt */
393 devpriv->IntEnable &= ~DAC_READY;
394 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
396 /* Clear interrupt status */
397 devpriv->IntStatus |= DAC_READY;
398 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
400 /* Get channel number and range */
401 chan = CR_CHAN(insn->chanspec);
402 range = CR_RANGE(insn->chanspec);
404 /* Set up range and channel data */
405 /* Bit 4 = 1 : Bipolar */
406 /* Bit 5 = 0 : 5V */
407 /* Bit 5 = 1 : 10V */
408 /* Bits 8-9 : Channel number */
409 devpriv->DacCmdStatus &= 0xfccf;
410 devpriv->DacCmdStatus |= this_board->rangecode[range];
411 devpriv->DacCmdStatus |= (chan << 8);
413 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
415 for (n = 0; n < insn->n; n++) {
416 /* Wait for analogue output data register to be
417 * ready for new data, or get fed up waiting */
418 timeout = 100;
419 while (timeout--) {
420 if (!(readw(devpriv->io_addr +
421 ICP_MULTI_DAC_CSR) & DAC_BSY))
422 goto dac_ready;
424 #ifdef ICP_MULTI_EXTDEBUG
425 if (!(timeout % 10))
426 printk(KERN_DEBUG
427 "icp multi A n=%d tm=%d ST=%4x\n", n,
428 timeout,
429 readw(devpriv->io_addr +
430 ICP_MULTI_DAC_CSR));
431 #endif
433 udelay(1);
436 /* If we reach here, a timeout has occurred */
437 comedi_error(dev, "D/A insn timeout");
439 /* Disable interrupt */
440 devpriv->IntEnable &= ~DAC_READY;
441 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
443 /* Clear interrupt status */
444 devpriv->IntStatus |= DAC_READY;
445 writew(devpriv->IntStatus,
446 devpriv->io_addr + ICP_MULTI_INT_STAT);
448 /* Clear data received */
449 devpriv->ao_data[chan] = 0;
451 #ifdef ICP_MULTI_EXTDEBUG
452 printk(KERN_DEBUG
453 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
455 #endif
456 return -ETIME;
458 dac_ready:
459 /* Write data to analogue output data register */
460 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
462 /* Set DAC_ST bit to write the data to selected channel */
463 devpriv->DacCmdStatus |= DAC_ST;
464 writew(devpriv->DacCmdStatus,
465 devpriv->io_addr + ICP_MULTI_DAC_CSR);
466 devpriv->DacCmdStatus &= ~DAC_ST;
468 /* Save analogue output data */
469 devpriv->ao_data[chan] = data[n];
472 #ifdef ICP_MULTI_EXTDEBUG
473 printk(KERN_DEBUG
474 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
475 #endif
476 return n;
480 ==============================================================================
482 Name: icp_multi_insn_read_ao
484 Description:
485 This function reads a single analogue output.
487 Parameters:
488 struct comedi_device *dev Pointer to current device structure
489 struct comedi_subdevice *s Pointer to current subdevice structure
490 struct comedi_insn *insn Pointer to current comedi instruction
491 unsigned int *data Pointer to analogue output data
493 Returns:int Nmuber of instructions executed
495 ==============================================================================
497 static int icp_multi_insn_read_ao(struct comedi_device *dev,
498 struct comedi_subdevice *s,
499 struct comedi_insn *insn, unsigned int *data)
501 int n, chan;
503 /* Get channel number */
504 chan = CR_CHAN(insn->chanspec);
506 /* Read analogue outputs */
507 for (n = 0; n < insn->n; n++)
508 data[n] = devpriv->ao_data[chan];
510 return n;
514 ==============================================================================
516 Name: icp_multi_insn_bits_di
518 Description:
519 This function reads the digital inputs.
521 Parameters:
522 struct comedi_device *dev Pointer to current device structure
523 struct comedi_subdevice *s Pointer to current subdevice structure
524 struct comedi_insn *insn Pointer to current comedi instruction
525 unsigned int *data Pointer to analogue output data
527 Returns:int Nmuber of instructions executed
529 ==============================================================================
531 static int icp_multi_insn_bits_di(struct comedi_device *dev,
532 struct comedi_subdevice *s,
533 struct comedi_insn *insn, unsigned int *data)
535 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
537 return 2;
541 ==============================================================================
543 Name: icp_multi_insn_bits_do
545 Description:
546 This function writes the appropriate digital outputs.
548 Parameters:
549 struct comedi_device *dev Pointer to current device structure
550 struct comedi_subdevice *s Pointer to current subdevice structure
551 struct comedi_insn *insn Pointer to current comedi instruction
552 unsigned int *data Pointer to analogue output data
554 Returns:int Nmuber of instructions executed
556 ==============================================================================
558 static int icp_multi_insn_bits_do(struct comedi_device *dev,
559 struct comedi_subdevice *s,
560 struct comedi_insn *insn, unsigned int *data)
562 #ifdef ICP_MULTI_EXTDEBUG
563 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
564 #endif
566 if (data[0]) {
567 s->state &= ~data[0];
568 s->state |= (data[0] & data[1]);
570 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
572 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
575 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
577 #ifdef ICP_MULTI_EXTDEBUG
578 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
579 #endif
580 return 2;
584 ==============================================================================
586 Name: icp_multi_insn_read_ctr
588 Description:
589 This function reads the specified counter.
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
597 Returns:int Nmuber of instructions executed
599 ==============================================================================
601 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
602 struct comedi_subdevice *s,
603 struct comedi_insn *insn, unsigned int *data)
605 return 0;
609 ==============================================================================
611 Name: icp_multi_insn_write_ctr
613 Description:
614 This function write to the specified counter.
616 Parameters:
617 struct comedi_device *dev Pointer to current device structure
618 struct comedi_subdevice *s Pointer to current subdevice structure
619 struct comedi_insn *insn Pointer to current comedi instruction
620 unsigned int *data Pointer to counter data
622 Returns:int Nmuber of instructions executed
624 ==============================================================================
626 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
627 struct comedi_subdevice *s,
628 struct comedi_insn *insn,
629 unsigned int *data)
631 return 0;
635 ==============================================================================
637 Name: interrupt_service_icp_multi
639 Description:
640 This function is the interrupt service routine for all
641 interrupts generated by the icp multi board.
643 Parameters:
644 int irq
645 void *d Pointer to current device
647 ==============================================================================
649 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
651 struct comedi_device *dev = d;
652 int int_no;
654 #ifdef ICP_MULTI_EXTDEBUG
655 printk(KERN_DEBUG
656 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
657 irq);
658 #endif
660 /* Is this interrupt from our board? */
661 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
662 if (!int_no)
663 /* No, exit */
664 return IRQ_NONE;
666 #ifdef ICP_MULTI_EXTDEBUG
667 printk(KERN_DEBUG
668 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
669 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
670 #endif
672 /* Determine which interrupt is active & handle it */
673 switch (int_no) {
674 case ADC_READY:
675 break;
676 case DAC_READY:
677 break;
678 case DOUT_ERROR:
679 break;
680 case DIN_STATUS:
681 break;
682 case CIE0:
683 break;
684 case CIE1:
685 break;
686 case CIE2:
687 break;
688 case CIE3:
689 break;
690 default:
691 break;
695 #ifdef ICP_MULTI_EXTDEBUG
696 printk(KERN_DEBUG
697 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
698 #endif
699 return IRQ_HANDLED;
704 ==============================================================================
706 Name: setup_channel_list
708 Description:
709 This function sets the appropriate channel selection,
710 differential input mode and range bits in the ADC Command/
711 Status register.
713 Parameters:
714 struct comedi_device *dev Pointer to current sevice structure
715 struct comedi_subdevice *s Pointer to current subdevice structure
716 unsigned int *chanlist Pointer to packed channel list
717 unsigned int n_chan Number of channels to scan
719 Returns:Void
721 ==============================================================================
723 static void setup_channel_list(struct comedi_device *dev,
724 struct comedi_subdevice *s,
725 unsigned int *chanlist, unsigned int n_chan)
727 unsigned int i, range, chanprog;
728 unsigned int diff;
730 #ifdef ICP_MULTI_EXTDEBUG
731 printk(KERN_DEBUG
732 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
733 #endif
734 devpriv->act_chanlist_len = n_chan;
735 devpriv->act_chanlist_pos = 0;
737 for (i = 0; i < n_chan; i++) {
738 /* Get channel */
739 chanprog = CR_CHAN(chanlist[i]);
741 /* Determine if it is a differential channel (Bit 15 = 1) */
742 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
743 diff = 1;
744 chanprog &= 0x0007;
745 } else {
746 diff = 0;
747 chanprog &= 0x000f;
750 /* Clear channel, range and input mode bits
751 * in A/D command/status register */
752 devpriv->AdcCmdStatus &= 0xf00f;
754 /* Set channel number and differential mode status bit */
755 if (diff) {
756 /* Set channel number, bits 9-11 & mode, bit 6 */
757 devpriv->AdcCmdStatus |= (chanprog << 9);
758 devpriv->AdcCmdStatus |= ADC_DI;
759 } else
760 /* Set channel number, bits 8-11 */
761 devpriv->AdcCmdStatus |= (chanprog << 8);
763 /* Get range for current channel */
764 range = this_board->rangecode[CR_RANGE(chanlist[i])];
765 /* Set range. bits 4-5 */
766 devpriv->AdcCmdStatus |= range;
768 /* Output channel, range, mode to ICP Multi */
769 writew(devpriv->AdcCmdStatus,
770 devpriv->io_addr + ICP_MULTI_ADC_CSR);
772 #ifdef ICP_MULTI_EXTDEBUG
773 printk(KERN_DEBUG
774 "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
775 devpriv->act_chanlist[i]);
776 #endif
782 ==============================================================================
784 Name: icp_multi_reset
786 Description:
787 This function resets the icp multi device to a 'safe' state
789 Parameters:
790 struct comedi_device *dev Pointer to current sevice structure
792 Returns:int 0 = success
794 ==============================================================================
796 static int icp_multi_reset(struct comedi_device *dev)
798 unsigned int i;
800 #ifdef ICP_MULTI_EXTDEBUG
801 printk(KERN_DEBUG
802 "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
803 #endif
804 /* Clear INT enables and requests */
805 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
806 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
808 if (this_board->n_aochan)
809 /* Set DACs to 0..5V range and 0V output */
810 for (i = 0; i < this_board->n_aochan; i++) {
811 devpriv->DacCmdStatus &= 0xfcce;
813 /* Set channel number */
814 devpriv->DacCmdStatus |= (i << 8);
816 /* Output 0V */
817 writew(0, devpriv->io_addr + ICP_MULTI_AO);
819 /* Set start conversion bit */
820 devpriv->DacCmdStatus |= DAC_ST;
822 /* Output to command / status register */
823 writew(devpriv->DacCmdStatus,
824 devpriv->io_addr + ICP_MULTI_DAC_CSR);
826 /* Delay to allow DAC time to recover */
827 udelay(1);
829 /* Digital outputs to 0 */
830 writew(0, devpriv->io_addr + ICP_MULTI_DO);
832 #ifdef ICP_MULTI_EXTDEBUG
833 printk(KERN_DEBUG
834 "icp multi EDBG: END: icp_multi_reset(...)\n");
835 #endif
836 return 0;
840 ==============================================================================
842 Name: icp_multi_attach
844 Description:
845 This function sets up all the appropriate data for the current
846 device.
848 Parameters:
849 struct comedi_device *dev Pointer to current device structure
850 struct comedi_devconfig *it Pointer to current device configuration
852 Returns:int 0 = success
854 ==============================================================================
856 static int icp_multi_attach(struct comedi_device *dev,
857 struct comedi_devconfig *it)
859 struct comedi_subdevice *s;
860 int ret, subdev, n_subdevices;
861 unsigned int irq;
862 struct pcilst_struct *card = NULL;
863 resource_size_t io_addr[5], iobase;
864 unsigned char pci_bus, pci_slot, pci_func;
866 printk(KERN_WARNING
867 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
869 /* Alocate private data storage space */
870 ret = alloc_private(dev, sizeof(struct icp_multi_private));
871 if (ret < 0)
872 return ret;
874 /* Initialise list of PCI cards in system, if not already done so */
875 if (pci_list_builded++ == 0) {
876 pci_card_list_init(PCI_VENDOR_ID_ICP,
877 #ifdef ICP_MULTI_EXTDEBUG
879 #else
881 #endif
885 printk(KERN_WARNING
886 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
887 this_board->name);
889 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
890 this_board->device_id, it->options[0],
891 it->options[1]);
893 if (card == NULL)
894 return -EIO;
896 devpriv->card = card;
898 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
899 &irq)) < 0) {
900 printk(KERN_WARNING " - Can't get configuration data!\n");
901 return -EIO;
904 iobase = io_addr[2];
905 devpriv->phys_iobase = iobase;
907 printk(KERN_WARNING
908 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
909 (unsigned long long)iobase);
911 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
913 if (devpriv->io_addr == NULL) {
914 printk(KERN_WARNING "ioremap failed.\n");
915 return -ENOMEM;
917 #ifdef ICP_MULTI_EXTDEBUG
918 printk(KERN_DEBUG
919 "0x%08llx mapped to %p, ", (unsigned long long)iobase,
920 devpriv->io_addr);
921 #endif
923 dev->board_name = this_board->name;
925 n_subdevices = 0;
926 if (this_board->n_aichan)
927 n_subdevices++;
928 if (this_board->n_aochan)
929 n_subdevices++;
930 if (this_board->n_dichan)
931 n_subdevices++;
932 if (this_board->n_dochan)
933 n_subdevices++;
934 if (this_board->n_ctrs)
935 n_subdevices++;
937 ret = alloc_subdevices(dev, n_subdevices);
938 if (ret < 0)
939 return ret;
941 icp_multi_reset(dev);
943 if (this_board->have_irq) {
944 if (irq) {
945 if (request_irq(irq, interrupt_service_icp_multi,
946 IRQF_SHARED, "Inova Icp Multi", dev)) {
947 printk(KERN_WARNING
948 "unable to allocate IRQ %u, DISABLING IT",
949 irq);
950 irq = 0; /* Can't use IRQ */
951 } else
952 printk(KERN_WARNING ", irq=%u", irq);
953 } else
954 printk(KERN_WARNING ", IRQ disabled");
955 } else
956 irq = 0;
958 dev->irq = irq;
960 printk(KERN_WARNING ".\n");
962 subdev = 0;
964 if (this_board->n_aichan) {
965 s = dev->subdevices + subdev;
966 dev->read_subdev = s;
967 s->type = COMEDI_SUBD_AI;
968 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
969 if (this_board->n_aichand)
970 s->subdev_flags |= SDF_DIFF;
971 s->n_chan = this_board->n_aichan;
972 s->maxdata = this_board->ai_maxdata;
973 s->len_chanlist = this_board->n_aichan;
974 s->range_table = this_board->rangelist_ai;
975 s->insn_read = icp_multi_insn_read_ai;
976 subdev++;
979 if (this_board->n_aochan) {
980 s = dev->subdevices + subdev;
981 s->type = COMEDI_SUBD_AO;
982 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
983 s->n_chan = this_board->n_aochan;
984 s->maxdata = this_board->ao_maxdata;
985 s->len_chanlist = this_board->n_aochan;
986 s->range_table = this_board->rangelist_ao;
987 s->insn_write = icp_multi_insn_write_ao;
988 s->insn_read = icp_multi_insn_read_ao;
989 subdev++;
992 if (this_board->n_dichan) {
993 s = dev->subdevices + subdev;
994 s->type = COMEDI_SUBD_DI;
995 s->subdev_flags = SDF_READABLE;
996 s->n_chan = this_board->n_dichan;
997 s->maxdata = 1;
998 s->len_chanlist = this_board->n_dichan;
999 s->range_table = &range_digital;
1000 s->io_bits = 0;
1001 s->insn_bits = icp_multi_insn_bits_di;
1002 subdev++;
1005 if (this_board->n_dochan) {
1006 s = dev->subdevices + subdev;
1007 s->type = COMEDI_SUBD_DO;
1008 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1009 s->n_chan = this_board->n_dochan;
1010 s->maxdata = 1;
1011 s->len_chanlist = this_board->n_dochan;
1012 s->range_table = &range_digital;
1013 s->io_bits = (1 << this_board->n_dochan) - 1;
1014 s->state = 0;
1015 s->insn_bits = icp_multi_insn_bits_do;
1016 subdev++;
1019 if (this_board->n_ctrs) {
1020 s = dev->subdevices + subdev;
1021 s->type = COMEDI_SUBD_COUNTER;
1022 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1023 s->n_chan = this_board->n_ctrs;
1024 s->maxdata = 0xffff;
1025 s->len_chanlist = this_board->n_ctrs;
1026 s->state = 0;
1027 s->insn_read = icp_multi_insn_read_ctr;
1028 s->insn_write = icp_multi_insn_write_ctr;
1029 subdev++;
1032 devpriv->valid = 1;
1034 #ifdef ICP_MULTI_EXTDEBUG
1035 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
1036 #endif
1038 return 0;
1042 ==============================================================================
1044 Name: icp_multi_detach
1046 Description:
1047 This function releases all the resources used by the current
1048 device.
1050 Parameters:
1051 struct comedi_device *dev Pointer to current device structure
1053 Returns:int 0 = success
1055 ==============================================================================
1057 static int icp_multi_detach(struct comedi_device *dev)
1060 if (dev->private)
1061 if (devpriv->valid)
1062 icp_multi_reset(dev);
1064 if (dev->irq)
1065 free_irq(dev->irq, dev);
1067 if (dev->private && devpriv->io_addr)
1068 iounmap(devpriv->io_addr);
1070 if (dev->private && devpriv->card)
1071 pci_card_free(devpriv->card);
1073 if (--pci_list_builded == 0)
1074 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1076 return 0;
1079 MODULE_AUTHOR("Comedi http://www.comedi.org");
1080 MODULE_DESCRIPTION("Comedi low-level driver");
1081 MODULE_LICENSE("GPL");