linux/drivers/staging/comedi/drivers/adl_pci9111.c
<<
>>
Prefs
   1/*
   2
   3comedi/drivers/adl_pci9111.c
   4
   5Hardware driver for PCI9111 ADLink cards:
   6
   7PCI-9111HR
   8
   9Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
  10
  11This program is free software; you can redistribute it and/or modify
  12it under the terms of the GNU General Public License as published by
  13the Free Software Foundation; either version 2 of the License, or
  14(at your option) any later version.
  15
  16This program is distributed in the hope that it will be useful,
  17but WITHOUT ANY WARRANTY; without even the implied warranty of
  18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19GNU General Public License for more details.
  20*/
  21
  22/*
  23Driver: adl_pci9111
  24Description: Adlink PCI-9111HR
  25Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
  26Devices: [ADLink] PCI-9111HR (adl_pci9111)
  27Status: experimental
  28
  29Supports:
  30
  31        - ai_insn read
  32        - ao_insn read/write
  33        - di_insn read
  34        - do_insn read/write
  35        - ai_do_cmd mode with the following sources:
  36
  37        - start_src             TRIG_NOW
  38        - scan_begin_src        TRIG_FOLLOW     TRIG_TIMER      TRIG_EXT
  39        - convert_src                           TRIG_TIMER      TRIG_EXT
  40        - scan_end_src          TRIG_COUNT
  41        - stop_src              TRIG_COUNT      TRIG_NONE
  42
  43The scanned channels must be consecutive and start from 0. They must
  44all have the same range and aref.
  45
  46Configuration options: not applicable, uses PCI auto config
  47*/
  48
  49/*
  50CHANGELOG:
  51
  522005/02/17 Extend AI streaming capabilities. Now, scan_begin_arg can be
  53a multiple of chanlist_len*convert_arg.
  542002/02/19 Fixed the two's complement conversion in pci9111_(hr_)ai_get_data.
  552002/02/18 Added external trigger support for analog input.
  56
  57TODO:
  58
  59        - Really test implemented functionality.
  60        - Add support for the PCI-9111DG with a probe routine to identify
  61          the card type (perhaps with the help of the channel number readback
  62          of the A/D Data register).
  63        - Add external multiplexer support.
  64
  65*/
  66
  67#include <linux/module.h>
  68#include <linux/pci.h>
  69#include <linux/delay.h>
  70#include <linux/interrupt.h>
  71
  72#include "../comedidev.h"
  73
  74#include "8253.h"
  75#include "plx9052.h"
  76#include "comedi_fc.h"
  77
  78#define PCI9111_FIFO_HALF_SIZE  512
  79
  80#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS    10000
  81
  82#define PCI9111_RANGE_SETTING_DELAY             10
  83#define PCI9111_AI_INSTANT_READ_UDELAY_US       2
  84
  85/*
  86 * IO address map and bit defines
  87 */
  88#define PCI9111_AI_FIFO_REG             0x00
  89#define PCI9111_AO_REG                  0x00
  90#define PCI9111_DIO_REG                 0x02
  91#define PCI9111_EDIO_REG                0x04
  92#define PCI9111_AI_CHANNEL_REG          0x06
  93#define PCI9111_AI_RANGE_STAT_REG       0x08
  94#define PCI9111_AI_STAT_AD_BUSY         (1 << 7)
  95#define PCI9111_AI_STAT_FF_FF           (1 << 6)
  96#define PCI9111_AI_STAT_FF_HF           (1 << 5)
  97#define PCI9111_AI_STAT_FF_EF           (1 << 4)
  98#define PCI9111_AI_RANGE_MASK           (7 << 0)
  99#define PCI9111_AI_TRIG_CTRL_REG        0x0a
 100#define PCI9111_AI_TRIG_CTRL_TRGEVENT   (1 << 5)
 101#define PCI9111_AI_TRIG_CTRL_POTRG      (1 << 4)
 102#define PCI9111_AI_TRIG_CTRL_PTRG       (1 << 3)
 103#define PCI9111_AI_TRIG_CTRL_ETIS       (1 << 2)
 104#define PCI9111_AI_TRIG_CTRL_TPST       (1 << 1)
 105#define PCI9111_AI_TRIG_CTRL_ASCAN      (1 << 0)
 106#define PCI9111_INT_CTRL_REG            0x0c
 107#define PCI9111_INT_CTRL_ISC2           (1 << 3)
 108#define PCI9111_INT_CTRL_FFEN           (1 << 2)
 109#define PCI9111_INT_CTRL_ISC1           (1 << 1)
 110#define PCI9111_INT_CTRL_ISC0           (1 << 0)
 111#define PCI9111_SOFT_TRIG_REG           0x0e
 112#define PCI9111_8254_BASE_REG           0x40
 113#define PCI9111_INT_CLR_REG             0x48
 114
 115/* PLX 9052 Local Interrupt 1 enabled and active */
 116#define PCI9111_LI1_ACTIVE      (PLX9052_INTCSR_LI1ENAB |       \
 117                                 PLX9052_INTCSR_LI1STAT)
 118
 119/* PLX 9052 Local Interrupt 2 enabled and active */
 120#define PCI9111_LI2_ACTIVE      (PLX9052_INTCSR_LI2ENAB |       \
 121                                 PLX9052_INTCSR_LI2STAT)
 122
 123static const struct comedi_lrange pci9111_ai_range = {
 124        5, {
 125                BIP_RANGE(10),
 126                BIP_RANGE(5),
 127                BIP_RANGE(2.5),
 128                BIP_RANGE(1.25),
 129                BIP_RANGE(0.625)
 130        }
 131};
 132
 133struct pci9111_private_data {
 134        unsigned long lcr_io_base;
 135
 136        unsigned int scan_delay;
 137        unsigned int chunk_counter;
 138        unsigned int chunk_num_samples;
 139
 140        unsigned int div1;
 141        unsigned int div2;
 142
 143        unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
 144};
 145
 146static void plx9050_interrupt_control(unsigned long io_base,
 147                                      bool LINTi1_enable,
 148                                      bool LINTi1_active_high,
 149                                      bool LINTi2_enable,
 150                                      bool LINTi2_active_high,
 151                                      bool interrupt_enable)
 152{
 153        int flags = 0;
 154
 155        if (LINTi1_enable)
 156                flags |= PLX9052_INTCSR_LI1ENAB;
 157        if (LINTi1_active_high)
 158                flags |= PLX9052_INTCSR_LI1POL;
 159        if (LINTi2_enable)
 160                flags |= PLX9052_INTCSR_LI2ENAB;
 161        if (LINTi2_active_high)
 162                flags |= PLX9052_INTCSR_LI2POL;
 163
 164        if (interrupt_enable)
 165                flags |= PLX9052_INTCSR_PCIENAB;
 166
 167        outb(flags, io_base + PLX9052_INTCSR);
 168}
 169
 170static void pci9111_timer_set(struct comedi_device *dev)
 171{
 172        struct pci9111_private_data *dev_private = dev->private;
 173        unsigned long timer_base = dev->iobase + PCI9111_8254_BASE_REG;
 174
 175        i8254_set_mode(timer_base, 1, 0, I8254_MODE0 | I8254_BINARY);
 176        i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
 177        i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
 178
 179        udelay(1);
 180
 181        i8254_write(timer_base, 1, 2, dev_private->div2);
 182        i8254_write(timer_base, 1, 1, dev_private->div1);
 183}
 184
 185enum pci9111_ISC0_sources {
 186        irq_on_eoc,
 187        irq_on_fifo_half_full
 188};
 189
 190enum pci9111_ISC1_sources {
 191        irq_on_timer_tick,
 192        irq_on_external_trigger
 193};
 194
 195static void pci9111_interrupt_source_set(struct comedi_device *dev,
 196                                         enum pci9111_ISC0_sources irq_0_source,
 197                                         enum pci9111_ISC1_sources irq_1_source)
 198{
 199        int flags;
 200
 201        /* Read the current interrupt control bits */
 202        flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
 203        /* Shift the bits so they are compatible with the write register */
 204        flags >>= 4;
 205        /* Mask off the ISCx bits */
 206        flags &= 0xc0;
 207
 208        /* Now set the new ISCx bits */
 209        if (irq_0_source == irq_on_fifo_half_full)
 210                flags |= PCI9111_INT_CTRL_ISC0;
 211
 212        if (irq_1_source == irq_on_external_trigger)
 213                flags |= PCI9111_INT_CTRL_ISC1;
 214
 215        outb(flags, dev->iobase + PCI9111_INT_CTRL_REG);
 216}
 217
 218static void pci9111_fifo_reset(struct comedi_device *dev)
 219{
 220        unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG;
 221
 222        /* To reset the FIFO, set FFEN sequence as 0 -> 1 -> 0 */
 223        outb(0, int_ctrl_reg);
 224        outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg);
 225        outb(0, int_ctrl_reg);
 226}
 227
 228static int pci9111_ai_cancel(struct comedi_device *dev,
 229                             struct comedi_subdevice *s)
 230{
 231        struct pci9111_private_data *dev_private = dev->private;
 232
 233        /*  Disable interrupts */
 234        plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
 235                                  true, false);
 236
 237        /* disable A/D triggers (software trigger mode) and auto scan off */
 238        outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
 239
 240        pci9111_fifo_reset(dev);
 241
 242        return 0;
 243}
 244
 245static int pci9111_ai_check_chanlist(struct comedi_device *dev,
 246                                     struct comedi_subdevice *s,
 247                                     struct comedi_cmd *cmd)
 248{
 249        unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
 250        unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
 251        int i;
 252
 253        for (i = 1; i < cmd->chanlist_len; i++) {
 254                unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 255                unsigned int range = CR_RANGE(cmd->chanlist[i]);
 256                unsigned int aref = CR_AREF(cmd->chanlist[i]);
 257
 258                if (chan != i) {
 259                        dev_dbg(dev->class_dev,
 260                                "entries in chanlist must be consecutive channels,counting upwards from 0\n");
 261                        return -EINVAL;
 262                }
 263
 264                if (range != range0) {
 265                        dev_dbg(dev->class_dev,
 266                                "entries in chanlist must all have the same gain\n");
 267                        return -EINVAL;
 268                }
 269
 270                if (aref != aref0) {
 271                        dev_dbg(dev->class_dev,
 272                                "entries in chanlist must all have the same reference\n");
 273                        return -EINVAL;
 274                }
 275        }
 276
 277        return 0;
 278}
 279
 280static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
 281                                  struct comedi_subdevice *s,
 282                                  struct comedi_cmd *cmd)
 283{
 284        struct pci9111_private_data *dev_private = dev->private;
 285        int err = 0;
 286        unsigned int arg;
 287
 288        /* Step 1 : check if triggers are trivially valid */
 289
 290        err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
 291        err |= cfc_check_trigger_src(&cmd->scan_begin_src,
 292                                        TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
 293        err |= cfc_check_trigger_src(&cmd->convert_src,
 294                                        TRIG_TIMER | TRIG_EXT);
 295        err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 296        err |= cfc_check_trigger_src(&cmd->stop_src,
 297                                        TRIG_COUNT | TRIG_NONE);
 298
 299        if (err)
 300                return 1;
 301
 302        /* Step 2a : make sure trigger sources are unique */
 303
 304        err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
 305        err |= cfc_check_trigger_is_unique(cmd->convert_src);
 306        err |= cfc_check_trigger_is_unique(cmd->stop_src);
 307
 308        /* Step 2b : and mutually compatible */
 309
 310        if (cmd->scan_begin_src != TRIG_FOLLOW) {
 311                if (cmd->scan_begin_src != cmd->convert_src)
 312                        err |= -EINVAL;
 313        }
 314
 315        if (err)
 316                return 2;
 317
 318        /* Step 3: check if arguments are trivially valid */
 319
 320        err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
 321
 322        if (cmd->convert_src == TRIG_TIMER)
 323                err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
 324                                        PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
 325        else    /* TRIG_EXT */
 326                err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
 327
 328        if (cmd->scan_begin_src == TRIG_TIMER)
 329                err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
 330                                        PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
 331        else    /* TRIG_FOLLOW || TRIG_EXT */
 332                err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 333
 334        err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
 335
 336        if (cmd->stop_src == TRIG_COUNT)
 337                err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
 338        else    /* TRIG_NONE */
 339                err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
 340
 341        if (err)
 342                return 3;
 343
 344        /* Step 4: fix up any arguments */
 345
 346        if (cmd->convert_src == TRIG_TIMER) {
 347                arg = cmd->convert_arg;
 348                i8253_cascade_ns_to_timer(I8254_OSC_BASE_2MHZ,
 349                                          &dev_private->div1,
 350                                          &dev_private->div2,
 351                                          &arg, cmd->flags);
 352                err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
 353        }
 354
 355        /*
 356         * There's only one timer on this card, so the scan_begin timer
 357         * must be a multiple of chanlist_len*convert_arg
 358         */
 359        if (cmd->scan_begin_src == TRIG_TIMER) {
 360                arg = cmd->chanlist_len * cmd->convert_arg;
 361
 362                if (arg < cmd->scan_begin_arg)
 363                        arg *= (cmd->scan_begin_arg / arg);
 364
 365                err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
 366        }
 367
 368        if (err)
 369                return 4;
 370
 371        /* Step 5: check channel list if it exists */
 372        if (cmd->chanlist && cmd->chanlist_len > 0)
 373                err |= pci9111_ai_check_chanlist(dev, s, cmd);
 374
 375        if (err)
 376                return 5;
 377
 378        return 0;
 379
 380}
 381
 382static int pci9111_ai_do_cmd(struct comedi_device *dev,
 383                             struct comedi_subdevice *s)
 384{
 385        struct pci9111_private_data *dev_private = dev->private;
 386        struct comedi_cmd *cmd = &s->async->cmd;
 387        unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
 388        unsigned int trig = 0;
 389
 390        /*  Set channel scan limit */
 391        /*  PCI9111 allows only scanning from channel 0 to channel n */
 392        /*  TODO: handle the case of an external multiplexer */
 393
 394        if (cmd->chanlist_len > 1)
 395                trig |= PCI9111_AI_TRIG_CTRL_ASCAN;
 396
 397        outb(last_chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
 398
 399        /*  Set gain */
 400        /*  This is the same gain on every channel */
 401
 402        outb(CR_RANGE(cmd->chanlist[0]) & PCI9111_AI_RANGE_MASK,
 403                dev->iobase + PCI9111_AI_RANGE_STAT_REG);
 404
 405        /*  Set timer pacer */
 406        dev_private->scan_delay = 0;
 407        if (cmd->convert_src == TRIG_TIMER) {
 408                trig |= PCI9111_AI_TRIG_CTRL_TPST;
 409                pci9111_timer_set(dev);
 410                pci9111_fifo_reset(dev);
 411                pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
 412                                             irq_on_timer_tick);
 413                plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
 414                                          false, true, true);
 415
 416                if (cmd->scan_begin_src == TRIG_TIMER) {
 417                        dev_private->scan_delay = (cmd->scan_begin_arg /
 418                                (cmd->convert_arg * cmd->chanlist_len)) - 1;
 419                }
 420        } else {        /* TRIG_EXT */
 421                trig |= PCI9111_AI_TRIG_CTRL_ETIS;
 422                pci9111_fifo_reset(dev);
 423                pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
 424                                             irq_on_timer_tick);
 425                plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
 426                                          false, true, true);
 427        }
 428        outb(trig, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
 429
 430        dev_private->chunk_counter = 0;
 431        dev_private->chunk_num_samples = cmd->chanlist_len *
 432                                         (1 + dev_private->scan_delay);
 433
 434        return 0;
 435}
 436
 437static void pci9111_ai_munge(struct comedi_device *dev,
 438                             struct comedi_subdevice *s, void *data,
 439                             unsigned int num_bytes,
 440                             unsigned int start_chan_index)
 441{
 442        unsigned short *array = data;
 443        unsigned int maxdata = s->maxdata;
 444        unsigned int invert = (maxdata + 1) >> 1;
 445        unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
 446        unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
 447        unsigned int i;
 448
 449        for (i = 0; i < num_samples; i++)
 450                array[i] = ((array[i] >> shift) & maxdata) ^ invert;
 451}
 452
 453static void pci9111_handle_fifo_half_full(struct comedi_device *dev,
 454                                          struct comedi_subdevice *s)
 455{
 456        struct pci9111_private_data *devpriv = dev->private;
 457        struct comedi_cmd *cmd = &s->async->cmd;
 458        unsigned int samples;
 459
 460        samples = comedi_nsamples_left(s, PCI9111_FIFO_HALF_SIZE);
 461        insw(dev->iobase + PCI9111_AI_FIFO_REG,
 462             devpriv->ai_bounce_buffer, samples);
 463
 464        if (devpriv->scan_delay < 1) {
 465                comedi_buf_write_samples(s, devpriv->ai_bounce_buffer, samples);
 466        } else {
 467                unsigned int pos = 0;
 468                unsigned int to_read;
 469
 470                while (pos < samples) {
 471                        if (devpriv->chunk_counter < cmd->chanlist_len) {
 472                                to_read = cmd->chanlist_len -
 473                                          devpriv->chunk_counter;
 474
 475                                if (to_read > samples - pos)
 476                                        to_read = samples - pos;
 477
 478                                comedi_buf_write_samples(s,
 479                                                devpriv->ai_bounce_buffer + pos,
 480                                                to_read);
 481                        } else {
 482                                to_read = devpriv->chunk_num_samples -
 483                                          devpriv->chunk_counter;
 484
 485                                if (to_read > samples - pos)
 486                                        to_read = samples - pos;
 487                        }
 488
 489                        pos += to_read;
 490                        devpriv->chunk_counter += to_read;
 491
 492                        if (devpriv->chunk_counter >=
 493                            devpriv->chunk_num_samples)
 494                                devpriv->chunk_counter = 0;
 495                }
 496        }
 497}
 498
 499static irqreturn_t pci9111_interrupt(int irq, void *p_device)
 500{
 501        struct comedi_device *dev = p_device;
 502        struct pci9111_private_data *dev_private = dev->private;
 503        struct comedi_subdevice *s = dev->read_subdev;
 504        struct comedi_async *async;
 505        struct comedi_cmd *cmd;
 506        unsigned int status;
 507        unsigned long irq_flags;
 508        unsigned char intcsr;
 509
 510        if (!dev->attached) {
 511                /*  Ignore interrupt before device fully attached. */
 512                /*  Might not even have allocated subdevices yet! */
 513                return IRQ_NONE;
 514        }
 515
 516        async = s->async;
 517        cmd = &async->cmd;
 518
 519        spin_lock_irqsave(&dev->spinlock, irq_flags);
 520
 521        /*  Check if we are source of interrupt */
 522        intcsr = inb(dev_private->lcr_io_base + PLX9052_INTCSR);
 523        if (!(((intcsr & PLX9052_INTCSR_PCIENAB) != 0) &&
 524              (((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) ||
 525               ((intcsr & PCI9111_LI2_ACTIVE) == PCI9111_LI2_ACTIVE)))) {
 526                /*  Not the source of the interrupt. */
 527                /*  (N.B. not using PLX9052_INTCSR_SOFTINT) */
 528                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 529                return IRQ_NONE;
 530        }
 531
 532        if ((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) {
 533                /*  Interrupt comes from fifo_half-full signal */
 534
 535                status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
 536
 537                /* '0' means FIFO is full, data may have been lost */
 538                if (!(status & PCI9111_AI_STAT_FF_FF)) {
 539                        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 540                        dev_dbg(dev->class_dev, "fifo overflow\n");
 541                        outb(0, dev->iobase + PCI9111_INT_CLR_REG);
 542                        async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
 543                        comedi_handle_events(dev, s);
 544
 545                        return IRQ_HANDLED;
 546                }
 547
 548                /* '0' means FIFO is half-full */
 549                if (!(status & PCI9111_AI_STAT_FF_HF))
 550                        pci9111_handle_fifo_half_full(dev, s);
 551        }
 552
 553        if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
 554                async->events |= COMEDI_CB_EOA;
 555
 556        outb(0, dev->iobase + PCI9111_INT_CLR_REG);
 557
 558        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 559
 560        comedi_handle_events(dev, s);
 561
 562        return IRQ_HANDLED;
 563}
 564
 565static int pci9111_ai_eoc(struct comedi_device *dev,
 566                          struct comedi_subdevice *s,
 567                          struct comedi_insn *insn,
 568                          unsigned long context)
 569{
 570        unsigned int status;
 571
 572        status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
 573        if (status & PCI9111_AI_STAT_FF_EF)
 574                return 0;
 575        return -EBUSY;
 576}
 577
 578static int pci9111_ai_insn_read(struct comedi_device *dev,
 579                                struct comedi_subdevice *s,
 580                                struct comedi_insn *insn, unsigned int *data)
 581{
 582        unsigned int chan = CR_CHAN(insn->chanspec);
 583        unsigned int range = CR_RANGE(insn->chanspec);
 584        unsigned int maxdata = s->maxdata;
 585        unsigned int invert = (maxdata + 1) >> 1;
 586        unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
 587        unsigned int status;
 588        int ret;
 589        int i;
 590
 591        outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
 592
 593        status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
 594        if ((status & PCI9111_AI_RANGE_MASK) != range) {
 595                outb(range & PCI9111_AI_RANGE_MASK,
 596                        dev->iobase + PCI9111_AI_RANGE_STAT_REG);
 597        }
 598
 599        pci9111_fifo_reset(dev);
 600
 601        for (i = 0; i < insn->n; i++) {
 602                /* Generate a software trigger */
 603                outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG);
 604
 605                ret = comedi_timeout(dev, s, insn, pci9111_ai_eoc, 0);
 606                if (ret) {
 607                        pci9111_fifo_reset(dev);
 608                        return ret;
 609                }
 610
 611                data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
 612                data[i] = ((data[i] >> shift) & maxdata) ^ invert;
 613        }
 614
 615        return i;
 616}
 617
 618static int pci9111_ao_insn_write(struct comedi_device *dev,
 619                                 struct comedi_subdevice *s,
 620                                 struct comedi_insn *insn,
 621                                 unsigned int *data)
 622{
 623        unsigned int chan = CR_CHAN(insn->chanspec);
 624        unsigned int val = s->readback[chan];
 625        int i;
 626
 627        for (i = 0; i < insn->n; i++) {
 628                val = data[i];
 629                outw(val, dev->iobase + PCI9111_AO_REG);
 630        }
 631        s->readback[chan] = val;
 632
 633        return insn->n;
 634}
 635
 636static int pci9111_di_insn_bits(struct comedi_device *dev,
 637                                struct comedi_subdevice *s,
 638                                struct comedi_insn *insn,
 639                                unsigned int *data)
 640{
 641        data[1] = inw(dev->iobase + PCI9111_DIO_REG);
 642
 643        return insn->n;
 644}
 645
 646static int pci9111_do_insn_bits(struct comedi_device *dev,
 647                                struct comedi_subdevice *s,
 648                                struct comedi_insn *insn,
 649                                unsigned int *data)
 650{
 651        if (comedi_dio_update_state(s, data))
 652                outw(s->state, dev->iobase + PCI9111_DIO_REG);
 653
 654        data[1] = s->state;
 655
 656        return insn->n;
 657}
 658
 659static int pci9111_reset(struct comedi_device *dev)
 660{
 661        struct pci9111_private_data *dev_private = dev->private;
 662
 663        /*  Set trigger source to software */
 664        plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
 665                                  true, false);
 666
 667        /* disable A/D triggers (software trigger mode) and auto scan off */
 668        outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
 669
 670        /* Reset 8254 chip */
 671        dev_private->div1 = 0;
 672        dev_private->div2 = 0;
 673        pci9111_timer_set(dev);
 674
 675        return 0;
 676}
 677
 678static int pci9111_auto_attach(struct comedi_device *dev,
 679                                         unsigned long context_unused)
 680{
 681        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 682        struct pci9111_private_data *dev_private;
 683        struct comedi_subdevice *s;
 684        int ret;
 685
 686        dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
 687        if (!dev_private)
 688                return -ENOMEM;
 689
 690        ret = comedi_pci_enable(dev);
 691        if (ret)
 692                return ret;
 693        dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
 694        dev->iobase = pci_resource_start(pcidev, 2);
 695
 696        pci9111_reset(dev);
 697
 698        if (pcidev->irq) {
 699                ret = request_irq(pcidev->irq, pci9111_interrupt,
 700                                  IRQF_SHARED, dev->board_name, dev);
 701                if (ret == 0)
 702                        dev->irq = pcidev->irq;
 703        }
 704
 705        ret = comedi_alloc_subdevices(dev, 4);
 706        if (ret)
 707                return ret;
 708
 709        s = &dev->subdevices[0];
 710        s->type         = COMEDI_SUBD_AI;
 711        s->subdev_flags = SDF_READABLE | SDF_COMMON;
 712        s->n_chan       = 16;
 713        s->maxdata      = 0xffff;
 714        s->range_table  = &pci9111_ai_range;
 715        s->insn_read    = pci9111_ai_insn_read;
 716        if (dev->irq) {
 717                dev->read_subdev = s;
 718                s->subdev_flags |= SDF_CMD_READ;
 719                s->len_chanlist = s->n_chan;
 720                s->do_cmdtest   = pci9111_ai_do_cmd_test;
 721                s->do_cmd       = pci9111_ai_do_cmd;
 722                s->cancel       = pci9111_ai_cancel;
 723                s->munge        = pci9111_ai_munge;
 724        }
 725
 726        s = &dev->subdevices[1];
 727        s->type         = COMEDI_SUBD_AO;
 728        s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
 729        s->n_chan       = 1;
 730        s->maxdata      = 0x0fff;
 731        s->len_chanlist = 1;
 732        s->range_table  = &range_bipolar10;
 733        s->insn_write   = pci9111_ao_insn_write;
 734
 735        ret = comedi_alloc_subdev_readback(s);
 736        if (ret)
 737                return ret;
 738
 739        s = &dev->subdevices[2];
 740        s->type         = COMEDI_SUBD_DI;
 741        s->subdev_flags = SDF_READABLE;
 742        s->n_chan       = 16;
 743        s->maxdata      = 1;
 744        s->range_table  = &range_digital;
 745        s->insn_bits    = pci9111_di_insn_bits;
 746
 747        s = &dev->subdevices[3];
 748        s->type         = COMEDI_SUBD_DO;
 749        s->subdev_flags = SDF_WRITABLE;
 750        s->n_chan       = 16;
 751        s->maxdata      = 1;
 752        s->range_table  = &range_digital;
 753        s->insn_bits    = pci9111_do_insn_bits;
 754
 755        return 0;
 756}
 757
 758static void pci9111_detach(struct comedi_device *dev)
 759{
 760        if (dev->iobase)
 761                pci9111_reset(dev);
 762        comedi_pci_detach(dev);
 763}
 764
 765static struct comedi_driver adl_pci9111_driver = {
 766        .driver_name    = "adl_pci9111",
 767        .module         = THIS_MODULE,
 768        .auto_attach    = pci9111_auto_attach,
 769        .detach         = pci9111_detach,
 770};
 771
 772static int pci9111_pci_probe(struct pci_dev *dev,
 773                             const struct pci_device_id *id)
 774{
 775        return comedi_pci_auto_config(dev, &adl_pci9111_driver,
 776                                      id->driver_data);
 777}
 778
 779static const struct pci_device_id pci9111_pci_table[] = {
 780        { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) },
 781        /* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */
 782        { 0 }
 783};
 784MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
 785
 786static struct pci_driver adl_pci9111_pci_driver = {
 787        .name           = "adl_pci9111",
 788        .id_table       = pci9111_pci_table,
 789        .probe          = pci9111_pci_probe,
 790        .remove         = comedi_pci_auto_unconfig,
 791};
 792module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
 793
 794MODULE_AUTHOR("Comedi http://www.comedi.org");
 795MODULE_DESCRIPTION("Comedi low-level driver");
 796MODULE_LICENSE("GPL");
 797
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.