staging: comedi: Fix typo in comedi
[linux-2.6-block.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
194b9e3c 47Configuration options: not applicable, uses PCI auto config
96341f71
AS
48*/
49
33782dd5
HS
50#include <linux/pci.h>
51#include <linux/delay.h>
25436dc9 52#include <linux/interrupt.h>
96341f71 53
33782dd5 54#include "../comedidev.h"
96341f71 55
554e02c9 56#define PCI_DEVICE_ID_ICP_MULTI 0x8000
96341f71 57
96341f71
AS
58#define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
59#define ICP_MULTI_AI 2 /* R: Analogue input data */
60#define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */
61#define ICP_MULTI_AO 6 /* R/W: Analogue output data */
62#define ICP_MULTI_DI 8 /* R/W: Digital inouts */
63#define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */
64#define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */
65#define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */
66#define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */
67#define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */
68#define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */
69#define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */
70
71#define ICP_MULTI_SIZE 0x20 /* 32 bytes */
72
b6c77757 73/* Define bits from ADC command/status register */
96341f71
AS
74#define ADC_ST 0x0001 /* Start ADC */
75#define ADC_BSY 0x0001 /* ADC busy */
76#define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
77#define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
78#define ADC_DI 0x0040 /* Differential input mode 1 = differential */
79
b6c77757 80/* Define bits from DAC command/status register */
96341f71
AS
81#define DAC_ST 0x0001 /* Start DAC */
82#define DAC_BSY 0x0001 /* DAC busy */
83#define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
84#define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
85
b6c77757 86/* Define bits from interrupt enable/status registers */
96341f71
AS
87#define ADC_READY 0x0001 /* A/d conversion ready interrupt */
88#define DAC_READY 0x0002 /* D/a conversion ready interrupt */
89#define DOUT_ERROR 0x0004 /* Digital output error interrupt */
90#define DIN_STATUS 0x0008 /* Digital input status change interrupt */
91#define CIE0 0x0010 /* Counter 0 overrun interrupt */
92#define CIE1 0x0020 /* Counter 1 overrun interrupt */
93#define CIE2 0x0040 /* Counter 2 overrun interrupt */
94#define CIE3 0x0080 /* Counter 3 overrun interrupt */
95
b6c77757
BP
96/* Useful definitions */
97#define Status_IRQ 0x00ff /* All interrupts */
96341f71 98
b6c77757 99/* Define analogue range */
9ced1de6 100static const struct comedi_lrange range_analog = { 4, {
0a85b6f0
MT
101 UNI_RANGE(5),
102 UNI_RANGE(10),
103 BIP_RANGE(5),
104 BIP_RANGE(10)
105 }
96341f71
AS
106};
107
108static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
109
96341f71
AS
110/*
111==============================================================================
112 Data & Structure declarations
113==============================================================================
114*/
96341f71 115
52bfe6c8 116struct icp_multi_private {
b6c77757 117 char valid; /* card is usable */
86a5eb8c 118 void __iomem *io_addr; /* Pointer to mapped io address */
b6c77757
BP
119 unsigned int AdcCmdStatus; /* ADC Command/Status register */
120 unsigned int DacCmdStatus; /* DAC Command/Status register */
121 unsigned int IntEnable; /* Interrupt Enable register */
122 unsigned int IntStatus; /* Interrupt Status register */
20ce161d 123 unsigned int act_chanlist[32]; /* list of scanned channel */
b6c77757
BP
124 unsigned char act_chanlist_len; /* len of scanlist */
125 unsigned char act_chanlist_pos; /* actual position in MUX list */
126 unsigned int *ai_chanlist; /* actaul chanlist */
0a85b6f0 127 short *ai_data; /* data buffer */
790c5541 128 short ao_data[4]; /* data output buffer */
0a85b6f0 129 short di_data; /* Digital input data */
b6c77757 130 unsigned int do_data; /* Remember digital output data */
52bfe6c8 131};
96341f71 132
0a85b6f0
MT
133static void setup_channel_list(struct comedi_device *dev,
134 struct comedi_subdevice *s,
d79fc8e1
HS
135 unsigned int *chanlist, unsigned int n_chan)
136{
65e9e2dc 137 struct icp_multi_private *devpriv = dev->private;
d79fc8e1
HS
138 unsigned int i, range, chanprog;
139 unsigned int diff;
96341f71 140
d79fc8e1
HS
141 devpriv->act_chanlist_len = n_chan;
142 devpriv->act_chanlist_pos = 0;
143
144 for (i = 0; i < n_chan; i++) {
145 /* Get channel */
146 chanprog = CR_CHAN(chanlist[i]);
147
148 /* Determine if it is a differential channel (Bit 15 = 1) */
149 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
150 diff = 1;
151 chanprog &= 0x0007;
152 } else {
153 diff = 0;
154 chanprog &= 0x000f;
155 }
156
157 /* Clear channel, range and input mode bits
158 * in A/D command/status register */
159 devpriv->AdcCmdStatus &= 0xf00f;
160
161 /* Set channel number and differential mode status bit */
162 if (diff) {
163 /* Set channel number, bits 9-11 & mode, bit 6 */
164 devpriv->AdcCmdStatus |= (chanprog << 9);
165 devpriv->AdcCmdStatus |= ADC_DI;
166 } else
167 /* Set channel number, bits 8-11 */
168 devpriv->AdcCmdStatus |= (chanprog << 8);
169
170 /* Get range for current channel */
d68a8635 171 range = range_codes_analog[CR_RANGE(chanlist[i])];
d79fc8e1
HS
172 /* Set range. bits 4-5 */
173 devpriv->AdcCmdStatus |= range;
174
175 /* Output channel, range, mode to ICP Multi */
176 writew(devpriv->AdcCmdStatus,
177 devpriv->io_addr + ICP_MULTI_ADC_CSR);
d79fc8e1 178 }
d79fc8e1 179}
96341f71 180
0a85b6f0
MT
181static int icp_multi_insn_read_ai(struct comedi_device *dev,
182 struct comedi_subdevice *s,
183 struct comedi_insn *insn, unsigned int *data)
96341f71 184{
65e9e2dc 185 struct icp_multi_private *devpriv = dev->private;
96341f71
AS
186 int n, timeout;
187
b6c77757 188 /* Disable A/D conversion ready interrupt */
96341f71
AS
189 devpriv->IntEnable &= ~ADC_READY;
190 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
191
b6c77757 192 /* Clear interrupt status */
96341f71
AS
193 devpriv->IntStatus |= ADC_READY;
194 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
195
a622afcb 196 /* Set up appropriate channel, mode and range data, for specified ch */
96341f71
AS
197 setup_channel_list(dev, s, &insn->chanspec, 1);
198
96341f71 199 for (n = 0; n < insn->n; n++) {
b6c77757 200 /* Set start ADC bit */
96341f71
AS
201 devpriv->AdcCmdStatus |= ADC_ST;
202 writew(devpriv->AdcCmdStatus,
0a85b6f0 203 devpriv->io_addr + ICP_MULTI_ADC_CSR);
96341f71
AS
204 devpriv->AdcCmdStatus &= ~ADC_ST;
205
5f74ea14 206 udelay(1);
96341f71 207
b6c77757 208 /* Wait for conversion to complete, or get fed up waiting */
96341f71
AS
209 timeout = 100;
210 while (timeout--) {
211 if (!(readw(devpriv->io_addr +
0a85b6f0 212 ICP_MULTI_ADC_CSR) & ADC_BSY))
96341f71
AS
213 goto conv_finish;
214
5f74ea14 215 udelay(1);
96341f71
AS
216 }
217
b6c77757 218 /* If we reach here, a timeout has occurred */
96341f71
AS
219 comedi_error(dev, "A/D insn timeout");
220
b6c77757 221 /* Disable interrupt */
96341f71
AS
222 devpriv->IntEnable &= ~ADC_READY;
223 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
224
b6c77757 225 /* Clear interrupt status */
96341f71
AS
226 devpriv->IntStatus |= ADC_READY;
227 writew(devpriv->IntStatus,
0a85b6f0 228 devpriv->io_addr + ICP_MULTI_INT_STAT);
96341f71 229
b6c77757 230 /* Clear data received */
96341f71
AS
231 data[n] = 0;
232
96341f71
AS
233 return -ETIME;
234
0a85b6f0 235conv_finish:
96341f71 236 data[n] =
0a85b6f0 237 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
96341f71
AS
238 }
239
b6c77757 240 /* Disable 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
96341f71
AS
248 return n;
249}
250
0a85b6f0
MT
251static int icp_multi_insn_write_ao(struct comedi_device *dev,
252 struct comedi_subdevice *s,
253 struct comedi_insn *insn, unsigned int *data)
96341f71 254{
65e9e2dc 255 struct icp_multi_private *devpriv = dev->private;
96341f71
AS
256 int n, chan, range, timeout;
257
b6c77757 258 /* Disable D/A conversion ready interrupt */
96341f71
AS
259 devpriv->IntEnable &= ~DAC_READY;
260 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
261
b6c77757 262 /* Clear interrupt status */
96341f71
AS
263 devpriv->IntStatus |= DAC_READY;
264 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
265
b6c77757 266 /* Get channel number and range */
96341f71
AS
267 chan = CR_CHAN(insn->chanspec);
268 range = CR_RANGE(insn->chanspec);
269
b6c77757
BP
270 /* Set up range and channel data */
271 /* Bit 4 = 1 : Bipolar */
272 /* Bit 5 = 0 : 5V */
273 /* Bit 5 = 1 : 10V */
274 /* Bits 8-9 : Channel number */
96341f71 275 devpriv->DacCmdStatus &= 0xfccf;
d68a8635 276 devpriv->DacCmdStatus |= range_codes_analog[range];
96341f71
AS
277 devpriv->DacCmdStatus |= (chan << 8);
278
279 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
280
281 for (n = 0; n < insn->n; n++) {
a622afcb
DH
282 /* Wait for analogue output data register to be
283 * ready for new data, or get fed up waiting */
96341f71
AS
284 timeout = 100;
285 while (timeout--) {
286 if (!(readw(devpriv->io_addr +
0a85b6f0 287 ICP_MULTI_DAC_CSR) & DAC_BSY))
96341f71
AS
288 goto dac_ready;
289
5f74ea14 290 udelay(1);
96341f71
AS
291 }
292
b6c77757 293 /* If we reach here, a timeout has occurred */
96341f71
AS
294 comedi_error(dev, "D/A insn timeout");
295
b6c77757 296 /* Disable interrupt */
96341f71
AS
297 devpriv->IntEnable &= ~DAC_READY;
298 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
299
b6c77757 300 /* Clear interrupt status */
96341f71
AS
301 devpriv->IntStatus |= DAC_READY;
302 writew(devpriv->IntStatus,
0a85b6f0 303 devpriv->io_addr + ICP_MULTI_INT_STAT);
96341f71 304
b6c77757 305 /* Clear data received */
96341f71
AS
306 devpriv->ao_data[chan] = 0;
307
96341f71
AS
308 return -ETIME;
309
0a85b6f0 310dac_ready:
b6c77757 311 /* Write data to analogue output data register */
96341f71
AS
312 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
313
b6c77757 314 /* Set DAC_ST bit to write the data to selected channel */
96341f71
AS
315 devpriv->DacCmdStatus |= DAC_ST;
316 writew(devpriv->DacCmdStatus,
0a85b6f0 317 devpriv->io_addr + ICP_MULTI_DAC_CSR);
96341f71
AS
318 devpriv->DacCmdStatus &= ~DAC_ST;
319
b6c77757 320 /* Save analogue output data */
96341f71
AS
321 devpriv->ao_data[chan] = data[n];
322 }
323
96341f71
AS
324 return n;
325}
326
0a85b6f0
MT
327static int icp_multi_insn_read_ao(struct comedi_device *dev,
328 struct comedi_subdevice *s,
329 struct comedi_insn *insn, unsigned int *data)
96341f71 330{
65e9e2dc 331 struct icp_multi_private *devpriv = dev->private;
96341f71
AS
332 int n, chan;
333
b6c77757 334 /* Get channel number */
96341f71
AS
335 chan = CR_CHAN(insn->chanspec);
336
b6c77757 337 /* Read analogue outputs */
96341f71
AS
338 for (n = 0; n < insn->n; n++)
339 data[n] = devpriv->ao_data[chan];
340
341 return n;
342}
343
0a85b6f0
MT
344static int icp_multi_insn_bits_di(struct comedi_device *dev,
345 struct comedi_subdevice *s,
346 struct comedi_insn *insn, unsigned int *data)
96341f71 347{
65e9e2dc
HS
348 struct icp_multi_private *devpriv = dev->private;
349
96341f71
AS
350 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
351
a2714e3e 352 return insn->n;
96341f71
AS
353}
354
0a85b6f0
MT
355static int icp_multi_insn_bits_do(struct comedi_device *dev,
356 struct comedi_subdevice *s,
357 struct comedi_insn *insn, unsigned int *data)
96341f71 358{
65e9e2dc
HS
359 struct icp_multi_private *devpriv = dev->private;
360
96341f71
AS
361 if (data[0]) {
362 s->state &= ~data[0];
363 s->state |= (data[0] & data[1]);
364
ca5edf2f 365 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
96341f71
AS
366
367 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
368 }
369
370 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
371
a2714e3e 372 return insn->n;
96341f71
AS
373}
374
0a85b6f0
MT
375static int icp_multi_insn_read_ctr(struct comedi_device *dev,
376 struct comedi_subdevice *s,
377 struct comedi_insn *insn, unsigned int *data)
96341f71
AS
378{
379 return 0;
380}
381
0a85b6f0
MT
382static int icp_multi_insn_write_ctr(struct comedi_device *dev,
383 struct comedi_subdevice *s,
384 struct comedi_insn *insn,
385 unsigned int *data)
96341f71
AS
386{
387 return 0;
388}
389
70265d24 390static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
96341f71 391{
71b5f4f1 392 struct comedi_device *dev = d;
65e9e2dc 393 struct icp_multi_private *devpriv = dev->private;
96341f71
AS
394 int int_no;
395
b6c77757 396 /* Is this interrupt from our board? */
96341f71
AS
397 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
398 if (!int_no)
b6c77757 399 /* No, exit */
96341f71
AS
400 return IRQ_NONE;
401
b6c77757 402 /* Determine which interrupt is active & handle it */
96341f71
AS
403 switch (int_no) {
404 case ADC_READY:
405 break;
406 case DAC_READY:
407 break;
408 case DOUT_ERROR:
409 break;
410 case DIN_STATUS:
411 break;
412 case CIE0:
413 break;
414 case CIE1:
415 break;
416 case CIE2:
417 break;
418 case CIE3:
419 break;
420 default:
421 break;
422
423 }
424
96341f71
AS
425 return IRQ_HANDLED;
426}
427
428#if 0
0a85b6f0
MT
429static int check_channel_list(struct comedi_device *dev,
430 struct comedi_subdevice *s,
431 unsigned int *chanlist, unsigned int n_chan)
96341f71
AS
432{
433 unsigned int i;
434
b6c77757 435 /* Check that we at least have one channel to check */
96341f71
AS
436 if (n_chan < 1) {
437 comedi_error(dev, "range/channel list is empty!");
438 return 0;
439 }
b6c77757 440 /* Check all channels */
96341f71 441 for (i = 0; i < n_chan; i++) {
b6c77757 442 /* Check that channel number is < maximum */
96341f71 443 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
08567ce9 444 if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
96341f71 445 comedi_error(dev,
ca5edf2f 446 "Incorrect differential ai ch-nr");
96341f71
AS
447 return 0;
448 }
449 } else {
281ecb06 450 if (CR_CHAN(chanlist[i]) > s->n_chan) {
96341f71 451 comedi_error(dev,
0a85b6f0 452 "Incorrect ai channel number");
96341f71
AS
453 return 0;
454 }
455 }
456 }
457 return 1;
458}
459#endif
460
71b5f4f1 461static int icp_multi_reset(struct comedi_device *dev)
96341f71 462{
65e9e2dc 463 struct icp_multi_private *devpriv = dev->private;
96341f71
AS
464 unsigned int i;
465
b6c77757 466 /* Clear INT enables and requests */
96341f71
AS
467 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
468 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
469
fafe91a8
HS
470 /* Set DACs to 0..5V range and 0V output */
471 for (i = 0; i < 4; i++) {
472 devpriv->DacCmdStatus &= 0xfcce;
96341f71 473
fafe91a8
HS
474 /* Set channel number */
475 devpriv->DacCmdStatus |= (i << 8);
96341f71 476
fafe91a8
HS
477 /* Output 0V */
478 writew(0, devpriv->io_addr + ICP_MULTI_AO);
96341f71 479
fafe91a8
HS
480 /* Set start conversion bit */
481 devpriv->DacCmdStatus |= DAC_ST;
96341f71 482
fafe91a8
HS
483 /* Output to command / status register */
484 writew(devpriv->DacCmdStatus,
485 devpriv->io_addr + ICP_MULTI_DAC_CSR);
96341f71 486
fafe91a8
HS
487 /* Delay to allow DAC time to recover */
488 udelay(1);
489 }
490
491 /* Digital outputs to 0 */
96341f71
AS
492 writew(0, devpriv->io_addr + ICP_MULTI_DO);
493
96341f71
AS
494 return 0;
495}
496
a690b7e5 497static int icp_multi_auto_attach(struct comedi_device *dev,
750af5e5 498 unsigned long context_unused)
96341f71 499{
750af5e5 500 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
65e9e2dc 501 struct icp_multi_private *devpriv;
34c43922 502 struct comedi_subdevice *s;
194b9e3c 503 resource_size_t iobase;
12b4a097 504 int ret;
194b9e3c 505
194b9e3c 506 dev->board_name = dev->driver->driver_name;
96341f71 507
c34fa261
HS
508 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
509 if (!devpriv)
510 return -ENOMEM;
511 dev->private = devpriv;
96341f71 512
194b9e3c
HS
513 ret = comedi_pci_enable(pcidev, dev->board_name);
514 if (ret)
515 return ret;
516 iobase = pci_resource_start(pcidev, 2);
517 dev->iobase = iobase;
96341f71 518
96341f71 519 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
194b9e3c 520 if (!devpriv->io_addr)
96341f71 521 return -ENOMEM;
96341f71 522
12b4a097 523 ret = comedi_alloc_subdevices(dev, 5);
8b6c5694 524 if (ret)
96341f71 525 return ret;
96341f71
AS
526
527 icp_multi_reset(dev);
528
194b9e3c
HS
529 if (pcidev->irq) {
530 ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
531 IRQF_SHARED, dev->board_name, dev);
532 if (ret == 0)
533 dev->irq = pcidev->irq;
798cdd05 534 }
96341f71 535
12b4a097 536 s = &dev->subdevices[0];
281ecb06
HS
537 dev->read_subdev = s;
538 s->type = COMEDI_SUBD_AI;
08567ce9 539 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
281ecb06 540 s->n_chan = 16;
efac035c 541 s->maxdata = 0x0fff;
281ecb06 542 s->len_chanlist = 16;
abdeac3f 543 s->range_table = &range_analog;
281ecb06 544 s->insn_read = icp_multi_insn_read_ai;
96341f71 545
12b4a097 546 s = &dev->subdevices[1];
fafe91a8
HS
547 s->type = COMEDI_SUBD_AO;
548 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
549 s->n_chan = 4;
68b82e09 550 s->maxdata = 0x0fff;
fafe91a8
HS
551 s->len_chanlist = 4;
552 s->range_table = &range_analog;
553 s->insn_write = icp_multi_insn_write_ao;
554 s->insn_read = icp_multi_insn_read_ao;
96341f71 555
12b4a097 556 s = &dev->subdevices[2];
48f31251
HS
557 s->type = COMEDI_SUBD_DI;
558 s->subdev_flags = SDF_READABLE;
559 s->n_chan = 16;
560 s->maxdata = 1;
561 s->len_chanlist = 16;
562 s->range_table = &range_digital;
563 s->io_bits = 0;
564 s->insn_bits = icp_multi_insn_bits_di;
96341f71 565
12b4a097 566 s = &dev->subdevices[3];
2aa70705
HS
567 s->type = COMEDI_SUBD_DO;
568 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
569 s->n_chan = 8;
570 s->maxdata = 1;
571 s->len_chanlist = 8;
572 s->range_table = &range_digital;
573 s->io_bits = 0xff;
574 s->state = 0;
575 s->insn_bits = icp_multi_insn_bits_do;
96341f71 576
12b4a097 577 s = &dev->subdevices[4];
5b93da54
HS
578 s->type = COMEDI_SUBD_COUNTER;
579 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
580 s->n_chan = 4;
581 s->maxdata = 0xffff;
582 s->len_chanlist = 4;
583 s->state = 0;
584 s->insn_read = icp_multi_insn_read_ctr;
585 s->insn_write = icp_multi_insn_write_ctr;
96341f71
AS
586
587 devpriv->valid = 1;
588
798cdd05
HS
589 dev_info(dev->class_dev, "%s attached, irq %sabled\n",
590 dev->board_name, dev->irq ? "en" : "dis");
591
96341f71
AS
592 return 0;
593}
594
484ecc95 595static void icp_multi_detach(struct comedi_device *dev)
96341f71 596{
194b9e3c 597 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
65e9e2dc 598 struct icp_multi_private *devpriv = dev->private;
194b9e3c 599
65e9e2dc 600 if (devpriv)
96341f71
AS
601 if (devpriv->valid)
602 icp_multi_reset(dev);
96341f71 603 if (dev->irq)
5f74ea14 604 free_irq(dev->irq, dev);
65e9e2dc 605 if (devpriv && devpriv->io_addr)
96341f71 606 iounmap(devpriv->io_addr);
194b9e3c
HS
607 if (pcidev) {
608 if (dev->iobase)
609 comedi_pci_disable(pcidev);
610 }
96341f71 611}
90f703d3 612
d79fc8e1
HS
613static struct comedi_driver icp_multi_driver = {
614 .driver_name = "icp_multi",
615 .module = THIS_MODULE,
750af5e5 616 .auto_attach = icp_multi_auto_attach,
d79fc8e1 617 .detach = icp_multi_detach,
d79fc8e1 618};
554e02c9 619
a690b7e5 620static int icp_multi_pci_probe(struct pci_dev *dev,
b8f4ac23 621 const struct pci_device_id *id)
554e02c9 622{
b8f4ac23 623 return comedi_pci_auto_config(dev, &icp_multi_driver, id->driver_data);
554e02c9
HS
624}
625
554e02c9
HS
626static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
627 { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
628 { 0 }
629};
630MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
631
632static struct pci_driver icp_multi_pci_driver = {
633 .name = "icp_multi",
634 .id_table = icp_multi_pci_table,
635 .probe = icp_multi_pci_probe,
9901a4d7 636 .remove = comedi_pci_auto_unconfig,
554e02c9
HS
637};
638module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
d79fc8e1 639
90f703d3
AT
640MODULE_AUTHOR("Comedi http://www.comedi.org");
641MODULE_DESCRIPTION("Comedi low-level driver");
642MODULE_LICENSE("GPL");