root/drivers/staging/comedi/drivers/pcmmio.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmmio_dio_write
  2. pcmmio_dio_read
  3. pcmmio_dio_insn_bits
  4. pcmmio_dio_insn_config
  5. pcmmio_reset
  6. pcmmio_stop_intr
  7. pcmmio_handle_dio_intr
  8. interrupt_pcmmio
  9. pcmmio_start_intr
  10. pcmmio_cancel
  11. pcmmio_inttrig_start_intr
  12. pcmmio_cmd
  13. pcmmio_cmdtest
  14. pcmmio_ai_eoc
  15. pcmmio_ai_insn_read
  16. pcmmio_ao_eoc
  17. pcmmio_ao_insn_write
  18. pcmmio_attach

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * pcmmio.c
   4  * Driver for Winsystems PC-104 based multifunction IO board.
   5  *
   6  * COMEDI - Linux Control and Measurement Device Interface
   7  * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
   8  */
   9 
  10 /*
  11  * Driver: pcmmio
  12  * Description: A driver for the PCM-MIO multifunction board
  13  * Devices: [Winsystems] PCM-MIO (pcmmio)
  14  * Author: Calin Culianu <calin@ajvar.org>
  15  * Updated: Wed, May 16 2007 16:21:10 -0500
  16  * Status: works
  17  *
  18  * A driver for the PCM-MIO multifunction board from Winsystems. This
  19  * is a PC-104 based I/O board. It contains four subdevices:
  20  *
  21  *      subdevice 0 - 16 channels of 16-bit AI
  22  *      subdevice 1 - 8 channels of 16-bit AO
  23  *      subdevice 2 - first 24 channels of the 48 channel of DIO
  24  *                      (with edge-triggered interrupt support)
  25  *      subdevice 3 - last 24 channels of the 48 channel DIO
  26  *                      (no interrupt support for this bank of channels)
  27  *
  28  * Some notes:
  29  *
  30  * Synchronous reads and writes are the only things implemented for analog
  31  * input and output. The hardware itself can do streaming acquisition, etc.
  32  *
  33  * Asynchronous I/O for the DIO subdevices *is* implemented, however! They
  34  * are basically edge-triggered interrupts for any configuration of the
  35  * channels in subdevice 2.
  36  *
  37  * Also note that this interrupt support is untested.
  38  *
  39  * A few words about edge-detection IRQ support (commands on DIO):
  40  *
  41  * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
  42  * of the board to the comedi_config command. The board IRQ is not jumpered
  43  * but rather configured through software, so any IRQ from 1-15 is OK.
  44  *
  45  * Due to the genericity of the comedi API, you need to create a special
  46  * comedi_command in order to use edge-triggered interrupts for DIO.
  47  *
  48  * Use comedi_commands with TRIG_NOW.  Your callback will be called each
  49  * time an edge is detected on the specified DIO line(s), and the data
  50  * values will be two sample_t's, which should be concatenated to form
  51  * one 32-bit unsigned int. This value is the mask of channels that had
  52  * edges detected from your channel list. Note that the bits positions
  53  * in the mask correspond to positions in your chanlist when you
  54  * specified the command and *not* channel id's!
  55  *
  56  * To set the polarity of the edge-detection interrupts pass a nonzero value
  57  * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
  58  * value for both CR_RANGE and CR_AREF if you want edge-down polarity.
  59  *
  60  * Configuration Options:
  61  *   [0] - I/O port base address
  62  *   [1] - IRQ (optional -- for edge-detect interrupt support only,
  63  *              leave out if you don't need this feature)
  64  */
  65 
  66 #include <linux/module.h>
  67 #include <linux/interrupt.h>
  68 #include <linux/slab.h>
  69 
  70 #include "../comedidev.h"
  71 
  72 /*
  73  * Register I/O map
  74  */
  75 #define PCMMIO_AI_LSB_REG                       0x00
  76 #define PCMMIO_AI_MSB_REG                       0x01
  77 #define PCMMIO_AI_CMD_REG                       0x02
  78 #define PCMMIO_AI_CMD_SE                        BIT(7)
  79 #define PCMMIO_AI_CMD_ODD_CHAN                  BIT(6)
  80 #define PCMMIO_AI_CMD_CHAN_SEL(x)               (((x) & 0x3) << 4)
  81 #define PCMMIO_AI_CMD_RANGE(x)                  (((x) & 0x3) << 2)
  82 #define PCMMIO_RESOURCE_REG                     0x02
  83 #define PCMMIO_RESOURCE_IRQ(x)                  (((x) & 0xf) << 0)
  84 #define PCMMIO_AI_STATUS_REG                    0x03
  85 #define PCMMIO_AI_STATUS_DATA_READY             BIT(7)
  86 #define PCMMIO_AI_STATUS_DATA_DMA_PEND          BIT(6)
  87 #define PCMMIO_AI_STATUS_CMD_DMA_PEND           BIT(5)
  88 #define PCMMIO_AI_STATUS_IRQ_PEND               BIT(4)
  89 #define PCMMIO_AI_STATUS_DATA_DRQ_ENA           BIT(2)
  90 #define PCMMIO_AI_STATUS_REG_SEL                BIT(3)
  91 #define PCMMIO_AI_STATUS_CMD_DRQ_ENA            BIT(1)
  92 #define PCMMIO_AI_STATUS_IRQ_ENA                BIT(0)
  93 #define PCMMIO_AI_RES_ENA_REG                   0x03
  94 #define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS        (0 << 3)
  95 #define PCMMIO_AI_RES_ENA_AI_RES_ACCESS         BIT(3)
  96 #define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS        BIT(4)
  97 #define PCMMIO_AI_2ND_ADC_OFFSET                0x04
  98 
  99 #define PCMMIO_AO_LSB_REG                       0x08
 100 #define PCMMIO_AO_LSB_SPAN(x)                   (((x) & 0xf) << 0)
 101 #define PCMMIO_AO_MSB_REG                       0x09
 102 #define PCMMIO_AO_CMD_REG                       0x0a
 103 #define PCMMIO_AO_CMD_WR_SPAN                   (0x2 << 4)
 104 #define PCMMIO_AO_CMD_WR_CODE                   (0x3 << 4)
 105 #define PCMMIO_AO_CMD_UPDATE                    (0x4 << 4)
 106 #define PCMMIO_AO_CMD_UPDATE_ALL                (0x5 << 4)
 107 #define PCMMIO_AO_CMD_WR_SPAN_UPDATE            (0x6 << 4)
 108 #define PCMMIO_AO_CMD_WR_CODE_UPDATE            (0x7 << 4)
 109 #define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL        (0x8 << 4)
 110 #define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL        (0x9 << 4)
 111 #define PCMMIO_AO_CMD_RD_B1_SPAN                (0xa << 4)
 112 #define PCMMIO_AO_CMD_RD_B1_CODE                (0xb << 4)
 113 #define PCMMIO_AO_CMD_RD_B2_SPAN                (0xc << 4)
 114 #define PCMMIO_AO_CMD_RD_B2_CODE                (0xd << 4)
 115 #define PCMMIO_AO_CMD_NOP                       (0xf << 4)
 116 #define PCMMIO_AO_CMD_CHAN_SEL(x)               (((x) & 0x03) << 1)
 117 #define PCMMIO_AO_CMD_CHAN_SEL_ALL              (0x0f << 0)
 118 #define PCMMIO_AO_STATUS_REG                    0x0b
 119 #define PCMMIO_AO_STATUS_DATA_READY             BIT(7)
 120 #define PCMMIO_AO_STATUS_DATA_DMA_PEND          BIT(6)
 121 #define PCMMIO_AO_STATUS_CMD_DMA_PEND           BIT(5)
 122 #define PCMMIO_AO_STATUS_IRQ_PEND               BIT(4)
 123 #define PCMMIO_AO_STATUS_DATA_DRQ_ENA           BIT(2)
 124 #define PCMMIO_AO_STATUS_REG_SEL                BIT(3)
 125 #define PCMMIO_AO_STATUS_CMD_DRQ_ENA            BIT(1)
 126 #define PCMMIO_AO_STATUS_IRQ_ENA                BIT(0)
 127 #define PCMMIO_AO_RESOURCE_ENA_REG              0x0b
 128 #define PCMMIO_AO_2ND_DAC_OFFSET                0x04
 129 
 130 /*
 131  * WinSystems WS16C48
 132  *
 133  * Offset    Page 0       Page 1       Page 2       Page 3
 134  * ------  -----------  -----------  -----------  -----------
 135  *  0x10   Port 0 I/O   Port 0 I/O   Port 0 I/O   Port 0 I/O
 136  *  0x11   Port 1 I/O   Port 1 I/O   Port 1 I/O   Port 1 I/O
 137  *  0x12   Port 2 I/O   Port 2 I/O   Port 2 I/O   Port 2 I/O
 138  *  0x13   Port 3 I/O   Port 3 I/O   Port 3 I/O   Port 3 I/O
 139  *  0x14   Port 4 I/O   Port 4 I/O   Port 4 I/O   Port 4 I/O
 140  *  0x15   Port 5 I/O   Port 5 I/O   Port 5 I/O   Port 5 I/O
 141  *  0x16   INT_PENDING  INT_PENDING  INT_PENDING  INT_PENDING
 142  *  0x17    Page/Lock    Page/Lock    Page/Lock    Page/Lock
 143  *  0x18       N/A         POL_0       ENAB_0       INT_ID0
 144  *  0x19       N/A         POL_1       ENAB_1       INT_ID1
 145  *  0x1a       N/A         POL_2       ENAB_2       INT_ID2
 146  */
 147 #define PCMMIO_PORT_REG(x)                      (0x10 + (x))
 148 #define PCMMIO_INT_PENDING_REG                  0x16
 149 #define PCMMIO_PAGE_LOCK_REG                    0x17
 150 #define PCMMIO_LOCK_PORT(x)                     ((1 << (x)) & 0x3f)
 151 #define PCMMIO_PAGE(x)                          (((x) & 0x3) << 6)
 152 #define PCMMIO_PAGE_MASK                        PCMUIO_PAGE(3)
 153 #define PCMMIO_PAGE_POL                         1
 154 #define PCMMIO_PAGE_ENAB                        2
 155 #define PCMMIO_PAGE_INT_ID                      3
 156 #define PCMMIO_PAGE_REG(x)                      (0x18 + (x))
 157 
 158 static const struct comedi_lrange pcmmio_ai_ranges = {
 159         4, {
 160                 BIP_RANGE(5),
 161                 BIP_RANGE(10),
 162                 UNI_RANGE(5),
 163                 UNI_RANGE(10)
 164         }
 165 };
 166 
 167 static const struct comedi_lrange pcmmio_ao_ranges = {
 168         6, {
 169                 UNI_RANGE(5),
 170                 UNI_RANGE(10),
 171                 BIP_RANGE(5),
 172                 BIP_RANGE(10),
 173                 BIP_RANGE(2.5),
 174                 RANGE(-2.5, 7.5)
 175         }
 176 };
 177 
 178 struct pcmmio_private {
 179         spinlock_t pagelock;    /* protects the page registers */
 180         spinlock_t spinlock;    /* protects the member variables */
 181         unsigned int enabled_mask;
 182         unsigned int active:1;
 183 };
 184 
 185 static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
 186                              int page, int port)
 187 {
 188         struct pcmmio_private *devpriv = dev->private;
 189         unsigned long iobase = dev->iobase;
 190         unsigned long flags;
 191 
 192         spin_lock_irqsave(&devpriv->pagelock, flags);
 193         if (page == 0) {
 194                 /* Port registers are valid for any page */
 195                 outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
 196                 outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
 197                 outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
 198         } else {
 199                 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
 200                 outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
 201                 outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
 202                 outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
 203         }
 204         spin_unlock_irqrestore(&devpriv->pagelock, flags);
 205 }
 206 
 207 static unsigned int pcmmio_dio_read(struct comedi_device *dev,
 208                                     int page, int port)
 209 {
 210         struct pcmmio_private *devpriv = dev->private;
 211         unsigned long iobase = dev->iobase;
 212         unsigned long flags;
 213         unsigned int val;
 214 
 215         spin_lock_irqsave(&devpriv->pagelock, flags);
 216         if (page == 0) {
 217                 /* Port registers are valid for any page */
 218                 val = inb(iobase + PCMMIO_PORT_REG(port + 0));
 219                 val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
 220                 val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
 221         } else {
 222                 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
 223                 val = inb(iobase + PCMMIO_PAGE_REG(0));
 224                 val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
 225                 val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
 226         }
 227         spin_unlock_irqrestore(&devpriv->pagelock, flags);
 228 
 229         return val;
 230 }
 231 
 232 /*
 233  * Each channel can be individually programmed for input or output.
 234  * Writing a '0' to a channel causes the corresponding output pin
 235  * to go to a high-z state (pulled high by an external 10K resistor).
 236  * This allows it to be used as an input. When used in the input mode,
 237  * a read reflects the inverted state of the I/O pin, such that a
 238  * high on the pin will read as a '0' in the register. Writing a '1'
 239  * to a bit position causes the pin to sink current (up to 12mA),
 240  * effectively pulling it low.
 241  */
 242 static int pcmmio_dio_insn_bits(struct comedi_device *dev,
 243                                 struct comedi_subdevice *s,
 244                                 struct comedi_insn *insn,
 245                                 unsigned int *data)
 246 {
 247         /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
 248         int port = s->index == 2 ? 0 : 3;
 249         unsigned int chanmask = (1 << s->n_chan) - 1;
 250         unsigned int mask;
 251         unsigned int val;
 252 
 253         mask = comedi_dio_update_state(s, data);
 254         if (mask) {
 255                 /*
 256                  * Outputs are inverted, invert the state and
 257                  * update the channels.
 258                  *
 259                  * The s->io_bits mask makes sure the input channels
 260                  * are '0' so that the outputs pins stay in a high
 261                  * z-state.
 262                  */
 263                 val = ~s->state & chanmask;
 264                 val &= s->io_bits;
 265                 pcmmio_dio_write(dev, val, 0, port);
 266         }
 267 
 268         /* get inverted state of the channels from the port */
 269         val = pcmmio_dio_read(dev, 0, port);
 270 
 271         /* return the true state of the channels */
 272         data[1] = ~val & chanmask;
 273 
 274         return insn->n;
 275 }
 276 
 277 static int pcmmio_dio_insn_config(struct comedi_device *dev,
 278                                   struct comedi_subdevice *s,
 279                                   struct comedi_insn *insn,
 280                                   unsigned int *data)
 281 {
 282         /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
 283         int port = s->index == 2 ? 0 : 3;
 284         int ret;
 285 
 286         ret = comedi_dio_insn_config(dev, s, insn, data, 0);
 287         if (ret)
 288                 return ret;
 289 
 290         if (data[0] == INSN_CONFIG_DIO_INPUT)
 291                 pcmmio_dio_write(dev, s->io_bits, 0, port);
 292 
 293         return insn->n;
 294 }
 295 
 296 static void pcmmio_reset(struct comedi_device *dev)
 297 {
 298         /* Clear all the DIO port bits */
 299         pcmmio_dio_write(dev, 0, 0, 0);
 300         pcmmio_dio_write(dev, 0, 0, 3);
 301 
 302         /* Clear all the paged registers */
 303         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0);
 304         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
 305         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
 306 }
 307 
 308 /* devpriv->spinlock is already locked */
 309 static void pcmmio_stop_intr(struct comedi_device *dev,
 310                              struct comedi_subdevice *s)
 311 {
 312         struct pcmmio_private *devpriv = dev->private;
 313 
 314         devpriv->enabled_mask = 0;
 315         devpriv->active = 0;
 316         s->async->inttrig = NULL;
 317 
 318         /* disable all dio interrupts */
 319         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
 320 }
 321 
 322 static void pcmmio_handle_dio_intr(struct comedi_device *dev,
 323                                    struct comedi_subdevice *s,
 324                                    unsigned int triggered)
 325 {
 326         struct pcmmio_private *devpriv = dev->private;
 327         struct comedi_cmd *cmd = &s->async->cmd;
 328         unsigned int val = 0;
 329         unsigned long flags;
 330         int i;
 331 
 332         spin_lock_irqsave(&devpriv->spinlock, flags);
 333 
 334         if (!devpriv->active)
 335                 goto done;
 336 
 337         if (!(triggered & devpriv->enabled_mask))
 338                 goto done;
 339 
 340         for (i = 0; i < cmd->chanlist_len; i++) {
 341                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 342 
 343                 if (triggered & (1 << chan))
 344                         val |= (1 << i);
 345         }
 346 
 347         comedi_buf_write_samples(s, &val, 1);
 348 
 349         if (cmd->stop_src == TRIG_COUNT &&
 350             s->async->scans_done >= cmd->stop_arg)
 351                 s->async->events |= COMEDI_CB_EOA;
 352 
 353 done:
 354         spin_unlock_irqrestore(&devpriv->spinlock, flags);
 355 
 356         comedi_handle_events(dev, s);
 357 }
 358 
 359 static irqreturn_t interrupt_pcmmio(int irq, void *d)
 360 {
 361         struct comedi_device *dev = d;
 362         struct comedi_subdevice *s = dev->read_subdev;
 363         unsigned int triggered;
 364         unsigned char int_pend;
 365 
 366         /* are there any interrupts pending */
 367         int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
 368         if (!int_pend)
 369                 return IRQ_NONE;
 370 
 371         /* get, and clear, the pending interrupts */
 372         triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0);
 373         pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
 374 
 375         pcmmio_handle_dio_intr(dev, s, triggered);
 376 
 377         return IRQ_HANDLED;
 378 }
 379 
 380 /* devpriv->spinlock is already locked */
 381 static void pcmmio_start_intr(struct comedi_device *dev,
 382                               struct comedi_subdevice *s)
 383 {
 384         struct pcmmio_private *devpriv = dev->private;
 385         struct comedi_cmd *cmd = &s->async->cmd;
 386         unsigned int bits = 0;
 387         unsigned int pol_bits = 0;
 388         int i;
 389 
 390         devpriv->enabled_mask = 0;
 391         devpriv->active = 1;
 392         if (cmd->chanlist) {
 393                 for (i = 0; i < cmd->chanlist_len; i++) {
 394                         unsigned int chanspec = cmd->chanlist[i];
 395                         unsigned int chan = CR_CHAN(chanspec);
 396                         unsigned int range = CR_RANGE(chanspec);
 397                         unsigned int aref = CR_AREF(chanspec);
 398 
 399                         bits |= (1 << chan);
 400                         pol_bits |= (((aref || range) ? 1 : 0) << chan);
 401                 }
 402         }
 403         bits &= ((1 << s->n_chan) - 1);
 404         devpriv->enabled_mask = bits;
 405 
 406         /* set polarity and enable interrupts */
 407         pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0);
 408         pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0);
 409 }
 410 
 411 static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 412 {
 413         struct pcmmio_private *devpriv = dev->private;
 414         unsigned long flags;
 415 
 416         spin_lock_irqsave(&devpriv->spinlock, flags);
 417         if (devpriv->active)
 418                 pcmmio_stop_intr(dev, s);
 419         spin_unlock_irqrestore(&devpriv->spinlock, flags);
 420 
 421         return 0;
 422 }
 423 
 424 static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
 425                                      struct comedi_subdevice *s,
 426                                      unsigned int trig_num)
 427 {
 428         struct pcmmio_private *devpriv = dev->private;
 429         struct comedi_cmd *cmd = &s->async->cmd;
 430         unsigned long flags;
 431 
 432         if (trig_num != cmd->start_arg)
 433                 return -EINVAL;
 434 
 435         spin_lock_irqsave(&devpriv->spinlock, flags);
 436         s->async->inttrig = NULL;
 437         if (devpriv->active)
 438                 pcmmio_start_intr(dev, s);
 439         spin_unlock_irqrestore(&devpriv->spinlock, flags);
 440 
 441         return 1;
 442 }
 443 
 444 /*
 445  * 'do_cmd' function for an 'INTERRUPT' subdevice.
 446  */
 447 static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 448 {
 449         struct pcmmio_private *devpriv = dev->private;
 450         struct comedi_cmd *cmd = &s->async->cmd;
 451         unsigned long flags;
 452 
 453         spin_lock_irqsave(&devpriv->spinlock, flags);
 454         devpriv->active = 1;
 455 
 456         /* Set up start of acquisition. */
 457         if (cmd->start_src == TRIG_INT)
 458                 s->async->inttrig = pcmmio_inttrig_start_intr;
 459         else    /* TRIG_NOW */
 460                 pcmmio_start_intr(dev, s);
 461 
 462         spin_unlock_irqrestore(&devpriv->spinlock, flags);
 463 
 464         return 0;
 465 }
 466 
 467 static int pcmmio_cmdtest(struct comedi_device *dev,
 468                           struct comedi_subdevice *s,
 469                           struct comedi_cmd *cmd)
 470 {
 471         int err = 0;
 472 
 473         /* Step 1 : check if triggers are trivially valid */
 474 
 475         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
 476         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 477         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
 478         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 479         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 480 
 481         if (err)
 482                 return 1;
 483 
 484         /* Step 2a : make sure trigger sources are unique */
 485 
 486         err |= comedi_check_trigger_is_unique(cmd->start_src);
 487         err |= comedi_check_trigger_is_unique(cmd->stop_src);
 488 
 489         /* Step 2b : and mutually compatible */
 490 
 491         if (err)
 492                 return 2;
 493 
 494         /* Step 3: check if arguments are trivially valid */
 495 
 496         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 497         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 498         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 499         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 500                                            cmd->chanlist_len);
 501 
 502         if (cmd->stop_src == TRIG_COUNT)
 503                 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 504         else    /* TRIG_NONE */
 505                 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 506 
 507         if (err)
 508                 return 3;
 509 
 510         /* step 4: fix up any arguments */
 511 
 512         /* if (err) return 4; */
 513 
 514         return 0;
 515 }
 516 
 517 static int pcmmio_ai_eoc(struct comedi_device *dev,
 518                          struct comedi_subdevice *s,
 519                          struct comedi_insn *insn,
 520                          unsigned long context)
 521 {
 522         unsigned char status;
 523 
 524         status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
 525         if (status & PCMMIO_AI_STATUS_DATA_READY)
 526                 return 0;
 527         return -EBUSY;
 528 }
 529 
 530 static int pcmmio_ai_insn_read(struct comedi_device *dev,
 531                                struct comedi_subdevice *s,
 532                                struct comedi_insn *insn,
 533                                unsigned int *data)
 534 {
 535         unsigned long iobase = dev->iobase;
 536         unsigned int chan = CR_CHAN(insn->chanspec);
 537         unsigned int range = CR_RANGE(insn->chanspec);
 538         unsigned int aref = CR_AREF(insn->chanspec);
 539         unsigned char cmd = 0;
 540         unsigned int val;
 541         int ret;
 542         int i;
 543 
 544         /*
 545          * The PCM-MIO uses two Linear Tech LTC1859CG 8-channel A/D converters.
 546          * The devices use a full duplex serial interface which transmits and
 547          * receives data simultaneously. An 8-bit command is shifted into the
 548          * ADC interface to configure it for the next conversion. At the same
 549          * time, the data from the previous conversion is shifted out of the
 550          * device. Consequently, the conversion result is delayed by one
 551          * conversion from the command word.
 552          *
 553          * Setup the cmd for the conversions then do a dummy conversion to
 554          * flush the junk data. Then do each conversion requested by the
 555          * comedi_insn. Note that the last conversion will leave junk data
 556          * in ADC which will get flushed on the next comedi_insn.
 557          */
 558 
 559         if (chan > 7) {
 560                 chan -= 8;
 561                 iobase += PCMMIO_AI_2ND_ADC_OFFSET;
 562         }
 563 
 564         if (aref == AREF_GROUND)
 565                 cmd |= PCMMIO_AI_CMD_SE;
 566         if (chan % 2)
 567                 cmd |= PCMMIO_AI_CMD_ODD_CHAN;
 568         cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
 569         cmd |= PCMMIO_AI_CMD_RANGE(range);
 570 
 571         outb(cmd, iobase + PCMMIO_AI_CMD_REG);
 572 
 573         ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
 574         if (ret)
 575                 return ret;
 576 
 577         val = inb(iobase + PCMMIO_AI_LSB_REG);
 578         val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
 579 
 580         for (i = 0; i < insn->n; i++) {
 581                 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
 582 
 583                 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
 584                 if (ret)
 585                         return ret;
 586 
 587                 val = inb(iobase + PCMMIO_AI_LSB_REG);
 588                 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
 589 
 590                 /* bipolar data is two's complement */
 591                 if (comedi_range_is_bipolar(s, range))
 592                         val = comedi_offset_munge(s, val);
 593 
 594                 data[i] = val;
 595         }
 596 
 597         return insn->n;
 598 }
 599 
 600 static int pcmmio_ao_eoc(struct comedi_device *dev,
 601                          struct comedi_subdevice *s,
 602                          struct comedi_insn *insn,
 603                          unsigned long context)
 604 {
 605         unsigned char status;
 606 
 607         status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
 608         if (status & PCMMIO_AO_STATUS_DATA_READY)
 609                 return 0;
 610         return -EBUSY;
 611 }
 612 
 613 static int pcmmio_ao_insn_write(struct comedi_device *dev,
 614                                 struct comedi_subdevice *s,
 615                                 struct comedi_insn *insn,
 616                                 unsigned int *data)
 617 {
 618         unsigned long iobase = dev->iobase;
 619         unsigned int chan = CR_CHAN(insn->chanspec);
 620         unsigned int range = CR_RANGE(insn->chanspec);
 621         unsigned char cmd = 0;
 622         int ret;
 623         int i;
 624 
 625         /*
 626          * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
 627          * is a 4-channel converter with software-selectable output range.
 628          */
 629 
 630         if (chan > 3) {
 631                 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
 632                 iobase += PCMMIO_AO_2ND_DAC_OFFSET;
 633         } else {
 634                 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
 635         }
 636 
 637         /* set the range for the channel */
 638         outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG);
 639         outb(0, iobase + PCMMIO_AO_MSB_REG);
 640         outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG);
 641 
 642         ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
 643         if (ret)
 644                 return ret;
 645 
 646         for (i = 0; i < insn->n; i++) {
 647                 unsigned int val = data[i];
 648 
 649                 /* write the data to the channel */
 650                 outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG);
 651                 outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG);
 652                 outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE,
 653                      iobase + PCMMIO_AO_CMD_REG);
 654 
 655                 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
 656                 if (ret)
 657                         return ret;
 658 
 659                 s->readback[chan] = val;
 660         }
 661 
 662         return insn->n;
 663 }
 664 
 665 static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 666 {
 667         struct pcmmio_private *devpriv;
 668         struct comedi_subdevice *s;
 669         int ret;
 670 
 671         ret = comedi_request_region(dev, it->options[0], 32);
 672         if (ret)
 673                 return ret;
 674 
 675         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 676         if (!devpriv)
 677                 return -ENOMEM;
 678 
 679         spin_lock_init(&devpriv->pagelock);
 680         spin_lock_init(&devpriv->spinlock);
 681 
 682         pcmmio_reset(dev);
 683 
 684         if (it->options[1]) {
 685                 ret = request_irq(it->options[1], interrupt_pcmmio, 0,
 686                                   dev->board_name, dev);
 687                 if (ret == 0) {
 688                         dev->irq = it->options[1];
 689 
 690                         /* configure the interrupt routing on the board */
 691                         outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS,
 692                              dev->iobase + PCMMIO_AI_RES_ENA_REG);
 693                         outb(PCMMIO_RESOURCE_IRQ(dev->irq),
 694                              dev->iobase + PCMMIO_RESOURCE_REG);
 695                 }
 696         }
 697 
 698         ret = comedi_alloc_subdevices(dev, 4);
 699         if (ret)
 700                 return ret;
 701 
 702         /* Analog Input subdevice */
 703         s = &dev->subdevices[0];
 704         s->type         = COMEDI_SUBD_AI;
 705         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
 706         s->n_chan       = 16;
 707         s->maxdata      = 0xffff;
 708         s->range_table  = &pcmmio_ai_ranges;
 709         s->insn_read    = pcmmio_ai_insn_read;
 710 
 711         /* initialize the resource enable register by clearing it */
 712         outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
 713              dev->iobase + PCMMIO_AI_RES_ENA_REG);
 714         outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
 715              dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET);
 716 
 717         /* Analog Output subdevice */
 718         s = &dev->subdevices[1];
 719         s->type         = COMEDI_SUBD_AO;
 720         s->subdev_flags = SDF_READABLE;
 721         s->n_chan       = 8;
 722         s->maxdata      = 0xffff;
 723         s->range_table  = &pcmmio_ao_ranges;
 724         s->insn_write   = pcmmio_ao_insn_write;
 725 
 726         ret = comedi_alloc_subdev_readback(s);
 727         if (ret)
 728                 return ret;
 729 
 730         /* initialize the resource enable register by clearing it */
 731         outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG);
 732         outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET +
 733                 PCMMIO_AO_RESOURCE_ENA_REG);
 734 
 735         /* Digital I/O subdevice with interrupt support */
 736         s = &dev->subdevices[2];
 737         s->type         = COMEDI_SUBD_DIO;
 738         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 739         s->n_chan       = 24;
 740         s->maxdata      = 1;
 741         s->len_chanlist = 1;
 742         s->range_table  = &range_digital;
 743         s->insn_bits    = pcmmio_dio_insn_bits;
 744         s->insn_config  = pcmmio_dio_insn_config;
 745         if (dev->irq) {
 746                 dev->read_subdev = s;
 747                 s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL | SDF_PACKED;
 748                 s->len_chanlist = s->n_chan;
 749                 s->cancel       = pcmmio_cancel;
 750                 s->do_cmd       = pcmmio_cmd;
 751                 s->do_cmdtest   = pcmmio_cmdtest;
 752         }
 753 
 754         /* Digital I/O subdevice */
 755         s = &dev->subdevices[3];
 756         s->type         = COMEDI_SUBD_DIO;
 757         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 758         s->n_chan       = 24;
 759         s->maxdata      = 1;
 760         s->range_table  = &range_digital;
 761         s->insn_bits    = pcmmio_dio_insn_bits;
 762         s->insn_config  = pcmmio_dio_insn_config;
 763 
 764         return 0;
 765 }
 766 
 767 static struct comedi_driver pcmmio_driver = {
 768         .driver_name    = "pcmmio",
 769         .module         = THIS_MODULE,
 770         .attach         = pcmmio_attach,
 771         .detach         = comedi_legacy_detach,
 772 };
 773 module_comedi_driver(pcmmio_driver);
 774 
 775 MODULE_AUTHOR("Comedi http://www.comedi.org");
 776 MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
 777 MODULE_LICENSE("GPL");

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