2 comedi/drivers/icp_multi.c
4 COMEDI - Linux Control and Measurement Device Interface
5 Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
30 The driver works for analog input and output and digital input and output.
31 It does not work with interrupts or with the counters. Currently no support
34 It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35 resolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Input
36 ranges can be individually programmed for each channel. Voltage or current
37 measurement is selected by jumper.
39 There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
41 16 x Digital Inputs, 24V
43 8 x Digital Outputs, 24V, 1A
48 [0] - PCI bus number - if bus number and slot number are 0,
49 then driver search for first unused card
53 #include <linux/interrupt.h>
54 #include "../comedidev.h"
56 #include <linux/delay.h>
57 #include <linux/pci.h>
59 #include "icp_multi.h"
61 #define DEVICE_ID 0x8000 /* Device ID */
63 #define ICP_MULTI_EXTDEBUG
65 /* Hardware types of the cards */
66 #define TYPE_ICP_MULTI 0
68 #define IORANGE_ICP_MULTI 32
70 #define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
71 #define ICP_MULTI_AI 2 /* R: Analogue input data */
72 #define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */
73 #define ICP_MULTI_AO 6 /* R/W: Analogue output data */
74 #define ICP_MULTI_DI 8 /* R/W: Digital inouts */
75 #define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */
76 #define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */
77 #define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */
78 #define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */
79 #define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */
80 #define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */
81 #define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */
83 #define ICP_MULTI_SIZE 0x20 /* 32 bytes */
85 /* Define bits from ADC command/status register */
86 #define ADC_ST 0x0001 /* Start ADC */
87 #define ADC_BSY 0x0001 /* ADC busy */
88 #define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
89 #define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
90 #define ADC_DI 0x0040 /* Differential input mode 1 = differential */
92 /* Define bits from DAC command/status register */
93 #define DAC_ST 0x0001 /* Start DAC */
94 #define DAC_BSY 0x0001 /* DAC busy */
95 #define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
96 #define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
98 /* Define bits from interrupt enable/status registers */
99 #define ADC_READY 0x0001 /* A/d conversion ready interrupt */
100 #define DAC_READY 0x0002 /* D/a conversion ready interrupt */
101 #define DOUT_ERROR 0x0004 /* Digital output error interrupt */
102 #define DIN_STATUS 0x0008 /* Digital input status change interrupt */
103 #define CIE0 0x0010 /* Counter 0 overrun interrupt */
104 #define CIE1 0x0020 /* Counter 1 overrun interrupt */
105 #define CIE2 0x0040 /* Counter 2 overrun interrupt */
106 #define CIE3 0x0080 /* Counter 3 overrun interrupt */
108 /* Useful definitions */
109 #define Status_IRQ 0x00ff /* All interrupts */
111 /* Define analogue range */
112 static const struct comedi_lrange range_analog
= { 4, {
120 static const char range_codes_analog
[] = { 0x00, 0x20, 0x10, 0x30 };
123 ==============================================================================
125 ==============================================================================
127 static int icp_multi_attach(struct comedi_device
*dev
,
128 struct comedi_devconfig
*it
);
129 static int icp_multi_detach(struct comedi_device
*dev
);
132 ==============================================================================
133 Data & Structure declarations
134 ==============================================================================
136 static unsigned short pci_list_builded
; /*>0 list of card is known */
139 const char *name
; /* driver name */
141 int iorange
; /* I/O range len */
142 char have_irq
; /* 1=card support IRQ */
143 char cardtype
; /* 0=ICP Multi */
144 int n_aichan
; /* num of A/D chans */
145 int n_aichand
; /* num of A/D chans in diff mode */
146 int n_aochan
; /* num of D/A chans */
147 int n_dichan
; /* num of DI chans */
148 int n_dochan
; /* num of DO chans */
149 int n_ctrs
; /* num of counters */
150 int ai_maxdata
; /* resolution of A/D */
151 int ao_maxdata
; /* resolution of D/A */
152 const struct comedi_lrange
*rangelist_ai
; /* rangelist for A/D */
153 const char *rangecode
; /* range codes for programming */
154 const struct comedi_lrange
*rangelist_ao
; /* rangelist for D/A */
157 static const struct boardtype boardtypes
[] = {
158 {"icp_multi", /* Driver name */
159 DEVICE_ID
, /* PCI device ID */
160 IORANGE_ICP_MULTI
, /* I/O range length */
161 1, /* 1=Card supports interrupts */
162 TYPE_ICP_MULTI
, /* Card type = ICP MULTI */
163 16, /* Num of A/D channels */
164 8, /* Num of A/D channels in diff mode */
165 4, /* Num of D/A channels */
166 16, /* Num of digital inputs */
167 8, /* Num of digital outputs */
168 4, /* Num of counters */
169 0x0fff, /* Resolution of A/D */
170 0x0fff, /* Resolution of D/A */
171 &range_analog
, /* Rangelist for A/D */
172 range_codes_analog
, /* Range codes for programming */
173 &range_analog
}, /* Rangelist for D/A */
176 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
178 static struct comedi_driver driver_icp_multi
= {
179 driver_name
: "icp_multi",
180 module
: THIS_MODULE
,
181 attach
: icp_multi_attach
,
182 detach
: icp_multi_detach
,
183 num_names
: n_boardtypes
,
184 board_name
: &boardtypes
[0].name
,
185 offset
: sizeof(struct boardtype
),
188 static int __init
driver_icp_multi_init_module(void)
190 return comedi_driver_register(&driver_icp_multi
);
193 static void __exit
driver_icp_multi_cleanup_module(void)
195 comedi_driver_unregister(&driver_icp_multi
);
198 module_init(driver_icp_multi_init_module
);
199 module_exit(driver_icp_multi_cleanup_module
);
201 struct icp_multi_private
{
202 struct pcilst_struct
*card
; /* pointer to card */
203 char valid
; /* card is usable */
204 void *io_addr
; /* Pointer to mapped io address */
205 resource_size_t phys_iobase
; /* Physical io address */
206 unsigned int AdcCmdStatus
; /* ADC Command/Status register */
207 unsigned int DacCmdStatus
; /* DAC Command/Status register */
208 unsigned int IntEnable
; /* Interrupt Enable register */
209 unsigned int IntStatus
; /* Interrupt Status register */
210 unsigned int act_chanlist
[32]; /* list of scaned channel */
211 unsigned char act_chanlist_len
; /* len of scanlist */
212 unsigned char act_chanlist_pos
; /* actual position in MUX list */
213 unsigned int *ai_chanlist
; /* actaul chanlist */
214 short *ai_data
; /* data buffer */
215 short ao_data
[4]; /* data output buffer */
216 short di_data
; /* Digital input data */
217 unsigned int do_data
; /* Remember digital output data */
220 #define devpriv ((struct icp_multi_private *)dev->private)
221 #define this_board ((const struct boardtype *)dev->board_ptr)
224 ==============================================================================
225 More forward declarations
226 ==============================================================================
229 static void setup_channel_list(struct comedi_device
*dev
,
230 struct comedi_subdevice
*s
,
231 unsigned int *chanlist
, unsigned int n_chan
);
232 static int icp_multi_reset(struct comedi_device
*dev
);
235 ==============================================================================
237 ==============================================================================
241 ==============================================================================
243 Name: icp_multi_insn_read_ai
246 This function reads a single analogue input.
249 struct comedi_device *dev Pointer to current device structure
250 struct comedi_subdevice *s Pointer to current subdevice structure
251 struct comedi_insn *insn Pointer to current comedi instruction
252 unsigned int *data Pointer to analogue input data
254 Returns:int Nmuber of instructions executed
256 ==============================================================================
258 static int icp_multi_insn_read_ai(struct comedi_device
*dev
,
259 struct comedi_subdevice
*s
,
260 struct comedi_insn
*insn
, unsigned int *data
)
264 #ifdef ICP_MULTI_EXTDEBUG
265 printk(KERN_DEBUG
"icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
267 /* Disable A/D conversion ready interrupt */
268 devpriv
->IntEnable
&= ~ADC_READY
;
269 writew(devpriv
->IntEnable
, devpriv
->io_addr
+ ICP_MULTI_INT_EN
);
271 /* Clear interrupt status */
272 devpriv
->IntStatus
|= ADC_READY
;
273 writew(devpriv
->IntStatus
, devpriv
->io_addr
+ ICP_MULTI_INT_STAT
);
275 /* Set up appropriate channel, mode and range data, for specified ch */
276 setup_channel_list(dev
, s
, &insn
->chanspec
, 1);
278 #ifdef ICP_MULTI_EXTDEBUG
279 printk(KERN_DEBUG
"icp_multi A ST=%4x IO=%p\n",
280 readw(devpriv
->io_addr
+ ICP_MULTI_ADC_CSR
),
281 devpriv
->io_addr
+ ICP_MULTI_ADC_CSR
);
284 for (n
= 0; n
< insn
->n
; n
++) {
285 /* Set start ADC bit */
286 devpriv
->AdcCmdStatus
|= ADC_ST
;
287 writew(devpriv
->AdcCmdStatus
,
288 devpriv
->io_addr
+ ICP_MULTI_ADC_CSR
);
289 devpriv
->AdcCmdStatus
&= ~ADC_ST
;
291 #ifdef ICP_MULTI_EXTDEBUG
292 printk(KERN_DEBUG
"icp multi B n=%d ST=%4x\n", n
,
293 readw(devpriv
->io_addr
+ ICP_MULTI_ADC_CSR
));
298 #ifdef ICP_MULTI_EXTDEBUG
299 printk(KERN_DEBUG
"icp multi C n=%d ST=%4x\n", n
,
300 readw(devpriv
->io_addr
+ ICP_MULTI_ADC_CSR
));
303 /* Wait for conversion to complete, or get fed up waiting */
306 if (!(readw(devpriv
->io_addr
+
307 ICP_MULTI_ADC_CSR
) & ADC_BSY
))
310 #ifdef ICP_MULTI_EXTDEBUG
313 "icp multi D n=%d tm=%d ST=%4x\n", n
,
315 readw(devpriv
->io_addr
+
322 /* If we reach here, a timeout has occurred */
323 comedi_error(dev
, "A/D insn timeout");
325 /* Disable interrupt */
326 devpriv
->IntEnable
&= ~ADC_READY
;
327 writew(devpriv
->IntEnable
, devpriv
->io_addr
+ ICP_MULTI_INT_EN
);
329 /* Clear interrupt status */
330 devpriv
->IntStatus
|= ADC_READY
;
331 writew(devpriv
->IntStatus
,
332 devpriv
->io_addr
+ ICP_MULTI_INT_STAT
);
334 /* Clear data received */
337 #ifdef ICP_MULTI_EXTDEBUG
339 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
346 (readw(devpriv
->io_addr
+ ICP_MULTI_AI
) >> 4) & 0x0fff;
349 /* Disable interrupt */
350 devpriv
->IntEnable
&= ~ADC_READY
;
351 writew(devpriv
->IntEnable
, devpriv
->io_addr
+ ICP_MULTI_INT_EN
);
353 /* Clear interrupt status */
354 devpriv
->IntStatus
|= ADC_READY
;
355 writew(devpriv
->IntStatus
, devpriv
->io_addr
+ ICP_MULTI_INT_STAT
);
357 #ifdef ICP_MULTI_EXTDEBUG
359 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n
);
365 ==============================================================================
367 Name: icp_multi_insn_write_ao
370 This function writes a single analogue output.
373 struct comedi_device *dev Pointer to current device structure
374 struct comedi_subdevice *s Pointer to current subdevice structure
375 struct comedi_insn *insn Pointer to current comedi instruction
376 unsigned int *data Pointer to analogue output data
378 Returns:int Nmuber of instructions executed
380 ==============================================================================
382 static int icp_multi_insn_write_ao(struct comedi_device
*dev
,
383 struct comedi_subdevice
*s
,
384 struct comedi_insn
*insn
, unsigned int *data
)
386 int n
, chan
, range
, timeout
;
388 #ifdef ICP_MULTI_EXTDEBUG
390 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
392 /* Disable D/A conversion ready interrupt */
393 devpriv
->IntEnable
&= ~DAC_READY
;
394 writew(devpriv
->IntEnable
, devpriv
->io_addr
+ ICP_MULTI_INT_EN
);
396 /* Clear interrupt status */
397 devpriv
->IntStatus
|= DAC_READY
;
398 writew(devpriv
->IntStatus
, devpriv
->io_addr
+ ICP_MULTI_INT_STAT
);
400 /* Get channel number and range */
401 chan
= CR_CHAN(insn
->chanspec
);
402 range
= CR_RANGE(insn
->chanspec
);
404 /* Set up range and channel data */
405 /* Bit 4 = 1 : Bipolar */
407 /* Bit 5 = 1 : 10V */
408 /* Bits 8-9 : Channel number */
409 devpriv
->DacCmdStatus
&= 0xfccf;
410 devpriv
->DacCmdStatus
|= this_board
->rangecode
[range
];
411 devpriv
->DacCmdStatus
|= (chan
<< 8);
413 writew(devpriv
->DacCmdStatus
, devpriv
->io_addr
+ ICP_MULTI_DAC_CSR
);
415 for (n
= 0; n
< insn
->n
; n
++) {
416 /* Wait for analogue output data register to be
417 * ready for new data, or get fed up waiting */
420 if (!(readw(devpriv
->io_addr
+
421 ICP_MULTI_DAC_CSR
) & DAC_BSY
))
424 #ifdef ICP_MULTI_EXTDEBUG
427 "icp multi A n=%d tm=%d ST=%4x\n", n
,
429 readw(devpriv
->io_addr
+
436 /* If we reach here, a timeout has occurred */
437 comedi_error(dev
, "D/A insn timeout");
439 /* Disable interrupt */
440 devpriv
->IntEnable
&= ~DAC_READY
;
441 writew(devpriv
->IntEnable
, devpriv
->io_addr
+ ICP_MULTI_INT_EN
);
443 /* Clear interrupt status */
444 devpriv
->IntStatus
|= DAC_READY
;
445 writew(devpriv
->IntStatus
,
446 devpriv
->io_addr
+ ICP_MULTI_INT_STAT
);
448 /* Clear data received */
449 devpriv
->ao_data
[chan
] = 0;
451 #ifdef ICP_MULTI_EXTDEBUG
453 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
459 /* Write data to analogue output data register */
460 writew(data
[n
], devpriv
->io_addr
+ ICP_MULTI_AO
);
462 /* Set DAC_ST bit to write the data to selected channel */
463 devpriv
->DacCmdStatus
|= DAC_ST
;
464 writew(devpriv
->DacCmdStatus
,
465 devpriv
->io_addr
+ ICP_MULTI_DAC_CSR
);
466 devpriv
->DacCmdStatus
&= ~DAC_ST
;
468 /* Save analogue output data */
469 devpriv
->ao_data
[chan
] = data
[n
];
472 #ifdef ICP_MULTI_EXTDEBUG
474 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n
);
480 ==============================================================================
482 Name: icp_multi_insn_read_ao
485 This function reads a single analogue output.
488 struct comedi_device *dev Pointer to current device structure
489 struct comedi_subdevice *s Pointer to current subdevice structure
490 struct comedi_insn *insn Pointer to current comedi instruction
491 unsigned int *data Pointer to analogue output data
493 Returns:int Nmuber of instructions executed
495 ==============================================================================
497 static int icp_multi_insn_read_ao(struct comedi_device
*dev
,
498 struct comedi_subdevice
*s
,
499 struct comedi_insn
*insn
, unsigned int *data
)
503 /* Get channel number */
504 chan
= CR_CHAN(insn
->chanspec
);
506 /* Read analogue outputs */
507 for (n
= 0; n
< insn
->n
; n
++)
508 data
[n
] = devpriv
->ao_data
[chan
];
514 ==============================================================================
516 Name: icp_multi_insn_bits_di
519 This function reads the digital inputs.
522 struct comedi_device *dev Pointer to current device structure
523 struct comedi_subdevice *s Pointer to current subdevice structure
524 struct comedi_insn *insn Pointer to current comedi instruction
525 unsigned int *data Pointer to analogue output data
527 Returns:int Nmuber of instructions executed
529 ==============================================================================
531 static int icp_multi_insn_bits_di(struct comedi_device
*dev
,
532 struct comedi_subdevice
*s
,
533 struct comedi_insn
*insn
, unsigned int *data
)
535 data
[1] = readw(devpriv
->io_addr
+ ICP_MULTI_DI
);
541 ==============================================================================
543 Name: icp_multi_insn_bits_do
546 This function writes the appropriate digital outputs.
549 struct comedi_device *dev Pointer to current device structure
550 struct comedi_subdevice *s Pointer to current subdevice structure
551 struct comedi_insn *insn Pointer to current comedi instruction
552 unsigned int *data Pointer to analogue output data
554 Returns:int Nmuber of instructions executed
556 ==============================================================================
558 static int icp_multi_insn_bits_do(struct comedi_device
*dev
,
559 struct comedi_subdevice
*s
,
560 struct comedi_insn
*insn
, unsigned int *data
)
562 #ifdef ICP_MULTI_EXTDEBUG
563 printk(KERN_DEBUG
"icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
567 s
->state
&= ~data
[0];
568 s
->state
|= (data
[0] & data
[1]);
570 printk(KERN_DEBUG
"Digital outputs = %4x \n", s
->state
);
572 writew(s
->state
, devpriv
->io_addr
+ ICP_MULTI_DO
);
575 data
[1] = readw(devpriv
->io_addr
+ ICP_MULTI_DI
);
577 #ifdef ICP_MULTI_EXTDEBUG
578 printk(KERN_DEBUG
"icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
584 ==============================================================================
586 Name: icp_multi_insn_read_ctr
589 This function reads 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_read_ctr(struct comedi_device
*dev
,
602 struct comedi_subdevice
*s
,
603 struct comedi_insn
*insn
, unsigned int *data
)
609 ==============================================================================
611 Name: icp_multi_insn_write_ctr
614 This function write to the specified counter.
617 struct comedi_device *dev Pointer to current device structure
618 struct comedi_subdevice *s Pointer to current subdevice structure
619 struct comedi_insn *insn Pointer to current comedi instruction
620 unsigned int *data Pointer to counter data
622 Returns:int Nmuber of instructions executed
624 ==============================================================================
626 static int icp_multi_insn_write_ctr(struct comedi_device
*dev
,
627 struct comedi_subdevice
*s
,
628 struct comedi_insn
*insn
,
635 ==============================================================================
637 Name: interrupt_service_icp_multi
640 This function is the interrupt service routine for all
641 interrupts generated by the icp multi board.
645 void *d Pointer to current device
647 ==============================================================================
649 static irqreturn_t
interrupt_service_icp_multi(int irq
, void *d
)
651 struct comedi_device
*dev
= d
;
654 #ifdef ICP_MULTI_EXTDEBUG
656 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
660 /* Is this interrupt from our board? */
661 int_no
= readw(devpriv
->io_addr
+ ICP_MULTI_INT_STAT
) & Status_IRQ
;
666 #ifdef ICP_MULTI_EXTDEBUG
668 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
669 readw(devpriv
->io_addr
+ ICP_MULTI_INT_STAT
));
672 /* Determine which interrupt is active & handle it */
695 #ifdef ICP_MULTI_EXTDEBUG
697 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
704 ==============================================================================
706 Name: setup_channel_list
709 This function sets the appropriate channel selection,
710 differential input mode and range bits in the ADC Command/
714 struct comedi_device *dev Pointer to current sevice structure
715 struct comedi_subdevice *s Pointer to current subdevice structure
716 unsigned int *chanlist Pointer to packed channel list
717 unsigned int n_chan Number of channels to scan
721 ==============================================================================
723 static void setup_channel_list(struct comedi_device
*dev
,
724 struct comedi_subdevice
*s
,
725 unsigned int *chanlist
, unsigned int n_chan
)
727 unsigned int i
, range
, chanprog
;
730 #ifdef ICP_MULTI_EXTDEBUG
732 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan
);
734 devpriv
->act_chanlist_len
= n_chan
;
735 devpriv
->act_chanlist_pos
= 0;
737 for (i
= 0; i
< n_chan
; i
++) {
739 chanprog
= CR_CHAN(chanlist
[i
]);
741 /* Determine if it is a differential channel (Bit 15 = 1) */
742 if (CR_AREF(chanlist
[i
]) == AREF_DIFF
) {
750 /* Clear channel, range and input mode bits
751 * in A/D command/status register */
752 devpriv
->AdcCmdStatus
&= 0xf00f;
754 /* Set channel number and differential mode status bit */
756 /* Set channel number, bits 9-11 & mode, bit 6 */
757 devpriv
->AdcCmdStatus
|= (chanprog
<< 9);
758 devpriv
->AdcCmdStatus
|= ADC_DI
;
760 /* Set channel number, bits 8-11 */
761 devpriv
->AdcCmdStatus
|= (chanprog
<< 8);
763 /* Get range for current channel */
764 range
= this_board
->rangecode
[CR_RANGE(chanlist
[i
])];
765 /* Set range. bits 4-5 */
766 devpriv
->AdcCmdStatus
|= range
;
768 /* Output channel, range, mode to ICP Multi */
769 writew(devpriv
->AdcCmdStatus
,
770 devpriv
->io_addr
+ ICP_MULTI_ADC_CSR
);
772 #ifdef ICP_MULTI_EXTDEBUG
774 "GS: %2d. [%4x]=%4x %4x\n", i
, chanprog
, range
,
775 devpriv
->act_chanlist
[i
]);
782 ==============================================================================
784 Name: icp_multi_reset
787 This function resets the icp multi device to a 'safe' state
790 struct comedi_device *dev Pointer to current sevice structure
792 Returns:int 0 = success
794 ==============================================================================
796 static int icp_multi_reset(struct comedi_device
*dev
)
800 #ifdef ICP_MULTI_EXTDEBUG
802 "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
804 /* Clear INT enables and requests */
805 writew(0, devpriv
->io_addr
+ ICP_MULTI_INT_EN
);
806 writew(0x00ff, devpriv
->io_addr
+ ICP_MULTI_INT_STAT
);
808 if (this_board
->n_aochan
)
809 /* Set DACs to 0..5V range and 0V output */
810 for (i
= 0; i
< this_board
->n_aochan
; i
++) {
811 devpriv
->DacCmdStatus
&= 0xfcce;
813 /* Set channel number */
814 devpriv
->DacCmdStatus
|= (i
<< 8);
817 writew(0, devpriv
->io_addr
+ ICP_MULTI_AO
);
819 /* Set start conversion bit */
820 devpriv
->DacCmdStatus
|= DAC_ST
;
822 /* Output to command / status register */
823 writew(devpriv
->DacCmdStatus
,
824 devpriv
->io_addr
+ ICP_MULTI_DAC_CSR
);
826 /* Delay to allow DAC time to recover */
829 /* Digital outputs to 0 */
830 writew(0, devpriv
->io_addr
+ ICP_MULTI_DO
);
832 #ifdef ICP_MULTI_EXTDEBUG
834 "icp multi EDBG: END: icp_multi_reset(...)\n");
840 ==============================================================================
842 Name: icp_multi_attach
845 This function sets up all the appropriate data for the current
849 struct comedi_device *dev Pointer to current device structure
850 struct comedi_devconfig *it Pointer to current device configuration
852 Returns:int 0 = success
854 ==============================================================================
856 static int icp_multi_attach(struct comedi_device
*dev
,
857 struct comedi_devconfig
*it
)
859 struct comedi_subdevice
*s
;
860 int ret
, subdev
, n_subdevices
;
862 struct pcilst_struct
*card
= NULL
;
863 resource_size_t io_addr
[5], iobase
;
864 unsigned char pci_bus
, pci_slot
, pci_func
;
867 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
869 /* Alocate private data storage space */
870 ret
= alloc_private(dev
, sizeof(struct icp_multi_private
));
874 /* Initialise list of PCI cards in system, if not already done so */
875 if (pci_list_builded
++ == 0) {
876 pci_card_list_init(PCI_VENDOR_ID_ICP
,
877 #ifdef ICP_MULTI_EXTDEBUG
886 "Anne's comedi%d: icp_multi: board=%s", dev
->minor
,
889 card
= select_and_alloc_pci_card(PCI_VENDOR_ID_ICP
,
890 this_board
->device_id
, it
->options
[0],
896 devpriv
->card
= card
;
898 if ((pci_card_data(card
, &pci_bus
, &pci_slot
, &pci_func
, &io_addr
[0],
900 printk(KERN_WARNING
" - Can't get configuration data!\n");
905 devpriv
->phys_iobase
= iobase
;
908 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus
, pci_slot
, pci_func
,
909 (unsigned long long)iobase
);
911 devpriv
->io_addr
= ioremap(iobase
, ICP_MULTI_SIZE
);
913 if (devpriv
->io_addr
== NULL
) {
914 printk(KERN_WARNING
"ioremap failed.\n");
917 #ifdef ICP_MULTI_EXTDEBUG
919 "0x%08llx mapped to %p, ", (unsigned long long)iobase
,
923 dev
->board_name
= this_board
->name
;
926 if (this_board
->n_aichan
)
928 if (this_board
->n_aochan
)
930 if (this_board
->n_dichan
)
932 if (this_board
->n_dochan
)
934 if (this_board
->n_ctrs
)
937 ret
= alloc_subdevices(dev
, n_subdevices
);
941 icp_multi_reset(dev
);
943 if (this_board
->have_irq
) {
945 if (request_irq(irq
, interrupt_service_icp_multi
,
946 IRQF_SHARED
, "Inova Icp Multi", dev
)) {
948 "unable to allocate IRQ %u, DISABLING IT",
950 irq
= 0; /* Can't use IRQ */
952 printk(KERN_WARNING
", irq=%u", irq
);
954 printk(KERN_WARNING
", IRQ disabled");
960 printk(KERN_WARNING
".\n");
964 if (this_board
->n_aichan
) {
965 s
= dev
->subdevices
+ subdev
;
966 dev
->read_subdev
= s
;
967 s
->type
= COMEDI_SUBD_AI
;
968 s
->subdev_flags
= SDF_READABLE
| SDF_COMMON
| SDF_GROUND
;
969 if (this_board
->n_aichand
)
970 s
->subdev_flags
|= SDF_DIFF
;
971 s
->n_chan
= this_board
->n_aichan
;
972 s
->maxdata
= this_board
->ai_maxdata
;
973 s
->len_chanlist
= this_board
->n_aichan
;
974 s
->range_table
= this_board
->rangelist_ai
;
975 s
->insn_read
= icp_multi_insn_read_ai
;
979 if (this_board
->n_aochan
) {
980 s
= dev
->subdevices
+ subdev
;
981 s
->type
= COMEDI_SUBD_AO
;
982 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
| SDF_COMMON
;
983 s
->n_chan
= this_board
->n_aochan
;
984 s
->maxdata
= this_board
->ao_maxdata
;
985 s
->len_chanlist
= this_board
->n_aochan
;
986 s
->range_table
= this_board
->rangelist_ao
;
987 s
->insn_write
= icp_multi_insn_write_ao
;
988 s
->insn_read
= icp_multi_insn_read_ao
;
992 if (this_board
->n_dichan
) {
993 s
= dev
->subdevices
+ subdev
;
994 s
->type
= COMEDI_SUBD_DI
;
995 s
->subdev_flags
= SDF_READABLE
;
996 s
->n_chan
= this_board
->n_dichan
;
998 s
->len_chanlist
= this_board
->n_dichan
;
999 s
->range_table
= &range_digital
;
1001 s
->insn_bits
= icp_multi_insn_bits_di
;
1005 if (this_board
->n_dochan
) {
1006 s
= dev
->subdevices
+ subdev
;
1007 s
->type
= COMEDI_SUBD_DO
;
1008 s
->subdev_flags
= SDF_WRITABLE
| SDF_READABLE
;
1009 s
->n_chan
= this_board
->n_dochan
;
1011 s
->len_chanlist
= this_board
->n_dochan
;
1012 s
->range_table
= &range_digital
;
1013 s
->io_bits
= (1 << this_board
->n_dochan
) - 1;
1015 s
->insn_bits
= icp_multi_insn_bits_do
;
1019 if (this_board
->n_ctrs
) {
1020 s
= dev
->subdevices
+ subdev
;
1021 s
->type
= COMEDI_SUBD_COUNTER
;
1022 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
| SDF_COMMON
;
1023 s
->n_chan
= this_board
->n_ctrs
;
1024 s
->maxdata
= 0xffff;
1025 s
->len_chanlist
= this_board
->n_ctrs
;
1027 s
->insn_read
= icp_multi_insn_read_ctr
;
1028 s
->insn_write
= icp_multi_insn_write_ctr
;
1034 #ifdef ICP_MULTI_EXTDEBUG
1035 printk(KERN_DEBUG
"icp multi EDBG: END: icp_multi_attach(...)\n");
1042 ==============================================================================
1044 Name: icp_multi_detach
1047 This function releases all the resources used by the current
1051 struct comedi_device *dev Pointer to current device structure
1053 Returns:int 0 = success
1055 ==============================================================================
1057 static int icp_multi_detach(struct comedi_device
*dev
)
1062 icp_multi_reset(dev
);
1065 free_irq(dev
->irq
, dev
);
1067 if (dev
->private && devpriv
->io_addr
)
1068 iounmap(devpriv
->io_addr
);
1070 if (dev
->private && devpriv
->card
)
1071 pci_card_free(devpriv
->card
);
1073 if (--pci_list_builded
== 0)
1074 pci_card_list_cleanup(PCI_VENDOR_ID_ICP
);
1079 MODULE_AUTHOR("Comedi http://www.comedi.org");
1080 MODULE_DESCRIPTION("Comedi low-level driver");
1081 MODULE_LICENSE("GPL");