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