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