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 "../comedidev.h"
55 #include <linux/delay.h>
56 #include <linux/pci.h>
58 #include "icp_multi.h"
60 #define DEVICE_ID 0x8000 /* Device ID */
62 #define ICP_MULTI_EXTDEBUG
64 // Hardware types of the cards
65 #define TYPE_ICP_MULTI 0
67 #define IORANGE_ICP_MULTI 32
69 #define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
70 #define ICP_MULTI_AI 2 /* R: Analogue input data */
71 #define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */
72 #define ICP_MULTI_AO 6 /* R/W: Analogue output data */
73 #define ICP_MULTI_DI 8 /* R/W: Digital inouts */
74 #define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */
75 #define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */
76 #define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */
77 #define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */
78 #define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */
79 #define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */
80 #define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */
82 #define ICP_MULTI_SIZE 0x20 /* 32 bytes */
84 // Define bits from ADC command/status register
85 #define ADC_ST 0x0001 /* Start ADC */
86 #define ADC_BSY 0x0001 /* ADC busy */
87 #define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
88 #define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
89 #define ADC_DI 0x0040 /* Differential input mode 1 = differential */
91 // Define bits from DAC command/status register
92 #define DAC_ST 0x0001 /* Start DAC */
93 #define DAC_BSY 0x0001 /* DAC busy */
94 #define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
95 #define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
97 // Define bits from interrupt enable/status registers
98 #define ADC_READY 0x0001 /* A/d conversion ready interrupt */
99 #define DAC_READY 0x0002 /* D/a conversion ready interrupt */
100 #define DOUT_ERROR 0x0004 /* Digital output error interrupt */
101 #define DIN_STATUS 0x0008 /* Digital input status change interrupt */
102 #define CIE0 0x0010 /* Counter 0 overrun interrupt */
103 #define CIE1 0x0020 /* Counter 1 overrun interrupt */
104 #define CIE2 0x0040 /* Counter 2 overrun interrupt */
105 #define CIE3 0x0080 /* Counter 3 overrun interrupt */
107 // Useful definitions
108 #define Status_IRQ 0x00ff // All interrupts
110 // Define analogue range
111 static const comedi_lrange range_analog = { 4, {
119 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
122 ==============================================================================
124 ==============================================================================
126 static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it);
127 static int icp_multi_detach(comedi_device * dev);
130 ==============================================================================
131 Data & Structure declarations
132 ==============================================================================
134 static unsigned short pci_list_builded = 0; /*>0 list of card is known */
137 const char *name; // driver name
139 int iorange; // I/O range len
140 char have_irq; // 1=card support IRQ
141 char cardtype; // 0=ICP Multi
142 int n_aichan; // num of A/D chans
143 int n_aichand; // num of A/D chans in diff mode
144 int n_aochan; // num of D/A chans
145 int n_dichan; // num of DI chans
146 int n_dochan; // num of DO chans
147 int n_ctrs; // num of counters
148 int ai_maxdata; // resolution of A/D
149 int ao_maxdata; // resolution of D/A
150 const comedi_lrange *rangelist_ai; // rangelist for A/D
151 const char *rangecode; // range codes for programming
152 const comedi_lrange *rangelist_ao; // rangelist for D/A
155 static const boardtype boardtypes[] = {
156 {"icp_multi", // Driver name
157 DEVICE_ID, // PCI device ID
158 IORANGE_ICP_MULTI, // I/O range length
159 1, // 1=Card supports interrupts
160 TYPE_ICP_MULTI, // Card type = ICP MULTI
161 16, // Num of A/D channels
162 8, // Num of A/D channels in diff mode
163 4, // Num of D/A channels
164 16, // Num of digital inputs
165 8, // Num of digital outputs
166 4, // Num of counters
167 0x0fff, // Resolution of A/D
168 0x0fff, // Resolution of D/A
169 &range_analog, // Rangelist for A/D
170 range_codes_analog, // Range codes for programming
171 &range_analog}, // Rangelist for D/A
174 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
176 static comedi_driver driver_icp_multi = {
177 driver_name:"icp_multi",
179 attach:icp_multi_attach,
180 detach:icp_multi_detach,
181 num_names:n_boardtypes,
182 board_name:&boardtypes[0].name,
183 offset:sizeof(boardtype),
186 COMEDI_INITCLEANUP(driver_icp_multi);
189 struct pcilst_struct *card; // pointer to card
190 char valid; // card is usable
191 void *io_addr; // Pointer to mapped io address
192 resource_size_t phys_iobase; // Physical io address
193 unsigned int AdcCmdStatus; // ADC Command/Status register
194 unsigned int DacCmdStatus; // DAC Command/Status register
195 unsigned int IntEnable; // Interrupt Enable register
196 unsigned int IntStatus; // Interrupt Status register
197 unsigned int act_chanlist[32]; // list of scaned channel
198 unsigned char act_chanlist_len; // len of scanlist
199 unsigned char act_chanlist_pos; // actual position in MUX list
200 unsigned int *ai_chanlist; // actaul chanlist
201 sampl_t *ai_data; // data buffer
202 sampl_t ao_data[4]; // data output buffer
203 sampl_t di_data; // Digital input data
204 unsigned int do_data; // Remember digital output data
207 #define devpriv ((icp_multi_private *)dev->private)
208 #define this_board ((const boardtype *)dev->board_ptr)
211 ==============================================================================
212 More forward declarations
213 ==============================================================================
217 static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
218 unsigned int *chanlist, unsigned int n_chan);
220 static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
221 unsigned int *chanlist, unsigned int n_chan);
222 static int icp_multi_reset(comedi_device * dev);
225 ==============================================================================
227 ==============================================================================
231 ==============================================================================
233 Name: icp_multi_insn_read_ai
236 This function reads a single analogue input.
239 comedi_device *dev Pointer to current device structure
240 comedi_subdevice *s Pointer to current subdevice structure
241 comedi_insn *insn Pointer to current comedi instruction
242 lsampl_t *data Pointer to analogue input data
244 Returns:int Nmuber of instructions executed
246 ==============================================================================
248 static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s,
249 comedi_insn * insn, lsampl_t * data)
253 #ifdef ICP_MULTI_EXTDEBUG
254 printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
256 // Disable A/D conversion ready interrupt
257 devpriv->IntEnable &= ~ADC_READY;
258 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
260 // Clear interrupt status
261 devpriv->IntStatus |= ADC_READY;
262 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
264 // Set up appropriate channel, mode and range data, for specified channel
265 setup_channel_list(dev, s, &insn->chanspec, 1);
267 #ifdef ICP_MULTI_EXTDEBUG
268 printk("icp_multi A ST=%4x IO=%p\n",
269 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
270 devpriv->io_addr + ICP_MULTI_ADC_CSR);
273 for (n = 0; n < insn->n; n++) {
275 devpriv->AdcCmdStatus |= ADC_ST;
276 writew(devpriv->AdcCmdStatus,
277 devpriv->io_addr + ICP_MULTI_ADC_CSR);
278 devpriv->AdcCmdStatus &= ~ADC_ST;
280 #ifdef ICP_MULTI_EXTDEBUG
281 printk("icp multi B n=%d ST=%4x\n", n,
282 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
287 #ifdef ICP_MULTI_EXTDEBUG
288 printk("icp multi C n=%d ST=%4x\n", n,
289 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
292 // Wait for conversion to complete, or get fed up waiting
295 if (!(readw(devpriv->io_addr +
296 ICP_MULTI_ADC_CSR) & ADC_BSY))
299 #ifdef ICP_MULTI_EXTDEBUG
301 printk("icp multi D n=%d tm=%d ST=%4x\n", n,
303 readw(devpriv->io_addr +
310 // If we reach here, a timeout has occurred
311 comedi_error(dev, "A/D insn timeout");
314 devpriv->IntEnable &= ~ADC_READY;
315 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
317 // Clear interrupt status
318 devpriv->IntStatus |= ADC_READY;
319 writew(devpriv->IntStatus,
320 devpriv->io_addr + ICP_MULTI_INT_STAT);
322 // Clear data received
325 #ifdef ICP_MULTI_EXTDEBUG
326 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
332 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
336 devpriv->IntEnable &= ~ADC_READY;
337 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
339 // Clear interrupt status
340 devpriv->IntStatus |= ADC_READY;
341 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
343 #ifdef ICP_MULTI_EXTDEBUG
344 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
350 ==============================================================================
352 Name: icp_multi_insn_write_ao
355 This function writes a single analogue output.
358 comedi_device *dev Pointer to current device structure
359 comedi_subdevice *s Pointer to current subdevice structure
360 comedi_insn *insn Pointer to current comedi instruction
361 lsampl_t *data Pointer to analogue output data
363 Returns:int Nmuber of instructions executed
365 ==============================================================================
367 static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s,
368 comedi_insn * insn, lsampl_t * data)
370 int n, chan, range, timeout;
372 #ifdef ICP_MULTI_EXTDEBUG
373 printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
375 // Disable D/A conversion ready interrupt
376 devpriv->IntEnable &= ~DAC_READY;
377 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
379 // Clear interrupt status
380 devpriv->IntStatus |= DAC_READY;
381 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
383 // Get channel number and range
384 chan = CR_CHAN(insn->chanspec);
385 range = CR_RANGE(insn->chanspec);
387 // Set up range and channel data
388 // Bit 4 = 1 : Bipolar
391 // Bits 8-9 : Channel number
392 devpriv->DacCmdStatus &= 0xfccf;
393 devpriv->DacCmdStatus |= this_board->rangecode[range];
394 devpriv->DacCmdStatus |= (chan << 8);
396 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
398 for (n = 0; n < insn->n; n++) {
399 // Wait for analogue output data register to be ready for new data, or get fed up waiting
402 if (!(readw(devpriv->io_addr +
403 ICP_MULTI_DAC_CSR) & DAC_BSY))
406 #ifdef ICP_MULTI_EXTDEBUG
408 printk("icp multi A n=%d tm=%d ST=%4x\n", n,
410 readw(devpriv->io_addr +
417 // If we reach here, a timeout has occurred
418 comedi_error(dev, "D/A insn timeout");
421 devpriv->IntEnable &= ~DAC_READY;
422 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
424 // Clear interrupt status
425 devpriv->IntStatus |= DAC_READY;
426 writew(devpriv->IntStatus,
427 devpriv->io_addr + ICP_MULTI_INT_STAT);
429 // Clear data received
430 devpriv->ao_data[chan] = 0;
432 #ifdef ICP_MULTI_EXTDEBUG
433 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
438 // Write data to analogue output data register
439 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
441 // Set DAC_ST bit to write the data to selected channel
442 devpriv->DacCmdStatus |= DAC_ST;
443 writew(devpriv->DacCmdStatus,
444 devpriv->io_addr + ICP_MULTI_DAC_CSR);
445 devpriv->DacCmdStatus &= ~DAC_ST;
447 // Save analogue output data
448 devpriv->ao_data[chan] = data[n];
451 #ifdef ICP_MULTI_EXTDEBUG
452 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
458 ==============================================================================
460 Name: icp_multi_insn_read_ao
463 This function reads a single analogue output.
466 comedi_device *dev Pointer to current device structure
467 comedi_subdevice *s Pointer to current subdevice structure
468 comedi_insn *insn Pointer to current comedi instruction
469 lsampl_t *data Pointer to analogue output data
471 Returns:int Nmuber of instructions executed
473 ==============================================================================
475 static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s,
476 comedi_insn * insn, lsampl_t * data)
480 // Get channel number
481 chan = CR_CHAN(insn->chanspec);
483 // Read analogue outputs
484 for (n = 0; n < insn->n; n++)
485 data[n] = devpriv->ao_data[chan];
491 ==============================================================================
493 Name: icp_multi_insn_bits_di
496 This function reads the digital inputs.
499 comedi_device *dev Pointer to current device structure
500 comedi_subdevice *s Pointer to current subdevice structure
501 comedi_insn *insn Pointer to current comedi instruction
502 lsampl_t *data Pointer to analogue output data
504 Returns:int Nmuber of instructions executed
506 ==============================================================================
508 static int icp_multi_insn_bits_di(comedi_device * dev, comedi_subdevice * s,
509 comedi_insn * insn, lsampl_t * data)
511 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
517 ==============================================================================
519 Name: icp_multi_insn_bits_do
522 This function writes the appropriate digital outputs.
525 comedi_device *dev Pointer to current device structure
526 comedi_subdevice *s Pointer to current subdevice structure
527 comedi_insn *insn Pointer to current comedi instruction
528 lsampl_t *data Pointer to analogue output data
530 Returns:int Nmuber of instructions executed
532 ==============================================================================
534 static int icp_multi_insn_bits_do(comedi_device * dev, comedi_subdevice * s,
535 comedi_insn * insn, lsampl_t * data)
537 #ifdef ICP_MULTI_EXTDEBUG
538 printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
542 s->state &= ~data[0];
543 s->state |= (data[0] & data[1]);
545 printk("Digital outputs = %4x \n", s->state);
547 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
550 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
552 #ifdef ICP_MULTI_EXTDEBUG
553 printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
559 ==============================================================================
561 Name: icp_multi_insn_read_ctr
564 This function reads the specified counter.
567 comedi_device *dev Pointer to current device structure
568 comedi_subdevice *s Pointer to current subdevice structure
569 comedi_insn *insn Pointer to current comedi instruction
570 lsampl_t *data Pointer to counter data
572 Returns:int Nmuber of instructions executed
574 ==============================================================================
576 static int icp_multi_insn_read_ctr(comedi_device * dev, comedi_subdevice * s,
577 comedi_insn * insn, lsampl_t * data)
583 ==============================================================================
585 Name: icp_multi_insn_write_ctr
588 This function write to the specified counter.
591 comedi_device *dev Pointer to current device structure
592 comedi_subdevice *s Pointer to current subdevice structure
593 comedi_insn *insn Pointer to current comedi instruction
594 lsampl_t *data Pointer to counter data
596 Returns:int Nmuber of instructions executed
598 ==============================================================================
600 static int icp_multi_insn_write_ctr(comedi_device * dev, comedi_subdevice * s,
601 comedi_insn * insn, lsampl_t * data)
607 ==============================================================================
609 Name: interrupt_service_icp_multi
612 This function is the interrupt service routine for all
613 interrupts generated by the icp multi board.
617 void *d Pointer to current device
619 ==============================================================================
621 static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
623 comedi_device *dev = d;
626 #ifdef ICP_MULTI_EXTDEBUG
627 printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
631 // Is this interrupt from our board?
632 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
637 #ifdef ICP_MULTI_EXTDEBUG
638 printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
639 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
642 // Determine which interrupt is active & handle it
665 #ifdef ICP_MULTI_EXTDEBUG
666 printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
673 ==============================================================================
675 Name: check_channel_list
678 This function checks if the channel list, provided by user
682 comedi_device *dev Pointer to current sevice structure
683 comedi_subdevice *s Pointer to current subdevice structure
684 unsigned int *chanlist Pointer to packed channel list
685 unsigned int n_chan Number of channels to scan
687 Returns:int 0 = failure
690 ==============================================================================
692 static int check_channel_list(comedi_device * dev, comedi_subdevice * s,
693 unsigned int *chanlist, unsigned int n_chan)
697 #ifdef ICP_MULTI_EXTDEBUG
698 printk("icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
700 // Check that we at least have one channel to check
702 comedi_error(dev, "range/channel list is empty!");
705 // Check all channels
706 for (i = 0; i < n_chan; i++) {
707 // Check that channel number is < maximum
708 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
709 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
711 "Incorrect differential ai channel number");
715 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
717 "Incorrect ai channel number");
727 ==============================================================================
729 Name: setup_channel_list
732 This function sets the appropriate channel selection,
733 differential input mode and range bits in the ADC Command/
737 comedi_device *dev Pointer to current sevice structure
738 comedi_subdevice *s Pointer to current subdevice structure
739 unsigned int *chanlist Pointer to packed channel list
740 unsigned int n_chan Number of channels to scan
744 ==============================================================================
746 static void setup_channel_list(comedi_device * dev, comedi_subdevice * s,
747 unsigned int *chanlist, unsigned int n_chan)
749 unsigned int i, range, chanprog;
752 #ifdef ICP_MULTI_EXTDEBUG
753 printk("icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
755 devpriv->act_chanlist_len = n_chan;
756 devpriv->act_chanlist_pos = 0;
758 for (i = 0; i < n_chan; i++) {
760 chanprog = CR_CHAN(chanlist[i]);
762 // Determine if it is a differential channel (Bit 15 = 1)
763 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
771 // Clear channel, range and input mode bits in A/D command/status register
772 devpriv->AdcCmdStatus &= 0xf00f;
774 // Set channel number and differential mode status bit
776 // Set channel number, bits 9-11 & mode, bit 6
777 devpriv->AdcCmdStatus |= (chanprog << 9);
778 devpriv->AdcCmdStatus |= ADC_DI;
780 // Set channel number, bits 8-11
781 devpriv->AdcCmdStatus |= (chanprog << 8);
783 // Get range for current channel
784 range = this_board->rangecode[CR_RANGE(chanlist[i])];
785 // Set range. bits 4-5
786 devpriv->AdcCmdStatus |= range;
788 /* Output channel, range, mode to ICP Multi */
789 writew(devpriv->AdcCmdStatus,
790 devpriv->io_addr + ICP_MULTI_ADC_CSR);
792 #ifdef ICP_MULTI_EXTDEBUG
793 printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
794 devpriv->act_chanlist[i]);
801 ==============================================================================
803 Name: icp_multi_reset
806 This function resets the icp multi device to a 'safe' state
809 comedi_device *dev Pointer to current sevice structure
811 Returns:int 0 = success
813 ==============================================================================
815 static int icp_multi_reset(comedi_device * dev)
819 #ifdef ICP_MULTI_EXTDEBUG
820 printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
822 // Clear INT enables and requests
823 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
824 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
826 if (this_board->n_aochan)
827 // Set DACs to 0..5V range and 0V output
828 for (i = 0; i < this_board->n_aochan; i++) {
829 devpriv->DacCmdStatus &= 0xfcce;
831 // Set channel number
832 devpriv->DacCmdStatus |= (i << 8);
835 writew(0, devpriv->io_addr + ICP_MULTI_AO);
837 // Set start conversion bit
838 devpriv->DacCmdStatus |= DAC_ST;
840 // Output to command / status register
841 writew(devpriv->DacCmdStatus,
842 devpriv->io_addr + ICP_MULTI_DAC_CSR);
844 // Delay to allow DAC time to recover
847 // Digital outputs to 0
848 writew(0, devpriv->io_addr + ICP_MULTI_DO);
850 #ifdef ICP_MULTI_EXTDEBUG
851 printk("icp multi EDBG: END: icp_multi_reset(...)\n");
857 ==============================================================================
859 Name: icp_multi_attach
862 This function sets up all the appropriate data for the current
866 comedi_device *dev Pointer to current device structure
867 comedi_devconfig *it Pointer to current device configuration
869 Returns:int 0 = success
871 ==============================================================================
873 static int icp_multi_attach(comedi_device * dev, comedi_devconfig * it)
876 int ret, subdev, n_subdevices;
878 struct pcilst_struct *card = NULL;
879 resource_size_t io_addr[5], iobase;
880 unsigned char pci_bus, pci_slot, pci_func;
882 printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
884 // Alocate private data storage space
885 if ((ret = alloc_private(dev, sizeof(icp_multi_private))) < 0)
888 // Initialise list of PCI cards in system, if not already done so
889 if (pci_list_builded++ == 0) {
890 pci_card_list_init(PCI_VENDOR_ID_ICP,
891 #ifdef ICP_MULTI_EXTDEBUG
899 printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
902 if ((card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
903 this_board->device_id, it->options[0],
904 it->options[1])) == NULL)
907 devpriv->card = card;
909 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
911 printk(" - Can't get configuration data!\n");
916 devpriv->phys_iobase = iobase;
918 printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
919 (unsigned long long)iobase);
921 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
923 if (devpriv->io_addr == NULL) {
924 printk("ioremap failed.\n");
927 #ifdef ICP_MULTI_EXTDEBUG
928 printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
932 dev->board_name = this_board->name;
935 if (this_board->n_aichan)
937 if (this_board->n_aochan)
939 if (this_board->n_dichan)
941 if (this_board->n_dochan)
943 if (this_board->n_ctrs)
946 if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) {
950 icp_multi_reset(dev);
952 if (this_board->have_irq) {
954 if (comedi_request_irq(irq, interrupt_service_icp_multi,
955 IRQF_SHARED, "Inova Icp Multi", dev)) {
956 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
957 irq = 0; /* Can't use IRQ */
959 printk(", irq=%u", irq);
961 printk(", IRQ disabled");
971 if (this_board->n_aichan) {
972 s = dev->subdevices + subdev;
973 dev->read_subdev = s;
974 s->type = COMEDI_SUBD_AI;
975 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
976 if (this_board->n_aichand)
977 s->subdev_flags |= SDF_DIFF;
978 s->n_chan = this_board->n_aichan;
979 s->maxdata = this_board->ai_maxdata;
980 s->len_chanlist = this_board->n_aichan;
981 s->range_table = this_board->rangelist_ai;
982 s->insn_read = icp_multi_insn_read_ai;
986 if (this_board->n_aochan) {
987 s = dev->subdevices + subdev;
988 s->type = COMEDI_SUBD_AO;
989 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
990 s->n_chan = this_board->n_aochan;
991 s->maxdata = this_board->ao_maxdata;
992 s->len_chanlist = this_board->n_aochan;
993 s->range_table = this_board->rangelist_ao;
994 s->insn_write = icp_multi_insn_write_ao;
995 s->insn_read = icp_multi_insn_read_ao;
999 if (this_board->n_dichan) {
1000 s = dev->subdevices + subdev;
1001 s->type = COMEDI_SUBD_DI;
1002 s->subdev_flags = SDF_READABLE;
1003 s->n_chan = this_board->n_dichan;
1005 s->len_chanlist = this_board->n_dichan;
1006 s->range_table = &range_digital;
1008 s->insn_bits = icp_multi_insn_bits_di;
1012 if (this_board->n_dochan) {
1013 s = dev->subdevices + subdev;
1014 s->type = COMEDI_SUBD_DO;
1015 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1016 s->n_chan = this_board->n_dochan;
1018 s->len_chanlist = this_board->n_dochan;
1019 s->range_table = &range_digital;
1020 s->io_bits = (1 << this_board->n_dochan) - 1;
1022 s->insn_bits = icp_multi_insn_bits_do;
1026 if (this_board->n_ctrs) {
1027 s = dev->subdevices + subdev;
1028 s->type = COMEDI_SUBD_COUNTER;
1029 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1030 s->n_chan = this_board->n_ctrs;
1031 s->maxdata = 0xffff;
1032 s->len_chanlist = this_board->n_ctrs;
1034 s->insn_read = icp_multi_insn_read_ctr;
1035 s->insn_write = icp_multi_insn_write_ctr;
1041 #ifdef ICP_MULTI_EXTDEBUG
1042 printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1049 ==============================================================================
1051 Name: icp_multi_detach
1054 This function releases all the resources used by the current
1058 comedi_device *dev Pointer to current device structure
1060 Returns:int 0 = success
1062 ==============================================================================
1064 static int icp_multi_detach(comedi_device * dev)
1069 icp_multi_reset(dev);
1072 comedi_free_irq(dev->irq, dev);
1074 if (dev->private && devpriv->io_addr)
1075 iounmap(devpriv->io_addr);
1077 if (dev->private && devpriv->card)
1078 pci_card_free(devpriv->card);
1080 if (--pci_list_builded == 0) {
1081 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);