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