linux/drivers/comedi/drivers/pcmmio.c
<<
>>
Prefs
   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
 158static 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
 167static 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
 178struct 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
 185static 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
 207static 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 */
 242static 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
 277static 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
 296static 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 */
 309static 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
 322static 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
 353done:
 354        spin_unlock_irqrestore(&devpriv->spinlock, flags);
 355
 356        comedi_handle_events(dev, s);
 357}
 358
 359static 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 */
 381static 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
 411static 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
 424static 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 */
 447static 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
 467static 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
 517static 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
 530static 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
 600static 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
 613static 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
 665static 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
 767static struct comedi_driver pcmmio_driver = {
 768        .driver_name    = "pcmmio",
 769        .module         = THIS_MODULE,
 770        .attach         = pcmmio_attach,
 771        .detach         = comedi_legacy_detach,
 772};
 773module_comedi_driver(pcmmio_driver);
 774
 775MODULE_AUTHOR("Comedi https://www.comedi.org");
 776MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
 777MODULE_LICENSE("GPL");
 778