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