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