Merge branch 'for-2.6.31' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[pandora-kernel.git] / 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 <linux/interrupt.h>
54 #include "../comedidev.h"
55
56 #include <linux/delay.h>
57 #include <linux/pci.h>
58
59 #include "icp_multi.h"
60
61 #define DEVICE_ID       0x8000  /* Device ID */
62
63 #define ICP_MULTI_EXTDEBUG
64
65 /*  Hardware types of the cards */
66 #define TYPE_ICP_MULTI  0
67
68 #define IORANGE_ICP_MULTI       32
69
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 */
82
83 #define ICP_MULTI_SIZE          0x20    /* 32 bytes */
84
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 */
91
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 */
97
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 */
107
108 /*  Useful definitions */
109 #define Status_IRQ      0x00ff  /*  All interrupts */
110
111 /*  Define analogue range */
112 static const struct comedi_lrange range_analog = { 4, {
113                         UNI_RANGE(5),
114                         UNI_RANGE(10),
115                         BIP_RANGE(5),
116                         BIP_RANGE(10)
117         }
118 };
119
120 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
121
122 /*
123 ==============================================================================
124         Forward declarations
125 ==============================================================================
126 */
127 static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it);
128 static int icp_multi_detach(struct comedi_device *dev);
129
130 /*
131 ==============================================================================
132         Data & Structure declarations
133 ==============================================================================
134 */
135 static unsigned short pci_list_builded = 0;     /*>0 list of card is known */
136
137 struct boardtype {
138         const char *name;       /*  driver name */
139         int device_id;
140         int iorange;            /*  I/O range len */
141         char have_irq;          /*  1=card support IRQ */
142         char cardtype;          /*  0=ICP Multi */
143         int n_aichan;           /*  num of A/D chans */
144         int n_aichand;          /*  num of A/D chans in diff mode */
145         int n_aochan;           /*  num of D/A chans */
146         int n_dichan;           /*  num of DI chans */
147         int n_dochan;           /*  num of DO chans */
148         int n_ctrs;             /*  num of counters */
149         int ai_maxdata;         /*  resolution of A/D */
150         int ao_maxdata;         /*  resolution of D/A */
151         const struct comedi_lrange *rangelist_ai;       /*  rangelist for A/D */
152         const char *rangecode;  /*  range codes for programming */
153         const struct comedi_lrange *rangelist_ao;       /*  rangelist for D/A */
154 };
155
156 static const struct boardtype boardtypes[] = {
157         {"icp_multi",           /*  Driver name */
158                         DEVICE_ID,      /*  PCI device ID */
159                         IORANGE_ICP_MULTI,      /*  I/O range length */
160                         1,      /*  1=Card supports interrupts */
161                         TYPE_ICP_MULTI, /*  Card type = ICP MULTI */
162                         16,     /*  Num of A/D channels */
163                         8,      /*  Num of A/D channels in diff mode */
164                         4,      /*  Num of D/A channels */
165                         16,     /*  Num of digital inputs */
166                         8,      /*  Num of digital outputs */
167                         4,      /*  Num of counters */
168                         0x0fff, /*  Resolution of A/D */
169                         0x0fff, /*  Resolution of D/A */
170                         &range_analog,  /*  Rangelist for A/D */
171                         range_codes_analog,     /*  Range codes for programming */
172                 &range_analog}, /*  Rangelist for D/A */
173 };
174
175 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
176
177 static struct comedi_driver driver_icp_multi = {
178       driver_name:"icp_multi",
179       module : THIS_MODULE,
180       attach : icp_multi_attach,
181       detach : icp_multi_detach,
182       num_names : n_boardtypes,
183       board_name : &boardtypes[0].name,
184       offset : sizeof(struct boardtype),
185 };
186
187 COMEDI_INITCLEANUP(driver_icp_multi);
188
189 struct icp_multi_private {
190         struct pcilst_struct *card;     /*  pointer to card */
191         char valid;             /*  card is usable */
192         void *io_addr;          /*  Pointer to mapped io address */
193         resource_size_t phys_iobase;    /*  Physical io address */
194         unsigned int AdcCmdStatus;      /*  ADC Command/Status register */
195         unsigned int DacCmdStatus;      /*  DAC Command/Status register */
196         unsigned int IntEnable; /*  Interrupt Enable register */
197         unsigned int IntStatus; /*  Interrupt Status register */
198         unsigned int act_chanlist[32];  /*  list of scaned channel */
199         unsigned char act_chanlist_len; /*  len of scanlist */
200         unsigned char act_chanlist_pos; /*  actual position in MUX list */
201         unsigned int *ai_chanlist;      /*  actaul chanlist */
202         short *ai_data; /*  data buffer */
203         short ao_data[4];       /*  data output buffer */
204         short di_data;  /*  Digital input data */
205         unsigned int do_data;   /*  Remember digital output data */
206 };
207
208 #define devpriv ((struct icp_multi_private *)dev->private)
209 #define this_board ((const struct boardtype *)dev->board_ptr)
210
211 /*
212 ==============================================================================
213         More forward declarations
214 ==============================================================================
215 */
216
217 #if 0
218 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
219         unsigned int *chanlist, unsigned int n_chan);
220 #endif
221 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
222         unsigned int *chanlist, unsigned int n_chan);
223 static int icp_multi_reset(struct comedi_device *dev);
224
225 /*
226 ==============================================================================
227         Functions
228 ==============================================================================
229 */
230
231 /*
232 ==============================================================================
233
234         Name:   icp_multi_insn_read_ai
235
236         Description:
237                 This function reads a single analogue input.
238
239         Parameters:
240                 struct comedi_device *dev       Pointer to current device structure
241                 struct comedi_subdevice *s      Pointer to current subdevice structure
242                 struct comedi_insn *insn        Pointer to current comedi instruction
243                 unsigned int *data              Pointer to analogue input data
244
245         Returns:int                     Nmuber of instructions executed
246
247 ==============================================================================
248 */
249 static int icp_multi_insn_read_ai(struct comedi_device *dev, struct comedi_subdevice *s,
250         struct comedi_insn *insn, unsigned int *data)
251 {
252         int n, timeout;
253
254 #ifdef ICP_MULTI_EXTDEBUG
255         printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
256 #endif
257         /*  Disable A/D conversion ready interrupt */
258         devpriv->IntEnable &= ~ADC_READY;
259         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
260
261         /*  Clear interrupt status */
262         devpriv->IntStatus |= ADC_READY;
263         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
264
265         /*  Set up appropriate channel, mode and range data, for specified channel */
266         setup_channel_list(dev, s, &insn->chanspec, 1);
267
268 #ifdef ICP_MULTI_EXTDEBUG
269         printk("icp_multi A ST=%4x IO=%p\n",
270                 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
271                 devpriv->io_addr + ICP_MULTI_ADC_CSR);
272 #endif
273
274         for (n = 0; n < insn->n; n++) {
275                 /*  Set start ADC bit */
276                 devpriv->AdcCmdStatus |= ADC_ST;
277                 writew(devpriv->AdcCmdStatus,
278                         devpriv->io_addr + ICP_MULTI_ADC_CSR);
279                 devpriv->AdcCmdStatus &= ~ADC_ST;
280
281 #ifdef ICP_MULTI_EXTDEBUG
282                 printk("icp multi B n=%d ST=%4x\n", n,
283                         readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
284 #endif
285
286                 udelay(1);
287
288 #ifdef ICP_MULTI_EXTDEBUG
289                 printk("icp multi C n=%d ST=%4x\n", n,
290                         readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
291 #endif
292
293                 /*  Wait for conversion to complete, or get fed up waiting */
294                 timeout = 100;
295                 while (timeout--) {
296                         if (!(readw(devpriv->io_addr +
297                                                 ICP_MULTI_ADC_CSR) & ADC_BSY))
298                                 goto conv_finish;
299
300 #ifdef ICP_MULTI_EXTDEBUG
301                         if (!(timeout % 10))
302                                 printk("icp multi D n=%d tm=%d ST=%4x\n", n,
303                                         timeout,
304                                         readw(devpriv->io_addr +
305                                                 ICP_MULTI_ADC_CSR));
306 #endif
307
308                         udelay(1);
309                 }
310
311                 /*  If we reach here, a timeout has occurred */
312                 comedi_error(dev, "A/D insn timeout");
313
314                 /*  Disable interrupt */
315                 devpriv->IntEnable &= ~ADC_READY;
316                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
317
318                 /*  Clear interrupt status */
319                 devpriv->IntStatus |= ADC_READY;
320                 writew(devpriv->IntStatus,
321                         devpriv->io_addr + ICP_MULTI_INT_STAT);
322
323                 /*  Clear data received */
324                 data[n] = 0;
325
326 #ifdef ICP_MULTI_EXTDEBUG
327                 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
328 #endif
329                 return -ETIME;
330
331               conv_finish:
332                 data[n] =
333                         (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
334         }
335
336         /*  Disable interrupt */
337         devpriv->IntEnable &= ~ADC_READY;
338         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
339
340         /*  Clear interrupt status */
341         devpriv->IntStatus |= ADC_READY;
342         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
343
344 #ifdef ICP_MULTI_EXTDEBUG
345         printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
346 #endif
347         return n;
348 }
349
350 /*
351 ==============================================================================
352
353         Name:   icp_multi_insn_write_ao
354
355         Description:
356                 This function writes a single analogue output.
357
358         Parameters:
359                 struct comedi_device *dev       Pointer to current device structure
360                 struct comedi_subdevice *s      Pointer to current subdevice structure
361                 struct comedi_insn *insn        Pointer to current comedi instruction
362                 unsigned int *data              Pointer to analogue output data
363
364         Returns:int                     Nmuber of instructions executed
365
366 ==============================================================================
367 */
368 static int icp_multi_insn_write_ao(struct comedi_device *dev, struct comedi_subdevice *s,
369         struct comedi_insn *insn, unsigned int *data)
370 {
371         int n, chan, range, timeout;
372
373 #ifdef ICP_MULTI_EXTDEBUG
374         printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
375 #endif
376         /*  Disable D/A conversion ready interrupt */
377         devpriv->IntEnable &= ~DAC_READY;
378         writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
379
380         /*  Clear interrupt status */
381         devpriv->IntStatus |= DAC_READY;
382         writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
383
384         /*  Get channel number and range */
385         chan = CR_CHAN(insn->chanspec);
386         range = CR_RANGE(insn->chanspec);
387
388         /*  Set up range and channel data */
389         /*  Bit 4 = 1 : Bipolar */
390         /*  Bit 5 = 0 : 5V */
391         /*  Bit 5 = 1 : 10V */
392         /*  Bits 8-9 : Channel number */
393         devpriv->DacCmdStatus &= 0xfccf;
394         devpriv->DacCmdStatus |= this_board->rangecode[range];
395         devpriv->DacCmdStatus |= (chan << 8);
396
397         writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
398
399         for (n = 0; n < insn->n; n++) {
400                 /*  Wait for analogue output data register to be ready for new data, or get fed up waiting */
401                 timeout = 100;
402                 while (timeout--) {
403                         if (!(readw(devpriv->io_addr +
404                                                 ICP_MULTI_DAC_CSR) & DAC_BSY))
405                                 goto dac_ready;
406
407 #ifdef ICP_MULTI_EXTDEBUG
408                         if (!(timeout % 10))
409                                 printk("icp multi A n=%d tm=%d ST=%4x\n", n,
410                                         timeout,
411                                         readw(devpriv->io_addr +
412                                                 ICP_MULTI_DAC_CSR));
413 #endif
414
415                         udelay(1);
416                 }
417
418                 /*  If we reach here, a timeout has occurred */
419                 comedi_error(dev, "D/A insn timeout");
420
421                 /*  Disable interrupt */
422                 devpriv->IntEnable &= ~DAC_READY;
423                 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
424
425                 /*  Clear interrupt status */
426                 devpriv->IntStatus |= DAC_READY;
427                 writew(devpriv->IntStatus,
428                         devpriv->io_addr + ICP_MULTI_INT_STAT);
429
430                 /*  Clear data received */
431                 devpriv->ao_data[chan] = 0;
432
433 #ifdef ICP_MULTI_EXTDEBUG
434                 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
435 #endif
436                 return -ETIME;
437
438               dac_ready:
439                 /*  Write data to analogue output data register */
440                 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
441
442                 /*  Set DAC_ST bit to write the data to selected channel */
443                 devpriv->DacCmdStatus |= DAC_ST;
444                 writew(devpriv->DacCmdStatus,
445                         devpriv->io_addr + ICP_MULTI_DAC_CSR);
446                 devpriv->DacCmdStatus &= ~DAC_ST;
447
448                 /*  Save analogue output data */
449                 devpriv->ao_data[chan] = data[n];
450         }
451
452 #ifdef ICP_MULTI_EXTDEBUG
453         printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
454 #endif
455         return n;
456 }
457
458 /*
459 ==============================================================================
460
461         Name:   icp_multi_insn_read_ao
462
463         Description:
464                 This function reads a single analogue output.
465
466         Parameters:
467                 struct comedi_device *dev       Pointer to current device structure
468                 struct comedi_subdevice *s      Pointer to current subdevice structure
469                 struct comedi_insn *insn        Pointer to current comedi instruction
470                 unsigned int *data              Pointer to analogue output data
471
472         Returns:int                     Nmuber of instructions executed
473
474 ==============================================================================
475 */
476 static int icp_multi_insn_read_ao(struct comedi_device *dev, struct comedi_subdevice *s,
477         struct comedi_insn *insn, unsigned int *data)
478 {
479         int n, chan;
480
481         /*  Get channel number */
482         chan = CR_CHAN(insn->chanspec);
483
484         /*  Read analogue outputs */
485         for (n = 0; n < insn->n; n++)
486                 data[n] = devpriv->ao_data[chan];
487
488         return n;
489 }
490
491 /*
492 ==============================================================================
493
494         Name:   icp_multi_insn_bits_di
495
496         Description:
497                 This function reads the digital inputs.
498
499         Parameters:
500                 struct comedi_device *dev       Pointer to current device structure
501                 struct comedi_subdevice *s      Pointer to current subdevice structure
502                 struct comedi_insn *insn        Pointer to current comedi instruction
503                 unsigned int *data              Pointer to analogue output data
504
505         Returns:int                     Nmuber of instructions executed
506
507 ==============================================================================
508 */
509 static int icp_multi_insn_bits_di(struct comedi_device *dev, struct comedi_subdevice *s,
510         struct comedi_insn *insn, unsigned int *data)
511 {
512         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
513
514         return 2;
515 }
516
517 /*
518 ==============================================================================
519
520         Name:   icp_multi_insn_bits_do
521
522         Description:
523                 This function writes the appropriate digital outputs.
524
525         Parameters:
526                 struct comedi_device *dev       Pointer to current device structure
527                 struct comedi_subdevice *s      Pointer to current subdevice structure
528                 struct comedi_insn *insn        Pointer to current comedi instruction
529                 unsigned int *data              Pointer to analogue output data
530
531         Returns:int                     Nmuber of instructions executed
532
533 ==============================================================================
534 */
535 static int icp_multi_insn_bits_do(struct comedi_device *dev, struct comedi_subdevice *s,
536         struct comedi_insn *insn, unsigned int *data)
537 {
538 #ifdef ICP_MULTI_EXTDEBUG
539         printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
540 #endif
541
542         if (data[0]) {
543                 s->state &= ~data[0];
544                 s->state |= (data[0] & data[1]);
545
546                 printk("Digital outputs = %4x \n", s->state);
547
548                 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
549         }
550
551         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
552
553 #ifdef ICP_MULTI_EXTDEBUG
554         printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
555 #endif
556         return 2;
557 }
558
559 /*
560 ==============================================================================
561
562         Name:   icp_multi_insn_read_ctr
563
564         Description:
565                 This function reads the specified counter.
566
567         Parameters:
568                 struct comedi_device *dev       Pointer to current device structure
569                 struct comedi_subdevice *s      Pointer to current subdevice structure
570                 struct comedi_insn *insn        Pointer to current comedi instruction
571                 unsigned int *data              Pointer to counter data
572
573         Returns:int                     Nmuber of instructions executed
574
575 ==============================================================================
576 */
577 static int icp_multi_insn_read_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
578         struct comedi_insn *insn, unsigned int *data)
579 {
580         return 0;
581 }
582
583 /*
584 ==============================================================================
585
586         Name:   icp_multi_insn_write_ctr
587
588         Description:
589                 This function write to the specified counter.
590
591         Parameters:
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
596
597         Returns:int                     Nmuber of instructions executed
598
599 ==============================================================================
600 */
601 static int icp_multi_insn_write_ctr(struct comedi_device *dev, struct comedi_subdevice *s,
602         struct comedi_insn *insn, unsigned int *data)
603 {
604         return 0;
605 }
606
607 /*
608 ==============================================================================
609
610         Name:   interrupt_service_icp_multi
611
612         Description:
613                 This function is the interrupt service routine for all
614                 interrupts generated by the icp multi board.
615
616         Parameters:
617                 int irq
618                 void *d                 Pointer to current device
619
620 ==============================================================================
621 */
622 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
623 {
624         struct comedi_device *dev = d;
625         int int_no;
626
627 #ifdef ICP_MULTI_EXTDEBUG
628         printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
629                 irq);
630 #endif
631
632         /*  Is this interrupt from our board? */
633         int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
634         if (!int_no)
635                 /*  No, exit */
636                 return IRQ_NONE;
637
638 #ifdef ICP_MULTI_EXTDEBUG
639         printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
640                 readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
641 #endif
642
643         /*  Determine which interrupt is active & handle it */
644         switch (int_no) {
645         case ADC_READY:
646                 break;
647         case DAC_READY:
648                 break;
649         case DOUT_ERROR:
650                 break;
651         case DIN_STATUS:
652                 break;
653         case CIE0:
654                 break;
655         case CIE1:
656                 break;
657         case CIE2:
658                 break;
659         case CIE3:
660                 break;
661         default:
662                 break;
663
664         }
665
666 #ifdef ICP_MULTI_EXTDEBUG
667         printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
668 #endif
669         return IRQ_HANDLED;
670 }
671
672 #if 0
673 /*
674 ==============================================================================
675
676         Name:   check_channel_list
677
678         Description:
679                 This function checks if the channel list, provided by user
680                 is built correctly
681
682         Parameters:
683                 struct comedi_device *dev       Pointer to current sevice structure
684                 struct comedi_subdevice *s      Pointer to current subdevice structure
685                 unsigned int *chanlist  Pointer to packed channel list
686                 unsigned int n_chan     Number of channels to scan
687
688         Returns:int 0 = failure
689                     1 = success
690
691 ==============================================================================
692 */
693 static int check_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
694         unsigned int *chanlist, unsigned int n_chan)
695 {
696         unsigned int i;
697
698 #ifdef ICP_MULTI_EXTDEBUG
699         printk("icp multi EDBG:  check_channel_list(...,%d)\n", n_chan);
700 #endif
701         /*  Check that we at least have one channel to check */
702         if (n_chan < 1) {
703                 comedi_error(dev, "range/channel list is empty!");
704                 return 0;
705         }
706         /*  Check all channels */
707         for (i = 0; i < n_chan; i++) {
708                 /*  Check that channel number is < maximum */
709                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
710                         if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
711                                 comedi_error(dev,
712                                         "Incorrect differential ai channel number");
713                                 return 0;
714                         }
715                 } else {
716                         if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
717                                 comedi_error(dev,
718                                         "Incorrect ai channel number");
719                                 return 0;
720                         }
721                 }
722         }
723         return 1;
724 }
725 #endif
726
727 /*
728 ==============================================================================
729
730         Name:   setup_channel_list
731
732         Description:
733                 This function sets the appropriate channel selection,
734                 differential input mode and range bits in the ADC Command/
735                 Status register.
736
737         Parameters:
738                 struct comedi_device *dev       Pointer to current sevice structure
739                 struct comedi_subdevice *s      Pointer to current subdevice structure
740                 unsigned int *chanlist  Pointer to packed channel list
741                 unsigned int n_chan     Number of channels to scan
742
743         Returns:Void
744
745 ==============================================================================
746 */
747 static void setup_channel_list(struct comedi_device *dev, struct comedi_subdevice *s,
748         unsigned int *chanlist, unsigned int n_chan)
749 {
750         unsigned int i, range, chanprog;
751         unsigned int diff;
752
753 #ifdef ICP_MULTI_EXTDEBUG
754         printk("icp multi EDBG:  setup_channel_list(...,%d)\n", n_chan);
755 #endif
756         devpriv->act_chanlist_len = n_chan;
757         devpriv->act_chanlist_pos = 0;
758
759         for (i = 0; i < n_chan; i++) {
760                 /*  Get channel */
761                 chanprog = CR_CHAN(chanlist[i]);
762
763                 /*  Determine if it is a differential channel (Bit 15  = 1) */
764                 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
765                         diff = 1;
766                         chanprog &= 0x0007;
767                 } else {
768                         diff = 0;
769                         chanprog &= 0x000f;
770                 }
771
772                 /*  Clear channel, range and input mode bits in A/D command/status register */
773                 devpriv->AdcCmdStatus &= 0xf00f;
774
775                 /*  Set channel number and differential mode status bit */
776                 if (diff) {
777                         /*  Set channel number, bits 9-11 & mode, bit 6 */
778                         devpriv->AdcCmdStatus |= (chanprog << 9);
779                         devpriv->AdcCmdStatus |= ADC_DI;
780                 } else
781                         /*  Set channel number, bits 8-11 */
782                         devpriv->AdcCmdStatus |= (chanprog << 8);
783
784                 /*  Get range for current channel */
785                 range = this_board->rangecode[CR_RANGE(chanlist[i])];
786                 /*  Set range. bits 4-5 */
787                 devpriv->AdcCmdStatus |= range;
788
789                 /* Output channel, range, mode to ICP Multi */
790                 writew(devpriv->AdcCmdStatus,
791                         devpriv->io_addr + ICP_MULTI_ADC_CSR);
792
793 #ifdef ICP_MULTI_EXTDEBUG
794                 printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
795                         devpriv->act_chanlist[i]);
796 #endif
797         }
798
799 }
800
801 /*
802 ==============================================================================
803
804         Name:   icp_multi_reset
805
806         Description:
807                 This function resets the icp multi device to a 'safe' state
808
809         Parameters:
810                 struct comedi_device *dev       Pointer to current sevice structure
811
812         Returns:int     0 = success
813
814 ==============================================================================
815 */
816 static int icp_multi_reset(struct comedi_device *dev)
817 {
818         unsigned int i;
819
820 #ifdef ICP_MULTI_EXTDEBUG
821         printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
822 #endif
823         /*  Clear INT enables and requests */
824         writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
825         writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
826
827         if (this_board->n_aochan)
828                 /*  Set DACs to 0..5V range and 0V output */
829                 for (i = 0; i < this_board->n_aochan; i++) {
830                         devpriv->DacCmdStatus &= 0xfcce;
831
832                         /*  Set channel number */
833                         devpriv->DacCmdStatus |= (i << 8);
834
835                         /*  Output 0V */
836                         writew(0, devpriv->io_addr + ICP_MULTI_AO);
837
838                         /*  Set start conversion bit */
839                         devpriv->DacCmdStatus |= DAC_ST;
840
841                         /*  Output to command / status register */
842                         writew(devpriv->DacCmdStatus,
843                                 devpriv->io_addr + ICP_MULTI_DAC_CSR);
844
845                         /*  Delay to allow DAC time to recover */
846                         udelay(1);
847                 }
848         /*  Digital outputs to 0 */
849         writew(0, devpriv->io_addr + ICP_MULTI_DO);
850
851 #ifdef ICP_MULTI_EXTDEBUG
852         printk("icp multi EDBG: END: icp_multi_reset(...)\n");
853 #endif
854         return 0;
855 }
856
857 /*
858 ==============================================================================
859
860         Name:   icp_multi_attach
861
862         Description:
863                 This function sets up all the appropriate data for the current
864                 device.
865
866         Parameters:
867                 struct comedi_device *dev       Pointer to current device structure
868                 struct comedi_devconfig *it     Pointer to current device configuration
869
870         Returns:int     0 = success
871
872 ==============================================================================
873 */
874 static int icp_multi_attach(struct comedi_device *dev, struct comedi_devconfig *it)
875 {
876         struct comedi_subdevice *s;
877         int ret, subdev, n_subdevices;
878         unsigned int irq;
879         struct pcilst_struct *card = NULL;
880         resource_size_t io_addr[5], iobase;
881         unsigned char pci_bus, pci_slot, pci_func;
882
883         printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
884
885         /*  Alocate private data storage space */
886         ret = alloc_private(dev, sizeof(struct icp_multi_private));
887         if (ret < 0)
888                 return ret;
889
890         /*  Initialise list of PCI cards in system, if not already done so */
891         if (pci_list_builded++ == 0) {
892                 pci_card_list_init(PCI_VENDOR_ID_ICP,
893 #ifdef ICP_MULTI_EXTDEBUG
894                         1
895 #else
896                         0
897 #endif
898                         );
899         }
900
901         printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
902                 this_board->name);
903
904         card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
905                                          this_board->device_id, it->options[0],
906                                          it->options[1]);
907
908         if (card == NULL)
909                 return -EIO;
910
911         devpriv->card = card;
912
913         if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
914                                 &irq)) < 0) {
915                 printk(" - Can't get configuration data!\n");
916                 return -EIO;
917         }
918
919         iobase = io_addr[2];
920         devpriv->phys_iobase = iobase;
921
922         printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
923                 (unsigned long long)iobase);
924
925         devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
926
927         if (devpriv->io_addr == NULL) {
928                 printk("ioremap failed.\n");
929                 return -ENOMEM;
930         }
931 #ifdef ICP_MULTI_EXTDEBUG
932         printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
933                 devpriv->io_addr);
934 #endif
935
936         dev->board_name = this_board->name;
937
938         n_subdevices = 0;
939         if (this_board->n_aichan)
940                 n_subdevices++;
941         if (this_board->n_aochan)
942                 n_subdevices++;
943         if (this_board->n_dichan)
944                 n_subdevices++;
945         if (this_board->n_dochan)
946                 n_subdevices++;
947         if (this_board->n_ctrs)
948                 n_subdevices++;
949
950         ret = alloc_subdevices(dev, n_subdevices);
951         if (ret < 0)
952                 return ret;
953
954         icp_multi_reset(dev);
955
956         if (this_board->have_irq) {
957                 if (irq) {
958                         if (request_irq(irq, interrupt_service_icp_multi,
959                                         IRQF_SHARED, "Inova Icp Multi", dev)) {
960                                 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
961                                 irq = 0;        /* Can't use IRQ */
962                         } else
963                                 printk(", irq=%u", irq);
964                 } else
965                         printk(", IRQ disabled");
966         } else
967                 irq = 0;
968
969         dev->irq = irq;
970
971         printk(".\n");
972
973         subdev = 0;
974
975         if (this_board->n_aichan) {
976                 s = dev->subdevices + subdev;
977                 dev->read_subdev = s;
978                 s->type = COMEDI_SUBD_AI;
979                 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
980                 if (this_board->n_aichand)
981                         s->subdev_flags |= SDF_DIFF;
982                 s->n_chan = this_board->n_aichan;
983                 s->maxdata = this_board->ai_maxdata;
984                 s->len_chanlist = this_board->n_aichan;
985                 s->range_table = this_board->rangelist_ai;
986                 s->insn_read = icp_multi_insn_read_ai;
987                 subdev++;
988         }
989
990         if (this_board->n_aochan) {
991                 s = dev->subdevices + subdev;
992                 s->type = COMEDI_SUBD_AO;
993                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
994                 s->n_chan = this_board->n_aochan;
995                 s->maxdata = this_board->ao_maxdata;
996                 s->len_chanlist = this_board->n_aochan;
997                 s->range_table = this_board->rangelist_ao;
998                 s->insn_write = icp_multi_insn_write_ao;
999                 s->insn_read = icp_multi_insn_read_ao;
1000                 subdev++;
1001         }
1002
1003         if (this_board->n_dichan) {
1004                 s = dev->subdevices + subdev;
1005                 s->type = COMEDI_SUBD_DI;
1006                 s->subdev_flags = SDF_READABLE;
1007                 s->n_chan = this_board->n_dichan;
1008                 s->maxdata = 1;
1009                 s->len_chanlist = this_board->n_dichan;
1010                 s->range_table = &range_digital;
1011                 s->io_bits = 0;
1012                 s->insn_bits = icp_multi_insn_bits_di;
1013                 subdev++;
1014         }
1015
1016         if (this_board->n_dochan) {
1017                 s = dev->subdevices + subdev;
1018                 s->type = COMEDI_SUBD_DO;
1019                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1020                 s->n_chan = this_board->n_dochan;
1021                 s->maxdata = 1;
1022                 s->len_chanlist = this_board->n_dochan;
1023                 s->range_table = &range_digital;
1024                 s->io_bits = (1 << this_board->n_dochan) - 1;
1025                 s->state = 0;
1026                 s->insn_bits = icp_multi_insn_bits_do;
1027                 subdev++;
1028         }
1029
1030         if (this_board->n_ctrs) {
1031                 s = dev->subdevices + subdev;
1032                 s->type = COMEDI_SUBD_COUNTER;
1033                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1034                 s->n_chan = this_board->n_ctrs;
1035                 s->maxdata = 0xffff;
1036                 s->len_chanlist = this_board->n_ctrs;
1037                 s->state = 0;
1038                 s->insn_read = icp_multi_insn_read_ctr;
1039                 s->insn_write = icp_multi_insn_write_ctr;
1040                 subdev++;
1041         }
1042
1043         devpriv->valid = 1;
1044
1045 #ifdef ICP_MULTI_EXTDEBUG
1046         printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1047 #endif
1048
1049         return 0;
1050 }
1051
1052 /*
1053 ==============================================================================
1054
1055         Name:   icp_multi_detach
1056
1057         Description:
1058                 This function releases all the resources used by the current
1059                 device.
1060
1061         Parameters:
1062                 struct comedi_device *dev       Pointer to current device structure
1063
1064         Returns:int     0 = success
1065
1066 ==============================================================================
1067 */
1068 static int icp_multi_detach(struct comedi_device *dev)
1069 {
1070
1071         if (dev->private)
1072                 if (devpriv->valid)
1073                         icp_multi_reset(dev);
1074
1075         if (dev->irq)
1076                 free_irq(dev->irq, dev);
1077
1078         if (dev->private && devpriv->io_addr)
1079                 iounmap(devpriv->io_addr);
1080
1081         if (dev->private && devpriv->card)
1082                 pci_card_free(devpriv->card);
1083
1084         if (--pci_list_builded == 0)
1085                 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1086
1087         return 0;
1088 }