Added some ack'ing of b interrupts, and do acks before handling
[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;     /*>0 list of card is known */
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:     (const char**)boardtypes,
189         offset:         sizeof(boardtype),
190 };
191 COMEDI_INITCLEANUP(driver_icp_multi);
192
193 typedef struct{
194         struct pcilst_struct    *card;                  // pointer to card
195         char                    valid;                  // card is usable
196         void                    *io_addr;               // Pointer to mapped io address
197         resource_size_t         phys_iobase;            // Physical io address
198         unsigned int            AdcCmdStatus;           // ADC Command/Status register
199         unsigned int            DacCmdStatus;           // DAC Command/Status register
200         unsigned int            IntEnable;              // Interrupt Enable register
201         unsigned int            IntStatus;              // Interrupt Status register
202         unsigned int            act_chanlist[32];       // list of scaned channel
203         unsigned char           act_chanlist_len;       // len of scanlist
204         unsigned char           act_chanlist_pos;       // actual position in MUX list
205         unsigned int            *ai_chanlist;           // actaul chanlist
206         sampl_t                 *ai_data;               // data buffer
207         sampl_t                 ao_data[4];             // data output buffer
208         sampl_t                 di_data;                // Digital input data
209         unsigned int            do_data;                // Remember digital output data
210 } icp_multi_private;
211
212 #define devpriv ((icp_multi_private *)dev->private)
213 #define this_board ((boardtype *)dev->board_ptr)
214
215 /*
216 ==============================================================================
217         More forward declarations
218 ==============================================================================
219 */
220
221 #if 0
222 static int check_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);
223 #endif
224 static void setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);
225 static int icp_multi_reset(comedi_device *dev);
226
227
228 /*
229 ==============================================================================
230         Functions
231 ==============================================================================
232 */
233
234
235 /*
236 ==============================================================================
237
238         Name:   icp_multi_insn_read_ai
239
240         Description:
241                 This function reads a single analogue input.
242
243         Parameters:
244                 comedi_device *dev      Pointer to current device structure
245                 comedi_subdevice *s     Pointer to current subdevice structure
246                 comedi_insn *insn       Pointer to current comedi instruction
247                 lsampl_t *data          Pointer to analogue input data
248
249         Returns:int                     Nmuber of instructions executed
250
251 ==============================================================================
252 */
253 static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
254 {
255         int n,timeout;
256
257 #ifdef ICP_MULTI_EXTDEBUG
258         printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
259 #endif
260         // Disable A/D conversion ready interrupt
261         devpriv->IntEnable &= ~ADC_READY;
262         writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
263
264         // Clear interrupt status
265         devpriv->IntStatus |= ADC_READY;
266         writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
267
268         // Set up appropriate channel, mode and range data, for specified channel
269         setup_channel_list(dev, s, &insn->chanspec, 1);
270
271 #ifdef ICP_MULTI_EXTDEBUG
272         printk("icp_multi A ST=%4x IO=%p\n",readw(devpriv->io_addr+ICP_MULTI_ADC_CSR), devpriv->io_addr+ICP_MULTI_ADC_CSR);
273 #endif
274
275         for (n=0; n<insn->n; n++) {
276                 // Set start ADC bit
277                 devpriv->AdcCmdStatus |= ADC_ST;
278                 writew(devpriv->AdcCmdStatus, devpriv->io_addr+ICP_MULTI_ADC_CSR);
279                 devpriv->AdcCmdStatus &= ~ADC_ST;
280
281 #ifdef ICP_MULTI_EXTDEBUG
282                 printk("icp multi B n=%d ST=%4x\n",n,readw(devpriv->io_addr+ICP_MULTI_ADC_CSR));
283 #endif
284
285                 comedi_udelay(1);
286
287 #ifdef ICP_MULTI_EXTDEBUG
288                 printk("icp multi C n=%d ST=%4x\n",n,readw(devpriv->io_addr+ICP_MULTI_ADC_CSR));
289 #endif
290
291                 // Wait for conversion to complete, or get fed up waiting
292                 timeout=100;
293                 while (timeout--) {
294                         if (!(readw(devpriv->io_addr+ICP_MULTI_ADC_CSR) & ADC_BSY))
295                                 goto conv_finish;
296
297 #ifdef ICP_MULTI_EXTDEBUG
298                         if (!(timeout%10))
299                                 printk("icp multi D n=%d tm=%d ST=%4x\n",n,timeout,readw(devpriv->io_addr+ICP_MULTI_ADC_CSR));
300 #endif
301
302                         comedi_udelay(1);
303                 }
304
305                 // If we reach here, a timeout has occurred
306                 comedi_error(dev,"A/D insn timeout");
307
308                 // Disable interrupt
309                 devpriv->IntEnable &= ~ADC_READY;
310                 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
311
312                 // Clear interrupt status
313                 devpriv->IntStatus |= ADC_READY;
314                 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
315
316                 // Clear data received
317                 data[n]=0;
318
319 #ifdef ICP_MULTI_EXTDEBUG
320                 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);
321 #endif
322                 return -ETIME;
323
324 conv_finish:
325                 data[n] = (readw(devpriv->io_addr+ICP_MULTI_AI) >> 4 ) & 0x0fff;
326         }
327
328         // Disable interrupt
329         devpriv->IntEnable &= ~ADC_READY;
330         writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
331
332         // Clear interrupt status
333         devpriv->IntStatus |= ADC_READY;
334         writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
335
336 #ifdef ICP_MULTI_EXTDEBUG
337                 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);
338 #endif
339         return n;
340 }
341
342 /*
343 ==============================================================================
344
345         Name:   icp_multi_insn_write_ao
346
347         Description:
348                 This function writes a single analogue output.
349
350         Parameters:
351                 comedi_device *dev      Pointer to current device structure
352                 comedi_subdevice *s     Pointer to current subdevice structure
353                 comedi_insn *insn       Pointer to current comedi instruction
354                 lsampl_t *data          Pointer to analogue output data
355
356         Returns:int                     Nmuber of instructions executed
357
358 ==============================================================================
359 */
360 static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
361 {
362         int n, chan, range, timeout;
363
364 #ifdef ICP_MULTI_EXTDEBUG
365         printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
366 #endif
367         // Disable D/A conversion ready interrupt
368         devpriv->IntEnable &= ~DAC_READY;
369         writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
370
371         // Clear interrupt status
372         devpriv->IntStatus |= DAC_READY;
373         writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
374
375         // Get channel number and range
376         chan = CR_CHAN(insn->chanspec);
377         range = CR_RANGE(insn->chanspec);
378
379         // Set up range and channel data
380         // Bit 4 = 1 : Bipolar
381         // Bit 5 = 0 : 5V
382         // Bit 5 = 1 : 10V
383         // Bits 8-9 : Channel number
384         devpriv->DacCmdStatus &= 0xfccf;
385         devpriv->DacCmdStatus |= this_board->rangecode[range];
386         devpriv->DacCmdStatus |= (chan << 8);
387
388         writew(devpriv->DacCmdStatus, devpriv->io_addr+ICP_MULTI_DAC_CSR);
389
390         for (n=0; n<insn->n; n++) {
391                 // Wait for analogue output data register to be ready for new data, or get fed up waiting
392                 timeout=100;
393                 while (timeout--) {
394                         if (!(readw(devpriv->io_addr+ICP_MULTI_DAC_CSR) & DAC_BSY))
395                                 goto dac_ready;
396
397 #ifdef ICP_MULTI_EXTDEBUG
398                         if (!(timeout%10))
399                                 printk("icp multi A n=%d tm=%d ST=%4x\n",n,timeout,readw(devpriv->io_addr+ICP_MULTI_DAC_CSR));
400 #endif
401
402                         comedi_udelay(1);
403                 }
404
405                 // If we reach here, a timeout has occurred
406                 comedi_error(dev,"D/A insn timeout");
407
408                 // Disable interrupt
409                 devpriv->IntEnable &= ~DAC_READY;
410                 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
411
412                 // Clear interrupt status
413                 devpriv->IntStatus |= DAC_READY;
414                 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
415
416                 // Clear data received
417                 devpriv->ao_data[chan]=0;
418
419 #ifdef ICP_MULTI_EXTDEBUG
420                 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);
421 #endif
422                 return -ETIME;
423
424 dac_ready:
425                 // Write data to analogue output data register
426                 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
427
428                 // Set DAC_ST bit to write the data to selected channel
429                 devpriv->DacCmdStatus |= DAC_ST;
430                 writew(devpriv->DacCmdStatus, devpriv->io_addr+ICP_MULTI_DAC_CSR);
431                 devpriv->DacCmdStatus &= ~DAC_ST;
432
433                 // Save analogue output data
434                 devpriv->ao_data[chan]=data[n];
435         }
436
437 #ifdef ICP_MULTI_EXTDEBUG
438                 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);
439 #endif
440                 return n;
441 }
442
443 /*
444 ==============================================================================
445
446         Name:   icp_multi_insn_read_ao
447
448         Description:
449                 This function reads a single analogue output.
450
451         Parameters:
452                 comedi_device *dev      Pointer to current device structure
453                 comedi_subdevice *s     Pointer to current subdevice structure
454                 comedi_insn *insn       Pointer to current comedi instruction
455                 lsampl_t *data          Pointer to analogue output data
456
457         Returns:int                     Nmuber of instructions executed
458
459 ==============================================================================
460 */
461 static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
462 {
463         int n,chan;
464
465         // Get channel number
466         chan = CR_CHAN(insn->chanspec);
467
468         // Read analogue outputs
469         for (n=0; n<insn->n; n++)
470                 data[n]=devpriv->ao_data[chan];
471
472         return n;
473 }
474
475 /*
476 ==============================================================================
477
478         Name:   icp_multi_insn_bits_di
479
480         Description:
481                 This function reads the digital inputs.
482
483         Parameters:
484                 comedi_device *dev      Pointer to current device structure
485                 comedi_subdevice *s     Pointer to current subdevice structure
486                 comedi_insn *insn       Pointer to current comedi instruction
487                 lsampl_t *data          Pointer to analogue output data
488
489         Returns:int                     Nmuber of instructions executed
490
491 ==============================================================================
492 */
493 static int icp_multi_insn_bits_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
494 {
495         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
496
497         return 2;
498 }
499
500 /*
501 ==============================================================================
502
503         Name:   icp_multi_insn_bits_do
504
505         Description:
506                 This function writes the appropriate digital outputs.
507
508         Parameters:
509                 comedi_device *dev      Pointer to current device structure
510                 comedi_subdevice *s     Pointer to current subdevice structure
511                 comedi_insn *insn       Pointer to current comedi instruction
512                 lsampl_t *data          Pointer to analogue output data
513
514         Returns:int                     Nmuber of instructions executed
515
516 ==============================================================================
517 */
518 static int icp_multi_insn_bits_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
519 {
520 #ifdef ICP_MULTI_EXTDEBUG
521         printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
522 #endif
523
524         if (data[0]) {
525                 s->state &= ~data[0];
526                 s->state |= (data[0] & data[1]);
527
528                 printk("Digital outputs = %4x \n", s->state);
529
530                 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
531         }
532
533         data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
534
535 #ifdef ICP_MULTI_EXTDEBUG
536                 printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
537 #endif
538         return 2;
539 }
540
541
542 /*
543 ==============================================================================
544
545         Name:   icp_multi_insn_read_ctr
546
547         Description:
548                 This function reads the specified counter.
549
550         Parameters:
551                 comedi_device *dev      Pointer to current device structure
552                 comedi_subdevice *s     Pointer to current subdevice structure
553                 comedi_insn *insn       Pointer to current comedi instruction
554                 lsampl_t *data          Pointer to counter data
555
556         Returns:int                     Nmuber of instructions executed
557
558 ==============================================================================
559 */
560 static int icp_multi_insn_read_ctr(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
561 {
562         return 0;
563 }
564
565
566 /*
567 ==============================================================================
568
569         Name:   icp_multi_insn_write_ctr
570
571         Description:
572                 This function write to the specified counter.
573
574         Parameters:
575                 comedi_device *dev      Pointer to current device structure
576                 comedi_subdevice *s     Pointer to current subdevice structure
577                 comedi_insn *insn       Pointer to current comedi instruction
578                 lsampl_t *data          Pointer to counter data
579
580         Returns:int                     Nmuber of instructions executed
581
582 ==============================================================================
583 */
584 static int icp_multi_insn_write_ctr(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
585 {
586         return 0;
587 }
588
589
590
591 /*
592 ==============================================================================
593
594         Name:   interrupt_service_icp_multi
595
596         Description:
597                 This function is the interrupt service routine for all
598                 interrupts generated by the icp multi board.
599
600         Parameters:
601                 int irq
602                 void *d                 Pointer to current device
603
604 ==============================================================================
605 */
606 static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
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(devpriv->io_addr + 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(devpriv->io_addr + 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, devpriv->io_addr+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, devpriv->io_addr + ICP_MULTI_INT_EN);
811         writew(0x00ff, devpriv->io_addr + 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, devpriv->io_addr+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, devpriv->io_addr+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, devpriv->io_addr + 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;
865         unsigned int            irq;
866         struct pcilst_struct    *card=NULL;
867         resource_size_t         io_addr[5], iobase;
868         unsigned char           pci_bus, pci_slot, pci_func;
869
870         printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
871
872         // Alocate private data storage space
873         if ((ret=alloc_private(dev, sizeof(icp_multi_private)))<0)
874                 return ret;
875
876         // Initialise list of PCI cards in system, if not already done so
877         if (pci_list_builded++ == 0) {
878                 pci_card_list_init(PCI_VENDOR_ID_ICP,
879 #ifdef ICP_MULTI_EXTDEBUG
880                                                     1
881 #else
882                                                     0
883 #endif
884                         );
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         devpriv->card = card;
893
894         if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0], &irq, &master))<0) {
895                 printk(" - Can't get configuration data!\n");
896                 return -EIO;
897         }
898
899         iobase=io_addr[2];
900         devpriv->phys_iobase = iobase;
901
902         printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
903                         (unsigned long long)iobase);
904
905         devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
906
907         if (devpriv->io_addr == NULL) {
908                 printk("ioremap failed.\n");
909                 return -ENOMEM;
910         }
911
912 #ifdef ICP_MULTI_EXTDEBUG
913         printk("0x%08llx mapped to %p, ", (unsigned long long)iobase, devpriv->io_addr);
914 #endif
915
916         dev->board_name = this_board->name;
917
918         n_subdevices = 0;
919         if (this_board->n_aichan) n_subdevices++;
920         if (this_board->n_aochan) n_subdevices++;
921         if (this_board->n_dichan) n_subdevices++;
922         if (this_board->n_dochan) n_subdevices++;
923         if (this_board->n_ctrs)   n_subdevices++;
924
925         if((ret=alloc_subdevices(dev, n_subdevices))<0) {
926                 return ret;
927         }
928
929         if (this_board->have_irq) {
930                 if (irq)  {
931                         if (comedi_request_irq(irq, interrupt_service_icp_multi, IRQF_SHARED, "Inova Icp Multi", dev)) {
932                                 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
933                                 irq=0; /* Can't use IRQ */
934                         }
935                         else
936                                 printk(", irq=%u", irq);
937                 }
938                 else
939                         printk(", IRQ disabled");
940         }
941         else
942                 irq=0;
943
944         dev->irq = irq;
945
946         printk(".\n");
947
948         subdev=0;
949
950         if (this_board->n_aichan) {
951                 s = dev->subdevices + subdev;
952                 dev->read_subdev = s;
953                 s->type = COMEDI_SUBD_AI;
954                 s->subdev_flags = SDF_READABLE|SDF_COMMON|SDF_GROUND;
955                 if (this_board->n_aichand)
956                         s->subdev_flags |= SDF_DIFF;
957                 s->n_chan = this_board->n_aichan;
958                 s->maxdata = this_board->ai_maxdata;
959                 s->len_chanlist = this_board->n_aichan;
960                 s->range_table = this_board->rangelist_ai;
961                 s->insn_read=icp_multi_insn_read_ai;
962                 subdev++;
963         }
964
965         if (this_board->n_aochan) {
966                 s = dev->subdevices + subdev;
967                 s->type = COMEDI_SUBD_AO;
968                 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
969                 s->n_chan = this_board->n_aochan;
970                 s->maxdata = this_board->ao_maxdata;
971                 s->len_chanlist = this_board->n_aochan;
972                 s->range_table = this_board->rangelist_ao;
973                 s->insn_write=icp_multi_insn_write_ao;
974                 s->insn_read=icp_multi_insn_read_ao;
975                 subdev++;
976         }
977
978         if (this_board->n_dichan) {
979                 s = dev->subdevices + subdev;
980                 s->type = COMEDI_SUBD_DI;
981                 s->subdev_flags = SDF_READABLE;
982                 s->n_chan = this_board->n_dichan;
983                 s->maxdata = 1;
984                 s->len_chanlist = this_board->n_dichan;
985                 s->range_table = &range_digital;
986                 s->io_bits=0;
987                 s->insn_bits=icp_multi_insn_bits_di;
988                 subdev++;
989         }
990
991         if (this_board->n_dochan) {
992                 s = dev->subdevices + subdev;
993                 s->type = COMEDI_SUBD_DO;
994                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
995                 s->n_chan = this_board->n_dochan;
996                 s->maxdata = 1;
997                 s->len_chanlist = this_board->n_dochan;
998                 s->range_table = &range_digital;
999                 s->io_bits=(1 << this_board->n_dochan)-1;
1000                 s->state=0;
1001                 s->insn_bits=icp_multi_insn_bits_do;
1002                 subdev++;
1003         }
1004
1005         if (this_board->n_ctrs) {
1006                 s = dev->subdevices + subdev;
1007                 s->type = COMEDI_SUBD_COUNTER;
1008                 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
1009                 s->n_chan = this_board->n_ctrs;
1010                 s->maxdata = 0xffff;
1011                 s->len_chanlist = this_board->n_ctrs;
1012                 s->state=0;
1013                 s->insn_read=icp_multi_insn_read_ctr;
1014                 s->insn_write=icp_multi_insn_write_ctr;
1015                 subdev++;
1016         }
1017
1018         devpriv->valid = 1;
1019
1020         icp_multi_reset(dev);
1021
1022 #ifdef ICP_MULTI_EXTDEBUG
1023         printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1024 #endif
1025
1026         return 0;
1027 }
1028
1029 /*
1030 ==============================================================================
1031
1032         Name:   icp_multi_detach
1033
1034         Description:
1035                 This function releases all the resources used by the current
1036                 device.
1037
1038         Parameters:
1039                 comedi_device *dev      Pointer to current device structure
1040
1041         Returns:int     0 = success
1042
1043 ==============================================================================
1044 */
1045 static int icp_multi_detach(comedi_device *dev)
1046 {
1047
1048         if (dev->private)
1049                 if (devpriv->valid)
1050                         icp_multi_reset(dev);
1051
1052         if (dev->irq)
1053                 comedi_free_irq(dev->irq,dev);
1054
1055         if (dev->private && devpriv->io_addr)
1056                 iounmap(devpriv->io_addr);
1057
1058         if (dev->private && devpriv->card)
1059                 pci_card_free(devpriv->card);
1060
1061         if (--pci_list_builded == 0) {
1062                 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1063         }
1064
1065
1066         return 0;
1067 }
1068