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