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, struct comedi_devconfig *it);
128 static int icp_multi_detach(struct comedi_device *dev);
131 ==============================================================================
132 Data & Structure declarations
133 ==============================================================================
135 static unsigned short pci_list_builded = 0; /*>0 list of card is known */
138 const char *name; /* driver name */
140 int iorange; /* I/O range len */
141 char have_irq; /* 1=card support IRQ */
142 char cardtype; /* 0=ICP Multi */
143 int n_aichan; /* num of A/D chans */
144 int n_aichand; /* num of A/D chans in diff mode */
145 int n_aochan; /* num of D/A chans */
146 int n_dichan; /* num of DI chans */
147 int n_dochan; /* num of DO chans */
148 int n_ctrs; /* num of counters */
149 int ai_maxdata; /* resolution of A/D */
150 int ao_maxdata; /* resolution of D/A */
151 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
152 const char *rangecode; /* range codes for programming */
153 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
156 static const struct boardtype boardtypes[] = {
157 {"icp_multi", /* Driver name */
158 DEVICE_ID, /* PCI device ID */
159 IORANGE_ICP_MULTI, /* I/O range length */
160 1, /* 1=Card supports interrupts */
161 TYPE_ICP_MULTI, /* Card type = ICP MULTI */
162 16, /* Num of A/D channels */
163 8, /* Num of A/D channels in diff mode */
164 4, /* Num of D/A channels */
165 16, /* Num of digital inputs */
166 8, /* Num of digital outputs */
167 4, /* Num of counters */
168 0x0fff, /* Resolution of A/D */
169 0x0fff, /* Resolution of D/A */
170 &range_analog, /* Rangelist for A/D */
171 range_codes_analog, /* Range codes for programming */
172 &range_analog}, /* Rangelist for D/A */
175 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
177 static struct comedi_driver driver_icp_multi = {
178 driver_name:"icp_multi",
179 module : THIS_MODULE,
180 attach : icp_multi_attach,
181 detach : icp_multi_detach,
182 num_names : n_boardtypes,
183 board_name : &boardtypes[0].name,
184 offset : sizeof(struct boardtype),
187 COMEDI_INITCLEANUP(driver_icp_multi);
189 struct icp_multi_private {
190 struct pcilst_struct *card; /* pointer to card */
191 char valid; /* card is usable */
192 void *io_addr; /* Pointer to mapped io address */
193 resource_size_t phys_iobase; /* Physical io address */
194 unsigned int AdcCmdStatus; /* ADC Command/Status register */
195 unsigned int DacCmdStatus; /* DAC Command/Status register */
196 unsigned int IntEnable; /* Interrupt Enable register */
197 unsigned int IntStatus; /* Interrupt Status register */
198 unsigned int act_chanlist[32]; /* list of scaned channel */
199 unsigned char act_chanlist_len; /* len of scanlist */
200 unsigned char act_chanlist_pos; /* actual position in MUX list */
201 unsigned int *ai_chanlist; /* actaul chanlist */
202 short *ai_data; /* data buffer */
203 short ao_data[4]; /* data output buffer */
204 short di_data; /* Digital input data */
205 unsigned int do_data; /* Remember digital output data */
208 #define devpriv ((struct icp_multi_private *)dev->private)
209 #define this_board ((const struct boardtype *)dev->board_ptr)
212 ==============================================================================
213 More forward declarations
214 ==============================================================================
218 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
219 unsigned int *chanlist, unsigned int n_chan);
221 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
222 unsigned int *chanlist, unsigned int n_chan);
223 static int icp_multi_reset(struct comedi_device *dev);
226 ==============================================================================
228 ==============================================================================
232 ==============================================================================
234 Name: icp_multi_insn_read_ai
237 This function reads a single analogue input.
240 struct comedi_device *dev Pointer to current device structure
241 struct comedi_subdevice *s Pointer to current subdevice structure
242 struct comedi_insn *insn Pointer to current comedi instruction
243 unsigned int *data Pointer to analogue input data
245 Returns:int Nmuber of instructions executed
247 ==============================================================================
249 static int icp_multi_insn_read_ai(struct comedi_device *dev, struct comedi_subdevice *s,
250 struct comedi_insn *insn, unsigned int *data)
254 #ifdef ICP_MULTI_EXTDEBUG
255 printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
257 /* Disable A/D conversion ready interrupt */
258 devpriv->IntEnable &= ~ADC_READY;
259 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
261 /* Clear interrupt status */
262 devpriv->IntStatus |= ADC_READY;
263 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
265 /* Set up appropriate channel, mode and range data, for specified channel */
266 setup_channel_list(dev, s, &insn->chanspec, 1);
268 #ifdef ICP_MULTI_EXTDEBUG
269 printk("icp_multi A ST=%4x IO=%p\n",
270 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
271 devpriv->io_addr + ICP_MULTI_ADC_CSR);
274 for (n = 0; n < insn->n; n++) {
275 /* Set start ADC bit */
276 devpriv->AdcCmdStatus |= ADC_ST;
277 writew(devpriv->AdcCmdStatus,
278 devpriv->io_addr + ICP_MULTI_ADC_CSR);
279 devpriv->AdcCmdStatus &= ~ADC_ST;
281 #ifdef ICP_MULTI_EXTDEBUG
282 printk("icp multi B n=%d ST=%4x\n", n,
283 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
288 #ifdef ICP_MULTI_EXTDEBUG
289 printk("icp multi C n=%d ST=%4x\n", n,
290 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
293 /* Wait for conversion to complete, or get fed up waiting */
296 if (!(readw(devpriv->io_addr +
297 ICP_MULTI_ADC_CSR) & ADC_BSY))
300 #ifdef ICP_MULTI_EXTDEBUG
302 printk("icp multi D n=%d tm=%d ST=%4x\n", n,
304 readw(devpriv->io_addr +
311 /* If we reach here, a timeout has occurred */
312 comedi_error(dev, "A/D insn timeout");
314 /* Disable interrupt */
315 devpriv->IntEnable &= ~ADC_READY;
316 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
318 /* Clear interrupt status */
319 devpriv->IntStatus |= ADC_READY;
320 writew(devpriv->IntStatus,
321 devpriv->io_addr + ICP_MULTI_INT_STAT);
323 /* Clear data received */
326 #ifdef ICP_MULTI_EXTDEBUG
327 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
333 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
336 /* Disable interrupt */
337 devpriv->IntEnable &= ~ADC_READY;
338 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
340 /* Clear interrupt status */
341 devpriv->IntStatus |= ADC_READY;
342 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
344 #ifdef ICP_MULTI_EXTDEBUG
345 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
351 ==============================================================================
353 Name: icp_multi_insn_write_ao
356 This function writes a single analogue output.
359 struct comedi_device *dev Pointer to current device structure
360 struct comedi_subdevice *s Pointer to current subdevice structure
361 struct comedi_insn *insn Pointer to current comedi instruction
362 unsigned int *data Pointer to analogue output data
364 Returns:int Nmuber of instructions executed
366 ==============================================================================
368 static int icp_multi_insn_write_ao(struct comedi_device *dev, struct comedi_subdevice *s,
369 struct comedi_insn *insn, unsigned int *data)
371 int n, chan, range, timeout;
373 #ifdef ICP_MULTI_EXTDEBUG
374 printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
376 /* Disable D/A conversion ready interrupt */
377 devpriv->IntEnable &= ~DAC_READY;
378 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
380 /* Clear interrupt status */
381 devpriv->IntStatus |= DAC_READY;
382 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
384 /* Get channel number and range */
385 chan = CR_CHAN(insn->chanspec);
386 range = CR_RANGE(insn->chanspec);
388 /* Set up range and channel data */
389 /* Bit 4 = 1 : Bipolar */
391 /* Bit 5 = 1 : 10V */
392 /* Bits 8-9 : Channel number */
393 devpriv->DacCmdStatus &= 0xfccf;
394 devpriv->DacCmdStatus |= this_board->rangecode[range];
395 devpriv->DacCmdStatus |= (chan << 8);
397 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
399 for (n = 0; n < insn->n; n++) {
400 /* Wait for analogue output data register to be ready for new data, or get fed up waiting */
403 if (!(readw(devpriv->io_addr +
404 ICP_MULTI_DAC_CSR) & DAC_BSY))
407 #ifdef ICP_MULTI_EXTDEBUG
409 printk("icp multi A n=%d tm=%d ST=%4x\n", n,
411 readw(devpriv->io_addr +
418 /* If we reach here, a timeout has occurred */
419 comedi_error(dev, "D/A insn timeout");
421 /* Disable interrupt */
422 devpriv->IntEnable &= ~DAC_READY;
423 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
425 /* Clear interrupt status */
426 devpriv->IntStatus |= DAC_READY;
427 writew(devpriv->IntStatus,
428 devpriv->io_addr + ICP_MULTI_INT_STAT);
430 /* Clear data received */
431 devpriv->ao_data[chan] = 0;
433 #ifdef ICP_MULTI_EXTDEBUG
434 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
439 /* Write data to analogue output data register */
440 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
442 /* Set DAC_ST bit to write the data to selected channel */
443 devpriv->DacCmdStatus |= DAC_ST;
444 writew(devpriv->DacCmdStatus,
445 devpriv->io_addr + ICP_MULTI_DAC_CSR);
446 devpriv->DacCmdStatus &= ~DAC_ST;
448 /* Save analogue output data */
449 devpriv->ao_data[chan] = data[n];
452 #ifdef ICP_MULTI_EXTDEBUG
453 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
459 ==============================================================================
461 Name: icp_multi_insn_read_ao
464 This function reads a single analogue output.
467 struct comedi_device *dev Pointer to current device structure
468 struct comedi_subdevice *s Pointer to current subdevice structure
469 struct comedi_insn *insn Pointer to current comedi instruction
470 unsigned int *data Pointer to analogue output data
472 Returns:int Nmuber of instructions executed
474 ==============================================================================
476 static int icp_multi_insn_read_ao(struct comedi_device *dev, struct comedi_subdevice *s,
477 struct comedi_insn *insn, unsigned int *data)
481 /* Get channel number */
482 chan = CR_CHAN(insn->chanspec);
484 /* Read analogue outputs */
485 for (n = 0; n < insn->n; n++)
486 data[n] = devpriv->ao_data[chan];
492 ==============================================================================
494 Name: icp_multi_insn_bits_di
497 This function reads the digital inputs.
500 struct comedi_device *dev Pointer to current device structure
501 struct comedi_subdevice *s Pointer to current subdevice structure
502 struct comedi_insn *insn Pointer to current comedi instruction
503 unsigned int *data Pointer to analogue output data
505 Returns:int Nmuber of instructions executed
507 ==============================================================================
509 static int icp_multi_insn_bits_di(struct comedi_device *dev, struct comedi_subdevice *s,
510 struct comedi_insn *insn, unsigned int *data)
512 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
518 ==============================================================================
520 Name: icp_multi_insn_bits_do
523 This function writes the appropriate digital outputs.
526 struct comedi_device *dev Pointer to current device structure
527 struct comedi_subdevice *s Pointer to current subdevice structure
528 struct comedi_insn *insn Pointer to current comedi instruction
529 unsigned int *data Pointer to analogue output data
531 Returns:int Nmuber of instructions executed
533 ==============================================================================
535 static int icp_multi_insn_bits_do(struct comedi_device *dev, struct comedi_subdevice *s,
536 struct comedi_insn *insn, unsigned int *data)
538 #ifdef ICP_MULTI_EXTDEBUG
539 printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
543 s->state &= ~data[0];
544 s->state |= (data[0] & data[1]);
546 printk("Digital outputs = %4x \n", s->state);
548 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
551 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
553 #ifdef ICP_MULTI_EXTDEBUG
554 printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
560 ==============================================================================
562 Name: icp_multi_insn_read_ctr
565 This function reads the specified counter.
568 struct comedi_device *dev Pointer to current device structure
569 struct comedi_subdevice *s Pointer to current subdevice structure
570 struct comedi_insn *insn Pointer to current comedi instruction
571 unsigned int *data Pointer to counter data
573 Returns:int Nmuber of instructions executed
575 ==============================================================================
577 static int icp_multi_insn_read_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
578 struct comedi_insn *insn, unsigned int *data)
584 ==============================================================================
586 Name: icp_multi_insn_write_ctr
589 This function write to the specified counter.
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_write_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
602 struct comedi_insn *insn, unsigned int *data)
608 ==============================================================================
610 Name: interrupt_service_icp_multi
613 This function is the interrupt service routine for all
614 interrupts generated by the icp multi board.
618 void *d Pointer to current device
620 ==============================================================================
622 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
624 struct comedi_device *dev = d;
627 #ifdef ICP_MULTI_EXTDEBUG
628 printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
632 /* Is this interrupt from our board? */
633 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
638 #ifdef ICP_MULTI_EXTDEBUG
639 printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
640 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
643 /* Determine which interrupt is active & handle it */
666 #ifdef ICP_MULTI_EXTDEBUG
667 printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
674 ==============================================================================
676 Name: check_channel_list
679 This function checks if the channel list, provided by user
683 struct comedi_device *dev Pointer to current sevice structure
684 struct comedi_subdevice *s Pointer to current subdevice structure
685 unsigned int *chanlist Pointer to packed channel list
686 unsigned int n_chan Number of channels to scan
688 Returns:int 0 = failure
691 ==============================================================================
693 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
694 unsigned int *chanlist, unsigned int n_chan)
698 #ifdef ICP_MULTI_EXTDEBUG
699 printk("icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
701 /* Check that we at least have one channel to check */
703 comedi_error(dev, "range/channel list is empty!");
706 /* Check all channels */
707 for (i = 0; i < n_chan; i++) {
708 /* Check that channel number is < maximum */
709 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
710 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
712 "Incorrect differential ai channel number");
716 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
718 "Incorrect ai channel number");
728 ==============================================================================
730 Name: setup_channel_list
733 This function sets the appropriate channel selection,
734 differential input mode and range bits in the ADC Command/
738 struct comedi_device *dev Pointer to current sevice structure
739 struct comedi_subdevice *s Pointer to current subdevice structure
740 unsigned int *chanlist Pointer to packed channel list
741 unsigned int n_chan Number of channels to scan
745 ==============================================================================
747 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
748 unsigned int *chanlist, unsigned int n_chan)
750 unsigned int i, range, chanprog;
753 #ifdef ICP_MULTI_EXTDEBUG
754 printk("icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
756 devpriv->act_chanlist_len = n_chan;
757 devpriv->act_chanlist_pos = 0;
759 for (i = 0; i < n_chan; i++) {
761 chanprog = CR_CHAN(chanlist[i]);
763 /* Determine if it is a differential channel (Bit 15 = 1) */
764 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
772 /* Clear channel, range and input mode bits in A/D command/status register */
773 devpriv->AdcCmdStatus &= 0xf00f;
775 /* Set channel number and differential mode status bit */
777 /* Set channel number, bits 9-11 & mode, bit 6 */
778 devpriv->AdcCmdStatus |= (chanprog << 9);
779 devpriv->AdcCmdStatus |= ADC_DI;
781 /* Set channel number, bits 8-11 */
782 devpriv->AdcCmdStatus |= (chanprog << 8);
784 /* Get range for current channel */
785 range = this_board->rangecode[CR_RANGE(chanlist[i])];
786 /* Set range. bits 4-5 */
787 devpriv->AdcCmdStatus |= range;
789 /* Output channel, range, mode to ICP Multi */
790 writew(devpriv->AdcCmdStatus,
791 devpriv->io_addr + ICP_MULTI_ADC_CSR);
793 #ifdef ICP_MULTI_EXTDEBUG
794 printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
795 devpriv->act_chanlist[i]);
802 ==============================================================================
804 Name: icp_multi_reset
807 This function resets the icp multi device to a 'safe' state
810 struct comedi_device *dev Pointer to current sevice structure
812 Returns:int 0 = success
814 ==============================================================================
816 static int icp_multi_reset(struct comedi_device *dev)
820 #ifdef ICP_MULTI_EXTDEBUG
821 printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
823 /* Clear INT enables and requests */
824 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
825 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
827 if (this_board->n_aochan)
828 /* Set DACs to 0..5V range and 0V output */
829 for (i = 0; i < this_board->n_aochan; i++) {
830 devpriv->DacCmdStatus &= 0xfcce;
832 /* Set channel number */
833 devpriv->DacCmdStatus |= (i << 8);
836 writew(0, devpriv->io_addr + ICP_MULTI_AO);
838 /* Set start conversion bit */
839 devpriv->DacCmdStatus |= DAC_ST;
841 /* Output to command / status register */
842 writew(devpriv->DacCmdStatus,
843 devpriv->io_addr + ICP_MULTI_DAC_CSR);
845 /* Delay to allow DAC time to recover */
848 /* Digital outputs to 0 */
849 writew(0, devpriv->io_addr + ICP_MULTI_DO);
851 #ifdef ICP_MULTI_EXTDEBUG
852 printk("icp multi EDBG: END: icp_multi_reset(...)\n");
858 ==============================================================================
860 Name: icp_multi_attach
863 This function sets up all the appropriate data for the current
867 struct comedi_device *dev Pointer to current device structure
868 struct comedi_devconfig *it Pointer to current device configuration
870 Returns:int 0 = success
872 ==============================================================================
874 static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it)
876 struct comedi_subdevice *s;
877 int ret, subdev, n_subdevices;
879 struct pcilst_struct *card = NULL;
880 resource_size_t io_addr[5], iobase;
881 unsigned char pci_bus, pci_slot, pci_func;
883 printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
885 /* Alocate private data storage space */
886 ret = alloc_private(dev, sizeof(struct icp_multi_private));
890 /* Initialise list of PCI cards in system, if not already done so */
891 if (pci_list_builded++ == 0) {
892 pci_card_list_init(PCI_VENDOR_ID_ICP,
893 #ifdef ICP_MULTI_EXTDEBUG
901 printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
904 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
905 this_board->device_id, it->options[0],
911 devpriv->card = card;
913 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
915 printk(" - Can't get configuration data!\n");
920 devpriv->phys_iobase = iobase;
922 printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
923 (unsigned long long)iobase);
925 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
927 if (devpriv->io_addr == NULL) {
928 printk("ioremap failed.\n");
931 #ifdef ICP_MULTI_EXTDEBUG
932 printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
936 dev->board_name = this_board->name;
939 if (this_board->n_aichan)
941 if (this_board->n_aochan)
943 if (this_board->n_dichan)
945 if (this_board->n_dochan)
947 if (this_board->n_ctrs)
950 ret = alloc_subdevices(dev, n_subdevices);
954 icp_multi_reset(dev);
956 if (this_board->have_irq) {
958 if (request_irq(irq, interrupt_service_icp_multi,
959 IRQF_SHARED, "Inova Icp Multi", dev)) {
960 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
961 irq = 0; /* Can't use IRQ */
963 printk(", irq=%u", irq);
965 printk(", IRQ disabled");
975 if (this_board->n_aichan) {
976 s = dev->subdevices + subdev;
977 dev->read_subdev = s;
978 s->type = COMEDI_SUBD_AI;
979 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
980 if (this_board->n_aichand)
981 s->subdev_flags |= SDF_DIFF;
982 s->n_chan = this_board->n_aichan;
983 s->maxdata = this_board->ai_maxdata;
984 s->len_chanlist = this_board->n_aichan;
985 s->range_table = this_board->rangelist_ai;
986 s->insn_read = icp_multi_insn_read_ai;
990 if (this_board->n_aochan) {
991 s = dev->subdevices + subdev;
992 s->type = COMEDI_SUBD_AO;
993 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
994 s->n_chan = this_board->n_aochan;
995 s->maxdata = this_board->ao_maxdata;
996 s->len_chanlist = this_board->n_aochan;
997 s->range_table = this_board->rangelist_ao;
998 s->insn_write = icp_multi_insn_write_ao;
999 s->insn_read = icp_multi_insn_read_ao;
1003 if (this_board->n_dichan) {
1004 s = dev->subdevices + subdev;
1005 s->type = COMEDI_SUBD_DI;
1006 s->subdev_flags = SDF_READABLE;
1007 s->n_chan = this_board->n_dichan;
1009 s->len_chanlist = this_board->n_dichan;
1010 s->range_table = &range_digital;
1012 s->insn_bits = icp_multi_insn_bits_di;
1016 if (this_board->n_dochan) {
1017 s = dev->subdevices + subdev;
1018 s->type = COMEDI_SUBD_DO;
1019 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1020 s->n_chan = this_board->n_dochan;
1022 s->len_chanlist = this_board->n_dochan;
1023 s->range_table = &range_digital;
1024 s->io_bits = (1 << this_board->n_dochan) - 1;
1026 s->insn_bits = icp_multi_insn_bits_do;
1030 if (this_board->n_ctrs) {
1031 s = dev->subdevices + subdev;
1032 s->type = COMEDI_SUBD_COUNTER;
1033 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1034 s->n_chan = this_board->n_ctrs;
1035 s->maxdata = 0xffff;
1036 s->len_chanlist = this_board->n_ctrs;
1038 s->insn_read = icp_multi_insn_read_ctr;
1039 s->insn_write = icp_multi_insn_write_ctr;
1045 #ifdef ICP_MULTI_EXTDEBUG
1046 printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1053 ==============================================================================
1055 Name: icp_multi_detach
1058 This function releases all the resources used by the current
1062 struct comedi_device *dev Pointer to current device structure
1064 Returns:int 0 = success
1066 ==============================================================================
1068 static int icp_multi_detach(struct comedi_device *dev)
1073 icp_multi_reset(dev);
1076 free_irq(dev->irq, dev);
1078 if (dev->private && devpriv->io_addr)
1079 iounmap(devpriv->io_addr);
1081 if (dev->private && devpriv->card)
1082 pci_card_free(devpriv->card);
1084 if (--pci_list_builded == 0)
1085 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);