root/drivers/staging/comedi/drivers/adl_pci9111.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. plx9050_interrupt_control
  2. pci9111_interrupt_source_set
  3. pci9111_fifo_reset
  4. pci9111_ai_cancel
  5. pci9111_ai_check_chanlist
  6. pci9111_ai_do_cmd_test
  7. pci9111_ai_do_cmd
  8. pci9111_ai_munge
  9. pci9111_handle_fifo_half_full
  10. pci9111_interrupt
  11. pci9111_ai_eoc
  12. pci9111_ai_insn_read
  13. pci9111_ao_insn_write
  14. pci9111_di_insn_bits
  15. pci9111_do_insn_bits
  16. pci9111_reset
  17. pci9111_auto_attach
  18. pci9111_detach
  19. pci9111_pci_probe

   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 
  97 static 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 
 107 struct 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 
 117 static 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 
 141 enum pci9111_ISC0_sources {
 142         irq_on_eoc,
 143         irq_on_fifo_half_full
 144 };
 145 
 146 enum pci9111_ISC1_sources {
 147         irq_on_timer_tick,
 148         irq_on_external_trigger
 149 };
 150 
 151 static 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 
 174 static 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 
 184 static 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 
 201 static 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 
 236 static 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 
 336 static 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 
 390 static 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 
 406 static 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 
 450 static 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 
 516 static 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 
 529 static 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 
 569 static 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 
 587 static 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 
 597 static 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 
 610 static 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 
 624 static 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 
 709 static void pci9111_detach(struct comedi_device *dev)
 710 {
 711         if (dev->iobase)
 712                 pci9111_reset(dev);
 713         comedi_pci_detach(dev);
 714 }
 715 
 716 static 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 
 723 static 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 
 730 static 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 };
 735 MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
 736 
 737 static 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 };
 743 module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
 744 
 745 MODULE_AUTHOR("Comedi http://www.comedi.org");
 746 MODULE_DESCRIPTION("Comedi low-level driver");
 747 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */