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