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