Staging: comedi: Remove comedi_lrange typedef
[linux-2.6] / drivers / staging / comedi / drivers / icp_multi.c
1 /*
2     comedi/drivers/icp_multi.c
3
4     COMEDI - Linux Control and Measurement Device Interface
5     Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
6
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.
11
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.
16
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.
20
21 */
22
23 /*
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
29
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.
33
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.
38
39 There are 4 x 12-bit Analogue Outputs.  Ranges : 5V, 10V, +/-5V, +/-10V
40
41 16 x Digital Inputs, 24V
42
43 8 x Digital Outputs, 24V, 1A
44
45 4 x 16-bit counters
46
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
51 */
52
53 #include "../comedidev.h"
54
55 #include <linux/delay.h>
56 #include <linux/pci.h>
57
58 #include "icp_multi.h"
59
60 #define DEVICE_ID       0x8000  /* Device ID */
61
62 #define ICP_MULTI_EXTDEBUG
63
64 /*  Hardware types of the cards */
65 #define TYPE_ICP_MULTI  0
66
67 #define IORANGE_ICP_MULTI       32
68
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 */
81
82 #define ICP_MULTI_SIZE          0x20    /* 32 bytes */
83
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 */
90
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 */
96
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 */
106
107 /*  Useful definitions */
108 #define Status_IRQ      0x00ff  /*  All interrupts */
109
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)
116         }
117 };
118
119 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
120
121 /*
122 ==============================================================================
123         Forward declarations
124 ==============================================================================
125 */
126 static int icp_multi_attach(struct comedi_device *dev, comedi_devconfig *it);
127 static int icp_multi_detach(struct comedi_device *dev);
128
129 /*
130 ==============================================================================
131         Data & Structure declarations
132 ==============================================================================
133 */
134 static unsigned short pci_list_builded = 0;     /*>0 list of card is known */
135
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 */
153 };
154
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 */
172 };
173
174 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
175
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),
184 };
185
186 COMEDI_INITCLEANUP(driver_icp_multi);
187
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 */
205 };
206
207 #define devpriv ((struct icp_multi_private *)dev->private)
208 #define this_board ((const struct boardtype *)dev->board_ptr)
209
210 /*
211 ==============================================================================
212         More forward declarations
213 ==============================================================================
214 */
215
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);
223
224 /*
225 ==============================================================================
226         Functions
227 ==============================================================================
228 */
229
230 /*
231 ==============================================================================
232
233         Name:   icp_multi_insn_read_ai
234
235         Description:
236                 This function reads a single analogue input.
237
238         Parameters:
239                 struct comedi_device *dev       Pointer to current device structure
240                 struct comedi_subdevice *s      Pointer to current subdevice structure
241                 comedi_insn *insn       Pointer to current comedi instruction
242                 unsigned int *data              Pointer to analogue input data
243
244         Returns:int                     Nmuber of instructions executed
245
246 ==============================================================================
247 */
248 static int icp_multi_insn_read_ai(struct comedi_device *dev, struct comedi_subdevice *s,
249         comedi_insn *insn, unsigned int *data)
250 {
251         int n, timeout;
252
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);
259
260         /*  Clear interrupt status */
261         devpriv->IntStatus |= ADC_READY;
262         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
263
264         /*  Set up appropriate channel, mode and range data, for specified channel */
265         setup_channel_list(dev, s, &insn->chanspec, 1);
266
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
272
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;
279
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
284
285                 comedi_udelay(1);
286
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
291
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;
298
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
306
307                         comedi_udelay(1);
308                 }
309
310                 /*  If we reach here, a timeout has occurred */
311                 comedi_error(dev, "A/D insn timeout");
312
313                 /*  Disable interrupt */
314                 devpriv->IntEnable &= ~ADC_READY;
315                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
316
317                 /*  Clear interrupt status */
318                 devpriv->IntStatus |= ADC_READY;
319                 writew(devpriv->IntStatus,
320                         devpriv->io_addr + ICP_MULTI_INT_STAT);
321
322                 /*  Clear data received */
323                 data[n] = 0;
324
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;
329
330               conv_finish:
331                 data[n] =
332                         (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
333         }
334
335         /*  Disable interrupt */
336         devpriv->IntEnable &= ~ADC_READY;
337         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
338
339         /*  Clear interrupt status */
340         devpriv->IntStatus |= ADC_READY;
341         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
342
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;
347 }
348
349 /*
350 ==============================================================================
351
352         Name:   icp_multi_insn_write_ao
353
354         Description:
355                 This function writes a single analogue output.
356
357         Parameters:
358                 struct comedi_device *dev       Pointer to current device structure
359                 struct comedi_subdevice *s      Pointer to current subdevice structure
360                 comedi_insn *insn       Pointer to current comedi instruction
361                 unsigned int *data              Pointer to analogue output data
362
363         Returns:int                     Nmuber of instructions executed
364
365 ==============================================================================
366 */
367 static int icp_multi_insn_write_ao(struct comedi_device *dev, struct comedi_subdevice *s,
368         comedi_insn *insn, unsigned int *data)
369 {
370         int n, chan, range, timeout;
371
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);
378
379         /*  Clear interrupt status */
380         devpriv->IntStatus |= DAC_READY;
381         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
382
383         /*  Get channel number and range */
384         chan = CR_CHAN(insn->chanspec);
385         range = CR_RANGE(insn->chanspec);
386
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);
395
396         writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
397
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;
405
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
413
414                         comedi_udelay(1);
415                 }
416
417                 /*  If we reach here, a timeout has occurred */
418                 comedi_error(dev, "D/A insn timeout");
419
420                 /*  Disable interrupt */
421                 devpriv->IntEnable &= ~DAC_READY;
422                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
423
424                 /*  Clear interrupt status */
425                 devpriv->IntStatus |= DAC_READY;
426                 writew(devpriv->IntStatus,
427                         devpriv->io_addr + ICP_MULTI_INT_STAT);
428
429                 /*  Clear data received */
430                 devpriv->ao_data[chan] = 0;
431
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;
436
437               dac_ready:
438                 /*  Write data to analogue output data register */
439                 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
440
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;
446
447                 /*  Save analogue output data */
448                 devpriv->ao_data[chan] = data[n];
449         }
450
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;
455 }
456
457 /*
458 ==============================================================================
459
460         Name:   icp_multi_insn_read_ao
461
462         Description:
463                 This function reads a single analogue output.
464
465         Parameters:
466                 struct comedi_device *dev       Pointer to current device structure
467                 struct comedi_subdevice *s      Pointer to current subdevice structure
468                 comedi_insn *insn       Pointer to current comedi instruction
469                 unsigned int *data              Pointer to analogue output data
470
471         Returns:int                     Nmuber of instructions executed
472
473 ==============================================================================
474 */
475 static int icp_multi_insn_read_ao(struct comedi_device *dev, struct comedi_subdevice *s,
476         comedi_insn *insn, unsigned int *data)
477 {
478         int n, chan;
479
480         /*  Get channel number */
481         chan = CR_CHAN(insn->chanspec);
482
483         /*  Read analogue outputs */
484         for (n = 0; n < insn->n; n++)
485                 data[n] = devpriv->ao_data[chan];
486
487         return n;
488 }
489
490 /*
491 ==============================================================================
492
493         Name:   icp_multi_insn_bits_di
494
495         Description:
496                 This function reads the digital inputs.
497
498         Parameters:
499                 struct comedi_device *dev       Pointer to current device structure
500                 struct comedi_subdevice *s      Pointer to current subdevice structure
501                 comedi_insn *insn       Pointer to current comedi instruction
502                 unsigned int *data              Pointer to analogue output data
503
504         Returns:int                     Nmuber of instructions executed
505
506 ==============================================================================
507 */
508 static int icp_multi_insn_bits_di(struct comedi_device *dev, struct comedi_subdevice *s,
509         comedi_insn *insn, unsigned int *data)
510 {
511         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
512
513         return 2;
514 }
515
516 /*
517 ==============================================================================
518
519         Name:   icp_multi_insn_bits_do
520
521         Description:
522                 This function writes the appropriate digital outputs.
523
524         Parameters:
525                 struct comedi_device *dev       Pointer to current device structure
526                 struct comedi_subdevice *s      Pointer to current subdevice structure
527                 comedi_insn *insn       Pointer to current comedi instruction
528                 unsigned int *data              Pointer to analogue output data
529
530         Returns:int                     Nmuber of instructions executed
531
532 ==============================================================================
533 */
534 static int icp_multi_insn_bits_do(struct comedi_device *dev, struct comedi_subdevice *s,
535         comedi_insn *insn, unsigned int *data)
536 {
537 #ifdef ICP_MULTI_EXTDEBUG
538         printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
539 #endif
540
541         if (data[0]) {
542                 s->state &= ~data[0];
543                 s->state |= (data[0] & data[1]);
544
545                 printk("Digital outputs = %4x \n", s->state);
546
547                 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
548         }
549
550         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
551
552 #ifdef ICP_MULTI_EXTDEBUG
553         printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
554 #endif
555         return 2;
556 }
557
558 /*
559 ==============================================================================
560
561         Name:   icp_multi_insn_read_ctr
562
563         Description:
564                 This function reads the specified counter.
565
566         Parameters:
567                 struct comedi_device *dev       Pointer to current device structure
568                 struct comedi_subdevice *s      Pointer to current subdevice structure
569                 comedi_insn *insn       Pointer to current comedi instruction
570                 unsigned int *data              Pointer to counter data
571
572         Returns:int                     Nmuber of instructions executed
573
574 ==============================================================================
575 */
576 static int icp_multi_insn_read_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
577         comedi_insn *insn, unsigned int *data)
578 {
579         return 0;
580 }
581
582 /*
583 ==============================================================================
584
585         Name:   icp_multi_insn_write_ctr
586
587         Description:
588                 This function write to the specified counter.
589
590         Parameters:
591                 struct comedi_device *dev       Pointer to current device structure
592                 struct comedi_subdevice *s      Pointer to current subdevice structure
593                 comedi_insn *insn       Pointer to current comedi instruction
594                 unsigned int *data              Pointer to counter data
595
596         Returns:int                     Nmuber of instructions executed
597
598 ==============================================================================
599 */
600 static int icp_multi_insn_write_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
601         comedi_insn *insn, unsigned int *data)
602 {
603         return 0;
604 }
605
606 /*
607 ==============================================================================
608
609         Name:   interrupt_service_icp_multi
610
611         Description:
612                 This function is the interrupt service routine for all
613                 interrupts generated by the icp multi board.
614
615         Parameters:
616                 int irq
617                 void *d                 Pointer to current device
618
619 ==============================================================================
620 */
621 static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
622 {
623         struct comedi_device *dev = d;
624         int int_no;
625
626 #ifdef ICP_MULTI_EXTDEBUG
627         printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
628                 irq);
629 #endif
630
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;
636
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
641
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;
662
663         }
664
665 #ifdef ICP_MULTI_EXTDEBUG
666         printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
667 #endif
668         return IRQ_HANDLED;
669 }
670
671 #if 0
672 /*
673 ==============================================================================
674
675         Name:   check_channel_list
676
677         Description:
678                 This function checks if the channel list, provided by user
679                 is built correctly
680
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
686
687         Returns:int 0 = failure
688                     1 = success
689
690 ==============================================================================
691 */
692 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
693         unsigned int *chanlist, unsigned int n_chan)
694 {
695         unsigned int i;
696
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;
704         }
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;
713                         }
714                 } else {
715                         if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
716                                 comedi_error(dev,
717                                         "Incorrect ai channel number");
718                                 return 0;
719                         }
720                 }
721         }
722         return 1;
723 }
724 #endif
725
726 /*
727 ==============================================================================
728
729         Name:   setup_channel_list
730
731         Description:
732                 This function sets the appropriate channel selection,
733                 differential input mode and range bits in the ADC Command/
734                 Status register.
735
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
741
742         Returns:Void
743
744 ==============================================================================
745 */
746 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
747         unsigned int *chanlist, unsigned int n_chan)
748 {
749         unsigned int i, range, chanprog;
750         unsigned int diff;
751
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;
757
758         for (i = 0; i < n_chan; i++) {
759                 /*  Get channel */
760                 chanprog = CR_CHAN(chanlist[i]);
761
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;
769                 }
770
771                 /*  Clear channel, range and input mode bits in A/D command/status register */
772                 devpriv->AdcCmdStatus &= 0xf00f;
773
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);
782
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;
787
788                 /* Output channel, range, mode to ICP Multi */
789                 writew(devpriv->AdcCmdStatus,
790                         devpriv->io_addr + ICP_MULTI_ADC_CSR);
791
792 #ifdef ICP_MULTI_EXTDEBUG
793                 printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
794                         devpriv->act_chanlist[i]);
795 #endif
796         }
797
798 }
799
800 /*
801 ==============================================================================
802
803         Name:   icp_multi_reset
804
805         Description:
806                 This function resets the icp multi device to a 'safe' state
807
808         Parameters:
809                 struct comedi_device *dev       Pointer to current sevice structure
810
811         Returns:int     0 = success
812
813 ==============================================================================
814 */
815 static int icp_multi_reset(struct comedi_device *dev)
816 {
817         unsigned int i;
818
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);
825
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;
830
831                         /*  Set channel number */
832                         devpriv->DacCmdStatus |= (i << 8);
833
834                         /*  Output 0V */
835                         writew(0, devpriv->io_addr + ICP_MULTI_AO);
836
837                         /*  Set start conversion bit */
838                         devpriv->DacCmdStatus |= DAC_ST;
839
840                         /*  Output to command / status register */
841                         writew(devpriv->DacCmdStatus,
842                                 devpriv->io_addr + ICP_MULTI_DAC_CSR);
843
844                         /*  Delay to allow DAC time to recover */
845                         comedi_udelay(1);
846                 }
847         /*  Digital outputs to 0 */
848         writew(0, devpriv->io_addr + ICP_MULTI_DO);
849
850 #ifdef ICP_MULTI_EXTDEBUG
851         printk("icp multi EDBG: END: icp_multi_reset(...)\n");
852 #endif
853         return 0;
854 }
855
856 /*
857 ==============================================================================
858
859         Name:   icp_multi_attach
860
861         Description:
862                 This function sets up all the appropriate data for the current
863                 device.
864
865         Parameters:
866                 struct comedi_device *dev       Pointer to current device structure
867                 comedi_devconfig *it    Pointer to current device configuration
868
869         Returns:int     0 = success
870
871 ==============================================================================
872 */
873 static int icp_multi_attach(struct comedi_device *dev, comedi_devconfig *it)
874 {
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;
881
882         printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
883
884         /*  Alocate private data storage space */
885         ret = alloc_private(dev, sizeof(struct icp_multi_private));
886         if (ret < 0)
887                 return ret;
888
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
893                         1
894 #else
895                         0
896 #endif
897                         );
898         }
899
900         printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
901                 this_board->name);
902
903         card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
904                                          this_board->device_id, it->options[0],
905                                          it->options[1]);
906
907         if (card == NULL)
908                 return -EIO;
909
910         devpriv->card = card;
911
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;
916         }
917
918         iobase = io_addr[2];
919         devpriv->phys_iobase = iobase;
920
921         printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
922                 (unsigned long long)iobase);
923
924         devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
925
926         if (devpriv->io_addr == NULL) {
927                 printk("ioremap failed.\n");
928                 return -ENOMEM;
929         }
930 #ifdef ICP_MULTI_EXTDEBUG
931         printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
932                 devpriv->io_addr);
933 #endif
934
935         dev->board_name = this_board->name;
936
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++;
948
949         ret = alloc_subdevices(dev, n_subdevices);
950         if (ret < 0)
951                 return ret;
952
953         icp_multi_reset(dev);
954
955         if (this_board->have_irq) {
956                 if (irq) {
957                         if (comedi_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;
967
968         dev->irq = irq;
969
970         printk(".\n");
971
972         subdev = 0;
973
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++;
987         }
988
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++;
1000         }
1001
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++;
1013         }
1014
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++;
1027         }
1028
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++;
1040         }
1041
1042         devpriv->valid = 1;
1043
1044 #ifdef ICP_MULTI_EXTDEBUG
1045         printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1046 #endif
1047
1048         return 0;
1049 }
1050
1051 /*
1052 ==============================================================================
1053
1054         Name:   icp_multi_detach
1055
1056         Description:
1057                 This function releases all the resources used by the current
1058                 device.
1059
1060         Parameters:
1061                 struct comedi_device *dev       Pointer to current device structure
1062
1063         Returns:int     0 = success
1064
1065 ==============================================================================
1066 */
1067 static int icp_multi_detach(struct comedi_device *dev)
1068 {
1069
1070         if (dev->private)
1071                 if (devpriv->valid)
1072                         icp_multi_reset(dev);
1073
1074         if (dev->irq)
1075                 comedi_free_irq(dev->irq, dev);
1076
1077         if (dev->private && devpriv->io_addr)
1078                 iounmap(devpriv->io_addr);
1079
1080         if (dev->private && devpriv->card)
1081                 pci_card_free(devpriv->card);
1082
1083         if (--pci_list_builded == 0)
1084                 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1085
1086         return 0;
1087 }