Staging: comedi: Remove comedi_device typedef
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / icp_multi.c
CommitLineData
96341f71
AS
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/*
24Driver: icp_multi
25Description: Inova ICP_MULTI
26Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27Devices: [Inova] ICP_MULTI (icp_multi)
28Status: works
29
30The driver works for analog input and output and digital input and output.
31It does not work with interrupts or with the counters. Currently no support
32for DMA.
33
34It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35resolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Input
36ranges can be individually programmed for each channel. Voltage or current
37measurement is selected by jumper.
38
39There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
40
4116 x Digital Inputs, 24V
42
438 x Digital Outputs, 24V, 1A
44
454 x 16-bit counters
46
47Options:
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
b6c77757 64/* Hardware types of the cards */
96341f71
AS
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
b6c77757 84/* Define bits from ADC command/status register */
96341f71
AS
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
b6c77757 91/* Define bits from DAC command/status register */
96341f71
AS
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
b6c77757 97/* Define bits from interrupt enable/status registers */
96341f71
AS
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
b6c77757
BP
107/* Useful definitions */
108#define Status_IRQ 0x00ff /* All interrupts */
96341f71 109
b6c77757 110/* Define analogue range */
96341f71
AS
111static 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
119static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
120
121/*
122==============================================================================
123 Forward declarations
124==============================================================================
125*/
71b5f4f1
BP
126static int icp_multi_attach(struct comedi_device *dev, comedi_devconfig *it);
127static int icp_multi_detach(struct comedi_device *dev);
96341f71
AS
128
129/*
130==============================================================================
131 Data & Structure declarations
132==============================================================================
133*/
134static unsigned short pci_list_builded = 0; /*>0 list of card is known */
135
52bfe6c8 136struct boardtype {
b6c77757 137 const char *name; /* driver name */
96341f71 138 int device_id;
b6c77757
BP
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 */
52bfe6c8 153};
96341f71 154
52bfe6c8 155static const struct boardtype boardtypes[] = {
b6c77757
BP
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 */
96341f71
AS
172};
173
52bfe6c8 174#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype))
96341f71
AS
175
176static comedi_driver driver_icp_multi = {
177 driver_name:"icp_multi",
f7266a48
BP
178 module : THIS_MODULE,
179 attach : icp_multi_attach,
180 detach : icp_multi_detach,
181 num_names : n_boardtypes,
51b713a6 182 board_name : &boardtypes[0].name,
52bfe6c8 183 offset : sizeof(struct boardtype),
96341f71
AS
184};
185
186COMEDI_INITCLEANUP(driver_icp_multi);
187
52bfe6c8 188struct icp_multi_private {
b6c77757
BP
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 */
790c5541
BP
201 short *ai_data; /* data buffer */
202 short ao_data[4]; /* data output buffer */
203 short di_data; /* Digital input data */
b6c77757 204 unsigned int do_data; /* Remember digital output data */
52bfe6c8 205};
96341f71 206
52bfe6c8
BP
207#define devpriv ((struct icp_multi_private *)dev->private)
208#define this_board ((const struct boardtype *)dev->board_ptr)
96341f71
AS
209
210/*
211==============================================================================
212 More forward declarations
213==============================================================================
214*/
215
216#if 0
71b5f4f1 217static int check_channel_list(struct comedi_device *dev, comedi_subdevice *s,
96341f71
AS
218 unsigned int *chanlist, unsigned int n_chan);
219#endif
71b5f4f1 220static void setup_channel_list(struct comedi_device *dev, comedi_subdevice *s,
96341f71 221 unsigned int *chanlist, unsigned int n_chan);
71b5f4f1 222static int icp_multi_reset(struct comedi_device *dev);
96341f71
AS
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:
71b5f4f1 239 struct comedi_device *dev Pointer to current device structure
96341f71
AS
240 comedi_subdevice *s Pointer to current subdevice structure
241 comedi_insn *insn Pointer to current comedi instruction
790c5541 242 unsigned int *data Pointer to analogue input data
96341f71
AS
243
244 Returns:int Nmuber of instructions executed
245
246==============================================================================
247*/
71b5f4f1 248static int icp_multi_insn_read_ai(struct comedi_device *dev, comedi_subdevice *s,
790c5541 249 comedi_insn *insn, unsigned int *data)
96341f71
AS
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
b6c77757 256 /* Disable A/D conversion ready interrupt */
96341f71
AS
257 devpriv->IntEnable &= ~ADC_READY;
258 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
259
b6c77757 260 /* Clear interrupt status */
96341f71
AS
261 devpriv->IntStatus |= ADC_READY;
262 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
263
b6c77757 264 /* Set up appropriate channel, mode and range data, for specified channel */
96341f71
AS
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++) {
b6c77757 274 /* Set start ADC bit */
96341f71
AS
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
b6c77757 292 /* Wait for conversion to complete, or get fed up waiting */
96341f71
AS
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
b6c77757 310 /* If we reach here, a timeout has occurred */
96341f71
AS
311 comedi_error(dev, "A/D insn timeout");
312
b6c77757 313 /* Disable interrupt */
96341f71
AS
314 devpriv->IntEnable &= ~ADC_READY;
315 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
316
b6c77757 317 /* Clear interrupt status */
96341f71
AS
318 devpriv->IntStatus |= ADC_READY;
319 writew(devpriv->IntStatus,
320 devpriv->io_addr + ICP_MULTI_INT_STAT);
321
b6c77757 322 /* Clear data received */
96341f71
AS
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
b6c77757 335 /* Disable interrupt */
96341f71
AS
336 devpriv->IntEnable &= ~ADC_READY;
337 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
338
b6c77757 339 /* Clear interrupt status */
96341f71
AS
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:
71b5f4f1 358 struct comedi_device *dev Pointer to current device structure
96341f71
AS
359 comedi_subdevice *s Pointer to current subdevice structure
360 comedi_insn *insn Pointer to current comedi instruction
790c5541 361 unsigned int *data Pointer to analogue output data
96341f71
AS
362
363 Returns:int Nmuber of instructions executed
364
365==============================================================================
366*/
71b5f4f1 367static int icp_multi_insn_write_ao(struct comedi_device *dev, comedi_subdevice *s,
790c5541 368 comedi_insn *insn, unsigned int *data)
96341f71
AS
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
b6c77757 375 /* Disable D/A conversion ready interrupt */
96341f71
AS
376 devpriv->IntEnable &= ~DAC_READY;
377 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
378
b6c77757 379 /* Clear interrupt status */
96341f71
AS
380 devpriv->IntStatus |= DAC_READY;
381 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
382
b6c77757 383 /* Get channel number and range */
96341f71
AS
384 chan = CR_CHAN(insn->chanspec);
385 range = CR_RANGE(insn->chanspec);
386
b6c77757
BP
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 */
96341f71
AS
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++) {
b6c77757 399 /* Wait for analogue output data register to be ready for new data, or get fed up waiting */
96341f71
AS
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
b6c77757 417 /* If we reach here, a timeout has occurred */
96341f71
AS
418 comedi_error(dev, "D/A insn timeout");
419
b6c77757 420 /* Disable interrupt */
96341f71
AS
421 devpriv->IntEnable &= ~DAC_READY;
422 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
423
b6c77757 424 /* Clear interrupt status */
96341f71
AS
425 devpriv->IntStatus |= DAC_READY;
426 writew(devpriv->IntStatus,
427 devpriv->io_addr + ICP_MULTI_INT_STAT);
428
b6c77757 429 /* Clear data received */
96341f71
AS
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:
b6c77757 438 /* Write data to analogue output data register */
96341f71
AS
439 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
440
b6c77757 441 /* Set DAC_ST bit to write the data to selected channel */
96341f71
AS
442 devpriv->DacCmdStatus |= DAC_ST;
443 writew(devpriv->DacCmdStatus,
444 devpriv->io_addr + ICP_MULTI_DAC_CSR);
445 devpriv->DacCmdStatus &= ~DAC_ST;
446
b6c77757 447 /* Save analogue output data */
96341f71
AS
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:
71b5f4f1 466 struct comedi_device *dev Pointer to current device structure
96341f71
AS
467 comedi_subdevice *s Pointer to current subdevice structure
468 comedi_insn *insn Pointer to current comedi instruction
790c5541 469 unsigned int *data Pointer to analogue output data
96341f71
AS
470
471 Returns:int Nmuber of instructions executed
472
473==============================================================================
474*/
71b5f4f1 475static int icp_multi_insn_read_ao(struct comedi_device *dev, comedi_subdevice *s,
790c5541 476 comedi_insn *insn, unsigned int *data)
96341f71
AS
477{
478 int n, chan;
479
b6c77757 480 /* Get channel number */
96341f71
AS
481 chan = CR_CHAN(insn->chanspec);
482
b6c77757 483 /* Read analogue outputs */
96341f71
AS
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:
71b5f4f1 499 struct comedi_device *dev Pointer to current device structure
96341f71
AS
500 comedi_subdevice *s Pointer to current subdevice structure
501 comedi_insn *insn Pointer to current comedi instruction
790c5541 502 unsigned int *data Pointer to analogue output data
96341f71
AS
503
504 Returns:int Nmuber of instructions executed
505
506==============================================================================
507*/
71b5f4f1 508static int icp_multi_insn_bits_di(struct comedi_device *dev, comedi_subdevice *s,
790c5541 509 comedi_insn *insn, unsigned int *data)
96341f71
AS
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:
71b5f4f1 525 struct comedi_device *dev Pointer to current device structure
96341f71
AS
526 comedi_subdevice *s Pointer to current subdevice structure
527 comedi_insn *insn Pointer to current comedi instruction
790c5541 528 unsigned int *data Pointer to analogue output data
96341f71
AS
529
530 Returns:int Nmuber of instructions executed
531
532==============================================================================
533*/
71b5f4f1 534static int icp_multi_insn_bits_do(struct comedi_device *dev, comedi_subdevice *s,
790c5541 535 comedi_insn *insn, unsigned int *data)
96341f71
AS
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:
71b5f4f1 567 struct comedi_device *dev Pointer to current device structure
96341f71
AS
568 comedi_subdevice *s Pointer to current subdevice structure
569 comedi_insn *insn Pointer to current comedi instruction
790c5541 570 unsigned int *data Pointer to counter data
96341f71
AS
571
572 Returns:int Nmuber of instructions executed
573
574==============================================================================
575*/
71b5f4f1 576static int icp_multi_insn_read_ctr(struct comedi_device *dev, comedi_subdevice *s,
790c5541 577 comedi_insn *insn, unsigned int *data)
96341f71
AS
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:
71b5f4f1 591 struct comedi_device *dev Pointer to current device structure
96341f71
AS
592 comedi_subdevice *s Pointer to current subdevice structure
593 comedi_insn *insn Pointer to current comedi instruction
790c5541 594 unsigned int *data Pointer to counter data
96341f71
AS
595
596 Returns:int Nmuber of instructions executed
597
598==============================================================================
599*/
71b5f4f1 600static int icp_multi_insn_write_ctr(struct comedi_device *dev, comedi_subdevice *s,
790c5541 601 comedi_insn *insn, unsigned int *data)
96341f71
AS
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*/
621static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
622{
71b5f4f1 623 struct comedi_device *dev = d;
96341f71
AS
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
b6c77757 631 /* Is this interrupt from our board? */
96341f71
AS
632 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
633 if (!int_no)
b6c77757 634 /* No, exit */
96341f71
AS
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
b6c77757 642 /* Determine which interrupt is active & handle it */
96341f71
AS
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:
71b5f4f1 682 struct comedi_device *dev Pointer to current sevice structure
96341f71
AS
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*/
71b5f4f1 692static int check_channel_list(struct comedi_device *dev, comedi_subdevice *s,
96341f71
AS
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
b6c77757 700 /* Check that we at least have one channel to check */
96341f71
AS
701 if (n_chan < 1) {
702 comedi_error(dev, "range/channel list is empty!");
703 return 0;
704 }
b6c77757 705 /* Check all channels */
96341f71 706 for (i = 0; i < n_chan; i++) {
b6c77757 707 /* Check that channel number is < maximum */
96341f71
AS
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:
71b5f4f1 737 struct comedi_device *dev Pointer to current sevice structure
96341f71
AS
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*/
71b5f4f1 746static void setup_channel_list(struct comedi_device *dev, comedi_subdevice *s,
96341f71
AS
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++) {
b6c77757 759 /* Get channel */
96341f71
AS
760 chanprog = CR_CHAN(chanlist[i]);
761
b6c77757 762 /* Determine if it is a differential channel (Bit 15 = 1) */
96341f71
AS
763 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
764 diff = 1;
765 chanprog &= 0x0007;
766 } else {
767 diff = 0;
768 chanprog &= 0x000f;
769 }
770
b6c77757 771 /* Clear channel, range and input mode bits in A/D command/status register */
96341f71
AS
772 devpriv->AdcCmdStatus &= 0xf00f;
773
b6c77757 774 /* Set channel number and differential mode status bit */
96341f71 775 if (diff) {
b6c77757 776 /* Set channel number, bits 9-11 & mode, bit 6 */
96341f71
AS
777 devpriv->AdcCmdStatus |= (chanprog << 9);
778 devpriv->AdcCmdStatus |= ADC_DI;
779 } else
b6c77757 780 /* Set channel number, bits 8-11 */
96341f71
AS
781 devpriv->AdcCmdStatus |= (chanprog << 8);
782
b6c77757 783 /* Get range for current channel */
96341f71 784 range = this_board->rangecode[CR_RANGE(chanlist[i])];
b6c77757 785 /* Set range. bits 4-5 */
96341f71
AS
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:
71b5f4f1 809 struct comedi_device *dev Pointer to current sevice structure
96341f71
AS
810
811 Returns:int 0 = success
812
813==============================================================================
814*/
71b5f4f1 815static int icp_multi_reset(struct comedi_device *dev)
96341f71
AS
816{
817 unsigned int i;
818
819#ifdef ICP_MULTI_EXTDEBUG
820 printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
821#endif
b6c77757 822 /* Clear INT enables and requests */
96341f71
AS
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)
b6c77757 827 /* Set DACs to 0..5V range and 0V output */
96341f71
AS
828 for (i = 0; i < this_board->n_aochan; i++) {
829 devpriv->DacCmdStatus &= 0xfcce;
830
b6c77757 831 /* Set channel number */
96341f71
AS
832 devpriv->DacCmdStatus |= (i << 8);
833
b6c77757 834 /* Output 0V */
96341f71
AS
835 writew(0, devpriv->io_addr + ICP_MULTI_AO);
836
b6c77757 837 /* Set start conversion bit */
96341f71
AS
838 devpriv->DacCmdStatus |= DAC_ST;
839
b6c77757 840 /* Output to command / status register */
96341f71
AS
841 writew(devpriv->DacCmdStatus,
842 devpriv->io_addr + ICP_MULTI_DAC_CSR);
843
b6c77757 844 /* Delay to allow DAC time to recover */
96341f71
AS
845 comedi_udelay(1);
846 }
b6c77757 847 /* Digital outputs to 0 */
96341f71
AS
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:
71b5f4f1 866 struct comedi_device *dev Pointer to current device structure
96341f71
AS
867 comedi_devconfig *it Pointer to current device configuration
868
869 Returns:int 0 = success
870
871==============================================================================
872*/
71b5f4f1 873static int icp_multi_attach(struct comedi_device *dev, comedi_devconfig *it)
96341f71
AS
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
b6c77757 884 /* Alocate private data storage space */
52bfe6c8 885 ret = alloc_private(dev, sizeof(struct icp_multi_private));
197c82bf 886 if (ret < 0)
96341f71
AS
887 return ret;
888
b6c77757 889 /* Initialise list of PCI cards in system, if not already done so */
96341f71
AS
890 if (pci_list_builded++ == 0) {
891 pci_card_list_init(PCI_VENDOR_ID_ICP,
892#ifdef ICP_MULTI_EXTDEBUG
893 1
894#else
895 0
896#endif
897 );
898 }
899
900 printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
901 this_board->name);
902
197c82bf
BP
903 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
904 this_board->device_id, it->options[0],
905 it->options[1]);
906
907 if (card == NULL)
96341f71
AS
908 return -EIO;
909
910 devpriv->card = card;
911
912 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
913 &irq)) < 0) {
914 printk(" - Can't get configuration data!\n");
915 return -EIO;
916 }
917
918 iobase = io_addr[2];
919 devpriv->phys_iobase = iobase;
920
921 printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
922 (unsigned long long)iobase);
923
924 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
925
926 if (devpriv->io_addr == NULL) {
927 printk("ioremap failed.\n");
928 return -ENOMEM;
929 }
930#ifdef ICP_MULTI_EXTDEBUG
931 printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
932 devpriv->io_addr);
933#endif
934
935 dev->board_name = this_board->name;
936
937 n_subdevices = 0;
938 if (this_board->n_aichan)
939 n_subdevices++;
940 if (this_board->n_aochan)
941 n_subdevices++;
942 if (this_board->n_dichan)
943 n_subdevices++;
944 if (this_board->n_dochan)
945 n_subdevices++;
946 if (this_board->n_ctrs)
947 n_subdevices++;
948
197c82bf 949 ret = alloc_subdevices(dev, n_subdevices);
51b713a6 950 if (ret < 0)
96341f71 951 return ret;
96341f71
AS
952
953 icp_multi_reset(dev);
954
955 if (this_board->have_irq) {
956 if (irq) {
957 if (comedi_request_irq(irq, interrupt_service_icp_multi,
958 IRQF_SHARED, "Inova Icp Multi", dev)) {
959 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
960 irq = 0; /* Can't use IRQ */
961 } else
962 printk(", irq=%u", irq);
963 } else
964 printk(", IRQ disabled");
965 } else
966 irq = 0;
967
968 dev->irq = irq;
969
970 printk(".\n");
971
972 subdev = 0;
973
974 if (this_board->n_aichan) {
975 s = dev->subdevices + subdev;
976 dev->read_subdev = s;
977 s->type = COMEDI_SUBD_AI;
978 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
979 if (this_board->n_aichand)
980 s->subdev_flags |= SDF_DIFF;
981 s->n_chan = this_board->n_aichan;
982 s->maxdata = this_board->ai_maxdata;
983 s->len_chanlist = this_board->n_aichan;
984 s->range_table = this_board->rangelist_ai;
985 s->insn_read = icp_multi_insn_read_ai;
986 subdev++;
987 }
988
989 if (this_board->n_aochan) {
990 s = dev->subdevices + subdev;
991 s->type = COMEDI_SUBD_AO;
992 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
993 s->n_chan = this_board->n_aochan;
994 s->maxdata = this_board->ao_maxdata;
995 s->len_chanlist = this_board->n_aochan;
996 s->range_table = this_board->rangelist_ao;
997 s->insn_write = icp_multi_insn_write_ao;
998 s->insn_read = icp_multi_insn_read_ao;
999 subdev++;
1000 }
1001
1002 if (this_board->n_dichan) {
1003 s = dev->subdevices + subdev;
1004 s->type = COMEDI_SUBD_DI;
1005 s->subdev_flags = SDF_READABLE;
1006 s->n_chan = this_board->n_dichan;
1007 s->maxdata = 1;
1008 s->len_chanlist = this_board->n_dichan;
1009 s->range_table = &range_digital;
1010 s->io_bits = 0;
1011 s->insn_bits = icp_multi_insn_bits_di;
1012 subdev++;
1013 }
1014
1015 if (this_board->n_dochan) {
1016 s = dev->subdevices + subdev;
1017 s->type = COMEDI_SUBD_DO;
1018 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1019 s->n_chan = this_board->n_dochan;
1020 s->maxdata = 1;
1021 s->len_chanlist = this_board->n_dochan;
1022 s->range_table = &range_digital;
1023 s->io_bits = (1 << this_board->n_dochan) - 1;
1024 s->state = 0;
1025 s->insn_bits = icp_multi_insn_bits_do;
1026 subdev++;
1027 }
1028
1029 if (this_board->n_ctrs) {
1030 s = dev->subdevices + subdev;
1031 s->type = COMEDI_SUBD_COUNTER;
1032 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1033 s->n_chan = this_board->n_ctrs;
1034 s->maxdata = 0xffff;
1035 s->len_chanlist = this_board->n_ctrs;
1036 s->state = 0;
1037 s->insn_read = icp_multi_insn_read_ctr;
1038 s->insn_write = icp_multi_insn_write_ctr;
1039 subdev++;
1040 }
1041
1042 devpriv->valid = 1;
1043
1044#ifdef ICP_MULTI_EXTDEBUG
1045 printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1046#endif
1047
1048 return 0;
1049}
1050
1051/*
1052==============================================================================
1053
1054 Name: icp_multi_detach
1055
1056 Description:
1057 This function releases all the resources used by the current
1058 device.
1059
1060 Parameters:
71b5f4f1 1061 struct comedi_device *dev Pointer to current device structure
96341f71
AS
1062
1063 Returns:int 0 = success
1064
1065==============================================================================
1066*/
71b5f4f1 1067static int icp_multi_detach(struct comedi_device *dev)
96341f71
AS
1068{
1069
1070 if (dev->private)
1071 if (devpriv->valid)
1072 icp_multi_reset(dev);
1073
1074 if (dev->irq)
1075 comedi_free_irq(dev->irq, dev);
1076
1077 if (dev->private && devpriv->io_addr)
1078 iounmap(devpriv->io_addr);
1079
1080 if (dev->private && devpriv->card)
1081 pci_card_free(devpriv->card);
1082
82675f35 1083 if (--pci_list_builded == 0)
96341f71 1084 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
96341f71
AS
1085
1086 return 0;
1087}