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.
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
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
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
48 [0] - PCI bus number - if bus number and slot number are 0,
49 then driver search for first unused card
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, {
120 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
123 ==============================================================================
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 */
139 const char *name; /* driver name */
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 ==============================================================================
230 static int check_channel_list(struct comedi_device *dev,
231 struct comedi_subdevice *s,
232 unsigned int *chanlist, unsigned int n_chan);
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);
240 ==============================================================================
242 ==============================================================================
246 ==============================================================================
248 Name: icp_multi_insn_read_ai
251 This function reads a single analogue input.
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
259 Returns:int Nmuber of instructions executed
261 ==============================================================================
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)
269 #ifdef ICP_MULTI_EXTDEBUG
270 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
272 /* Disable A/D conversion ready interrupt */
273 devpriv->IntEnable &= ~ADC_READY;
274 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
276 /* Clear interrupt status */
277 devpriv->IntStatus |= ADC_READY;
278 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
280 /* Set up appropriate channel, mode and range data, for specified ch */
281 setup_channel_list(dev, s, &insn->chanspec, 1);
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);
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;
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));
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));
308 /* Wait for conversion to complete, or get fed up waiting */
311 if (!(readw(devpriv->io_addr +
312 ICP_MULTI_ADC_CSR) & ADC_BSY))
315 #ifdef ICP_MULTI_EXTDEBUG
318 "icp multi D n=%d tm=%d ST=%4x\n", n,
320 readw(devpriv->io_addr +
327 /* If we reach here, a timeout has occurred */
328 comedi_error(dev, "A/D insn timeout");
330 /* Disable interrupt */
331 devpriv->IntEnable &= ~ADC_READY;
332 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
334 /* Clear interrupt status */
335 devpriv->IntStatus |= ADC_READY;
336 writew(devpriv->IntStatus,
337 devpriv->io_addr + ICP_MULTI_INT_STAT);
339 /* Clear data received */
342 #ifdef ICP_MULTI_EXTDEBUG
344 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
351 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
354 /* Disable interrupt */
355 devpriv->IntEnable &= ~ADC_READY;
356 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
358 /* Clear interrupt status */
359 devpriv->IntStatus |= ADC_READY;
360 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
362 #ifdef ICP_MULTI_EXTDEBUG
364 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
370 ==============================================================================
372 Name: icp_multi_insn_write_ao
375 This function writes a single analogue output.
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
383 Returns:int Nmuber of instructions executed
385 ==============================================================================
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)
391 int n, chan, range, timeout;
393 #ifdef ICP_MULTI_EXTDEBUG
395 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
397 /* Disable D/A conversion ready interrupt */
398 devpriv->IntEnable &= ~DAC_READY;
399 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
401 /* Clear interrupt status */
402 devpriv->IntStatus |= DAC_READY;
403 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
405 /* Get channel number and range */
406 chan = CR_CHAN(insn->chanspec);
407 range = CR_RANGE(insn->chanspec);
409 /* Set up range and channel data */
410 /* Bit 4 = 1 : Bipolar */
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);
418 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
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 */
425 if (!(readw(devpriv->io_addr +
426 ICP_MULTI_DAC_CSR) & DAC_BSY))
429 #ifdef ICP_MULTI_EXTDEBUG
432 "icp multi A n=%d tm=%d ST=%4x\n", n,
434 readw(devpriv->io_addr +
441 /* If we reach here, a timeout has occurred */
442 comedi_error(dev, "D/A insn timeout");
444 /* Disable interrupt */
445 devpriv->IntEnable &= ~DAC_READY;
446 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
448 /* Clear interrupt status */
449 devpriv->IntStatus |= DAC_READY;
450 writew(devpriv->IntStatus,
451 devpriv->io_addr + ICP_MULTI_INT_STAT);
453 /* Clear data received */
454 devpriv->ao_data[chan] = 0;
456 #ifdef ICP_MULTI_EXTDEBUG
458 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
464 /* Write data to analogue output data register */
465 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
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;
473 /* Save analogue output data */
474 devpriv->ao_data[chan] = data[n];
477 #ifdef ICP_MULTI_EXTDEBUG
479 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
485 ==============================================================================
487 Name: icp_multi_insn_read_ao
490 This function reads a single analogue output.
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
498 Returns:int Nmuber of instructions executed
500 ==============================================================================
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)
508 /* Get channel number */
509 chan = CR_CHAN(insn->chanspec);
511 /* Read analogue outputs */
512 for (n = 0; n < insn->n; n++)
513 data[n] = devpriv->ao_data[chan];
519 ==============================================================================
521 Name: icp_multi_insn_bits_di
524 This function reads the digital inputs.
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
532 Returns:int Nmuber of instructions executed
534 ==============================================================================
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)
540 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
546 ==============================================================================
548 Name: icp_multi_insn_bits_do
551 This function writes the appropriate digital outputs.
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
559 Returns:int Nmuber of instructions executed
561 ==============================================================================
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)
567 #ifdef ICP_MULTI_EXTDEBUG
568 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
572 s->state &= ~data[0];
573 s->state |= (data[0] & data[1]);
575 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
577 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
580 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
582 #ifdef ICP_MULTI_EXTDEBUG
583 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
589 ==============================================================================
591 Name: icp_multi_insn_read_ctr
594 This function reads the specified counter.
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
602 Returns:int Nmuber of instructions executed
604 ==============================================================================
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)
614 ==============================================================================
616 Name: icp_multi_insn_write_ctr
619 This function write to the specified counter.
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
627 Returns:int Nmuber of instructions executed
629 ==============================================================================
631 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
632 struct comedi_subdevice *s,
633 struct comedi_insn *insn,
640 ==============================================================================
642 Name: interrupt_service_icp_multi
645 This function is the interrupt service routine for all
646 interrupts generated by the icp multi board.
650 void *d Pointer to current device
652 ==============================================================================
654 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
656 struct comedi_device *dev = d;
659 #ifdef ICP_MULTI_EXTDEBUG
661 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
665 /* Is this interrupt from our board? */
666 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
671 #ifdef ICP_MULTI_EXTDEBUG
673 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
674 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
677 /* Determine which interrupt is active & handle it */
700 #ifdef ICP_MULTI_EXTDEBUG
702 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
709 ==============================================================================
711 Name: check_channel_list
714 This function checks if the channel list, provided by user
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
723 Returns:int 0 = failure
726 ==============================================================================
728 static int check_channel_list(struct comedi_device *dev,
729 struct comedi_subdevice *s,
730 unsigned int *chanlist, unsigned int n_chan)
734 #ifdef ICP_MULTI_EXTDEBUG
736 "icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
738 /* Check that we at least have one channel to check */
740 comedi_error(dev, "range/channel list is empty!");
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) {
749 "Incorrect differential ai ch-nr");
753 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
755 "Incorrect ai channel number");
765 ==============================================================================
767 Name: setup_channel_list
770 This function sets the appropriate channel selection,
771 differential input mode and range bits in the ADC Command/
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
782 ==============================================================================
784 static void setup_channel_list(struct comedi_device *dev,
785 struct comedi_subdevice *s,
786 unsigned int *chanlist, unsigned int n_chan)
788 unsigned int i, range, chanprog;
791 #ifdef ICP_MULTI_EXTDEBUG
793 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
795 devpriv->act_chanlist_len = n_chan;
796 devpriv->act_chanlist_pos = 0;
798 for (i = 0; i < n_chan; i++) {
800 chanprog = CR_CHAN(chanlist[i]);
802 /* Determine if it is a differential channel (Bit 15 = 1) */
803 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
811 /* Clear channel, range and input mode bits
812 * in A/D command/status register */
813 devpriv->AdcCmdStatus &= 0xf00f;
815 /* Set channel number and differential mode status bit */
817 /* Set channel number, bits 9-11 & mode, bit 6 */
818 devpriv->AdcCmdStatus |= (chanprog << 9);
819 devpriv->AdcCmdStatus |= ADC_DI;
821 /* Set channel number, bits 8-11 */
822 devpriv->AdcCmdStatus |= (chanprog << 8);
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;
829 /* Output channel, range, mode to ICP Multi */
830 writew(devpriv->AdcCmdStatus,
831 devpriv->io_addr + ICP_MULTI_ADC_CSR);
833 #ifdef ICP_MULTI_EXTDEBUG
835 "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
836 devpriv->act_chanlist[i]);
843 ==============================================================================
845 Name: icp_multi_reset
848 This function resets the icp multi device to a 'safe' state
851 struct comedi_device *dev Pointer to current service structure
853 Returns:int 0 = success
855 ==============================================================================
857 static int icp_multi_reset(struct comedi_device *dev)
861 #ifdef ICP_MULTI_EXTDEBUG
863 "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
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);
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;
874 /* Set channel number */
875 devpriv->DacCmdStatus |= (i << 8);
878 writew(0, devpriv->io_addr + ICP_MULTI_AO);
880 /* Set start conversion bit */
881 devpriv->DacCmdStatus |= DAC_ST;
883 /* Output to command / status register */
884 writew(devpriv->DacCmdStatus,
885 devpriv->io_addr + ICP_MULTI_DAC_CSR);
887 /* Delay to allow DAC time to recover */
890 /* Digital outputs to 0 */
891 writew(0, devpriv->io_addr + ICP_MULTI_DO);
893 #ifdef ICP_MULTI_EXTDEBUG
895 "icp multi EDBG: END: icp_multi_reset(...)\n");
901 ==============================================================================
903 Name: icp_multi_attach
906 This function sets up all the appropriate data for the current
910 struct comedi_device *dev Pointer to current device structure
911 struct comedi_devconfig *it Pointer to current device configuration
913 Returns:int 0 = success
915 ==============================================================================
917 static int icp_multi_attach(struct comedi_device *dev,
918 struct comedi_devconfig *it)
920 struct comedi_subdevice *s;
921 int ret, subdev, n_subdevices;
923 struct pcilst_struct *card = NULL;
924 resource_size_t io_addr[5], iobase;
925 unsigned char pci_bus, pci_slot, pci_func;
928 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
930 /* Alocate private data storage space */
931 ret = alloc_private(dev, sizeof(struct icp_multi_private));
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
947 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
950 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
951 this_board->device_id, it->options[0],
957 devpriv->card = card;
959 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
961 printk(KERN_WARNING " - Can't get configuration data!\n");
966 devpriv->phys_iobase = iobase;
969 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
970 (unsigned long long)iobase);
972 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
974 if (devpriv->io_addr == NULL) {
975 printk(KERN_WARNING "ioremap failed.\n");
978 #ifdef ICP_MULTI_EXTDEBUG
980 "0x%08llx mapped to %p, ", (unsigned long long)iobase,
984 dev->board_name = this_board->name;
987 if (this_board->n_aichan)
989 if (this_board->n_aochan)
991 if (this_board->n_dichan)
993 if (this_board->n_dochan)
995 if (this_board->n_ctrs)
998 ret = alloc_subdevices(dev, n_subdevices);
1002 icp_multi_reset(dev);
1004 if (this_board->have_irq) {
1006 if (request_irq(irq, interrupt_service_icp_multi,
1007 IRQF_SHARED, "Inova Icp Multi", dev)) {
1009 "unable to allocate IRQ %u, DISABLING IT",
1011 irq = 0; /* Can't use IRQ */
1013 printk(KERN_WARNING ", irq=%u", irq);
1015 printk(KERN_WARNING ", IRQ disabled");
1021 printk(KERN_WARNING ".\n");
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;
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;
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;
1059 s->len_chanlist = this_board->n_dichan;
1060 s->range_table = &range_digital;
1062 s->insn_bits = icp_multi_insn_bits_di;
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;
1072 s->len_chanlist = this_board->n_dochan;
1073 s->range_table = &range_digital;
1074 s->io_bits = (1 << this_board->n_dochan) - 1;
1076 s->insn_bits = icp_multi_insn_bits_do;
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;
1088 s->insn_read = icp_multi_insn_read_ctr;
1089 s->insn_write = icp_multi_insn_write_ctr;
1095 #ifdef ICP_MULTI_EXTDEBUG
1096 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
1103 ==============================================================================
1105 Name: icp_multi_detach
1108 This function releases all the resources used by the current
1112 struct comedi_device *dev Pointer to current device structure
1114 Returns:int 0 = success
1116 ==============================================================================
1118 static int icp_multi_detach(struct comedi_device *dev)
1123 icp_multi_reset(dev);
1126 free_irq(dev->irq, dev);
1128 if (dev->private && devpriv->io_addr)
1129 iounmap(devpriv->io_addr);
1131 if (dev->private && devpriv->card)
1132 pci_card_free(devpriv->card);
1134 if (--pci_list_builded == 0)
1135 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1140 MODULE_AUTHOR("Comedi http://www.comedi.org");
1141 MODULE_DESCRIPTION("Comedi low-level driver");
1142 MODULE_LICENSE("GPL");