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 ==============================================================================
124 Data & Structure declarations
125 ==============================================================================
127 static unsigned short pci_list_builded; /*>0 list of card is known */
130 const char *name; /* driver name */
132 int iorange; /* I/O range len */
133 char have_irq; /* 1=card support IRQ */
134 char cardtype; /* 0=ICP Multi */
135 int n_aichan; /* num of A/D chans */
136 int n_aichand; /* num of A/D chans in diff mode */
137 int n_aochan; /* num of D/A chans */
138 int n_dichan; /* num of DI chans */
139 int n_dochan; /* num of DO chans */
140 int n_ctrs; /* num of counters */
141 int ai_maxdata; /* resolution of A/D */
142 int ao_maxdata; /* resolution of D/A */
143 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
144 const char *rangecode; /* range codes for programming */
145 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
148 struct icp_multi_private {
149 struct pcilst_struct *card; /* pointer to card */
150 char valid; /* card is usable */
151 void __iomem *io_addr; /* Pointer to mapped io address */
152 resource_size_t phys_iobase; /* Physical io address */
153 unsigned int AdcCmdStatus; /* ADC Command/Status register */
154 unsigned int DacCmdStatus; /* DAC Command/Status register */
155 unsigned int IntEnable; /* Interrupt Enable register */
156 unsigned int IntStatus; /* Interrupt Status register */
157 unsigned int act_chanlist[32]; /* list of scaned channel */
158 unsigned char act_chanlist_len; /* len of scanlist */
159 unsigned char act_chanlist_pos; /* actual position in MUX list */
160 unsigned int *ai_chanlist; /* actaul chanlist */
161 short *ai_data; /* data buffer */
162 short ao_data[4]; /* data output buffer */
163 short di_data; /* Digital input data */
164 unsigned int do_data; /* Remember digital output data */
167 #define devpriv ((struct icp_multi_private *)dev->private)
168 #define this_board ((const struct boardtype *)dev->board_ptr)
171 ==============================================================================
173 Name: setup_channel_list
176 This function sets the appropriate channel selection,
177 differential input mode and range bits in the ADC Command/
181 struct comedi_device *dev Pointer to current service structure
182 struct comedi_subdevice *s Pointer to current subdevice structure
183 unsigned int *chanlist Pointer to packed channel list
184 unsigned int n_chan Number of channels to scan
188 ==============================================================================
190 static void setup_channel_list(struct comedi_device *dev,
191 struct comedi_subdevice *s,
192 unsigned int *chanlist, unsigned int n_chan)
194 unsigned int i, range, chanprog;
197 #ifdef ICP_MULTI_EXTDEBUG
199 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
201 devpriv->act_chanlist_len = n_chan;
202 devpriv->act_chanlist_pos = 0;
204 for (i = 0; i < n_chan; i++) {
206 chanprog = CR_CHAN(chanlist[i]);
208 /* Determine if it is a differential channel (Bit 15 = 1) */
209 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
217 /* Clear channel, range and input mode bits
218 * in A/D command/status register */
219 devpriv->AdcCmdStatus &= 0xf00f;
221 /* Set channel number and differential mode status bit */
223 /* Set channel number, bits 9-11 & mode, bit 6 */
224 devpriv->AdcCmdStatus |= (chanprog << 9);
225 devpriv->AdcCmdStatus |= ADC_DI;
227 /* Set channel number, bits 8-11 */
228 devpriv->AdcCmdStatus |= (chanprog << 8);
230 /* Get range for current channel */
231 range = this_board->rangecode[CR_RANGE(chanlist[i])];
232 /* Set range. bits 4-5 */
233 devpriv->AdcCmdStatus |= range;
235 /* Output channel, range, mode to ICP Multi */
236 writew(devpriv->AdcCmdStatus,
237 devpriv->io_addr + ICP_MULTI_ADC_CSR);
239 #ifdef ICP_MULTI_EXTDEBUG
241 "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
242 devpriv->act_chanlist[i]);
249 ==============================================================================
251 Name: icp_multi_insn_read_ai
254 This function reads a single analogue input.
257 struct comedi_device *dev Pointer to current device structure
258 struct comedi_subdevice *s Pointer to current subdevice structure
259 struct comedi_insn *insn Pointer to current comedi instruction
260 unsigned int *data Pointer to analogue input data
262 Returns:int Nmuber of instructions executed
264 ==============================================================================
266 static int icp_multi_insn_read_ai(struct comedi_device *dev,
267 struct comedi_subdevice *s,
268 struct comedi_insn *insn, unsigned int *data)
272 #ifdef ICP_MULTI_EXTDEBUG
273 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
275 /* Disable A/D conversion ready interrupt */
276 devpriv->IntEnable &= ~ADC_READY;
277 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
279 /* Clear interrupt status */
280 devpriv->IntStatus |= ADC_READY;
281 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
283 /* Set up appropriate channel, mode and range data, for specified ch */
284 setup_channel_list(dev, s, &insn->chanspec, 1);
286 #ifdef ICP_MULTI_EXTDEBUG
287 printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n",
288 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
289 devpriv->io_addr + ICP_MULTI_ADC_CSR);
292 for (n = 0; n < insn->n; n++) {
293 /* Set start ADC bit */
294 devpriv->AdcCmdStatus |= ADC_ST;
295 writew(devpriv->AdcCmdStatus,
296 devpriv->io_addr + ICP_MULTI_ADC_CSR);
297 devpriv->AdcCmdStatus &= ~ADC_ST;
299 #ifdef ICP_MULTI_EXTDEBUG
300 printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n,
301 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
306 #ifdef ICP_MULTI_EXTDEBUG
307 printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n,
308 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
311 /* Wait for conversion to complete, or get fed up waiting */
314 if (!(readw(devpriv->io_addr +
315 ICP_MULTI_ADC_CSR) & ADC_BSY))
318 #ifdef ICP_MULTI_EXTDEBUG
321 "icp multi D n=%d tm=%d ST=%4x\n", n,
323 readw(devpriv->io_addr +
330 /* If we reach here, a timeout has occurred */
331 comedi_error(dev, "A/D insn timeout");
333 /* Disable interrupt */
334 devpriv->IntEnable &= ~ADC_READY;
335 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
337 /* Clear interrupt status */
338 devpriv->IntStatus |= ADC_READY;
339 writew(devpriv->IntStatus,
340 devpriv->io_addr + ICP_MULTI_INT_STAT);
342 /* Clear data received */
345 #ifdef ICP_MULTI_EXTDEBUG
347 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
354 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
357 /* Disable interrupt */
358 devpriv->IntEnable &= ~ADC_READY;
359 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
361 /* Clear interrupt status */
362 devpriv->IntStatus |= ADC_READY;
363 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
365 #ifdef ICP_MULTI_EXTDEBUG
367 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
373 ==============================================================================
375 Name: icp_multi_insn_write_ao
378 This function writes a single analogue output.
381 struct comedi_device *dev Pointer to current device structure
382 struct comedi_subdevice *s Pointer to current subdevice structure
383 struct comedi_insn *insn Pointer to current comedi instruction
384 unsigned int *data Pointer to analogue output data
386 Returns:int Nmuber of instructions executed
388 ==============================================================================
390 static int icp_multi_insn_write_ao(struct comedi_device *dev,
391 struct comedi_subdevice *s,
392 struct comedi_insn *insn, unsigned int *data)
394 int n, chan, range, timeout;
396 #ifdef ICP_MULTI_EXTDEBUG
398 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
400 /* Disable D/A conversion ready interrupt */
401 devpriv->IntEnable &= ~DAC_READY;
402 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
404 /* Clear interrupt status */
405 devpriv->IntStatus |= DAC_READY;
406 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
408 /* Get channel number and range */
409 chan = CR_CHAN(insn->chanspec);
410 range = CR_RANGE(insn->chanspec);
412 /* Set up range and channel data */
413 /* Bit 4 = 1 : Bipolar */
415 /* Bit 5 = 1 : 10V */
416 /* Bits 8-9 : Channel number */
417 devpriv->DacCmdStatus &= 0xfccf;
418 devpriv->DacCmdStatus |= this_board->rangecode[range];
419 devpriv->DacCmdStatus |= (chan << 8);
421 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
423 for (n = 0; n < insn->n; n++) {
424 /* Wait for analogue output data register to be
425 * ready for new data, or get fed up waiting */
428 if (!(readw(devpriv->io_addr +
429 ICP_MULTI_DAC_CSR) & DAC_BSY))
432 #ifdef ICP_MULTI_EXTDEBUG
435 "icp multi A n=%d tm=%d ST=%4x\n", n,
437 readw(devpriv->io_addr +
444 /* If we reach here, a timeout has occurred */
445 comedi_error(dev, "D/A insn timeout");
447 /* Disable interrupt */
448 devpriv->IntEnable &= ~DAC_READY;
449 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
451 /* Clear interrupt status */
452 devpriv->IntStatus |= DAC_READY;
453 writew(devpriv->IntStatus,
454 devpriv->io_addr + ICP_MULTI_INT_STAT);
456 /* Clear data received */
457 devpriv->ao_data[chan] = 0;
459 #ifdef ICP_MULTI_EXTDEBUG
461 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
467 /* Write data to analogue output data register */
468 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
470 /* Set DAC_ST bit to write the data to selected channel */
471 devpriv->DacCmdStatus |= DAC_ST;
472 writew(devpriv->DacCmdStatus,
473 devpriv->io_addr + ICP_MULTI_DAC_CSR);
474 devpriv->DacCmdStatus &= ~DAC_ST;
476 /* Save analogue output data */
477 devpriv->ao_data[chan] = data[n];
480 #ifdef ICP_MULTI_EXTDEBUG
482 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
488 ==============================================================================
490 Name: icp_multi_insn_read_ao
493 This function reads a single analogue output.
496 struct comedi_device *dev Pointer to current device structure
497 struct comedi_subdevice *s Pointer to current subdevice structure
498 struct comedi_insn *insn Pointer to current comedi instruction
499 unsigned int *data Pointer to analogue output data
501 Returns:int Nmuber of instructions executed
503 ==============================================================================
505 static int icp_multi_insn_read_ao(struct comedi_device *dev,
506 struct comedi_subdevice *s,
507 struct comedi_insn *insn, unsigned int *data)
511 /* Get channel number */
512 chan = CR_CHAN(insn->chanspec);
514 /* Read analogue outputs */
515 for (n = 0; n < insn->n; n++)
516 data[n] = devpriv->ao_data[chan];
522 ==============================================================================
524 Name: icp_multi_insn_bits_di
527 This function reads the digital inputs.
530 struct comedi_device *dev Pointer to current device structure
531 struct comedi_subdevice *s Pointer to current subdevice structure
532 struct comedi_insn *insn Pointer to current comedi instruction
533 unsigned int *data Pointer to analogue output data
535 Returns:int Nmuber of instructions executed
537 ==============================================================================
539 static int icp_multi_insn_bits_di(struct comedi_device *dev,
540 struct comedi_subdevice *s,
541 struct comedi_insn *insn, unsigned int *data)
543 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
549 ==============================================================================
551 Name: icp_multi_insn_bits_do
554 This function writes the appropriate digital outputs.
557 struct comedi_device *dev Pointer to current device structure
558 struct comedi_subdevice *s Pointer to current subdevice structure
559 struct comedi_insn *insn Pointer to current comedi instruction
560 unsigned int *data Pointer to analogue output data
562 Returns:int Nmuber of instructions executed
564 ==============================================================================
566 static int icp_multi_insn_bits_do(struct comedi_device *dev,
567 struct comedi_subdevice *s,
568 struct comedi_insn *insn, unsigned int *data)
570 #ifdef ICP_MULTI_EXTDEBUG
571 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
575 s->state &= ~data[0];
576 s->state |= (data[0] & data[1]);
578 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
580 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
583 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
585 #ifdef ICP_MULTI_EXTDEBUG
586 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
592 ==============================================================================
594 Name: icp_multi_insn_read_ctr
597 This function reads the specified counter.
600 struct comedi_device *dev Pointer to current device structure
601 struct comedi_subdevice *s Pointer to current subdevice structure
602 struct comedi_insn *insn Pointer to current comedi instruction
603 unsigned int *data Pointer to counter data
605 Returns:int Nmuber of instructions executed
607 ==============================================================================
609 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
610 struct comedi_subdevice *s,
611 struct comedi_insn *insn, unsigned int *data)
617 ==============================================================================
619 Name: icp_multi_insn_write_ctr
622 This function write to the specified counter.
625 struct comedi_device *dev Pointer to current device structure
626 struct comedi_subdevice *s Pointer to current subdevice structure
627 struct comedi_insn *insn Pointer to current comedi instruction
628 unsigned int *data Pointer to counter data
630 Returns:int Nmuber of instructions executed
632 ==============================================================================
634 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
635 struct comedi_subdevice *s,
636 struct comedi_insn *insn,
643 ==============================================================================
645 Name: interrupt_service_icp_multi
648 This function is the interrupt service routine for all
649 interrupts generated by the icp multi board.
653 void *d Pointer to current device
655 ==============================================================================
657 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
659 struct comedi_device *dev = d;
662 #ifdef ICP_MULTI_EXTDEBUG
664 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
668 /* Is this interrupt from our board? */
669 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
674 #ifdef ICP_MULTI_EXTDEBUG
676 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
677 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
680 /* Determine which interrupt is active & handle it */
703 #ifdef ICP_MULTI_EXTDEBUG
705 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
712 ==============================================================================
714 Name: check_channel_list
717 This function checks if the channel list, provided by user
721 struct comedi_device *dev Pointer to current service structure
722 struct comedi_subdevice *s Pointer to current subdevice structure
723 unsigned int *chanlist Pointer to packed channel list
724 unsigned int n_chan Number of channels to scan
726 Returns:int 0 = failure
729 ==============================================================================
731 static int check_channel_list(struct comedi_device *dev,
732 struct comedi_subdevice *s,
733 unsigned int *chanlist, unsigned int n_chan)
737 #ifdef ICP_MULTI_EXTDEBUG
739 "icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
741 /* Check that we at least have one channel to check */
743 comedi_error(dev, "range/channel list is empty!");
746 /* Check all channels */
747 for (i = 0; i < n_chan; i++) {
748 /* Check that channel number is < maximum */
749 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
750 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
752 "Incorrect differential ai ch-nr");
756 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
758 "Incorrect ai channel number");
768 ==============================================================================
770 Name: icp_multi_reset
773 This function resets the icp multi device to a 'safe' state
776 struct comedi_device *dev Pointer to current service structure
778 Returns:int 0 = success
780 ==============================================================================
782 static int icp_multi_reset(struct comedi_device *dev)
786 #ifdef ICP_MULTI_EXTDEBUG
788 "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
790 /* Clear INT enables and requests */
791 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
792 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
794 if (this_board->n_aochan)
795 /* Set DACs to 0..5V range and 0V output */
796 for (i = 0; i < this_board->n_aochan; i++) {
797 devpriv->DacCmdStatus &= 0xfcce;
799 /* Set channel number */
800 devpriv->DacCmdStatus |= (i << 8);
803 writew(0, devpriv->io_addr + ICP_MULTI_AO);
805 /* Set start conversion bit */
806 devpriv->DacCmdStatus |= DAC_ST;
808 /* Output to command / status register */
809 writew(devpriv->DacCmdStatus,
810 devpriv->io_addr + ICP_MULTI_DAC_CSR);
812 /* Delay to allow DAC time to recover */
815 /* Digital outputs to 0 */
816 writew(0, devpriv->io_addr + ICP_MULTI_DO);
818 #ifdef ICP_MULTI_EXTDEBUG
820 "icp multi EDBG: END: icp_multi_reset(...)\n");
825 static int icp_multi_attach(struct comedi_device *dev,
826 struct comedi_devconfig *it)
828 struct comedi_subdevice *s;
829 int ret, subdev, n_subdevices;
831 struct pcilst_struct *card = NULL;
832 resource_size_t io_addr[5], iobase;
833 unsigned char pci_bus, pci_slot, pci_func;
836 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
838 /* Allocate private data storage space */
839 ret = alloc_private(dev, sizeof(struct icp_multi_private));
843 /* Initialise list of PCI cards in system, if not already done so */
844 if (pci_list_builded++ == 0) {
845 pci_card_list_init(PCI_VENDOR_ID_ICP,
846 #ifdef ICP_MULTI_EXTDEBUG
855 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
858 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
859 this_board->device_id, it->options[0],
865 devpriv->card = card;
867 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
869 printk(KERN_WARNING " - Can't get configuration data!\n");
874 devpriv->phys_iobase = iobase;
877 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
878 (unsigned long long)iobase);
880 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
882 if (devpriv->io_addr == NULL) {
883 printk(KERN_WARNING "ioremap failed.\n");
886 #ifdef ICP_MULTI_EXTDEBUG
888 "0x%08llx mapped to %p, ", (unsigned long long)iobase,
892 dev->board_name = this_board->name;
895 if (this_board->n_aichan)
897 if (this_board->n_aochan)
899 if (this_board->n_dichan)
901 if (this_board->n_dochan)
903 if (this_board->n_ctrs)
906 ret = comedi_alloc_subdevices(dev, n_subdevices);
910 icp_multi_reset(dev);
912 if (this_board->have_irq) {
914 if (request_irq(irq, interrupt_service_icp_multi,
915 IRQF_SHARED, "Inova Icp Multi", dev)) {
917 "unable to allocate IRQ %u, DISABLING IT",
919 irq = 0; /* Can't use IRQ */
921 printk(KERN_WARNING ", irq=%u", irq);
923 printk(KERN_WARNING ", IRQ disabled");
929 printk(KERN_WARNING ".\n");
933 if (this_board->n_aichan) {
934 s = dev->subdevices + subdev;
935 dev->read_subdev = s;
936 s->type = COMEDI_SUBD_AI;
937 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
938 if (this_board->n_aichand)
939 s->subdev_flags |= SDF_DIFF;
940 s->n_chan = this_board->n_aichan;
941 s->maxdata = this_board->ai_maxdata;
942 s->len_chanlist = this_board->n_aichan;
943 s->range_table = this_board->rangelist_ai;
944 s->insn_read = icp_multi_insn_read_ai;
948 if (this_board->n_aochan) {
949 s = dev->subdevices + subdev;
950 s->type = COMEDI_SUBD_AO;
951 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
952 s->n_chan = this_board->n_aochan;
953 s->maxdata = this_board->ao_maxdata;
954 s->len_chanlist = this_board->n_aochan;
955 s->range_table = this_board->rangelist_ao;
956 s->insn_write = icp_multi_insn_write_ao;
957 s->insn_read = icp_multi_insn_read_ao;
961 if (this_board->n_dichan) {
962 s = dev->subdevices + subdev;
963 s->type = COMEDI_SUBD_DI;
964 s->subdev_flags = SDF_READABLE;
965 s->n_chan = this_board->n_dichan;
967 s->len_chanlist = this_board->n_dichan;
968 s->range_table = &range_digital;
970 s->insn_bits = icp_multi_insn_bits_di;
974 if (this_board->n_dochan) {
975 s = dev->subdevices + subdev;
976 s->type = COMEDI_SUBD_DO;
977 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
978 s->n_chan = this_board->n_dochan;
980 s->len_chanlist = this_board->n_dochan;
981 s->range_table = &range_digital;
982 s->io_bits = (1 << this_board->n_dochan) - 1;
984 s->insn_bits = icp_multi_insn_bits_do;
988 if (this_board->n_ctrs) {
989 s = dev->subdevices + subdev;
990 s->type = COMEDI_SUBD_COUNTER;
991 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
992 s->n_chan = this_board->n_ctrs;
994 s->len_chanlist = this_board->n_ctrs;
996 s->insn_read = icp_multi_insn_read_ctr;
997 s->insn_write = icp_multi_insn_write_ctr;
1003 #ifdef ICP_MULTI_EXTDEBUG
1004 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
1010 static void icp_multi_detach(struct comedi_device *dev)
1014 icp_multi_reset(dev);
1016 free_irq(dev->irq, dev);
1017 if (dev->private && devpriv->io_addr)
1018 iounmap(devpriv->io_addr);
1019 if (dev->private && devpriv->card)
1020 pci_card_free(devpriv->card);
1021 if (--pci_list_builded == 0)
1022 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1025 static const struct boardtype boardtypes[] = {
1027 .name = "icp_multi",
1028 .device_id = DEVICE_ID,
1029 .iorange = IORANGE_ICP_MULTI,
1031 .cardtype = TYPE_ICP_MULTI,
1038 .ai_maxdata = 0x0fff,
1039 .ao_maxdata = 0x0fff,
1040 .rangelist_ai = &range_analog,
1041 .rangecode = range_codes_analog,
1042 .rangelist_ao = &range_analog,
1046 static struct comedi_driver icp_multi_driver = {
1047 .driver_name = "icp_multi",
1048 .module = THIS_MODULE,
1049 .attach = icp_multi_attach,
1050 .detach = icp_multi_detach,
1051 .num_names = ARRAY_SIZE(boardtypes),
1052 .board_name = &boardtypes[0].name,
1053 .offset = sizeof(struct boardtype),
1055 module_comedi_driver(icp_multi_driver);
1057 MODULE_AUTHOR("Comedi http://www.comedi.org");
1058 MODULE_DESCRIPTION("Comedi low-level driver");
1059 MODULE_LICENSE("GPL");