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