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