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