linux/drivers/staging/comedi/drivers/icp_multi.c
<<
>>
Prefs
   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
 237        Name:   icp_multi_insn_read_ai
 238
 239        Description:
 240                This function reads a single analogue input.
 241
 242        Parameters:
 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
 248        Returns: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 channel */
 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
 359        Name:   icp_multi_insn_write_ao
 360
 361        Description:
 362                This function writes a single analogue output.
 363
 364        Parameters:
 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
 370        Returns: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 ready for new data, or get fed up waiting */
 408                timeout = 100;
 409                while (timeout--) {
 410                        if (!(readw(devpriv->io_addr +
 411                                    ICP_MULTI_DAC_CSR) & DAC_BSY))
 412                                goto dac_ready;
 413
 414#ifdef ICP_MULTI_EXTDEBUG
 415                        if (!(timeout % 10))
 416                                printk("icp multi A n=%d tm=%d ST=%4x\n", n,
 417                                       timeout,
 418                                       readw(devpriv->io_addr +
 419                                             ICP_MULTI_DAC_CSR));
 420#endif
 421
 422                        udelay(1);
 423                }
 424
 425                /*  If we reach here, a timeout has occurred */
 426                comedi_error(dev, "D/A insn timeout");
 427
 428                /*  Disable interrupt */
 429                devpriv->IntEnable &= ~DAC_READY;
 430                writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
 431
 432                /*  Clear interrupt status */
 433                devpriv->IntStatus |= DAC_READY;
 434                writew(devpriv->IntStatus,
 435                       devpriv->io_addr + ICP_MULTI_INT_STAT);
 436
 437                /*  Clear data received */
 438                devpriv->ao_data[chan] = 0;
 439
 440#ifdef ICP_MULTI_EXTDEBUG
 441                printk
 442                    ("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
 443                     n);
 444#endif
 445                return -ETIME;
 446
 447dac_ready:
 448                /*  Write data to analogue output data register */
 449                writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
 450
 451                /*  Set DAC_ST bit to write the data to selected channel */
 452                devpriv->DacCmdStatus |= DAC_ST;
 453                writew(devpriv->DacCmdStatus,
 454                       devpriv->io_addr + ICP_MULTI_DAC_CSR);
 455                devpriv->DacCmdStatus &= ~DAC_ST;
 456
 457                /*  Save analogue output data */
 458                devpriv->ao_data[chan] = data[n];
 459        }
 460
 461#ifdef ICP_MULTI_EXTDEBUG
 462        printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
 463#endif
 464        return n;
 465}
 466
 467/*
 468==============================================================================
 469
 470        Name:   icp_multi_insn_read_ao
 471
 472        Description:
 473                This function reads a single analogue output.
 474
 475        Parameters:
 476                struct comedi_device *dev       Pointer to current device structure
 477                struct comedi_subdevice *s      Pointer to current subdevice structure
 478                struct comedi_insn *insn        Pointer to current comedi instruction
 479                unsigned int *data              Pointer to analogue output data
 480
 481        Returns:int                     Nmuber of instructions executed
 482
 483==============================================================================
 484*/
 485static int icp_multi_insn_read_ao(struct comedi_device *dev,
 486                                  struct comedi_subdevice *s,
 487                                  struct comedi_insn *insn, unsigned int *data)
 488{
 489        int n, chan;
 490
 491        /*  Get channel number */
 492        chan = CR_CHAN(insn->chanspec);
 493
 494        /*  Read analogue outputs */
 495        for (n = 0; n < insn->n; n++)
 496                data[n] = devpriv->ao_data[chan];
 497
 498        return n;
 499}
 500
 501/*
 502==============================================================================
 503
 504        Name:   icp_multi_insn_bits_di
 505
 506        Description:
 507                This function reads the digital inputs.
 508
 509        Parameters:
 510                struct comedi_device *dev       Pointer to current device structure
 511                struct comedi_subdevice *s      Pointer to current subdevice structure
 512                struct comedi_insn *insn        Pointer to current comedi instruction
 513                unsigned int *data              Pointer to analogue output data
 514
 515        Returns:int                     Nmuber of instructions executed
 516
 517==============================================================================
 518*/
 519static int icp_multi_insn_bits_di(struct comedi_device *dev,
 520                                  struct comedi_subdevice *s,
 521                                  struct comedi_insn *insn, unsigned int *data)
 522{
 523        data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
 524
 525        return 2;
 526}
 527
 528/*
 529==============================================================================
 530
 531        Name:   icp_multi_insn_bits_do
 532
 533        Description:
 534                This function writes the appropriate digital outputs.
 535
 536        Parameters:
 537                struct comedi_device *dev       Pointer to current device structure
 538                struct comedi_subdevice *s      Pointer to current subdevice structure
 539                struct comedi_insn *insn        Pointer to current comedi instruction
 540                unsigned int *data              Pointer to analogue output data
 541
 542        Returns:int                     Nmuber of instructions executed
 543
 544==============================================================================
 545*/
 546static int icp_multi_insn_bits_do(struct comedi_device *dev,
 547                                  struct comedi_subdevice *s,
 548                                  struct comedi_insn *insn, unsigned int *data)
 549{
 550#ifdef ICP_MULTI_EXTDEBUG
 551        printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
 552#endif
 553
 554        if (data[0]) {
 555                s->state &= ~data[0];
 556                s->state |= (data[0] & data[1]);
 557
 558                printk("Digital outputs = %4x \n", s->state);
 559
 560                writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
 561        }
 562
 563        data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
 564
 565#ifdef ICP_MULTI_EXTDEBUG
 566        printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
 567#endif
 568        return 2;
 569}
 570
 571/*
 572==============================================================================
 573
 574        Name:   icp_multi_insn_read_ctr
 575
 576        Description:
 577                This function reads the specified counter.
 578
 579        Parameters:
 580                struct comedi_device *dev       Pointer to current device structure
 581                struct comedi_subdevice *s      Pointer to current subdevice structure
 582                struct comedi_insn *insn        Pointer to current comedi instruction
 583                unsigned int *data              Pointer to counter data
 584
 585        Returns:int                     Nmuber of instructions executed
 586
 587==============================================================================
 588*/
 589static int icp_multi_insn_read_ctr(struct comedi_device *dev,
 590                                   struct comedi_subdevice *s,
 591                                   struct comedi_insn *insn, unsigned int *data)
 592{
 593        return 0;
 594}
 595
 596/*
 597==============================================================================
 598
 599        Name:   icp_multi_insn_write_ctr
 600
 601        Description:
 602                This function write to the specified counter.
 603
 604        Parameters:
 605                struct comedi_device *dev       Pointer to current device structure
 606                struct comedi_subdevice *s      Pointer to current subdevice structure
 607                struct comedi_insn *insn        Pointer to current comedi instruction
 608                unsigned int *data              Pointer to counter data
 609
 610        Returns:int                     Nmuber of instructions executed
 611
 612==============================================================================
 613*/
 614static int icp_multi_insn_write_ctr(struct comedi_device *dev,
 615                                    struct comedi_subdevice *s,
 616                                    struct comedi_insn *insn,
 617                                    unsigned int *data)
 618{
 619        return 0;
 620}
 621
 622/*
 623==============================================================================
 624
 625        Name:   interrupt_service_icp_multi
 626
 627        Description:
 628                This function is the interrupt service routine for all
 629                interrupts generated by the icp multi board.
 630
 631        Parameters:
 632                int irq
 633                void *d                 Pointer to current device
 634
 635==============================================================================
 636*/
 637static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
 638{
 639        struct comedi_device *dev = d;
 640        int int_no;
 641
 642#ifdef ICP_MULTI_EXTDEBUG
 643        printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
 644               irq);
 645#endif
 646
 647        /*  Is this interrupt from our board? */
 648        int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
 649        if (!int_no)
 650                /*  No, exit */
 651                return IRQ_NONE;
 652
 653#ifdef ICP_MULTI_EXTDEBUG
 654        printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
 655               readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
 656#endif
 657
 658        /*  Determine which interrupt is active & handle it */
 659        switch (int_no) {
 660        case ADC_READY:
 661                break;
 662        case DAC_READY:
 663                break;
 664        case DOUT_ERROR:
 665                break;
 666        case DIN_STATUS:
 667                break;
 668        case CIE0:
 669                break;
 670        case CIE1:
 671                break;
 672        case CIE2:
 673                break;
 674        case CIE3:
 675                break;
 676        default:
 677                break;
 678
 679        }
 680
 681#ifdef ICP_MULTI_EXTDEBUG
 682        printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
 683#endif
 684        return IRQ_HANDLED;
 685}
 686
 687#if 0
 688/*
 689==============================================================================
 690
 691        Name:   check_channel_list
 692
 693        Description:
 694                This function checks if the channel list, provided by user
 695                is built correctly
 696
 697        Parameters:
 698                struct comedi_device *dev       Pointer to current sevice structure
 699                struct comedi_subdevice *s      Pointer to current subdevice structure
 700                unsigned int *chanlist  Pointer to packed channel list
 701                unsigned int n_chan     Number of channels to scan
 702
 703        Returns:int 0 = failure
 704                    1 = success
 705
 706==============================================================================
 707*/
 708static int check_channel_list(struct comedi_device *dev,
 709                              struct comedi_subdevice *s,
 710                              unsigned int *chanlist, unsigned int n_chan)
 711{
 712        unsigned int i;
 713
 714#ifdef ICP_MULTI_EXTDEBUG
 715        printk("icp multi EDBG:  check_channel_list(...,%d)\n", n_chan);
 716#endif
 717        /*  Check that we at least have one channel to check */
 718        if (n_chan < 1) {
 719                comedi_error(dev, "range/channel list is empty!");
 720                return 0;
 721        }
 722        /*  Check all channels */
 723        for (i = 0; i < n_chan; i++) {
 724                /*  Check that channel number is < maximum */
 725                if (CR_AREF(chanlist[i]) == AREF_DIFF) {
 726                        if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
 727                                comedi_error(dev,
 728                                             "Incorrect differential ai channel number");
 729                                return 0;
 730                        }
 731                } else {
 732                        if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
 733                                comedi_error(dev,
 734                                             "Incorrect ai channel number");
 735                                return 0;
 736                        }
 737                }
 738        }
 739        return 1;
 740}
 741#endif
 742
 743/*
 744==============================================================================
 745
 746        Name:   setup_channel_list
 747
 748        Description:
 749                This function sets the appropriate channel selection,
 750                differential input mode and range bits in the ADC Command/
 751                Status register.
 752
 753        Parameters:
 754                struct comedi_device *dev       Pointer to current sevice structure
 755                struct comedi_subdevice *s      Pointer to current subdevice structure
 756                unsigned int *chanlist  Pointer to packed channel list
 757                unsigned int n_chan     Number of channels to scan
 758
 759        Returns:Void
 760
 761==============================================================================
 762*/
 763static void setup_channel_list(struct comedi_device *dev,
 764                               struct comedi_subdevice *s,
 765                               unsigned int *chanlist, unsigned int n_chan)
 766{
 767        unsigned int i, range, chanprog;
 768        unsigned int diff;
 769
 770#ifdef ICP_MULTI_EXTDEBUG
 771        printk("icp multi EDBG:  setup_channel_list(...,%d)\n", n_chan);
 772#endif
 773        devpriv->act_chanlist_len = n_chan;
 774        devpriv->act_chanlist_pos = 0;
 775
 776        for (i = 0; i < n_chan; i++) {
 777                /*  Get channel */
 778                chanprog = CR_CHAN(chanlist[i]);
 779
 780                /*  Determine if it is a differential channel (Bit 15  = 1) */
 781                if (CR_AREF(chanlist[i]) == AREF_DIFF) {
 782                        diff = 1;
 783                        chanprog &= 0x0007;
 784                } else {
 785                        diff = 0;
 786                        chanprog &= 0x000f;
 787                }
 788
 789                /*  Clear channel, range and input mode bits in A/D command/status register */
 790                devpriv->AdcCmdStatus &= 0xf00f;
 791
 792                /*  Set channel number and differential mode status bit */
 793                if (diff) {
 794                        /*  Set channel number, bits 9-11 & mode, bit 6 */
 795                        devpriv->AdcCmdStatus |= (chanprog << 9);
 796                        devpriv->AdcCmdStatus |= ADC_DI;
 797                } else
 798                        /*  Set channel number, bits 8-11 */
 799                        devpriv->AdcCmdStatus |= (chanprog << 8);
 800
 801                /*  Get range for current channel */
 802                range = this_board->rangecode[CR_RANGE(chanlist[i])];
 803                /*  Set range. bits 4-5 */
 804                devpriv->AdcCmdStatus |= range;
 805
 806                /* Output channel, range, mode to ICP Multi */
 807                writew(devpriv->AdcCmdStatus,
 808                       devpriv->io_addr + ICP_MULTI_ADC_CSR);
 809
 810#ifdef ICP_MULTI_EXTDEBUG
 811                printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
 812                       devpriv->act_chanlist[i]);
 813#endif
 814        }
 815
 816}
 817
 818/*
 819==============================================================================
 820
 821        Name:   icp_multi_reset
 822
 823        Description:
 824                This function resets the icp multi device to a 'safe' state
 825
 826        Parameters:
 827                struct comedi_device *dev       Pointer to current sevice structure
 828
 829        Returns:int     0 = success
 830
 831==============================================================================
 832*/
 833static int icp_multi_reset(struct comedi_device *dev)
 834{
 835        unsigned int i;
 836
 837#ifdef ICP_MULTI_EXTDEBUG
 838        printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
 839#endif
 840        /*  Clear INT enables and requests */
 841        writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
 842        writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
 843
 844        if (this_board->n_aochan)
 845                /*  Set DACs to 0..5V range and 0V output */
 846                for (i = 0; i < this_board->n_aochan; i++) {
 847                        devpriv->DacCmdStatus &= 0xfcce;
 848
 849                        /*  Set channel number */
 850                        devpriv->DacCmdStatus |= (i << 8);
 851
 852                        /*  Output 0V */
 853                        writew(0, devpriv->io_addr + ICP_MULTI_AO);
 854
 855                        /*  Set start conversion bit */
 856                        devpriv->DacCmdStatus |= DAC_ST;
 857
 858                        /*  Output to command / status register */
 859                        writew(devpriv->DacCmdStatus,
 860                               devpriv->io_addr + ICP_MULTI_DAC_CSR);
 861
 862                        /*  Delay to allow DAC time to recover */
 863                        udelay(1);
 864                }
 865        /*  Digital outputs to 0 */
 866        writew(0, devpriv->io_addr + ICP_MULTI_DO);
 867
 868#ifdef ICP_MULTI_EXTDEBUG
 869        printk("icp multi EDBG: END: icp_multi_reset(...)\n");
 870#endif
 871        return 0;
 872}
 873
 874/*
 875==============================================================================
 876
 877        Name:   icp_multi_attach
 878
 879        Description:
 880                This function sets up all the appropriate data for the current
 881                device.
 882
 883        Parameters:
 884                struct comedi_device *dev       Pointer to current device structure
 885                struct comedi_devconfig *it     Pointer to current device configuration
 886
 887        Returns:int     0 = success
 888
 889==============================================================================
 890*/
 891static int icp_multi_attach(struct comedi_device *dev,
 892                            struct comedi_devconfig *it)
 893{
 894        struct comedi_subdevice *s;
 895        int ret, subdev, n_subdevices;
 896        unsigned int irq;
 897        struct pcilst_struct *card = NULL;
 898        resource_size_t io_addr[5], iobase;
 899        unsigned char pci_bus, pci_slot, pci_func;
 900
 901        printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
 902
 903        /*  Alocate private data storage space */
 904        ret = alloc_private(dev, sizeof(struct icp_multi_private));
 905        if (ret < 0)
 906                return ret;
 907
 908        /*  Initialise list of PCI cards in system, if not already done so */
 909        if (pci_list_builded++ == 0) {
 910                pci_card_list_init(PCI_VENDOR_ID_ICP,
 911#ifdef ICP_MULTI_EXTDEBUG
 912                                   1
 913#else
 914                                   0
 915#endif
 916                    );
 917        }
 918
 919        printk("Anne's comedi%d: icp_multi: board=%s", dev->minor,
 920               this_board->name);
 921
 922        card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
 923                                         this_board->device_id, it->options[0],
 924                                         it->options[1]);
 925
 926        if (card == NULL)
 927                return -EIO;
 928
 929        devpriv->card = card;
 930
 931        if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
 932                           &irq)) < 0) {
 933                printk(" - Can't get configuration data!\n");
 934                return -EIO;
 935        }
 936
 937        iobase = io_addr[2];
 938        devpriv->phys_iobase = iobase;
 939
 940        printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
 941               (unsigned long long)iobase);
 942
 943        devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
 944
 945        if (devpriv->io_addr == NULL) {
 946                printk("ioremap failed.\n");
 947                return -ENOMEM;
 948        }
 949#ifdef ICP_MULTI_EXTDEBUG
 950        printk("0x%08llx mapped to %p, ", (unsigned long long)iobase,
 951               devpriv->io_addr);
 952#endif
 953
 954        dev->board_name = this_board->name;
 955
 956        n_subdevices = 0;
 957        if (this_board->n_aichan)
 958                n_subdevices++;
 959        if (this_board->n_aochan)
 960                n_subdevices++;
 961        if (this_board->n_dichan)
 962                n_subdevices++;
 963        if (this_board->n_dochan)
 964                n_subdevices++;
 965        if (this_board->n_ctrs)
 966                n_subdevices++;
 967
 968        ret = alloc_subdevices(dev, n_subdevices);
 969        if (ret < 0)
 970                return ret;
 971
 972        icp_multi_reset(dev);
 973
 974        if (this_board->have_irq) {
 975                if (irq) {
 976                        if (request_irq(irq, interrupt_service_icp_multi,
 977                                        IRQF_SHARED, "Inova Icp Multi", dev)) {
 978                                printk
 979                                    (", unable to allocate IRQ %u, DISABLING IT",
 980                                     irq);
 981                                irq = 0;        /* Can't use IRQ */
 982                        } else
 983                                printk(", irq=%u", irq);
 984                } else
 985                        printk(", IRQ disabled");
 986        } else
 987                irq = 0;
 988
 989        dev->irq = irq;
 990
 991        printk(".\n");
 992
 993        subdev = 0;
 994
 995        if (this_board->n_aichan) {
 996                s = dev->subdevices + subdev;
 997                dev->read_subdev = s;
 998                s->type = COMEDI_SUBD_AI;
 999                s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1000                if (this_board->n_aichand)
1001                        s->subdev_flags |= SDF_DIFF;
1002                s->n_chan = this_board->n_aichan;
1003                s->maxdata = this_board->ai_maxdata;
1004                s->len_chanlist = this_board->n_aichan;
1005                s->range_table = this_board->rangelist_ai;
1006                s->insn_read = icp_multi_insn_read_ai;
1007                subdev++;
1008        }
1009
1010        if (this_board->n_aochan) {
1011                s = dev->subdevices + subdev;
1012                s->type = COMEDI_SUBD_AO;
1013                s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1014                s->n_chan = this_board->n_aochan;
1015                s->maxdata = this_board->ao_maxdata;
1016                s->len_chanlist = this_board->n_aochan;
1017                s->range_table = this_board->rangelist_ao;
1018                s->insn_write = icp_multi_insn_write_ao;
1019                s->insn_read = icp_multi_insn_read_ao;
1020                subdev++;
1021        }
1022
1023        if (this_board->n_dichan) {
1024                s = dev->subdevices + subdev;
1025                s->type = COMEDI_SUBD_DI;
1026                s->subdev_flags = SDF_READABLE;
1027                s->n_chan = this_board->n_dichan;
1028                s->maxdata = 1;
1029                s->len_chanlist = this_board->n_dichan;
1030                s->range_table = &range_digital;
1031                s->io_bits = 0;
1032                s->insn_bits = icp_multi_insn_bits_di;
1033                subdev++;
1034        }
1035
1036        if (this_board->n_dochan) {
1037                s = dev->subdevices + subdev;
1038                s->type = COMEDI_SUBD_DO;
1039                s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1040                s->n_chan = this_board->n_dochan;
1041                s->maxdata = 1;
1042                s->len_chanlist = this_board->n_dochan;
1043                s->range_table = &range_digital;
1044                s->io_bits = (1 << this_board->n_dochan) - 1;
1045                s->state = 0;
1046                s->insn_bits = icp_multi_insn_bits_do;
1047                subdev++;
1048        }
1049
1050        if (this_board->n_ctrs) {
1051                s = dev->subdevices + subdev;
1052                s->type = COMEDI_SUBD_COUNTER;
1053                s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1054                s->n_chan = this_board->n_ctrs;
1055                s->maxdata = 0xffff;
1056                s->len_chanlist = this_board->n_ctrs;
1057                s->state = 0;
1058                s->insn_read = icp_multi_insn_read_ctr;
1059                s->insn_write = icp_multi_insn_write_ctr;
1060                subdev++;
1061        }
1062
1063        devpriv->valid = 1;
1064
1065#ifdef ICP_MULTI_EXTDEBUG
1066        printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1067#endif
1068
1069        return 0;
1070}
1071
1072/*
1073==============================================================================
1074
1075        Name:   icp_multi_detach
1076
1077        Description:
1078                This function releases all the resources used by the current
1079                device.
1080
1081        Parameters:
1082                struct comedi_device *dev       Pointer to current device structure
1083
1084        Returns:int     0 = success
1085
1086==============================================================================
1087*/
1088static int icp_multi_detach(struct comedi_device *dev)
1089{
1090
1091        if (dev->private)
1092                if (devpriv->valid)
1093                        icp_multi_reset(dev);
1094
1095        if (dev->irq)
1096                free_irq(dev->irq, dev);
1097
1098        if (dev->private && devpriv->io_addr)
1099                iounmap(devpriv->io_addr);
1100
1101        if (dev->private && devpriv->card)
1102                pci_card_free(devpriv->card);
1103
1104        if (--pci_list_builded == 0)
1105                pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
1106
1107        return 0;
1108}
1109