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