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