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