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 COMEDI_INITCLEANUP(driver_icp_multi);
190 struct icp_multi_private {
191 struct pcilst_struct *card; /* pointer to card */
192 char valid; /* card is usable */
193 void *io_addr; /* Pointer to mapped io address */
194 resource_size_t phys_iobase; /* Physical io address */
195 unsigned int AdcCmdStatus; /* ADC Command/Status register */
196 unsigned int DacCmdStatus; /* DAC Command/Status register */
197 unsigned int IntEnable; /* Interrupt Enable register */
198 unsigned int IntStatus; /* Interrupt Status register */
199 unsigned int act_chanlist[32]; /* list of scaned channel */
200 unsigned char act_chanlist_len; /* len of scanlist */
201 unsigned char act_chanlist_pos; /* actual position in MUX list */
202 unsigned int *ai_chanlist; /* actaul chanlist */
203 short *ai_data; /* data buffer */
204 short ao_data[4]; /* data output buffer */
205 short di_data; /* Digital input data */
206 unsigned int do_data; /* Remember digital output data */
209 #define devpriv ((struct icp_multi_private *)dev->private)
210 #define this_board ((const struct boardtype *)dev->board_ptr)
213 ==============================================================================
214 More forward declarations
215 ==============================================================================
219 static int check_channel_list(struct comedi_device *dev,
220 struct comedi_subdevice *s,
221 unsigned int *chanlist, unsigned int n_chan);
223 static void setup_channel_list(struct comedi_device *dev,
224 struct comedi_subdevice *s,
225 unsigned int *chanlist, unsigned int n_chan);
226 static int icp_multi_reset(struct comedi_device *dev);
229 ==============================================================================
231 ==============================================================================
235 ==============================================================================
237 Name: icp_multi_insn_read_ai
240 This function reads a single analogue input.
243 struct comedi_device *dev Pointer to current device structure
244 struct comedi_subdevice *s Pointer to current subdevice structure
245 struct comedi_insn *insn Pointer to current comedi instruction
246 unsigned int *data Pointer to analogue input data
248 Returns:int Nmuber of instructions executed
250 ==============================================================================
252 static int icp_multi_insn_read_ai(struct comedi_device *dev,
253 struct comedi_subdevice *s,
254 struct comedi_insn *insn, unsigned int *data)
258 #ifdef ICP_MULTI_EXTDEBUG
259 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
261 /* Disable A/D conversion ready interrupt */
262 devpriv->IntEnable &= ~ADC_READY;
263 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
265 /* Clear interrupt status */
266 devpriv->IntStatus |= ADC_READY;
267 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
269 /* Set up appropriate channel, mode and range data, for specified ch */
270 setup_channel_list(dev, s, &insn->chanspec, 1);
272 #ifdef ICP_MULTI_EXTDEBUG
273 printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n",
274 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
275 devpriv->io_addr + ICP_MULTI_ADC_CSR);
278 for (n = 0; n < insn->n; n++) {
279 /* Set start ADC bit */
280 devpriv->AdcCmdStatus |= ADC_ST;
281 writew(devpriv->AdcCmdStatus,
282 devpriv->io_addr + ICP_MULTI_ADC_CSR);
283 devpriv->AdcCmdStatus &= ~ADC_ST;
285 #ifdef ICP_MULTI_EXTDEBUG
286 printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n,
287 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
292 #ifdef ICP_MULTI_EXTDEBUG
293 printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n,
294 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
297 /* Wait for conversion to complete, or get fed up waiting */
300 if (!(readw(devpriv->io_addr +
301 ICP_MULTI_ADC_CSR) & ADC_BSY))
304 #ifdef ICP_MULTI_EXTDEBUG
307 "icp multi D n=%d tm=%d ST=%4x\n", n,
309 readw(devpriv->io_addr +
316 /* If we reach here, a timeout has occurred */
317 comedi_error(dev, "A/D insn timeout");
319 /* Disable interrupt */
320 devpriv->IntEnable &= ~ADC_READY;
321 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
323 /* Clear interrupt status */
324 devpriv->IntStatus |= ADC_READY;
325 writew(devpriv->IntStatus,
326 devpriv->io_addr + ICP_MULTI_INT_STAT);
328 /* Clear data received */
331 #ifdef ICP_MULTI_EXTDEBUG
333 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
340 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
343 /* Disable interrupt */
344 devpriv->IntEnable &= ~ADC_READY;
345 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
347 /* Clear interrupt status */
348 devpriv->IntStatus |= ADC_READY;
349 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
351 #ifdef ICP_MULTI_EXTDEBUG
353 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
359 ==============================================================================
361 Name: icp_multi_insn_write_ao
364 This function writes a single analogue output.
367 struct comedi_device *dev Pointer to current device structure
368 struct comedi_subdevice *s Pointer to current subdevice structure
369 struct comedi_insn *insn Pointer to current comedi instruction
370 unsigned int *data Pointer to analogue output data
372 Returns:int Nmuber of instructions executed
374 ==============================================================================
376 static int icp_multi_insn_write_ao(struct comedi_device *dev,
377 struct comedi_subdevice *s,
378 struct comedi_insn *insn, unsigned int *data)
380 int n, chan, range, timeout;
382 #ifdef ICP_MULTI_EXTDEBUG
384 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
386 /* Disable D/A conversion ready interrupt */
387 devpriv->IntEnable &= ~DAC_READY;
388 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
390 /* Clear interrupt status */
391 devpriv->IntStatus |= DAC_READY;
392 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
394 /* Get channel number and range */
395 chan = CR_CHAN(insn->chanspec);
396 range = CR_RANGE(insn->chanspec);
398 /* Set up range and channel data */
399 /* Bit 4 = 1 : Bipolar */
401 /* Bit 5 = 1 : 10V */
402 /* Bits 8-9 : Channel number */
403 devpriv->DacCmdStatus &= 0xfccf;
404 devpriv->DacCmdStatus |= this_board->rangecode[range];
405 devpriv->DacCmdStatus |= (chan << 8);
407 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
409 for (n = 0; n < insn->n; n++) {
410 /* Wait for analogue output data register to be
411 * ready for new data, or get fed up waiting */
414 if (!(readw(devpriv->io_addr +
415 ICP_MULTI_DAC_CSR) & DAC_BSY))
418 #ifdef ICP_MULTI_EXTDEBUG
421 "icp multi A n=%d tm=%d ST=%4x\n", n,
423 readw(devpriv->io_addr +
430 /* If we reach here, a timeout has occurred */
431 comedi_error(dev, "D/A insn timeout");
433 /* Disable interrupt */
434 devpriv->IntEnable &= ~DAC_READY;
435 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
437 /* Clear interrupt status */
438 devpriv->IntStatus |= DAC_READY;
439 writew(devpriv->IntStatus,
440 devpriv->io_addr + ICP_MULTI_INT_STAT);
442 /* Clear data received */
443 devpriv->ao_data[chan] = 0;
445 #ifdef ICP_MULTI_EXTDEBUG
447 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
453 /* Write data to analogue output data register */
454 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
456 /* Set DAC_ST bit to write the data to selected channel */
457 devpriv->DacCmdStatus |= DAC_ST;
458 writew(devpriv->DacCmdStatus,
459 devpriv->io_addr + ICP_MULTI_DAC_CSR);
460 devpriv->DacCmdStatus &= ~DAC_ST;
462 /* Save analogue output data */
463 devpriv->ao_data[chan] = data[n];
466 #ifdef ICP_MULTI_EXTDEBUG
468 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
474 ==============================================================================
476 Name: icp_multi_insn_read_ao
479 This function reads a single analogue output.
482 struct comedi_device *dev Pointer to current device structure
483 struct comedi_subdevice *s Pointer to current subdevice structure
484 struct comedi_insn *insn Pointer to current comedi instruction
485 unsigned int *data Pointer to analogue output data
487 Returns:int Nmuber of instructions executed
489 ==============================================================================
491 static int icp_multi_insn_read_ao(struct comedi_device *dev,
492 struct comedi_subdevice *s,
493 struct comedi_insn *insn, unsigned int *data)
497 /* Get channel number */
498 chan = CR_CHAN(insn->chanspec);
500 /* Read analogue outputs */
501 for (n = 0; n < insn->n; n++)
502 data[n] = devpriv->ao_data[chan];
508 ==============================================================================
510 Name: icp_multi_insn_bits_di
513 This function reads the digital inputs.
516 struct comedi_device *dev Pointer to current device structure
517 struct comedi_subdevice *s Pointer to current subdevice structure
518 struct comedi_insn *insn Pointer to current comedi instruction
519 unsigned int *data Pointer to analogue output data
521 Returns:int Nmuber of instructions executed
523 ==============================================================================
525 static int icp_multi_insn_bits_di(struct comedi_device *dev,
526 struct comedi_subdevice *s,
527 struct comedi_insn *insn, unsigned int *data)
529 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
535 ==============================================================================
537 Name: icp_multi_insn_bits_do
540 This function writes the appropriate digital outputs.
543 struct comedi_device *dev Pointer to current device structure
544 struct comedi_subdevice *s Pointer to current subdevice structure
545 struct comedi_insn *insn Pointer to current comedi instruction
546 unsigned int *data Pointer to analogue output data
548 Returns:int Nmuber of instructions executed
550 ==============================================================================
552 static int icp_multi_insn_bits_do(struct comedi_device *dev,
553 struct comedi_subdevice *s,
554 struct comedi_insn *insn, unsigned int *data)
556 #ifdef ICP_MULTI_EXTDEBUG
557 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
561 s->state &= ~data[0];
562 s->state |= (data[0] & data[1]);
564 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
566 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
569 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
571 #ifdef ICP_MULTI_EXTDEBUG
572 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
578 ==============================================================================
580 Name: icp_multi_insn_read_ctr
583 This function reads the specified counter.
586 struct comedi_device *dev Pointer to current device structure
587 struct comedi_subdevice *s Pointer to current subdevice structure
588 struct comedi_insn *insn Pointer to current comedi instruction
589 unsigned int *data Pointer to counter data
591 Returns:int Nmuber of instructions executed
593 ==============================================================================
595 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
596 struct comedi_subdevice *s,
597 struct comedi_insn *insn, unsigned int *data)
603 ==============================================================================
605 Name: icp_multi_insn_write_ctr
608 This function write to the specified counter.
611 struct comedi_device *dev Pointer to current device structure
612 struct comedi_subdevice *s Pointer to current subdevice structure
613 struct comedi_insn *insn Pointer to current comedi instruction
614 unsigned int *data Pointer to counter data
616 Returns:int Nmuber of instructions executed
618 ==============================================================================
620 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
621 struct comedi_subdevice *s,
622 struct comedi_insn *insn,
629 ==============================================================================
631 Name: interrupt_service_icp_multi
634 This function is the interrupt service routine for all
635 interrupts generated by the icp multi board.
639 void *d Pointer to current device
641 ==============================================================================
643 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
645 struct comedi_device *dev = d;
648 #ifdef ICP_MULTI_EXTDEBUG
650 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
654 /* Is this interrupt from our board? */
655 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
660 #ifdef ICP_MULTI_EXTDEBUG
662 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
663 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
666 /* Determine which interrupt is active & handle it */
689 #ifdef ICP_MULTI_EXTDEBUG
691 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
698 ==============================================================================
700 Name: check_channel_list
703 This function checks if the channel list, provided by user
707 struct comedi_device *dev Pointer to current sevice structure
708 struct comedi_subdevice *s Pointer to current subdevice structure
709 unsigned int *chanlist Pointer to packed channel list
710 unsigned int n_chan Number of channels to scan
712 Returns:int 0 = failure
715 ==============================================================================
717 static int check_channel_list(struct comedi_device *dev,
718 struct comedi_subdevice *s,
719 unsigned int *chanlist, unsigned int n_chan)
723 #ifdef ICP_MULTI_EXTDEBUG
725 "icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
727 /* Check that we at least have one channel to check */
729 comedi_error(dev, "range/channel list is empty!");
732 /* Check all channels */
733 for (i = 0; i < n_chan; i++) {
734 /* Check that channel number is < maximum */
735 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
736 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
738 "Incorrect differential ai ch-nr");
742 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
744 "Incorrect ai channel number");
754 ==============================================================================
756 Name: setup_channel_list
759 This function sets the appropriate channel selection,
760 differential input mode and range bits in the ADC Command/
764 struct comedi_device *dev Pointer to current sevice structure
765 struct comedi_subdevice *s Pointer to current subdevice structure
766 unsigned int *chanlist Pointer to packed channel list
767 unsigned int n_chan Number of channels to scan
771 ==============================================================================
773 static void setup_channel_list(struct comedi_device *dev,
774 struct comedi_subdevice *s,
775 unsigned int *chanlist, unsigned int n_chan)
777 unsigned int i, range, chanprog;
780 #ifdef ICP_MULTI_EXTDEBUG
782 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
784 devpriv->act_chanlist_len = n_chan;
785 devpriv->act_chanlist_pos = 0;
787 for (i = 0; i < n_chan; i++) {
789 chanprog = CR_CHAN(chanlist[i]);
791 /* Determine if it is a differential channel (Bit 15 = 1) */
792 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
800 /* Clear channel, range and input mode bits
801 * in A/D command/status register */
802 devpriv->AdcCmdStatus &= 0xf00f;
804 /* Set channel number and differential mode status bit */
806 /* Set channel number, bits 9-11 & mode, bit 6 */
807 devpriv->AdcCmdStatus |= (chanprog << 9);
808 devpriv->AdcCmdStatus |= ADC_DI;
810 /* Set channel number, bits 8-11 */
811 devpriv->AdcCmdStatus |= (chanprog << 8);
813 /* Get range for current channel */
814 range = this_board->rangecode[CR_RANGE(chanlist[i])];
815 /* Set range. bits 4-5 */
816 devpriv->AdcCmdStatus |= range;
818 /* Output channel, range, mode to ICP Multi */
819 writew(devpriv->AdcCmdStatus,
820 devpriv->io_addr + ICP_MULTI_ADC_CSR);
822 #ifdef ICP_MULTI_EXTDEBUG
824 "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
825 devpriv->act_chanlist[i]);
832 ==============================================================================
834 Name: icp_multi_reset
837 This function resets the icp multi device to a 'safe' state
840 struct comedi_device *dev Pointer to current sevice structure
842 Returns:int 0 = success
844 ==============================================================================
846 static int icp_multi_reset(struct comedi_device *dev)
850 #ifdef ICP_MULTI_EXTDEBUG
852 "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
854 /* Clear INT enables and requests */
855 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
856 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
858 if (this_board->n_aochan)
859 /* Set DACs to 0..5V range and 0V output */
860 for (i = 0; i < this_board->n_aochan; i++) {
861 devpriv->DacCmdStatus &= 0xfcce;
863 /* Set channel number */
864 devpriv->DacCmdStatus |= (i << 8);
867 writew(0, devpriv->io_addr + ICP_MULTI_AO);
869 /* Set start conversion bit */
870 devpriv->DacCmdStatus |= DAC_ST;
872 /* Output to command / status register */
873 writew(devpriv->DacCmdStatus,
874 devpriv->io_addr + ICP_MULTI_DAC_CSR);
876 /* Delay to allow DAC time to recover */
879 /* Digital outputs to 0 */
880 writew(0, devpriv->io_addr + ICP_MULTI_DO);
882 #ifdef ICP_MULTI_EXTDEBUG
884 "icp multi EDBG: END: icp_multi_reset(...)\n");
890 ==============================================================================
892 Name: icp_multi_attach
895 This function sets up all the appropriate data for the current
899 struct comedi_device *dev Pointer to current device structure
900 struct comedi_devconfig *it Pointer to current device configuration
902 Returns:int 0 = success
904 ==============================================================================
906 static int icp_multi_attach(struct comedi_device *dev,
907 struct comedi_devconfig *it)
909 struct comedi_subdevice *s;
910 int ret, subdev, n_subdevices;
912 struct pcilst_struct *card = NULL;
913 resource_size_t io_addr[5], iobase;
914 unsigned char pci_bus, pci_slot, pci_func;
917 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
919 /* Alocate private data storage space */
920 ret = alloc_private(dev, sizeof(struct icp_multi_private));
924 /* Initialise list of PCI cards in system, if not already done so */
925 if (pci_list_builded++ == 0) {
926 pci_card_list_init(PCI_VENDOR_ID_ICP,
927 #ifdef ICP_MULTI_EXTDEBUG
936 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
939 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
940 this_board->device_id, it->options[0],
946 devpriv->card = card;
948 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
950 printk(KERN_WARNING " - Can't get configuration data!\n");
955 devpriv->phys_iobase = iobase;
958 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
959 (unsigned long long)iobase);
961 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
963 if (devpriv->io_addr == NULL) {
964 printk(KERN_WARNING "ioremap failed.\n");
967 #ifdef ICP_MULTI_EXTDEBUG
969 "0x%08llx mapped to %p, ", (unsigned long long)iobase,
973 dev->board_name = this_board->name;
976 if (this_board->n_aichan)
978 if (this_board->n_aochan)
980 if (this_board->n_dichan)
982 if (this_board->n_dochan)
984 if (this_board->n_ctrs)
987 ret = alloc_subdevices(dev, n_subdevices);
991 icp_multi_reset(dev);
993 if (this_board->have_irq) {
995 if (request_irq(irq, interrupt_service_icp_multi,
996 IRQF_SHARED, "Inova Icp Multi", dev)) {
998 "unable to allocate IRQ %u, DISABLING IT",
1000 irq = 0; /* Can't use IRQ */
1002 printk(KERN_WARNING ", irq=%u", irq);
1004 printk(KERN_WARNING ", IRQ disabled");
1010 printk(KERN_WARNING ".\n");
1014 if (this_board->n_aichan) {
1015 s = dev->subdevices + subdev;
1016 dev->read_subdev = s;
1017 s->type = COMEDI_SUBD_AI;
1018 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1019 if (this_board->n_aichand)
1020 s->subdev_flags |= SDF_DIFF;
1021 s->n_chan = this_board->n_aichan;
1022 s->maxdata = this_board->ai_maxdata;
1023 s->len_chanlist = this_board->n_aichan;
1024 s->range_table = this_board->rangelist_ai;
1025 s->insn_read = icp_multi_insn_read_ai;
1029 if (this_board->n_aochan) {
1030 s = dev->subdevices + subdev;
1031 s->type = COMEDI_SUBD_AO;
1032 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1033 s->n_chan = this_board->n_aochan;
1034 s->maxdata = this_board->ao_maxdata;
1035 s->len_chanlist = this_board->n_aochan;
1036 s->range_table = this_board->rangelist_ao;
1037 s->insn_write = icp_multi_insn_write_ao;
1038 s->insn_read = icp_multi_insn_read_ao;
1042 if (this_board->n_dichan) {
1043 s = dev->subdevices + subdev;
1044 s->type = COMEDI_SUBD_DI;
1045 s->subdev_flags = SDF_READABLE;
1046 s->n_chan = this_board->n_dichan;
1048 s->len_chanlist = this_board->n_dichan;
1049 s->range_table = &range_digital;
1051 s->insn_bits = icp_multi_insn_bits_di;
1055 if (this_board->n_dochan) {
1056 s = dev->subdevices + subdev;
1057 s->type = COMEDI_SUBD_DO;
1058 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1059 s->n_chan = this_board->n_dochan;
1061 s->len_chanlist = this_board->n_dochan;
1062 s->range_table = &range_digital;
1063 s->io_bits = (1 << this_board->n_dochan) - 1;
1065 s->insn_bits = icp_multi_insn_bits_do;
1069 if (this_board->n_ctrs) {
1070 s = dev->subdevices + subdev;
1071 s->type = COMEDI_SUBD_COUNTER;
1072 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1073 s->n_chan = this_board->n_ctrs;
1074 s->maxdata = 0xffff;
1075 s->len_chanlist = this_board->n_ctrs;
1077 s->insn_read = icp_multi_insn_read_ctr;
1078 s->insn_write = icp_multi_insn_write_ctr;
1084 #ifdef ICP_MULTI_EXTDEBUG
1085 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
1092 ==============================================================================
1094 Name: icp_multi_detach
1097 This function releases all the resources used by the current
1101 struct comedi_device *dev Pointer to current device structure
1103 Returns:int 0 = success
1105 ==============================================================================
1107 static int icp_multi_detach(struct comedi_device *dev)
1112 icp_multi_reset(dev);
1115 free_irq(dev->irq, dev);
1117 if (dev->private && devpriv->io_addr)
1118 iounmap(devpriv->io_addr);
1120 if (dev->private && devpriv->card)
1121 pci_card_free(devpriv->card);
1123 if (--pci_list_builded == 0)
1124 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);