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