linux/drivers/staging/comedi/drivers/pcmmio.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/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/*
  19Driver: pcmmio
  20Description: A driver for the PCM-MIO multifunction board
  21Devices: [Winsystems] PCM-MIO (pcmmio)
  22Author: Calin Culianu <calin@ajvar.org>
  23Updated: Wed, May 16 2007 16:21:10 -0500
  24Status: works
  25
  26A driver for the relatively new PCM-MIO multifunction board from
  27Winsystems.  This board is a PC-104 based I/O board.  It contains
  28four subdevices:
  29  subdevice 0 - 16 channels of 16-bit AI
  30  subdevice 1 - 8 channels of 16-bit AO
  31  subdevice 2 - first 24 channels of the 48 channel of DIO
  32        (with edge-triggered interrupt support)
  33  subdevice 3 - last 24 channels of the 48 channel DIO
  34        (no interrupt support for this bank of channels)
  35
  36  Some notes:
  37
  38  Synchronous reads and writes are the only things implemented for AI and AO,
  39  even though the hardware itself can do streaming acquisition, etc.  Anyone
  40  want to add asynchronous I/O for AI/AO as a feature?  Be my guest...
  41
  42  Asynchronous I/O for the DIO subdevices *is* implemented, however!  They are
  43  basically edge-triggered interrupts for any configuration of the first
  44  24 DIO-lines.
  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
  69Configuration 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#include "comedi_fc.h"
  82
  83/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
  84#define CHANS_PER_PORT   8
  85#define PORTS_PER_ASIC   6
  86#define INTR_PORTS_PER_ASIC   3
  87#define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
  88#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
  89#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
  90#define INTR_CHANS_PER_ASIC 24
  91#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
  92#define MAX_DIO_CHANS   (PORTS_PER_ASIC*1*CHANS_PER_PORT)
  93#define MAX_ASICS       (MAX_DIO_CHANS/CHANS_PER_ASIC)
  94#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
  95/* IO Memory sizes */
  96#define ASIC_IOSIZE (0x0B)
  97#define PCMMIO48_IOSIZE ASIC_IOSIZE
  98
  99/* Some offsets - these are all in the 16byte IO memory offset from
 100   the base address.  Note that there is a paging scheme to swap out
 101   offsets 0x8-0xA using the PAGELOCK register.  See the table below.
 102
 103  Register(s)       Pages        R/W?        Description
 104  --------------------------------------------------------------
 105  REG_PORTx         All          R/W         Read/Write/Configure IO
 106  REG_INT_PENDING   All          ReadOnly    Quickly see which INT_IDx has int.
 107  REG_PAGELOCK      All          WriteOnly   Select a page
 108  REG_POLx          Pg. 1 only   WriteOnly   Select edge-detection polarity
 109  REG_ENABx         Pg. 2 only   WriteOnly   Enable/Disable edge-detect. int.
 110  REG_INT_IDx       Pg. 3 only   R/W         See which ports/bits have ints.
 111 */
 112#define REG_PORT0 0x0
 113#define REG_PORT1 0x1
 114#define REG_PORT2 0x2
 115#define REG_PORT3 0x3
 116#define REG_PORT4 0x4
 117#define REG_PORT5 0x5
 118#define REG_INT_PENDING 0x6
 119#define REG_PAGELOCK 0x7        /*
 120                                 * page selector register, upper 2 bits select
 121                                 * a page and bits 0-5 are used to 'lock down'
 122                                 * a particular port above to make it readonly.
 123                                 */
 124#define REG_POL0 0x8
 125#define REG_POL1 0x9
 126#define REG_POL2 0xA
 127#define REG_ENAB0 0x8
 128#define REG_ENAB1 0x9
 129#define REG_ENAB2 0xA
 130#define REG_INT_ID0 0x8
 131#define REG_INT_ID1 0x9
 132#define REG_INT_ID2 0xA
 133
 134#define NUM_PAGED_REGS 3
 135#define NUM_PAGES 4
 136#define FIRST_PAGED_REG 0x8
 137#define REG_PAGE_BITOFFSET 6
 138#define REG_LOCK_BITOFFSET 0
 139#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
 140#define REG_LOCK_MASK (~(REG_PAGE_MASK))
 141#define PAGE_POL 1
 142#define PAGE_ENAB 2
 143#define PAGE_INT_ID 3
 144
 145static const struct comedi_lrange ranges_ai = {
 146        4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
 147};
 148
 149static const struct comedi_lrange ranges_ao = {
 150        6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
 151          RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
 152};
 153
 154/* this structure is for data unique to this subdevice.  */
 155struct pcmmio_subdev_private {
 156
 157        union {
 158                /* for DIO: mapping of halfwords (bytes)
 159                   in port/chanarray to iobase */
 160                unsigned long iobases[PORTS_PER_SUBDEV];
 161
 162                /* for AI/AO */
 163                unsigned long iobase;
 164        };
 165        union {
 166                struct {
 167
 168                        /* The below is only used for intr subdevices */
 169                        struct {
 170                                /*
 171                                 * if non-negative, this subdev has an
 172                                 * interrupt asic
 173                                 */
 174                                int asic;
 175                                /*
 176                                 * if nonnegative, the first channel id for
 177                                 * interrupts.
 178                                 */
 179                                int first_chan;
 180                                /*
 181                                 * the number of asic channels in this subdev
 182                                 * that have interrutps
 183                                 */
 184                                int num_asic_chans;
 185                                /*
 186                                 * if nonnegative, the first channel id with
 187                                 * respect to the asic that has interrupts
 188                                 */
 189                                int asic_chan;
 190                                /*
 191                                 * subdev-relative channel mask for channels
 192                                 * we are interested in
 193                                 */
 194                                int enabled_mask;
 195                                int active;
 196                                int stop_count;
 197                                int continuous;
 198                                spinlock_t spinlock;
 199                        } intr;
 200                } dio;
 201                struct {
 202                        /* the last unsigned int data written */
 203                        unsigned int shadow_samples[8];
 204                } ao;
 205        };
 206};
 207
 208/*
 209 * this structure is for data unique to this hardware driver.  If
 210 * several hardware drivers keep similar information in this structure,
 211 * feel free to suggest moving the variable to the struct comedi_device struct.
 212 */
 213struct pcmmio_private {
 214        /* stuff for DIO */
 215        struct {
 216                unsigned char pagelock; /* current page and lock */
 217                /* shadow of POLx registers */
 218                unsigned char pol[NUM_PAGED_REGS];
 219                /* shadow of ENABx registers */
 220                unsigned char enab[NUM_PAGED_REGS];
 221                int num;
 222                unsigned long iobase;
 223                unsigned int irq;
 224                spinlock_t spinlock;
 225        } asics[MAX_ASICS];
 226        struct pcmmio_subdev_private *sprivs;
 227};
 228
 229#define subpriv ((struct pcmmio_subdev_private *)s->private)
 230
 231/* DIO devices are slightly special.  Although it is possible to
 232 * implement the insn_read/insn_write interface, it is much more
 233 * useful to applications if you implement the insn_bits interface.
 234 * This allows packed reading/writing of the DIO channels.  The
 235 * comedi core can convert between insn_bits and insn_read/write */
 236static int pcmmio_dio_insn_bits(struct comedi_device *dev,
 237                                struct comedi_subdevice *s,
 238                                struct comedi_insn *insn, unsigned int *data)
 239{
 240        int byte_no;
 241
 242        /* NOTE:
 243           reading a 0 means this channel was high
 244           writine a 0 sets the channel high
 245           reading a 1 means this channel was low
 246           writing a 1 means set this channel low
 247
 248           Therefore everything is always inverted. */
 249
 250        /* The insn data is a mask in data[0] and the new data
 251         * in data[1], each channel cooresponding to a bit. */
 252
 253#ifdef DAMMIT_ITS_BROKEN
 254        /* DEBUG */
 255        printk(KERN_DEBUG "write mask: %08x  data: %08x\n", data[0], data[1]);
 256#endif
 257
 258        s->state = 0;
 259
 260        for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
 261                /* address of 8-bit port */
 262                unsigned long ioaddr = subpriv->iobases[byte_no],
 263                    /* bit offset of port in 32-bit doubleword */
 264                    offset = byte_no * 8;
 265                /* this 8-bit port's data */
 266                unsigned char byte = 0,
 267                    /* The write mask for this port (if any) */
 268                    write_mask_byte = (data[0] >> offset) & 0xff,
 269                    /* The data byte for this port */
 270                    data_byte = (data[1] >> offset) & 0xff;
 271
 272                byte = inb(ioaddr);     /* read all 8-bits for this port */
 273
 274#ifdef DAMMIT_ITS_BROKEN
 275                /* DEBUG */
 276                printk
 277                    (KERN_DEBUG "byte %d wmb %02x db %02x offset %02d io %04x,"
 278                     " data_in %02x ", byte_no, (unsigned)write_mask_byte,
 279                     (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
 280#endif
 281
 282                if (write_mask_byte) {
 283                        /*
 284                         * this byte has some write_bits
 285                         * -- so set the output lines
 286                         */
 287                        /* clear bits for write mask */
 288                        byte &= ~write_mask_byte;
 289                        /* set to inverted data_byte */
 290                        byte |= ~data_byte & write_mask_byte;
 291                        /* Write out the new digital output state */
 292                        outb(byte, ioaddr);
 293                }
 294#ifdef DAMMIT_ITS_BROKEN
 295                /* DEBUG */
 296                printk(KERN_DEBUG "data_out_byte %02x\n", (unsigned)byte);
 297#endif
 298                /* save the digital input lines for this byte.. */
 299                s->state |= ((unsigned int)byte) << offset;
 300        }
 301
 302        /* now return the DIO lines to data[1] - note they came inverted! */
 303        data[1] = ~s->state;
 304
 305#ifdef DAMMIT_ITS_BROKEN
 306        /* DEBUG */
 307        printk(KERN_DEBUG "s->state %08x data_out %08x\n", s->state, data[1]);
 308#endif
 309
 310        return insn->n;
 311}
 312
 313static int pcmmio_dio_insn_config(struct comedi_device *dev,
 314                                  struct comedi_subdevice *s,
 315                                  struct comedi_insn *insn,
 316                                  unsigned int *data)
 317{
 318        unsigned int chan = CR_CHAN(insn->chanspec);
 319        int byte_no = chan / 8;
 320        int bit_no = chan % 8;
 321        int ret;
 322
 323        ret = comedi_dio_insn_config(dev, s, insn, data, 0);
 324        if (ret)
 325                return ret;
 326
 327        if (data[0] == INSN_CONFIG_DIO_INPUT) {
 328                unsigned long ioaddr = subpriv->iobases[byte_no];
 329                unsigned char val;
 330
 331                val = inb(ioaddr);
 332                val &= ~(1 << bit_no);
 333                outb(val, ioaddr);
 334        }
 335
 336        return insn->n;
 337}
 338
 339static void switch_page(struct comedi_device *dev, int asic, int page)
 340{
 341        struct pcmmio_private *devpriv = dev->private;
 342
 343        if (asic < 0 || asic >= 1)
 344                return;         /* paranoia */
 345        if (page < 0 || page >= NUM_PAGES)
 346                return;         /* more paranoia */
 347
 348        devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
 349        devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
 350
 351        /* now write out the shadow register */
 352        outb(devpriv->asics[asic].pagelock,
 353             devpriv->asics[asic].iobase + REG_PAGELOCK);
 354}
 355
 356static void init_asics(struct comedi_device *dev)
 357{                               /* sets up an
 358                                   ASIC chip to defaults */
 359        struct pcmmio_private *devpriv = dev->private;
 360        int asic;
 361
 362        for (asic = 0; asic < 1; ++asic) {
 363                int port, page;
 364                unsigned long baseaddr = devpriv->asics[asic].iobase;
 365
 366                switch_page(dev, asic, 0);      /* switch back to page 0 */
 367
 368                /* first, clear all the DIO port bits */
 369                for (port = 0; port < PORTS_PER_ASIC; ++port)
 370                        outb(0, baseaddr + REG_PORT0 + port);
 371
 372                /* Next, clear all the paged registers for each page */
 373                for (page = 1; page < NUM_PAGES; ++page) {
 374                        int reg;
 375                        /* now clear all the paged registers */
 376                        switch_page(dev, asic, page);
 377                        for (reg = FIRST_PAGED_REG;
 378                             reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
 379                                outb(0, baseaddr + reg);
 380                }
 381
 382                /* DEBUG  set rising edge interrupts on port0 of both asics */
 383                /*switch_page(dev, asic, PAGE_POL);
 384                   outb(0xff, baseaddr + REG_POL0);
 385                   switch_page(dev, asic, PAGE_ENAB);
 386                   outb(0xff, baseaddr + REG_ENAB0); */
 387                /* END DEBUG */
 388
 389                /* switch back to default page 0 */
 390                switch_page(dev, asic, 0);
 391        }
 392}
 393
 394#ifdef notused
 395static void lock_port(struct comedi_device *dev, int asic, int port)
 396{
 397        struct pcmmio_private *devpriv = dev->private;
 398
 399        if (asic < 0 || asic >= 1)
 400                return;         /* paranoia */
 401        if (port < 0 || port >= PORTS_PER_ASIC)
 402                return;         /* more paranoia */
 403
 404        devpriv->asics[asic].pagelock |= 0x1 << port;
 405        /* now write out the shadow register */
 406        outb(devpriv->asics[asic].pagelock,
 407             devpriv->asics[asic].iobase + REG_PAGELOCK);
 408        return;
 409}
 410
 411static void unlock_port(struct comedi_device *dev, int asic, int port)
 412{
 413        struct pcmmio_private *devpriv = dev->private;
 414
 415        if (asic < 0 || asic >= 1)
 416                return;         /* paranoia */
 417        if (port < 0 || port >= PORTS_PER_ASIC)
 418                return;         /* more paranoia */
 419        devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
 420        /* now write out the shadow register */
 421        outb(devpriv->asics[asic].pagelock,
 422             devpriv->asics[asic].iobase + REG_PAGELOCK);
 423}
 424#endif /* notused */
 425
 426static void pcmmio_stop_intr(struct comedi_device *dev,
 427                             struct comedi_subdevice *s)
 428{
 429        struct pcmmio_private *devpriv = dev->private;
 430        int nports, firstport, asic, port;
 431
 432        asic = subpriv->dio.intr.asic;
 433        if (asic < 0)
 434                return;         /* not an interrupt subdev */
 435
 436        subpriv->dio.intr.enabled_mask = 0;
 437        subpriv->dio.intr.active = 0;
 438        s->async->inttrig = NULL;
 439        nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
 440        firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
 441        switch_page(dev, asic, PAGE_ENAB);
 442        for (port = firstport; port < firstport + nports; ++port) {
 443                /* disable all intrs for this subdev.. */
 444                outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
 445        }
 446}
 447
 448static irqreturn_t interrupt_pcmmio(int irq, void *d)
 449{
 450        int asic, got1 = 0;
 451        struct comedi_device *dev = (struct comedi_device *)d;
 452        struct pcmmio_private *devpriv = dev->private;
 453        int i;
 454
 455        for (asic = 0; asic < MAX_ASICS; ++asic) {
 456                if (irq == devpriv->asics[asic].irq) {
 457                        unsigned long flags;
 458                        unsigned triggered = 0;
 459                        unsigned long iobase = devpriv->asics[asic].iobase;
 460                        /* it is an interrupt for ASIC #asic */
 461                        unsigned char int_pend;
 462
 463                        spin_lock_irqsave(&devpriv->asics[asic].spinlock,
 464                                          flags);
 465
 466                        int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
 467
 468                        if (int_pend) {
 469                                int port;
 470                                for (port = 0; port < INTR_PORTS_PER_ASIC;
 471                                     ++port) {
 472                                        if (int_pend & (0x1 << port)) {
 473                                                unsigned char
 474                                                    io_lines_with_edges = 0;
 475                                                switch_page(dev, asic,
 476                                                            PAGE_INT_ID);
 477                                                io_lines_with_edges =
 478                                                    inb(iobase +
 479                                                        REG_INT_ID0 + port);
 480
 481                                                if (io_lines_with_edges)
 482                                                        /*
 483                                                         * clear pending
 484                                                         * interrupt
 485                                                         */
 486                                                        outb(0, iobase +
 487                                                             REG_INT_ID0 +
 488                                                             port);
 489
 490                                                triggered |=
 491                                                    io_lines_with_edges <<
 492                                                    port * 8;
 493                                        }
 494                                }
 495
 496                                ++got1;
 497                        }
 498
 499                        spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
 500                                               flags);
 501
 502                        if (triggered) {
 503                                struct comedi_subdevice *s;
 504                                /*
 505                                 * TODO here: dispatch io lines to subdevs
 506                                 * with commands..
 507                                 */
 508                                printk
 509                                    (KERN_DEBUG "got edge detect interrupt %d asic %d which_chans: %06x\n",
 510                                     irq, asic, triggered);
 511                                for (i = 2; i < dev->n_subdevices; i++) {
 512                                        s = &dev->subdevices[i];
 513                                        /*
 514                                         * this is an interrupt subdev,
 515                                         * and it matches this asic!
 516                                         */
 517                                        if (subpriv->dio.intr.asic == asic) {
 518                                                unsigned long flags;
 519                                                unsigned oldevents;
 520
 521                                                spin_lock_irqsave(&subpriv->dio.
 522                                                                  intr.spinlock,
 523                                                                  flags);
 524
 525                                                oldevents = s->async->events;
 526
 527                                                if (subpriv->dio.intr.active) {
 528                                                        unsigned mytrig =
 529                                                            ((triggered >>
 530                                                              subpriv->dio.intr.asic_chan)
 531                                                             &
 532                                                             ((0x1 << subpriv->
 533                                                               dio.intr.
 534                                                               num_asic_chans) -
 535                                                              1)) << subpriv->
 536                                                            dio.intr.first_chan;
 537                                                        if (mytrig &
 538                                                            subpriv->dio.
 539                                                            intr.enabled_mask) {
 540                                                                unsigned int val
 541                                                                    = 0;
 542                                                                unsigned int n,
 543                                                                    ch, len;
 544
 545                                                                len =
 546                                                                    s->
 547                                                                    async->cmd.chanlist_len;
 548                                                                for (n = 0;
 549                                                                     n < len;
 550                                                                     n++) {
 551                                                                        ch = CR_CHAN(s->async->cmd.chanlist[n]);
 552                                                                        if (mytrig & (1U << ch))
 553                                                                                val |= (1U << n);
 554                                                                }
 555                                                                /* Write the scan to the buffer. */
 556                                                                if (comedi_buf_put(s->async, ((short *)&val)[0])
 557                                                                    &&
 558                                                                    comedi_buf_put
 559                                                                    (s->async,
 560                                                                     ((short *)
 561                                                                      &val)[1])) {
 562                                                                        s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
 563                                                                } else {
 564                                                                        /* Overflow! Stop acquisition!! */
 565                                                                        /* TODO: STOP_ACQUISITION_CALL_HERE!! */
 566                                                                        pcmmio_stop_intr
 567                                                                            (dev,
 568                                                                             s);
 569                                                                }
 570
 571                                                                /* Check for end of acquisition. */
 572                                                                if (!subpriv->dio.intr.continuous) {
 573                                                                        /* stop_src == TRIG_COUNT */
 574                                                                        if (subpriv->dio.intr.stop_count > 0) {
 575                                                                                subpriv->dio.intr.stop_count--;
 576                                                                                if (subpriv->dio.intr.stop_count == 0) {
 577                                                                                        s->async->events |= COMEDI_CB_EOA;
 578                                                                                        /* TODO: STOP_ACQUISITION_CALL_HERE!! */
 579                                                                                        pcmmio_stop_intr
 580                                                                                            (dev,
 581                                                                                             s);
 582                                                                                }
 583                                                                        }
 584                                                                }
 585                                                        }
 586                                                }
 587
 588                                                spin_unlock_irqrestore
 589                                                    (&subpriv->dio.intr.
 590                                                     spinlock, flags);
 591
 592                                                if (oldevents !=
 593                                                    s->async->events) {
 594                                                        comedi_event(dev, s);
 595                                                }
 596
 597                                        }
 598
 599                                }
 600                        }
 601
 602                }
 603        }
 604        if (!got1)
 605                return IRQ_NONE;        /* interrupt from other source */
 606        return IRQ_HANDLED;
 607}
 608
 609static int pcmmio_start_intr(struct comedi_device *dev,
 610                             struct comedi_subdevice *s)
 611{
 612        struct pcmmio_private *devpriv = dev->private;
 613
 614        if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
 615                /* An empty acquisition! */
 616                s->async->events |= COMEDI_CB_EOA;
 617                subpriv->dio.intr.active = 0;
 618                return 1;
 619        } else {
 620                unsigned bits = 0, pol_bits = 0, n;
 621                int nports, firstport, asic, port;
 622                struct comedi_cmd *cmd = &s->async->cmd;
 623
 624                asic = subpriv->dio.intr.asic;
 625                if (asic < 0)
 626                        return 1;       /* not an interrupt
 627                                           subdev */
 628                subpriv->dio.intr.enabled_mask = 0;
 629                subpriv->dio.intr.active = 1;
 630                nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
 631                firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
 632                if (cmd->chanlist) {
 633                        for (n = 0; n < cmd->chanlist_len; n++) {
 634                                bits |= (1U << CR_CHAN(cmd->chanlist[n]));
 635                                pol_bits |= (CR_AREF(cmd->chanlist[n])
 636                                             || CR_RANGE(cmd->
 637                                                         chanlist[n]) ? 1U : 0U)
 638                                    << CR_CHAN(cmd->chanlist[n]);
 639                        }
 640                }
 641                bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
 642                         1) << subpriv->dio.intr.first_chan;
 643                subpriv->dio.intr.enabled_mask = bits;
 644
 645                {
 646                        /*
 647                         * the below code configures the board
 648                         * to use a specific IRQ from 0-15.
 649                         */
 650                        unsigned char b;
 651                        /*
 652                         * set resource enable register
 653                         * to enable IRQ operation
 654                         */
 655                        outb(1 << 4, dev->iobase + 3);
 656                        /* set bits 0-3 of b to the irq number from 0-15 */
 657                        b = dev->irq & ((1 << 4) - 1);
 658                        outb(b, dev->iobase + 2);
 659                        /* done, we told the board what irq to use */
 660                }
 661
 662                switch_page(dev, asic, PAGE_ENAB);
 663                for (port = firstport; port < firstport + nports; ++port) {
 664                        unsigned enab =
 665                            bits >> (subpriv->dio.intr.first_chan + (port -
 666                                                                     firstport)
 667                                     * 8) & 0xff, pol =
 668                            pol_bits >> (subpriv->dio.intr.first_chan +
 669                                         (port - firstport) * 8) & 0xff;
 670                        /* set enab intrs for this subdev.. */
 671                        outb(enab,
 672                             devpriv->asics[asic].iobase + REG_ENAB0 + port);
 673                        switch_page(dev, asic, PAGE_POL);
 674                        outb(pol,
 675                             devpriv->asics[asic].iobase + REG_ENAB0 + port);
 676                }
 677        }
 678        return 0;
 679}
 680
 681static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 682{
 683        unsigned long flags;
 684
 685        spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
 686        if (subpriv->dio.intr.active)
 687                pcmmio_stop_intr(dev, s);
 688        spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
 689
 690        return 0;
 691}
 692
 693/*
 694 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
 695 */
 696static int
 697pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
 698                          unsigned int trignum)
 699{
 700        unsigned long flags;
 701        int event = 0;
 702
 703        if (trignum != 0)
 704                return -EINVAL;
 705
 706        spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
 707        s->async->inttrig = NULL;
 708        if (subpriv->dio.intr.active)
 709                event = pcmmio_start_intr(dev, s);
 710        spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
 711
 712        if (event)
 713                comedi_event(dev, s);
 714
 715        return 1;
 716}
 717
 718/*
 719 * 'do_cmd' function for an 'INTERRUPT' subdevice.
 720 */
 721static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 722{
 723        struct comedi_cmd *cmd = &s->async->cmd;
 724        unsigned long flags;
 725        int event = 0;
 726
 727        spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
 728        subpriv->dio.intr.active = 1;
 729
 730        /* Set up end of acquisition. */
 731        switch (cmd->stop_src) {
 732        case TRIG_COUNT:
 733                subpriv->dio.intr.continuous = 0;
 734                subpriv->dio.intr.stop_count = cmd->stop_arg;
 735                break;
 736        default:
 737                /* TRIG_NONE */
 738                subpriv->dio.intr.continuous = 1;
 739                subpriv->dio.intr.stop_count = 0;
 740                break;
 741        }
 742
 743        /* Set up start of acquisition. */
 744        switch (cmd->start_src) {
 745        case TRIG_INT:
 746                s->async->inttrig = pcmmio_inttrig_start_intr;
 747                break;
 748        default:
 749                /* TRIG_NOW */
 750                event = pcmmio_start_intr(dev, s);
 751                break;
 752        }
 753        spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
 754
 755        if (event)
 756                comedi_event(dev, s);
 757
 758        return 0;
 759}
 760
 761static int pcmmio_cmdtest(struct comedi_device *dev,
 762                          struct comedi_subdevice *s,
 763                          struct comedi_cmd *cmd)
 764{
 765        int err = 0;
 766
 767        /* Step 1 : check if triggers are trivially valid */
 768
 769        err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
 770        err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 771        err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
 772        err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 773        err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 774
 775        if (err)
 776                return 1;
 777
 778        /* Step 2a : make sure trigger sources are unique */
 779
 780        err |= cfc_check_trigger_is_unique(cmd->start_src);
 781        err |= cfc_check_trigger_is_unique(cmd->stop_src);
 782
 783        /* Step 2b : and mutually compatible */
 784
 785        if (err)
 786                return 2;
 787
 788        /* Step 3: check if arguments are trivially valid */
 789
 790        err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
 791        err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 792        err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
 793        err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
 794
 795        switch (cmd->stop_src) {
 796        case TRIG_COUNT:
 797                /* any count allowed */
 798                break;
 799        case TRIG_NONE:
 800                err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
 801                break;
 802        default:
 803                break;
 804        }
 805
 806        if (err)
 807                return 3;
 808
 809        /* step 4: fix up any arguments */
 810
 811        /* if (err) return 4; */
 812
 813        return 0;
 814}
 815
 816static int adc_wait_ready(unsigned long iobase)
 817{
 818        unsigned long retry = 100000;
 819        while (retry--)
 820                if (inb(iobase + 3) & 0x80)
 821                        return 0;
 822        return 1;
 823}
 824
 825/* All this is for AI and AO */
 826static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 827                    struct comedi_insn *insn, unsigned int *data)
 828{
 829        int n;
 830        unsigned long iobase = subpriv->iobase;
 831
 832        /*
 833           1. write the CMD byte (to BASE+2)
 834           2. read junk lo byte (BASE+0)
 835           3. read junk hi byte (BASE+1)
 836           4. (mux settled so) write CMD byte again (BASE+2)
 837           5. read valid lo byte(BASE+0)
 838           6. read valid hi byte(BASE+1)
 839
 840           Additionally note that the BASE += 4 if the channel >= 8
 841         */
 842
 843        /* convert n samples */
 844        for (n = 0; n < insn->n; n++) {
 845                unsigned chan = CR_CHAN(insn->chanspec), range =
 846                    CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
 847                unsigned char command_byte = 0;
 848                unsigned iooffset = 0;
 849                short sample, adc_adjust = 0;
 850
 851                if (chan > 7)
 852                        chan -= 8, iooffset = 4;        /*
 853                                                         * use the second dword
 854                                                         * for channels > 7
 855                                                         */
 856
 857                if (aref != AREF_DIFF) {
 858                        aref = AREF_GROUND;
 859                        command_byte |= 1 << 7; /*
 860                                                 * set bit 7 to indicate
 861                                                 * single-ended
 862                                                 */
 863                }
 864                if (range < 2)
 865                        adc_adjust = 0x8000;    /*
 866                                                 * bipolar ranges
 867                                                 * (-5,5 .. -10,10 need to be
 868                                                 * adjusted -- that is.. they
 869                                                 * need to wrap around by
 870                                                 * adding 0x8000
 871                                                 */
 872
 873                if (chan % 2) {
 874                        command_byte |= 1 << 6; /*
 875                                                 * odd-numbered channels
 876                                                 * have bit 6 set
 877                                                 */
 878                }
 879
 880                /* select the channel, bits 4-5 == chan/2 */
 881                command_byte |= ((chan / 2) & 0x3) << 4;
 882
 883                /* set the range, bits 2-3 */
 884                command_byte |= (range & 0x3) << 2;
 885
 886                /* need to do this twice to make sure mux settled */
 887                /* chan/range/aref select */
 888                outb(command_byte, iobase + iooffset + 2);
 889
 890                /* wait for the adc to say it finised the conversion */
 891                adc_wait_ready(iobase + iooffset);
 892
 893                /* select the chan/range/aref AGAIN */
 894                outb(command_byte, iobase + iooffset + 2);
 895
 896                adc_wait_ready(iobase + iooffset);
 897
 898                /* read data lo byte */
 899                sample = inb(iobase + iooffset + 0);
 900
 901                /* read data hi byte */
 902                sample |= inb(iobase + iooffset + 1) << 8;
 903                sample += adc_adjust;   /* adjustment .. munge data */
 904                data[n] = sample;
 905        }
 906        /* return the number of samples read/written */
 907        return n;
 908}
 909
 910static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
 911                    struct comedi_insn *insn, unsigned int *data)
 912{
 913        int n;
 914        for (n = 0; n < insn->n; n++) {
 915                unsigned chan = CR_CHAN(insn->chanspec);
 916                if (chan < s->n_chan)
 917                        data[n] = subpriv->ao.shadow_samples[chan];
 918        }
 919        return n;
 920}
 921
 922static int wait_dac_ready(unsigned long iobase)
 923{
 924        unsigned long retry = 100000L;
 925
 926        /* This may seem like an absurd way to handle waiting and violates the
 927           "no busy waiting" policy. The fact is that the hardware is
 928           normally so fast that we usually only need one time through the loop
 929           anyway. The longer timeout is for rare occasions and for detecting
 930           non-existent hardware.  */
 931
 932        while (retry--) {
 933                if (inb(iobase + 3) & 0x80)
 934                        return 0;
 935
 936        }
 937        return 1;
 938}
 939
 940static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
 941                    struct comedi_insn *insn, unsigned int *data)
 942{
 943        int n;
 944        unsigned iobase = subpriv->iobase, iooffset = 0;
 945
 946        for (n = 0; n < insn->n; n++) {
 947                unsigned chan = CR_CHAN(insn->chanspec), range =
 948                    CR_RANGE(insn->chanspec);
 949                if (chan < s->n_chan) {
 950                        unsigned char command_byte = 0, range_byte =
 951                            range & ((1 << 4) - 1);
 952                        if (chan >= 4)
 953                                chan -= 4, iooffset += 4;
 954                        /* set the range.. */
 955                        outb(range_byte, iobase + iooffset + 0);
 956                        outb(0, iobase + iooffset + 1);
 957
 958                        /* tell it to begin */
 959                        command_byte = (chan << 1) | 0x60;
 960                        outb(command_byte, iobase + iooffset + 2);
 961
 962                        wait_dac_ready(iobase + iooffset);
 963
 964                        /* low order byte */
 965                        outb(data[n] & 0xff, iobase + iooffset + 0);
 966
 967                        /* high order byte */
 968                        outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
 969
 970                        /*
 971                         * set bit 4 of command byte to indicate
 972                         * data is loaded and trigger conversion
 973                         */
 974                        command_byte = 0x70 | (chan << 1);
 975                        /* trigger converion */
 976                        outb(command_byte, iobase + iooffset + 2);
 977
 978                        wait_dac_ready(iobase + iooffset);
 979
 980                        /* save to shadow register for ao_rinsn */
 981                        subpriv->ao.shadow_samples[chan] = data[n];
 982                }
 983        }
 984        return n;
 985}
 986
 987static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 988{
 989        struct pcmmio_private *devpriv;
 990        struct comedi_subdevice *s;
 991        int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
 992            thisasic_chanct = 0;
 993        unsigned int irq[MAX_ASICS];
 994        int ret;
 995
 996        irq[0] = it->options[1];
 997
 998        ret = comedi_request_region(dev, it->options[0], 32);
 999        if (ret)
1000                return ret;
1001
1002        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1003        if (!devpriv)
1004                return -ENOMEM;
1005
1006        for (asic = 0; asic < MAX_ASICS; ++asic) {
1007                devpriv->asics[asic].num = asic;
1008                devpriv->asics[asic].iobase =
1009                    dev->iobase + 16 + asic * ASIC_IOSIZE;
1010                /*
1011                 * this gets actually set at the end of this function when we
1012                 * request_irqs
1013                 */
1014                devpriv->asics[asic].irq = 0;
1015                spin_lock_init(&devpriv->asics[asic].spinlock);
1016        }
1017
1018        chans_left = CHANS_PER_ASIC * 1;
1019        n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
1020        n_subdevs = n_dio_subdevs + 2;
1021        devpriv->sprivs =
1022            kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
1023                    GFP_KERNEL);
1024        if (!devpriv->sprivs) {
1025                printk(KERN_ERR "comedi%d: cannot allocate subdevice private data structures\n",
1026                                dev->minor);
1027                return -ENOMEM;
1028        }
1029
1030        ret = comedi_alloc_subdevices(dev, n_subdevs);
1031        if (ret)
1032                return ret;
1033
1034        /* First, AI */
1035        s = &dev->subdevices[0];
1036        s->private = &devpriv->sprivs[0];
1037        s->maxdata = 0xffff;
1038        s->range_table = &ranges_ai;
1039        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
1040        s->type = COMEDI_SUBD_AI;
1041        s->n_chan = 16;
1042        s->len_chanlist = s->n_chan;
1043        s->insn_read = ai_rinsn;
1044        subpriv->iobase = dev->iobase + 0;
1045        /* initialize the resource enable register by clearing it */
1046        outb(0, subpriv->iobase + 3);
1047        outb(0, subpriv->iobase + 4 + 3);
1048
1049        /* Next, AO */
1050        s = &dev->subdevices[1];
1051        s->private = &devpriv->sprivs[1];
1052        s->maxdata = 0xffff;
1053        s->range_table = &ranges_ao;
1054        s->subdev_flags = SDF_READABLE;
1055        s->type = COMEDI_SUBD_AO;
1056        s->n_chan = 8;
1057        s->len_chanlist = s->n_chan;
1058        s->insn_read = ao_rinsn;
1059        s->insn_write = ao_winsn;
1060        subpriv->iobase = dev->iobase + 8;
1061        /* initialize the resource enable register by clearing it */
1062        outb(0, subpriv->iobase + 3);
1063        outb(0, subpriv->iobase + 4 + 3);
1064
1065        port = 0;
1066        asic = 0;
1067        for (sdev_no = 2; sdev_no < dev->n_subdevices; ++sdev_no) {
1068                int byte_no;
1069
1070                s = &dev->subdevices[sdev_no];
1071                s->private = &devpriv->sprivs[sdev_no];
1072                s->maxdata = 1;
1073                s->range_table = &range_digital;
1074                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1075                s->type = COMEDI_SUBD_DIO;
1076                s->insn_bits = pcmmio_dio_insn_bits;
1077                s->insn_config = pcmmio_dio_insn_config;
1078                s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
1079                subpriv->dio.intr.asic = -1;
1080                subpriv->dio.intr.first_chan = -1;
1081                subpriv->dio.intr.asic_chan = -1;
1082                subpriv->dio.intr.num_asic_chans = -1;
1083                subpriv->dio.intr.active = 0;
1084                s->len_chanlist = 1;
1085
1086                /* save the ioport address for each 'port' of 8 channels in the
1087                   subdevice */
1088                for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
1089                        if (port >= PORTS_PER_ASIC) {
1090                                port = 0;
1091                                ++asic;
1092                                thisasic_chanct = 0;
1093                        }
1094                        subpriv->iobases[byte_no] =
1095                            devpriv->asics[asic].iobase + port;
1096
1097                        if (thisasic_chanct <
1098                            CHANS_PER_PORT * INTR_PORTS_PER_ASIC
1099                            && subpriv->dio.intr.asic < 0) {
1100                                /*
1101                                 * this is an interrupt subdevice,
1102                                 * so setup the struct
1103                                 */
1104                                subpriv->dio.intr.asic = asic;
1105                                subpriv->dio.intr.active = 0;
1106                                subpriv->dio.intr.stop_count = 0;
1107                                subpriv->dio.intr.first_chan = byte_no * 8;
1108                                subpriv->dio.intr.asic_chan = thisasic_chanct;
1109                                subpriv->dio.intr.num_asic_chans =
1110                                    s->n_chan - subpriv->dio.intr.first_chan;
1111                                s->cancel = pcmmio_cancel;
1112                                s->do_cmd = pcmmio_cmd;
1113                                s->do_cmdtest = pcmmio_cmdtest;
1114                                s->len_chanlist =
1115                                    subpriv->dio.intr.num_asic_chans;
1116                        }
1117                        thisasic_chanct += CHANS_PER_PORT;
1118                }
1119                spin_lock_init(&subpriv->dio.intr.spinlock);
1120
1121                chans_left -= s->n_chan;
1122
1123                if (!chans_left) {
1124                        /*
1125                         * reset the asic to our first asic,
1126                         * to do intr subdevs
1127                         */
1128                        asic = 0;
1129                        port = 0;
1130                }
1131
1132        }
1133
1134        init_asics(dev);        /* clear out all the registers, basically */
1135
1136        for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
1137                if (irq[asic]
1138                    && request_irq(irq[asic], interrupt_pcmmio,
1139                                   IRQF_SHARED, dev->board_name, dev)) {
1140                        int i;
1141                        /* unroll the allocated irqs.. */
1142                        for (i = asic - 1; i >= 0; --i) {
1143                                free_irq(irq[i], dev);
1144                                devpriv->asics[i].irq = irq[i] = 0;
1145                        }
1146                        irq[asic] = 0;
1147                }
1148                devpriv->asics[asic].irq = irq[asic];
1149        }
1150
1151        return 1;
1152}
1153
1154static void pcmmio_detach(struct comedi_device *dev)
1155{
1156        struct pcmmio_private *devpriv = dev->private;
1157        int i;
1158
1159        if (devpriv) {
1160                for (i = 0; i < MAX_ASICS; ++i) {
1161                        if (devpriv->asics[i].irq)
1162                                free_irq(devpriv->asics[i].irq, dev);
1163                }
1164                kfree(devpriv->sprivs);
1165        }
1166        comedi_legacy_detach(dev);
1167}
1168
1169static struct comedi_driver pcmmio_driver = {
1170        .driver_name    = "pcmmio",
1171        .module         = THIS_MODULE,
1172        .attach         = pcmmio_attach,
1173        .detach         = pcmmio_detach,
1174};
1175module_comedi_driver(pcmmio_driver);
1176
1177MODULE_AUTHOR("Comedi http://www.comedi.org");
1178MODULE_DESCRIPTION("Comedi low-level driver");
1179MODULE_LICENSE("GPL");
1180