Staging: comedi: remove comedi-specific wrappers
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / staging / comedi / drivers / icp_multi.c
blob4cfdfc55b6ed7d8b92f84bd31db8c2a92946da87
1 /*
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.
24 Driver: icp_multi
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
28 Status: works
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
32 for DMA.
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
45 4 x 16-bit counters
47 Options:
48 [0] - PCI bus number - if bus number and slot number are 0,
49 then driver search for first unused card
50 [1] - PCI slot number
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 struct comedi_lrange range_analog = { 4, {
112 UNI_RANGE(5),
113 UNI_RANGE(10),
114 BIP_RANGE(5),
115 BIP_RANGE(10)
119 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
122 ==============================================================================
123 Forward declarations
124 ==============================================================================
126 static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it);
127 static int icp_multi_detach(struct comedi_device *dev);
130 ==============================================================================
131 Data & Structure declarations
132 ==============================================================================
134 static unsigned short pci_list_builded = 0; /*>0 list of card is known */
136 struct boardtype {
137 const char *name; /* driver name */
138 int device_id;
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 struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
151 const char *rangecode; /* range codes for programming */
152 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
155 static const struct 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(struct boardtype))
176 static struct comedi_driver driver_icp_multi = {
177 driver_name:"icp_multi",
178 module : THIS_MODULE,
179 attach : icp_multi_attach,
180 detach : icp_multi_detach,
181 num_names : n_boardtypes,
182 board_name : &boardtypes[0].name,
183 offset : sizeof(struct boardtype),
186 COMEDI_INITCLEANUP(driver_icp_multi);
188 struct icp_multi_private {
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 short *ai_data; /* data buffer */
202 short ao_data[4]; /* data output buffer */
203 short di_data; /* Digital input data */
204 unsigned int do_data; /* Remember digital output data */
207 #define devpriv ((struct icp_multi_private *)dev->private)
208 #define this_board ((const struct boardtype *)dev->board_ptr)
211 ==============================================================================
212 More forward declarations
213 ==============================================================================
216 #if 0
217 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
218 unsigned int *chanlist, unsigned int n_chan);
219 #endif
220 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
221 unsigned int *chanlist, unsigned int n_chan);
222 static int icp_multi_reset(struct comedi_device *dev);
225 ==============================================================================
226 Functions
227 ==============================================================================
231 ==============================================================================
233 Name: icp_multi_insn_read_ai
235 Description:
236 This function reads a single analogue input.
238 Parameters:
239 struct comedi_device *dev Pointer to current device structure
240 struct comedi_subdevice *s Pointer to current subdevice structure
241 struct comedi_insn *insn Pointer to current comedi instruction
242 unsigned int *data Pointer to analogue input data
244 Returns:int Nmuber of instructions executed
246 ==============================================================================
248 static int icp_multi_insn_read_ai(struct comedi_device *dev, struct comedi_subdevice *s,
249 struct comedi_insn *insn, unsigned int *data)
251 int n, timeout;
253 #ifdef ICP_MULTI_EXTDEBUG
254 printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
255 #endif
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);
271 #endif
273 for (n = 0; n < insn->n; n++) {
274 /* Set start ADC bit */
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));
283 #endif
285 udelay(1);
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));
290 #endif
292 /* Wait for conversion to complete, or get fed up waiting */
293 timeout = 100;
294 while (timeout--) {
295 if (!(readw(devpriv->io_addr +
296 ICP_MULTI_ADC_CSR) & ADC_BSY))
297 goto conv_finish;
299 #ifdef ICP_MULTI_EXTDEBUG
300 if (!(timeout % 10))
301 printk("icp multi D n=%d tm=%d ST=%4x\n", n,
302 timeout,
303 readw(devpriv->io_addr +
304 ICP_MULTI_ADC_CSR));
305 #endif
307 udelay(1);
310 /* If we reach here, a timeout has occurred */
311 comedi_error(dev, "A/D insn timeout");
313 /* Disable interrupt */
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 */
323 data[n] = 0;
325 #ifdef ICP_MULTI_EXTDEBUG
326 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
327 #endif
328 return -ETIME;
330 conv_finish:
331 data[n] =
332 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
335 /* Disable interrupt */
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);
345 #endif
346 return n;
350 ==============================================================================
352 Name: icp_multi_insn_write_ao
354 Description:
355 This function writes a single analogue output.
357 Parameters:
358 struct comedi_device *dev Pointer to current device structure
359 struct comedi_subdevice *s Pointer to current subdevice structure
360 struct comedi_insn *insn Pointer to current comedi instruction
361 unsigned int *data Pointer to analogue output data
363 Returns:int Nmuber of instructions executed
365 ==============================================================================
367 static int icp_multi_insn_write_ao(struct comedi_device *dev, struct comedi_subdevice *s,
368 struct comedi_insn *insn, unsigned int *data)
370 int n, chan, range, timeout;
372 #ifdef ICP_MULTI_EXTDEBUG
373 printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
374 #endif
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 */
389 /* Bit 5 = 0 : 5V */
390 /* Bit 5 = 1 : 10V */
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 */
400 timeout = 100;
401 while (timeout--) {
402 if (!(readw(devpriv->io_addr +
403 ICP_MULTI_DAC_CSR) & DAC_BSY))
404 goto dac_ready;
406 #ifdef ICP_MULTI_EXTDEBUG
407 if (!(timeout % 10))
408 printk("icp multi A n=%d tm=%d ST=%4x\n", n,
409 timeout,
410 readw(devpriv->io_addr +
411 ICP_MULTI_DAC_CSR));
412 #endif
414 udelay(1);
417 /* If we reach here, a timeout has occurred */
418 comedi_error(dev, "D/A insn timeout");
420 /* Disable interrupt */
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);
434 #endif
435 return -ETIME;
437 dac_ready:
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);
453 #endif
454 return n;
458 ==============================================================================
460 Name: icp_multi_insn_read_ao
462 Description:
463 This function reads a single analogue output.
465 Parameters:
466 struct comedi_device *dev Pointer to current device structure
467 struct comedi_subdevice *s Pointer to current subdevice structure
468 struct comedi_insn *insn Pointer to current comedi instruction
469 unsigned int *data Pointer to analogue output data
471 Returns:int Nmuber of instructions executed
473 ==============================================================================
475 static int icp_multi_insn_read_ao(struct comedi_device *dev, struct comedi_subdevice *s,
476 struct comedi_insn *insn, unsigned int *data)
478 int n, chan;
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];
487 return n;
491 ==============================================================================
493 Name: icp_multi_insn_bits_di
495 Description:
496 This function reads the digital inputs.
498 Parameters:
499 struct comedi_device *dev Pointer to current device structure
500 struct comedi_subdevice *s Pointer to current subdevice structure
501 struct comedi_insn *insn Pointer to current comedi instruction
502 unsigned int *data Pointer to analogue output data
504 Returns:int Nmuber of instructions executed
506 ==============================================================================
508 static int icp_multi_insn_bits_di(struct comedi_device *dev, struct comedi_subdevice *s,
509 struct comedi_insn *insn, unsigned int *data)
511 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
513 return 2;
517 ==============================================================================
519 Name: icp_multi_insn_bits_do
521 Description:
522 This function writes the appropriate digital outputs.
524 Parameters:
525 struct comedi_device *dev Pointer to current device structure
526 struct comedi_subdevice *s Pointer to current subdevice structure
527 struct comedi_insn *insn Pointer to current comedi instruction
528 unsigned int *data Pointer to analogue output data
530 Returns:int Nmuber of instructions executed
532 ==============================================================================
534 static int icp_multi_insn_bits_do(struct comedi_device *dev, struct comedi_subdevice *s,
535 struct comedi_insn *insn, unsigned int *data)
537 #ifdef ICP_MULTI_EXTDEBUG
538 printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
539 #endif
541 if (data[0]) {
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");
554 #endif
555 return 2;
559 ==============================================================================
561 Name: icp_multi_insn_read_ctr
563 Description:
564 This function reads the specified counter.
566 Parameters:
567 struct comedi_device *dev Pointer to current device structure
568 struct comedi_subdevice *s Pointer to current subdevice structure
569 struct comedi_insn *insn Pointer to current comedi instruction
570 unsigned int *data Pointer to counter data
572 Returns:int Nmuber of instructions executed
574 ==============================================================================
576 static int icp_multi_insn_read_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
577 struct comedi_insn *insn, unsigned int *data)
579 return 0;
583 ==============================================================================
585 Name: icp_multi_insn_write_ctr
587 Description:
588 This function write to the specified counter.
590 Parameters:
591 struct comedi_device *dev Pointer to current device structure
592 struct comedi_subdevice *s Pointer to current subdevice structure
593 struct comedi_insn *insn Pointer to current comedi instruction
594 unsigned int *data Pointer to counter data
596 Returns:int Nmuber of instructions executed
598 ==============================================================================
600 static int icp_multi_insn_write_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
601 struct comedi_insn *insn, unsigned int *data)
603 return 0;
607 ==============================================================================
609 Name: interrupt_service_icp_multi
611 Description:
612 This function is the interrupt service routine for all
613 interrupts generated by the icp multi board.
615 Parameters:
616 int irq
617 void *d Pointer to current device
619 ==============================================================================
621 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
623 struct comedi_device *dev = d;
624 int int_no;
626 #ifdef ICP_MULTI_EXTDEBUG
627 printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
628 irq);
629 #endif
631 /* Is this interrupt from our board? */
632 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
633 if (!int_no)
634 /* No, exit */
635 return IRQ_NONE;
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));
640 #endif
642 /* Determine which interrupt is active & handle it */
643 switch (int_no) {
644 case ADC_READY:
645 break;
646 case DAC_READY:
647 break;
648 case DOUT_ERROR:
649 break;
650 case DIN_STATUS:
651 break;
652 case CIE0:
653 break;
654 case CIE1:
655 break;
656 case CIE2:
657 break;
658 case CIE3:
659 break;
660 default:
661 break;
665 #ifdef ICP_MULTI_EXTDEBUG
666 printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
667 #endif
668 return IRQ_HANDLED;
671 #if 0
673 ==============================================================================
675 Name: check_channel_list
677 Description:
678 This function checks if the channel list, provided by user
679 is built correctly
681 Parameters:
682 struct comedi_device *dev Pointer to current sevice structure
683 struct 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
688 1 = success
690 ==============================================================================
692 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
693 unsigned int *chanlist, unsigned int n_chan)
695 unsigned int i;
697 #ifdef ICP_MULTI_EXTDEBUG
698 printk("icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
699 #endif
700 /* Check that we at least have one channel to check */
701 if (n_chan < 1) {
702 comedi_error(dev, "range/channel list is empty!");
703 return 0;
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) {
710 comedi_error(dev,
711 "Incorrect differential ai channel number");
712 return 0;
714 } else {
715 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
716 comedi_error(dev,
717 "Incorrect ai channel number");
718 return 0;
722 return 1;
724 #endif
727 ==============================================================================
729 Name: setup_channel_list
731 Description:
732 This function sets the appropriate channel selection,
733 differential input mode and range bits in the ADC Command/
734 Status register.
736 Parameters:
737 struct comedi_device *dev Pointer to current sevice structure
738 struct 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
742 Returns:Void
744 ==============================================================================
746 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
747 unsigned int *chanlist, unsigned int n_chan)
749 unsigned int i, range, chanprog;
750 unsigned int diff;
752 #ifdef ICP_MULTI_EXTDEBUG
753 printk("icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
754 #endif
755 devpriv->act_chanlist_len = n_chan;
756 devpriv->act_chanlist_pos = 0;
758 for (i = 0; i < n_chan; i++) {
759 /* Get channel */
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) {
764 diff = 1;
765 chanprog &= 0x0007;
766 } else {
767 diff = 0;
768 chanprog &= 0x000f;
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 */
775 if (diff) {
776 /* Set channel number, bits 9-11 & mode, bit 6 */
777 devpriv->AdcCmdStatus |= (chanprog << 9);
778 devpriv->AdcCmdStatus |= ADC_DI;
779 } else
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]);
795 #endif
801 ==============================================================================
803 Name: icp_multi_reset
805 Description:
806 This function resets the icp multi device to a 'safe' state
808 Parameters:
809 struct comedi_device *dev Pointer to current sevice structure
811 Returns:int 0 = success
813 ==============================================================================
815 static int icp_multi_reset(struct comedi_device *dev)
817 unsigned int i;
819 #ifdef ICP_MULTI_EXTDEBUG
820 printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
821 #endif
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);
834 /* Output 0V */
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 */
845 udelay(1);
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");
852 #endif
853 return 0;
857 ==============================================================================
859 Name: icp_multi_attach
861 Description:
862 This function sets up all the appropriate data for the current
863 device.
865 Parameters:
866 struct comedi_device *dev Pointer to current device structure
867 struct comedi_devconfig *it Pointer to current device configuration
869 Returns:int 0 = success
871 ==============================================================================
873 static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it)
875 struct comedi_subdevice *s;
876 int ret, subdev, n_subdevices;
877 unsigned int irq;
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 ret = alloc_private(dev, sizeof(struct icp_multi_private));
886 if (ret < 0)
887 return ret;
889 /* Initialise list of PCI cards in system, if not already done so */
890 if (pci_list_builded++ == 0) {
891 pci_card_list_init(PCI_VENDOR_ID_ICP,
892 #ifdef ICP_MULTI_EXTDEBUG
894 #else
896 #endif
900 printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
901 this_board->name);
903 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
904 this_board->device_id, it->options[0],
905 it->options[1]);
907 if (card == NULL)
908 return -EIO;
910 devpriv->card = card;
912 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
913 &irq)) < 0) {
914 printk(" - Can't get configuration data!\n");
915 return -EIO;
918 iobase = io_addr[2];
919 devpriv->phys_iobase = iobase;
921 printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
922 (unsigned long long)iobase);
924 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
926 if (devpriv->io_addr == NULL) {
927 printk("ioremap failed.\n");
928 return -ENOMEM;
930 #ifdef ICP_MULTI_EXTDEBUG
931 printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
932 devpriv->io_addr);
933 #endif
935 dev->board_name = this_board->name;
937 n_subdevices = 0;
938 if (this_board->n_aichan)
939 n_subdevices++;
940 if (this_board->n_aochan)
941 n_subdevices++;
942 if (this_board->n_dichan)
943 n_subdevices++;
944 if (this_board->n_dochan)
945 n_subdevices++;
946 if (this_board->n_ctrs)
947 n_subdevices++;
949 ret = alloc_subdevices(dev, n_subdevices);
950 if (ret < 0)
951 return ret;
953 icp_multi_reset(dev);
955 if (this_board->have_irq) {
956 if (irq) {
957 if (request_irq(irq, interrupt_service_icp_multi,
958 IRQF_SHARED, "Inova Icp Multi", dev)) {
959 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
960 irq = 0; /* Can't use IRQ */
961 } else
962 printk(", irq=%u", irq);
963 } else
964 printk(", IRQ disabled");
965 } else
966 irq = 0;
968 dev->irq = irq;
970 printk(".\n");
972 subdev = 0;
974 if (this_board->n_aichan) {
975 s = dev->subdevices + subdev;
976 dev->read_subdev = s;
977 s->type = COMEDI_SUBD_AI;
978 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
979 if (this_board->n_aichand)
980 s->subdev_flags |= SDF_DIFF;
981 s->n_chan = this_board->n_aichan;
982 s->maxdata = this_board->ai_maxdata;
983 s->len_chanlist = this_board->n_aichan;
984 s->range_table = this_board->rangelist_ai;
985 s->insn_read = icp_multi_insn_read_ai;
986 subdev++;
989 if (this_board->n_aochan) {
990 s = dev->subdevices + subdev;
991 s->type = COMEDI_SUBD_AO;
992 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
993 s->n_chan = this_board->n_aochan;
994 s->maxdata = this_board->ao_maxdata;
995 s->len_chanlist = this_board->n_aochan;
996 s->range_table = this_board->rangelist_ao;
997 s->insn_write = icp_multi_insn_write_ao;
998 s->insn_read = icp_multi_insn_read_ao;
999 subdev++;
1002 if (this_board->n_dichan) {
1003 s = dev->subdevices + subdev;
1004 s->type = COMEDI_SUBD_DI;
1005 s->subdev_flags = SDF_READABLE;
1006 s->n_chan = this_board->n_dichan;
1007 s->maxdata = 1;
1008 s->len_chanlist = this_board->n_dichan;
1009 s->range_table = &range_digital;
1010 s->io_bits = 0;
1011 s->insn_bits = icp_multi_insn_bits_di;
1012 subdev++;
1015 if (this_board->n_dochan) {
1016 s = dev->subdevices + subdev;
1017 s->type = COMEDI_SUBD_DO;
1018 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1019 s->n_chan = this_board->n_dochan;
1020 s->maxdata = 1;
1021 s->len_chanlist = this_board->n_dochan;
1022 s->range_table = &range_digital;
1023 s->io_bits = (1 << this_board->n_dochan) - 1;
1024 s->state = 0;
1025 s->insn_bits = icp_multi_insn_bits_do;
1026 subdev++;
1029 if (this_board->n_ctrs) {
1030 s = dev->subdevices + subdev;
1031 s->type = COMEDI_SUBD_COUNTER;
1032 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1033 s->n_chan = this_board->n_ctrs;
1034 s->maxdata = 0xffff;
1035 s->len_chanlist = this_board->n_ctrs;
1036 s->state = 0;
1037 s->insn_read = icp_multi_insn_read_ctr;
1038 s->insn_write = icp_multi_insn_write_ctr;
1039 subdev++;
1042 devpriv->valid = 1;
1044 #ifdef ICP_MULTI_EXTDEBUG
1045 printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1046 #endif
1048 return 0;
1052 ==============================================================================
1054 Name: icp_multi_detach
1056 Description:
1057 This function releases all the resources used by the current
1058 device.
1060 Parameters:
1061 struct comedi_device *dev Pointer to current device structure
1063 Returns:int 0 = success
1065 ==============================================================================
1067 static int icp_multi_detach(struct comedi_device *dev)
1070 if (dev->private)
1071 if (devpriv->valid)
1072 icp_multi_reset(dev);
1074 if (dev->irq)
1075 free_irq(dev->irq, dev);
1077 if (dev->private && devpriv->io_addr)
1078 iounmap(devpriv->io_addr);
1080 if (dev->private && devpriv->card)
1081 pci_card_free(devpriv->card);
1083 if (--pci_list_builded == 0)
1084 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1086 return 0;