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