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