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