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