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