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 Forward declarations 125============================================================================== 126*/ 127static int icp_multi_attach(struct comedi_device *dev, 128 struct comedi_devconfig *it); 129static int icp_multi_detach(struct comedi_device *dev); 130 131/* 132============================================================================== 133 Data & Structure declarations 134============================================================================== 135*/ 136static unsigned short pci_list_builded; /*>0 list of card is known */ 137 138struct boardtype { 139 const char *name; /* driver name */ 140 int device_id; 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 */ 152 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */ 153 const char *rangecode; /* range codes for programming */ 154 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */ 155}; 156 157static const struct boardtype boardtypes[] = { 158 {"icp_multi", /* Driver name */ 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 */ 174}; 175 176#define n_boardtypes (sizeof(boardtypes)/sizeof(struct boardtype)) 177 178static struct comedi_driver driver_icp_multi = { 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), 186}; 187 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); 200 201struct icp_multi_private { 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 */ 214 short *ai_data; /* data buffer */ 215 short ao_data[4]; /* data output buffer */ 216 short di_data; /* Digital input data */ 217 unsigned int do_data; /* Remember digital output data */ 218}; 219 220#define devpriv ((struct icp_multi_private *)dev->private) 221#define this_board ((const struct boardtype *)dev->board_ptr) 222 223/* 224============================================================================== 225 More forward declarations 226============================================================================== 227*/ 228 229#if 0 230static int check_channel_list(struct comedi_device *dev, 231 struct comedi_subdevice *s, 232 unsigned int *chanlist, unsigned int n_chan); 233#endif 234static void setup_channel_list(struct comedi_device *dev, 235 struct comedi_subdevice *s, 236 unsigned int *chanlist, unsigned int n_chan); 237static int icp_multi_reset(struct comedi_device *dev); 238 239/* 240============================================================================== 241 Functions 242============================================================================== 243*/ 244 245/* 246============================================================================== 247 248Name: icp_multi_insn_read_ai 249 250Description: 251 This function reads a single analogue input. 252 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 258 259Returns:int Nmuber of instructions executed 260 261============================================================================== 262*/ 263static int icp_multi_insn_read_ai(struct comedi_device *dev, 264 struct comedi_subdevice *s, 265 struct comedi_insn *insn, unsigned int *data) 266{ 267 int n, timeout; 268 269#ifdef ICP_MULTI_EXTDEBUG 270 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n"); 271#endif 272 /* Disable A/D conversion ready interrupt */ 273 devpriv->IntEnable &= ~ADC_READY; 274 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 275 276 /* Clear interrupt status */ 277 devpriv->IntStatus |= ADC_READY; 278 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT); 279 280 /* Set up appropriate channel, mode and range data, for specified ch */ 281 setup_channel_list(dev, s, &insn->chanspec, 1); 282 283#ifdef ICP_MULTI_EXTDEBUG 284 printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n", 285 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR), 286 devpriv->io_addr + ICP_MULTI_ADC_CSR); 287#endif 288 289 for (n = 0; n < insn->n; n++) { 290 /* Set start ADC bit */ 291 devpriv->AdcCmdStatus |= ADC_ST; 292 writew(devpriv->AdcCmdStatus, 293 devpriv->io_addr + ICP_MULTI_ADC_CSR); 294 devpriv->AdcCmdStatus &= ~ADC_ST; 295 296#ifdef ICP_MULTI_EXTDEBUG 297 printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n, 298 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR)); 299#endif 300 301 udelay(1); 302 303#ifdef ICP_MULTI_EXTDEBUG 304 printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n, 305 readw(devpriv->io_addr + ICP_MULTI_ADC_CSR)); 306#endif 307 308 /* Wait for conversion to complete, or get fed up waiting */ 309 timeout = 100; 310 while (timeout--) { 311 if (!(readw(devpriv->io_addr + 312 ICP_MULTI_ADC_CSR) & ADC_BSY)) 313 goto conv_finish; 314 315#ifdef ICP_MULTI_EXTDEBUG 316 if (!(timeout % 10)) 317 printk(KERN_DEBUG 318 "icp multi D n=%d tm=%d ST=%4x\n", n, 319 timeout, 320 readw(devpriv->io_addr + 321 ICP_MULTI_ADC_CSR)); 322#endif 323 324 udelay(1); 325 } 326 327 /* If we reach here, a timeout has occurred */ 328 comedi_error(dev, "A/D insn timeout"); 329 330 /* Disable interrupt */ 331 devpriv->IntEnable &= ~ADC_READY; 332 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 333 334 /* Clear interrupt status */ 335 devpriv->IntStatus |= ADC_READY; 336 writew(devpriv->IntStatus, 337 devpriv->io_addr + ICP_MULTI_INT_STAT); 338 339 /* Clear data received */ 340 data[n] = 0; 341 342#ifdef ICP_MULTI_EXTDEBUG 343 printk(KERN_DEBUG 344 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", 345 n); 346#endif 347 return -ETIME; 348 349conv_finish: 350 data[n] = 351 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff; 352 } 353 354 /* Disable interrupt */ 355 devpriv->IntEnable &= ~ADC_READY; 356 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 357 358 /* Clear interrupt status */ 359 devpriv->IntStatus |= ADC_READY; 360 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT); 361 362#ifdef ICP_MULTI_EXTDEBUG 363 printk(KERN_DEBUG 364 "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n); 365#endif 366 return n; 367} 368 369/* 370============================================================================== 371 372Name: icp_multi_insn_write_ao 373 374Description: 375 This function writes a single analogue output. 376 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 382 383Returns:int Nmuber of instructions executed 384 385============================================================================== 386*/ 387static int icp_multi_insn_write_ao(struct comedi_device *dev, 388 struct comedi_subdevice *s, 389 struct comedi_insn *insn, unsigned int *data) 390{ 391 int n, chan, range, timeout; 392 393#ifdef ICP_MULTI_EXTDEBUG 394 printk(KERN_DEBUG 395 "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n"); 396#endif 397 /* Disable D/A conversion ready interrupt */ 398 devpriv->IntEnable &= ~DAC_READY; 399 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 400 401 /* Clear interrupt status */ 402 devpriv->IntStatus |= DAC_READY; 403 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT); 404 405 /* Get channel number and range */ 406 chan = CR_CHAN(insn->chanspec); 407 range = CR_RANGE(insn->chanspec); 408 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 */ 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++) { 421 /* Wait for analogue output data register to be 422 * ready for new data, or get fed up waiting */ 423 timeout = 100; 424 while (timeout--) { 425 if (!(readw(devpriv->io_addr + 426 ICP_MULTI_DAC_CSR) & DAC_BSY)) 427 goto dac_ready; 428 429#ifdef ICP_MULTI_EXTDEBUG 430 if (!(timeout % 10)) 431 printk(KERN_DEBUG 432 "icp multi A n=%d tm=%d ST=%4x\n", n, 433 timeout, 434 readw(devpriv->io_addr + 435 ICP_MULTI_DAC_CSR)); 436#endif 437 438 udelay(1); 439 } 440 441 /* If we reach here, a timeout has occurred */ 442 comedi_error(dev, "D/A insn timeout"); 443 444 /* Disable interrupt */ 445 devpriv->IntEnable &= ~DAC_READY; 446 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 447 448 /* Clear interrupt status */ 449 devpriv->IntStatus |= DAC_READY; 450 writew(devpriv->IntStatus, 451 devpriv->io_addr + ICP_MULTI_INT_STAT); 452 453 /* Clear data received */ 454 devpriv->ao_data[chan] = 0; 455 456#ifdef ICP_MULTI_EXTDEBUG 457 printk(KERN_DEBUG 458 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", 459 n); 460#endif 461 return -ETIME; 462 463dac_ready: 464 /* Write data to analogue output data register */ 465 writew(data[n], devpriv->io_addr + ICP_MULTI_AO); 466 467 /* Set DAC_ST bit to write the data to selected channel */ 468 devpriv->DacCmdStatus |= DAC_ST; 469 writew(devpriv->DacCmdStatus, 470 devpriv->io_addr + ICP_MULTI_DAC_CSR); 471 devpriv->DacCmdStatus &= ~DAC_ST; 472 473 /* Save analogue output data */ 474 devpriv->ao_data[chan] = data[n]; 475 } 476 477#ifdef ICP_MULTI_EXTDEBUG 478 printk(KERN_DEBUG 479 "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n); 480#endif 481 return n; 482} 483 484/* 485============================================================================== 486 487Name: icp_multi_insn_read_ao 488 489Description: 490 This function reads a single analogue output. 491 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 497 498Returns:int Nmuber of instructions executed 499 500============================================================================== 501*/ 502static int icp_multi_insn_read_ao(struct comedi_device *dev, 503 struct comedi_subdevice *s, 504 struct comedi_insn *insn, unsigned int *data) 505{ 506 int n, chan; 507 508 /* Get channel number */ 509 chan = CR_CHAN(insn->chanspec); 510 511 /* Read analogue outputs */ 512 for (n = 0; n < insn->n; n++) 513 data[n] = devpriv->ao_data[chan]; 514 515 return n; 516} 517 518/* 519============================================================================== 520 521Name: icp_multi_insn_bits_di 522 523Description: 524 This function reads the digital inputs. 525 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 531 532Returns:int Nmuber of instructions executed 533 534============================================================================== 535*/ 536static int icp_multi_insn_bits_di(struct comedi_device *dev, 537 struct comedi_subdevice *s, 538 struct comedi_insn *insn, unsigned int *data) 539{ 540 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI); 541 542 return 2; 543} 544 545/* 546============================================================================== 547 548Name: icp_multi_insn_bits_do 549 550Description: 551 This function writes the appropriate digital outputs. 552 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 558 559Returns:int Nmuber of instructions executed 560 561============================================================================== 562*/ 563static int icp_multi_insn_bits_do(struct comedi_device *dev, 564 struct comedi_subdevice *s, 565 struct comedi_insn *insn, unsigned int *data) 566{ 567#ifdef ICP_MULTI_EXTDEBUG 568 printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n"); 569#endif 570 571 if (data[0]) { 572 s->state &= ~data[0]; 573 s->state |= (data[0] & data[1]); 574 575 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state); 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 583 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n"); 584#endif 585 return 2; 586} 587 588/* 589============================================================================== 590 591Name: icp_multi_insn_read_ctr 592 593Description: 594 This function reads the specified counter. 595 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 601 602Returns:int Nmuber of instructions executed 603 604============================================================================== 605*/ 606static int icp_multi_insn_read_ctr(struct comedi_device *dev, 607 struct comedi_subdevice *s, 608 struct comedi_insn *insn, unsigned int *data) 609{ 610 return 0; 611} 612 613/* 614============================================================================== 615 616Name: icp_multi_insn_write_ctr 617 618Description: 619 This function write to the specified counter. 620 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 626 627Returns:int Nmuber of instructions executed 628 629============================================================================== 630*/ 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) 635{ 636 return 0; 637} 638 639/* 640============================================================================== 641 642Name: interrupt_service_icp_multi 643 644Description: 645 This function is the interrupt service routine for all 646 interrupts generated by the icp multi board. 647 648Parameters: 649 int irq 650 void *d Pointer to current device 651 652============================================================================== 653*/ 654static irqreturn_t interrupt_service_icp_multi(int irq, void *d) 655{ 656 struct comedi_device *dev = d; 657 int int_no; 658 659#ifdef ICP_MULTI_EXTDEBUG 660 printk(KERN_DEBUG 661 "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n", 662 irq); 663#endif 664 665 /* Is this interrupt from our board? */ 666 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ; 667 if (!int_no) 668 /* No, exit */ 669 return IRQ_NONE; 670 671#ifdef ICP_MULTI_EXTDEBUG 672 printk(KERN_DEBUG 673 "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n", 674 readw(devpriv->io_addr + ICP_MULTI_INT_STAT)); 675#endif 676 677 /* Determine which interrupt is active & handle it */ 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 701 printk(KERN_DEBUG 702 "icp multi EDBG: END: interrupt_service_icp_multi(...)\n"); 703#endif 704 return IRQ_HANDLED; 705} 706 707#if 0 708/* 709============================================================================== 710 711Name: check_channel_list 712 713Description: 714 This function checks if the channel list, provided by user 715 is built correctly 716 717Parameters: 718 struct comedi_device *dev Pointer to current service structure 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 722 723Returns:int 0 = failure 724 1 = success 725 726============================================================================== 727*/ 728static int check_channel_list(struct comedi_device *dev, 729 struct comedi_subdevice *s, 730 unsigned int *chanlist, unsigned int n_chan) 731{ 732 unsigned int i; 733 734#ifdef ICP_MULTI_EXTDEBUG 735 printk(KERN_DEBUG 736 "icp multi EDBG: check_channel_list(...,%d)\n", n_chan); 737#endif 738 /* Check that we at least have one channel to check */ 739 if (n_chan < 1) { 740 comedi_error(dev, "range/channel list is empty!"); 741 return 0; 742 } 743 /* Check all channels */ 744 for (i = 0; i < n_chan; i++) { 745 /* Check that channel number is < maximum */ 746 if (CR_AREF(chanlist[i]) == AREF_DIFF) { 747 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) { 748 comedi_error(dev, 749 "Incorrect differential ai ch-nr"); 750 return 0; 751 } 752 } else { 753 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) { 754 comedi_error(dev, 755 "Incorrect ai channel number"); 756 return 0; 757 } 758 } 759 } 760 return 1; 761} 762#endif 763 764/* 765============================================================================== 766 767Name: setup_channel_list 768 769Description: 770 This function sets the appropriate channel selection, 771 differential input mode and range bits in the ADC Command/ 772 Status register. 773 774Parameters: 775 struct comedi_device *dev Pointer to current service structure 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 779 780Returns:Void 781 782============================================================================== 783*/ 784static void setup_channel_list(struct comedi_device *dev, 785 struct comedi_subdevice *s, 786 unsigned int *chanlist, unsigned int n_chan) 787{ 788 unsigned int i, range, chanprog; 789 unsigned int diff; 790 791#ifdef ICP_MULTI_EXTDEBUG 792 printk(KERN_DEBUG 793 "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan); 794#endif 795 devpriv->act_chanlist_len = n_chan; 796 devpriv->act_chanlist_pos = 0; 797 798 for (i = 0; i < n_chan; i++) { 799 /* Get channel */ 800 chanprog = CR_CHAN(chanlist[i]); 801 802 /* Determine if it is a differential channel (Bit 15 = 1) */ 803 if (CR_AREF(chanlist[i]) == AREF_DIFF) { 804 diff = 1; 805 chanprog &= 0x0007; 806 } else { 807 diff = 0; 808 chanprog &= 0x000f; 809 } 810 811 /* Clear channel, range and input mode bits 812 * in A/D command/status register */ 813 devpriv->AdcCmdStatus &= 0xf00f; 814 815 /* Set channel number and differential mode status bit */ 816 if (diff) { 817 /* Set channel number, bits 9-11 & mode, bit 6 */ 818 devpriv->AdcCmdStatus |= (chanprog << 9); 819 devpriv->AdcCmdStatus |= ADC_DI; 820 } else 821 /* Set channel number, bits 8-11 */ 822 devpriv->AdcCmdStatus |= (chanprog << 8); 823 824 /* Get range for current channel */ 825 range = this_board->rangecode[CR_RANGE(chanlist[i])]; 826 /* Set range. bits 4-5 */ 827 devpriv->AdcCmdStatus |= range; 828 829 /* Output channel, range, mode to ICP Multi */ 830 writew(devpriv->AdcCmdStatus, 831 devpriv->io_addr + ICP_MULTI_ADC_CSR); 832 833#ifdef ICP_MULTI_EXTDEBUG 834 printk(KERN_DEBUG 835 "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range, 836 devpriv->act_chanlist[i]); 837#endif 838 } 839 840} 841 842/* 843============================================================================== 844 845Name: icp_multi_reset 846 847Description: 848 This function resets the icp multi device to a 'safe' state 849 850Parameters: 851 struct comedi_device *dev Pointer to current service structure 852 853Returns:int 0 = success 854 855============================================================================== 856*/ 857static int icp_multi_reset(struct comedi_device *dev) 858{ 859 unsigned int i; 860 861#ifdef ICP_MULTI_EXTDEBUG 862 printk(KERN_DEBUG 863 "icp_multi EDBG: BGN: icp_multi_reset(...)\n"); 864#endif 865 /* Clear INT enables and requests */ 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) 870 /* Set DACs to 0..5V range and 0V output */ 871 for (i = 0; i < this_board->n_aochan; i++) { 872 devpriv->DacCmdStatus &= 0xfcce; 873 874 /* Set channel number */ 875 devpriv->DacCmdStatus |= (i << 8); 876 877 /* Output 0V */ 878 writew(0, devpriv->io_addr + ICP_MULTI_AO); 879 880 /* Set start conversion bit */ 881 devpriv->DacCmdStatus |= DAC_ST; 882 883 /* Output to command / status register */ 884 writew(devpriv->DacCmdStatus, 885 devpriv->io_addr + ICP_MULTI_DAC_CSR); 886 887 /* Delay to allow DAC time to recover */ 888 udelay(1); 889 } 890 /* Digital outputs to 0 */ 891 writew(0, devpriv->io_addr + ICP_MULTI_DO); 892 893#ifdef ICP_MULTI_EXTDEBUG 894 printk(KERN_DEBUG 895 "icp multi EDBG: END: icp_multi_reset(...)\n"); 896#endif 897 return 0; 898} 899 900/* 901============================================================================== 902 903Name: icp_multi_attach 904 905Description: 906 This function sets up all the appropriate data for the current 907 device. 908 909Parameters: 910 struct comedi_device *dev Pointer to current device structure 911 struct comedi_devconfig *it Pointer to current device configuration 912 913Returns:int 0 = success 914 915============================================================================== 916*/ 917static int icp_multi_attach(struct comedi_device *dev, 918 struct comedi_devconfig *it) 919{ 920 struct comedi_subdevice *s; 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 927 printk(KERN_WARNING 928 "icp_multi EDBG: BGN: icp_multi_attach(...)\n"); 929 930 /* Alocate private data storage space */ 931 ret = alloc_private(dev, sizeof(struct icp_multi_private)); 932 if (ret < 0) 933 return ret; 934 935 /* Initialise list of PCI cards in system, if not already done so */ 936 if (pci_list_builded++ == 0) { 937 pci_card_list_init(PCI_VENDOR_ID_ICP, 938#ifdef ICP_MULTI_EXTDEBUG 939 1 940#else 941 0 942#endif 943 ); 944 } 945 946 printk(KERN_WARNING 947 "Anne's comedi%d: icp_multi: board=%s", dev->minor, 948 this_board->name); 949 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) 955 return -EIO; 956 957 devpriv->card = card; 958 959 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0], 960 &irq)) < 0) { 961 printk(KERN_WARNING " - Can't get configuration data!\n"); 962 return -EIO; 963 } 964 965 iobase = io_addr[2]; 966 devpriv->phys_iobase = iobase; 967 968 printk(KERN_WARNING 969 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func, 970 (unsigned long long)iobase); 971 972 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE); 973 974 if (devpriv->io_addr == NULL) { 975 printk(KERN_WARNING "ioremap failed.\n"); 976 return -ENOMEM; 977 } 978#ifdef ICP_MULTI_EXTDEBUG 979 printk(KERN_DEBUG 980 "0x%08llx mapped to %p, ", (unsigned long long)iobase, 981 devpriv->io_addr); 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 998 ret = alloc_subdevices(dev, n_subdevices); 999 if (ret < 0) 1000 return ret; 1001 1002 icp_multi_reset(dev); 1003 1004 if (this_board->have_irq) { 1005 if (irq) { 1006 if (request_irq(irq, interrupt_service_icp_multi, 1007 IRQF_SHARED, "Inova Icp Multi", dev)) { 1008 printk(KERN_WARNING 1009 "unable to allocate IRQ %u, DISABLING IT", 1010 irq); 1011 irq = 0; /* Can't use IRQ */ 1012 } else 1013 printk(KERN_WARNING ", irq=%u", irq); 1014 } else 1015 printk(KERN_WARNING ", IRQ disabled"); 1016 } else 1017 irq = 0; 1018 1019 dev->irq = irq; 1020 1021 printk(KERN_WARNING ".\n"); 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 1096 printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n"); 1097#endif 1098 1099 return 0; 1100} 1101 1102/* 1103============================================================================== 1104 1105Name: icp_multi_detach 1106 1107Description: 1108 This function releases all the resources used by the current 1109 device. 1110 1111Parameters: 1112 struct comedi_device *dev Pointer to current device structure 1113 1114Returns:int 0 = success 1115 1116============================================================================== 1117*/ 1118static int icp_multi_detach(struct comedi_device *dev) 1119{ 1120 1121 if (dev->private) 1122 if (devpriv->valid) 1123 icp_multi_reset(dev); 1124 1125 if (dev->irq) 1126 free_irq(dev->irq, dev); 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 1134 if (--pci_list_builded == 0) 1135 pci_card_list_cleanup(PCI_VENDOR_ID_ICP); 1136 1137 return 0; 1138} 1139 1140MODULE_AUTHOR("Comedi http://www.comedi.org"); 1141MODULE_DESCRIPTION("Comedi low-level driver"); 1142MODULE_LICENSE("GPL"); 1143