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