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