linux/drivers/staging/comedi/drivers/pcmuio.c
<<
>>
Prefs
   1/*
   2    comedi/drivers/pcmuio.c
   3    Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
   4
   5    COMEDI - Linux Control and Measurement Device Interface
   6    Copyright (C) 2006 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    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21*/
  22/*
  23Driver: pcmuio
  24Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
  25Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
  26Author: Calin Culianu <calin@ajvar.org>
  27Updated: Fri, 13 Jan 2006 12:01:01 -0500
  28Status: works
  29
  30A driver for the relatively straightforward-to-program PCM-UIO48A and
  31PCM-UIO96A boards from Winsystems.  These boards use either one or two
  32(in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
  33This chip is interesting in that each I/O line is individually
  34programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
  35on a per-channel basis).  Also, each chip supports edge-triggered
  36interrupts for the first 24 I/O lines.  Of course, since the
  3796-channel version of the board has two ASICs, it can detect polarity
  38changes on up to 48 I/O lines.  Since this is essentially an (non-PnP)
  39ISA board, I/O Address and IRQ selection are done through jumpers on
  40the board.  You need to pass that information to this driver as the
  41first and second comedi_config option, respectively.  Note that the
  4248-channel version uses 16 bytes of IO memory and the 96-channel
  43version uses 32-bytes (in case you are worried about conflicts).  The
  4448-channel board is split into two 24-channel comedi subdevices.
  45The 96-channel board is split into 4 24-channel DIO subdevices.
  46
  47Note that IRQ support has been added, but it is untested.
  48
  49To use edge-detection IRQ support, pass the IRQs of both ASICS
  50(for the 96 channel version) or just 1 ASIC (for 48-channel version).
  51Then, use use comedi_commands with TRIG_NOW.
  52Your callback will be called each time an edge is triggered, and the data
  53values will be two sample_t's, which should be concatenated to form one
  5432-bit unsigned int.  This value is the mask of channels that had
  55edges detected from your channel list.  Note that the bits positions
  56in the mask correspond to positions in your chanlist when you specified
  57the command and *not* channel id's!
  58
  59To set the polarity of the edge-detection interrupts pass a nonzero value for
  60either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
  61CR_RANGE and CR_AREF if you want edge-down polarity.
  62
  63In the 48-channel version:
  64
  65On subdev 0, the first 24 channels channels are edge-detect channels.
  66
  67In the 96-channel board you have the collowing channels that can do edge detection:
  68
  69subdev 0, channels 0-24  (first 24 channels of 1st ASIC)
  70subdev 2, channels 0-24  (first 24 channels of 2nd ASIC)
  71
  72Configuration Options:
  73  [0] - I/O port base address
  74  [1] - IRQ (for first ASIC, or first 24 channels)
  75  [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!)
  76*/
  77
  78#include <linux/interrupt.h>
  79#include "../comedidev.h"
  80#include "pcm_common.h"
  81
  82#include <linux/pci.h>          /* for PCI devices */
  83
  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*2*CHANS_PER_PORT)
  93#define MAX_ASICS       (MAX_DIO_CHANS/CHANS_PER_ASIC)
  94#define SDEV_NO ((int)(s - dev->subdevices))
  95#define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
  96/* IO Memory sizes */
  97#define ASIC_IOSIZE (0x10)
  98#define PCMUIO48_IOSIZE ASIC_IOSIZE
  99#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
 100
 101/* Some offsets - these are all in the 16byte IO memory offset from
 102   the base address.  Note that there is a paging scheme to swap out
 103   offsets 0x8-0xA using the PAGELOCK register.  See the table below.
 104
 105  Register(s)       Pages        R/W?        Description
 106  --------------------------------------------------------------
 107  REG_PORTx         All          R/W         Read/Write/Configure IO
 108  REG_INT_PENDING   All          ReadOnly    Quickly see which INT_IDx has int.
 109  REG_PAGELOCK      All          WriteOnly   Select a page
 110  REG_POLx          Pg. 1 only   WriteOnly   Select edge-detection polarity
 111  REG_ENABx         Pg. 2 only   WriteOnly   Enable/Disable edge-detect. int.
 112  REG_INT_IDx       Pg. 3 only   R/W         See which ports/bits have ints.
 113 */
 114#define REG_PORT0 0x0
 115#define REG_PORT1 0x1
 116#define REG_PORT2 0x2
 117#define REG_PORT3 0x3
 118#define REG_PORT4 0x4
 119#define REG_PORT5 0x5
 120#define REG_INT_PENDING 0x6
 121#define REG_PAGELOCK 0x7        /* page selector register, upper 2 bits select a page
 122                                   and bits 0-5 are used to 'lock down' a particular
 123                                   port above to make it readonly.  */
 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
 145/*
 146 * Board descriptions for two imaginary boards.  Describing the
 147 * boards in this way is optional, and completely driver-dependent.
 148 * Some drivers use arrays such as this, other do not.
 149 */
 150struct pcmuio_board {
 151        const char *name;
 152        const int num_asics;
 153        const int num_channels_per_port;
 154        const int num_ports;
 155};
 156
 157static const struct pcmuio_board pcmuio_boards[] = {
 158        {
 159         .name = "pcmuio48",
 160         .num_asics = 1,
 161         .num_ports = 6,
 162         },
 163        {
 164         .name = "pcmuio96",
 165         .num_asics = 2,
 166         .num_ports = 12,
 167         },
 168};
 169
 170/*
 171 * Useful for shorthand access to the particular board structure
 172 */
 173#define thisboard ((const struct pcmuio_board *)dev->board_ptr)
 174
 175/* this structure is for data unique to this subdevice.  */
 176struct pcmuio_subdev_private {
 177        /* mapping of halfwords (bytes) in port/chanarray to iobase */
 178        unsigned long iobases[PORTS_PER_SUBDEV];
 179
 180        /* The below is only used for intr subdevices */
 181        struct {
 182                int asic;       /* if non-negative, this subdev has an interrupt asic */
 183                int first_chan; /* if nonnegative, the first channel id for
 184                                   interrupts. */
 185                int num_asic_chans;     /* the number of asic channels in this subdev
 186                                           that have interrutps */
 187                int asic_chan;  /* if nonnegative, the first channel id with
 188                                   respect to the asic that has interrupts */
 189                int enabled_mask;       /* subdev-relative channel mask for channels
 190                                           we are interested in */
 191                int active;
 192                int stop_count;
 193                int continuous;
 194                spinlock_t spinlock;
 195        } intr;
 196};
 197
 198/* this structure is for data unique to this hardware driver.  If
 199   several hardware drivers keep similar information in this structure,
 200   feel free to suggest moving the variable to the struct comedi_device struct.  */
 201struct pcmuio_private {
 202        struct {
 203                unsigned char pagelock; /* current page and lock */
 204                unsigned char pol[NUM_PAGED_REGS];      /* shadow of POLx registers */
 205                unsigned char enab[NUM_PAGED_REGS];     /* shadow of ENABx registers */
 206                int num;
 207                unsigned long iobase;
 208                unsigned int irq;
 209                spinlock_t spinlock;
 210        } asics[MAX_ASICS];
 211        struct pcmuio_subdev_private *sprivs;
 212};
 213
 214/*
 215 * most drivers define the following macro to make it easy to
 216 * access the private structure.
 217 */
 218#define devpriv ((struct pcmuio_private *)dev->private)
 219#define subpriv ((struct pcmuio_subdev_private *)s->private)
 220/*
 221 * The struct comedi_driver structure tells the Comedi core module
 222 * which functions to call to configure/deconfigure (attach/detach)
 223 * the board, and also about the kernel module that contains
 224 * the device code.
 225 */
 226static int pcmuio_attach(struct comedi_device *dev,
 227                         struct comedi_devconfig *it);
 228static int pcmuio_detach(struct comedi_device *dev);
 229
 230static struct comedi_driver driver = {
 231        .driver_name = "pcmuio",
 232        .module = THIS_MODULE,
 233        .attach = pcmuio_attach,
 234        .detach = pcmuio_detach,
 235/* It is not necessary to implement the following members if you are
 236 * writing a driver for a ISA PnP or PCI card */
 237        /* Most drivers will support multiple types of boards by
 238         * having an array of board structures.  These were defined
 239         * in pcmuio_boards[] above.  Note that the element 'name'
 240         * was first in the structure -- Comedi uses this fact to
 241         * extract the name of the board without knowing any details
 242         * about the structure except for its length.
 243         * When a device is attached (by comedi_config), the name
 244         * of the device is given to Comedi, and Comedi tries to
 245         * match it by going through the list of board names.  If
 246         * there is a match, the address of the pointer is put
 247         * into dev->board_ptr and driver->attach() is called.
 248         *
 249         * Note that these are not necessary if you can determine
 250         * the type of board in software.  ISA PnP, PCI, and PCMCIA
 251         * devices are such boards.
 252         */
 253        .board_name = &pcmuio_boards[0].name,
 254        .offset = sizeof(struct pcmuio_board),
 255        .num_names = ARRAY_SIZE(pcmuio_boards),
 256};
 257
 258static int pcmuio_dio_insn_bits(struct comedi_device *dev,
 259                                struct comedi_subdevice *s,
 260                                struct comedi_insn *insn, unsigned int *data);
 261static int pcmuio_dio_insn_config(struct comedi_device *dev,
 262                                  struct comedi_subdevice *s,
 263                                  struct comedi_insn *insn, unsigned int *data);
 264
 265static irqreturn_t interrupt_pcmuio(int irq, void *d);
 266static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
 267static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
 268static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
 269static int pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
 270                          struct comedi_cmd *cmd);
 271
 272/* some helper functions to deal with specifics of this device's registers */
 273static void init_asics(struct comedi_device *dev);      /* sets up/clears ASIC chips to defaults */
 274static void switch_page(struct comedi_device *dev, int asic, int page);
 275#ifdef notused
 276static void lock_port(struct comedi_device *dev, int asic, int port);
 277static void unlock_port(struct comedi_device *dev, int asic, int port);
 278#endif
 279
 280/*
 281 * Attach is called by the Comedi core to configure the driver
 282 * for a particular board.  If you specified a board_name array
 283 * in the driver structure, dev->board_ptr contains that
 284 * address.
 285 */
 286static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 287{
 288        struct comedi_subdevice *s;
 289        int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
 290        unsigned long iobase;
 291        unsigned int irq[MAX_ASICS];
 292
 293        iobase = it->options[0];
 294        irq[0] = it->options[1];
 295        irq[1] = it->options[2];
 296
 297        printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
 298               iobase);
 299
 300        dev->iobase = iobase;
 301
 302        if (!iobase || !request_region(iobase,
 303                                       thisboard->num_asics * ASIC_IOSIZE,
 304                                       driver.driver_name)) {
 305                printk("I/O port conflict\n");
 306                return -EIO;
 307        }
 308
 309/*
 310 * Initialize dev->board_name.  Note that we can use the "thisboard"
 311 * macro now, since we just initialized it in the last line.
 312 */
 313        dev->board_name = thisboard->name;
 314
 315/*
 316 * Allocate the private structure area.  alloc_private() is a
 317 * convenient macro defined in comedidev.h.
 318 */
 319        if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
 320                printk("cannot allocate private data structure\n");
 321                return -ENOMEM;
 322        }
 323
 324        for (asic = 0; asic < MAX_ASICS; ++asic) {
 325                devpriv->asics[asic].num = asic;
 326                devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
 327                devpriv->asics[asic].irq = 0;   /* this gets actually set at the end of
 328                                                   this function when we
 329                                                   request_irqs */
 330                spin_lock_init(&devpriv->asics[asic].spinlock);
 331        }
 332
 333        chans_left = CHANS_PER_ASIC * thisboard->num_asics;
 334        n_subdevs = CALC_N_SUBDEVS(chans_left);
 335        devpriv->sprivs =
 336            kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
 337                    GFP_KERNEL);
 338        if (!devpriv->sprivs) {
 339                printk("cannot allocate subdevice private data structures\n");
 340                return -ENOMEM;
 341        }
 342        /*
 343         * Allocate the subdevice structures.  alloc_subdevice() is a
 344         * convenient macro defined in comedidev.h.
 345         *
 346         * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
 347         * 96-channel version of the board.
 348         */
 349        if (alloc_subdevices(dev, n_subdevs) < 0) {
 350                printk("cannot allocate subdevice data structures\n");
 351                return -ENOMEM;
 352        }
 353
 354        port = 0;
 355        asic = 0;
 356        for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
 357                int byte_no;
 358
 359                s = dev->subdevices + sdev_no;
 360                s->private = devpriv->sprivs + sdev_no;
 361                s->maxdata = 1;
 362                s->range_table = &range_digital;
 363                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 364                s->type = COMEDI_SUBD_DIO;
 365                s->insn_bits = pcmuio_dio_insn_bits;
 366                s->insn_config = pcmuio_dio_insn_config;
 367                s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
 368                subpriv->intr.asic = -1;
 369                subpriv->intr.first_chan = -1;
 370                subpriv->intr.asic_chan = -1;
 371                subpriv->intr.num_asic_chans = -1;
 372                subpriv->intr.active = 0;
 373                s->len_chanlist = 1;
 374
 375                /* save the ioport address for each 'port' of 8 channels in the
 376                   subdevice */
 377                for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
 378                        if (port >= PORTS_PER_ASIC) {
 379                                port = 0;
 380                                ++asic;
 381                                thisasic_chanct = 0;
 382                        }
 383                        subpriv->iobases[byte_no] =
 384                            devpriv->asics[asic].iobase + port;
 385
 386                        if (thisasic_chanct <
 387                            CHANS_PER_PORT * INTR_PORTS_PER_ASIC
 388                            && subpriv->intr.asic < 0) {
 389                                /* this is an interrupt subdevice, so setup the struct */
 390                                subpriv->intr.asic = asic;
 391                                subpriv->intr.active = 0;
 392                                subpriv->intr.stop_count = 0;
 393                                subpriv->intr.first_chan = byte_no * 8;
 394                                subpriv->intr.asic_chan = thisasic_chanct;
 395                                subpriv->intr.num_asic_chans =
 396                                    s->n_chan - subpriv->intr.first_chan;
 397                                dev->read_subdev = s;
 398                                s->subdev_flags |= SDF_CMD_READ;
 399                                s->cancel = pcmuio_cancel;
 400                                s->do_cmd = pcmuio_cmd;
 401                                s->do_cmdtest = pcmuio_cmdtest;
 402                                s->len_chanlist = subpriv->intr.num_asic_chans;
 403                        }
 404                        thisasic_chanct += CHANS_PER_PORT;
 405                }
 406                spin_lock_init(&subpriv->intr.spinlock);
 407
 408                chans_left -= s->n_chan;
 409
 410                if (!chans_left) {
 411                        asic = 0;       /* reset the asic to our first asic, to do intr subdevs */
 412                        port = 0;
 413                }
 414
 415        }
 416
 417        init_asics(dev);        /* clear out all the registers, basically */
 418
 419        for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
 420                if (irq[asic]
 421                    && request_irq(irq[asic], interrupt_pcmuio,
 422                                   IRQF_SHARED, thisboard->name, dev)) {
 423                        int i;
 424                        /* unroll the allocated irqs.. */
 425                        for (i = asic - 1; i >= 0; --i) {
 426                                free_irq(irq[i], dev);
 427                                devpriv->asics[i].irq = irq[i] = 0;
 428                        }
 429                        irq[asic] = 0;
 430                }
 431                devpriv->asics[asic].irq = irq[asic];
 432        }
 433
 434        dev->irq = irq[0];      /* grr.. wish comedi dev struct supported multiple
 435                                   irqs.. */
 436
 437        if (irq[0]) {
 438                printk("irq: %u ", irq[0]);
 439                if (irq[1] && thisboard->num_asics == 2)
 440                        printk("second ASIC irq: %u ", irq[1]);
 441        } else {
 442                printk("(IRQ mode disabled) ");
 443        }
 444
 445        printk("attached\n");
 446
 447        return 1;
 448}
 449
 450/*
 451 * _detach is called to deconfigure a device.  It should deallocate
 452 * resources.
 453 * This function is also called when _attach() fails, so it should be
 454 * careful not to release resources that were not necessarily
 455 * allocated by _attach().  dev->private and dev->subdevices are
 456 * deallocated automatically by the core.
 457 */
 458static int pcmuio_detach(struct comedi_device *dev)
 459{
 460        int i;
 461
 462        printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
 463        if (dev->iobase)
 464                release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
 465
 466        for (i = 0; i < MAX_ASICS; ++i) {
 467                if (devpriv->asics[i].irq)
 468                        free_irq(devpriv->asics[i].irq, dev);
 469        }
 470
 471        if (devpriv && devpriv->sprivs)
 472                kfree(devpriv->sprivs);
 473
 474        return 0;
 475}
 476
 477/* DIO devices are slightly special.  Although it is possible to
 478 * implement the insn_read/insn_write interface, it is much more
 479 * useful to applications if you implement the insn_bits interface.
 480 * This allows packed reading/writing of the DIO channels.  The
 481 * comedi core can convert between insn_bits and insn_read/write */
 482static int pcmuio_dio_insn_bits(struct comedi_device *dev,
 483                                struct comedi_subdevice *s,
 484                                struct comedi_insn *insn, unsigned int *data)
 485{
 486        int byte_no;
 487        if (insn->n != 2)
 488                return -EINVAL;
 489
 490        /* NOTE:
 491           reading a 0 means this channel was high
 492           writine a 0 sets the channel high
 493           reading a 1 means this channel was low
 494           writing a 1 means set this channel low
 495
 496           Therefore everything is always inverted. */
 497
 498        /* The insn data is a mask in data[0] and the new data
 499         * in data[1], each channel cooresponding to a bit. */
 500
 501#ifdef DAMMIT_ITS_BROKEN
 502        /* DEBUG */
 503        printk("write mask: %08x  data: %08x\n", data[0], data[1]);
 504#endif
 505
 506        s->state = 0;
 507
 508        for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
 509                /* address of 8-bit port */
 510                unsigned long ioaddr = subpriv->iobases[byte_no],
 511                    /* bit offset of port in 32-bit doubleword */
 512                    offset = byte_no * 8;
 513                /* this 8-bit port's data */
 514                unsigned char byte = 0,
 515                    /* The write mask for this port (if any) */
 516                    write_mask_byte = (data[0] >> offset) & 0xff,
 517                    /* The data byte for this port */
 518                    data_byte = (data[1] >> offset) & 0xff;
 519
 520                byte = inb(ioaddr);     /* read all 8-bits for this port */
 521
 522#ifdef DAMMIT_ITS_BROKEN
 523                /* DEBUG */
 524                printk
 525                    ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
 526                     byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
 527                     offset, ioaddr, (unsigned)byte);
 528#endif
 529
 530                if (write_mask_byte) {
 531                        /* this byte has some write_bits -- so set the output lines */
 532                        byte &= ~write_mask_byte;       /* clear bits for write mask */
 533                        byte |= ~data_byte & write_mask_byte;   /* set to inverted data_byte */
 534                        /* Write out the new digital output state */
 535                        outb(byte, ioaddr);
 536                }
 537#ifdef DAMMIT_ITS_BROKEN
 538                /* DEBUG */
 539                printk("data_out_byte %02x\n", (unsigned)byte);
 540#endif
 541                /* save the digital input lines for this byte.. */
 542                s->state |= ((unsigned int)byte) << offset;
 543        }
 544
 545        /* now return the DIO lines to data[1] - note they came inverted! */
 546        data[1] = ~s->state;
 547
 548#ifdef DAMMIT_ITS_BROKEN
 549        /* DEBUG */
 550        printk("s->state %08x data_out %08x\n", s->state, data[1]);
 551#endif
 552
 553        return 2;
 554}
 555
 556/* The input or output configuration of each digital line is
 557 * configured by a special insn_config instruction.  chanspec
 558 * contains the channel to be changed, and data[0] contains the
 559 * value COMEDI_INPUT or COMEDI_OUTPUT. */
 560static int pcmuio_dio_insn_config(struct comedi_device *dev,
 561                                  struct comedi_subdevice *s,
 562                                  struct comedi_insn *insn, unsigned int *data)
 563{
 564        int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
 565            chan % 8;
 566        unsigned long ioaddr;
 567        unsigned char byte;
 568
 569        /* Compute ioaddr for this channel */
 570        ioaddr = subpriv->iobases[byte_no];
 571
 572        /* NOTE:
 573           writing a 0 an IO channel's bit sets the channel to INPUT
 574           and pulls the line high as well
 575
 576           writing a 1 to an IO channel's  bit pulls the line low
 577
 578           All channels are implicitly always in OUTPUT mode -- but when
 579           they are high they can be considered to be in INPUT mode..
 580
 581           Thus, we only force channels low if the config request was INPUT,
 582           otherwise we do nothing to the hardware.    */
 583
 584        switch (data[0]) {
 585        case INSN_CONFIG_DIO_OUTPUT:
 586                /* save to io_bits -- don't actually do anything since
 587                   all input channels are also output channels... */
 588                s->io_bits |= 1 << chan;
 589                break;
 590        case INSN_CONFIG_DIO_INPUT:
 591                /* write a 0 to the actual register representing the channel
 592                   to set it to 'input'.  0 means "float high". */
 593                byte = inb(ioaddr);
 594                byte &= ~(1 << bit_no);
 595                                /**< set input channel to '0' */
 596
 597                /* write out byte -- this is the only time we actually affect the
 598                   hardware as all channels are implicitly output -- but input
 599                   channels are set to float-high */
 600                outb(byte, ioaddr);
 601
 602                /* save to io_bits */
 603                s->io_bits &= ~(1 << chan);
 604                break;
 605
 606        case INSN_CONFIG_DIO_QUERY:
 607                /* retreive from shadow register */
 608                data[1] =
 609                    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
 610                return insn->n;
 611                break;
 612
 613        default:
 614                return -EINVAL;
 615                break;
 616        }
 617
 618        return insn->n;
 619}
 620
 621static void init_asics(struct comedi_device *dev)
 622{                               /* sets up an
 623                                   ASIC chip to defaults */
 624        int asic;
 625
 626        for (asic = 0; asic < thisboard->num_asics; ++asic) {
 627                int port, page;
 628                unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
 629
 630                switch_page(dev, asic, 0);      /* switch back to page 0 */
 631
 632                /* first, clear all the DIO port bits */
 633                for (port = 0; port < PORTS_PER_ASIC; ++port)
 634                        outb(0, baseaddr + REG_PORT0 + port);
 635
 636                /* Next, clear all the paged registers for each page */
 637                for (page = 1; page < NUM_PAGES; ++page) {
 638                        int reg;
 639                        /* now clear all the paged registers */
 640                        switch_page(dev, asic, page);
 641                        for (reg = FIRST_PAGED_REG;
 642                             reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
 643                                outb(0, baseaddr + reg);
 644                }
 645
 646                /* DEBUG  set rising edge interrupts on port0 of both asics */
 647                /*switch_page(dev, asic, PAGE_POL);
 648                   outb(0xff, baseaddr + REG_POL0);
 649                   switch_page(dev, asic, PAGE_ENAB);
 650                   outb(0xff, baseaddr + REG_ENAB0); */
 651                /* END DEBUG */
 652
 653                switch_page(dev, asic, 0);      /* switch back to default page 0 */
 654
 655        }
 656}
 657
 658static void switch_page(struct comedi_device *dev, int asic, int page)
 659{
 660        if (asic < 0 || asic >= thisboard->num_asics)
 661                return;         /* paranoia */
 662        if (page < 0 || page >= NUM_PAGES)
 663                return;         /* more paranoia */
 664
 665        devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
 666        devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
 667
 668        /* now write out the shadow register */
 669        outb(devpriv->asics[asic].pagelock,
 670             dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
 671}
 672
 673#ifdef notused
 674static void lock_port(struct comedi_device *dev, int asic, int port)
 675{
 676        if (asic < 0 || asic >= thisboard->num_asics)
 677                return;         /* paranoia */
 678        if (port < 0 || port >= PORTS_PER_ASIC)
 679                return;         /* more paranoia */
 680
 681        devpriv->asics[asic].pagelock |= 0x1 << port;
 682        /* now write out the shadow register */
 683        outb(devpriv->asics[asic].pagelock,
 684             dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
 685}
 686
 687static void unlock_port(struct comedi_device *dev, int asic, int port)
 688{
 689        if (asic < 0 || asic >= thisboard->num_asics)
 690                return;         /* paranoia */
 691        if (port < 0 || port >= PORTS_PER_ASIC)
 692                return;         /* more paranoia */
 693        devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
 694        /* now write out the shadow register */
 695        outb(devpriv->asics[asic].pagelock,
 696             dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
 697}
 698#endif /* notused */
 699
 700static irqreturn_t interrupt_pcmuio(int irq, void *d)
 701{
 702        int asic, got1 = 0;
 703        struct comedi_device *dev = (struct comedi_device *)d;
 704
 705        for (asic = 0; asic < MAX_ASICS; ++asic) {
 706                if (irq == devpriv->asics[asic].irq) {
 707                        unsigned long flags;
 708                        unsigned triggered = 0;
 709                        unsigned long iobase = devpriv->asics[asic].iobase;
 710                        /* it is an interrupt for ASIC #asic */
 711                        unsigned char int_pend;
 712
 713                        spin_lock_irqsave(&devpriv->asics[asic].spinlock,
 714                                          flags);
 715
 716                        int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
 717
 718                        if (int_pend) {
 719                                int port;
 720                                for (port = 0; port < INTR_PORTS_PER_ASIC;
 721                                     ++port) {
 722                                        if (int_pend & (0x1 << port)) {
 723                                                unsigned char
 724                                                    io_lines_with_edges = 0;
 725                                                switch_page(dev, asic,
 726                                                            PAGE_INT_ID);
 727                                                io_lines_with_edges =
 728                                                    inb(iobase +
 729                                                        REG_INT_ID0 + port);
 730
 731                                                if (io_lines_with_edges)
 732                                                        /* clear pending interrupt */
 733                                                        outb(0, iobase +
 734                                                             REG_INT_ID0 +
 735                                                             port);
 736
 737                                                triggered |=
 738                                                    io_lines_with_edges <<
 739                                                    port * 8;
 740                                        }
 741                                }
 742
 743                                ++got1;
 744                        }
 745
 746                        spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
 747                                               flags);
 748
 749                        if (triggered) {
 750                                struct comedi_subdevice *s;
 751                                /* TODO here: dispatch io lines to subdevs with commands.. */
 752                                printk
 753                                    ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
 754                                     irq, asic, triggered);
 755                                for (s = dev->subdevices;
 756                                     s < dev->subdevices + dev->n_subdevices;
 757                                     ++s) {
 758                                        if (subpriv->intr.asic == asic) {       /* this is an interrupt subdev, and it matches this asic! */
 759                                                unsigned long flags;
 760                                                unsigned oldevents;
 761
 762                                                spin_lock_irqsave(&subpriv->
 763                                                                  intr.spinlock,
 764                                                                  flags);
 765
 766                                                oldevents = s->async->events;
 767
 768                                                if (subpriv->intr.active) {
 769                                                        unsigned mytrig =
 770                                                            ((triggered >>
 771                                                              subpriv->intr.asic_chan)
 772                                                             &
 773                                                             ((0x1 << subpriv->
 774                                                               intr.
 775                                                               num_asic_chans) -
 776                                                              1)) << subpriv->
 777                                                            intr.first_chan;
 778                                                        if (mytrig &
 779                                                            subpriv->intr.enabled_mask)
 780                                                        {
 781                                                                unsigned int val
 782                                                                    = 0;
 783                                                                unsigned int n,
 784                                                                    ch, len;
 785
 786                                                                len =
 787                                                                    s->
 788                                                                    async->cmd.chanlist_len;
 789                                                                for (n = 0;
 790                                                                     n < len;
 791                                                                     n++) {
 792                                                                        ch = CR_CHAN(s->async->cmd.chanlist[n]);
 793                                                                        if (mytrig & (1U << ch)) {
 794                                                                                val |= (1U << n);
 795                                                                        }
 796                                                                }
 797                                                                /* Write the scan to the buffer. */
 798                                                                if (comedi_buf_put(s->async, ((short *)&val)[0])
 799                                                                    &&
 800                                                                    comedi_buf_put
 801                                                                    (s->async,
 802                                                                     ((short *)
 803                                                                      &val)[1]))
 804                                                                {
 805                                                                        s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
 806                                                                } else {
 807                                                                        /* Overflow! Stop acquisition!! */
 808                                                                        /* TODO: STOP_ACQUISITION_CALL_HERE!! */
 809                                                                        pcmuio_stop_intr
 810                                                                            (dev,
 811                                                                             s);
 812                                                                }
 813
 814                                                                /* Check for end of acquisition. */
 815                                                                if (!subpriv->intr.continuous) {
 816                                                                        /* stop_src == TRIG_COUNT */
 817                                                                        if (subpriv->intr.stop_count > 0) {
 818                                                                                subpriv->intr.stop_count--;
 819                                                                                if (subpriv->intr.stop_count == 0) {
 820                                                                                        s->async->events |= COMEDI_CB_EOA;
 821                                                                                        /* TODO: STOP_ACQUISITION_CALL_HERE!! */
 822                                                                                        pcmuio_stop_intr
 823                                                                                            (dev,
 824                                                                                             s);
 825                                                                                }
 826                                                                        }
 827                                                                }
 828                                                        }
 829                                                }
 830
 831                                                spin_unlock_irqrestore
 832                                                    (&subpriv->intr.spinlock,
 833                                                     flags);
 834
 835                                                if (oldevents !=
 836                                                    s->async->events) {
 837                                                        comedi_event(dev, s);
 838                                                }
 839
 840                                        }
 841
 842                                }
 843                        }
 844
 845                }
 846        }
 847        if (!got1)
 848                return IRQ_NONE;        /* interrupt from other source */
 849        return IRQ_HANDLED;
 850}
 851
 852static void pcmuio_stop_intr(struct comedi_device *dev,
 853                             struct comedi_subdevice *s)
 854{
 855        int nports, firstport, asic, port;
 856
 857        asic = subpriv->intr.asic;
 858        if (asic < 0)
 859                return;         /* not an interrupt subdev */
 860
 861        subpriv->intr.enabled_mask = 0;
 862        subpriv->intr.active = 0;
 863        s->async->inttrig = 0;
 864        nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
 865        firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
 866        switch_page(dev, asic, PAGE_ENAB);
 867        for (port = firstport; port < firstport + nports; ++port) {
 868                /* disable all intrs for this subdev.. */
 869                outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
 870        }
 871}
 872
 873static int pcmuio_start_intr(struct comedi_device *dev,
 874                             struct comedi_subdevice *s)
 875{
 876        if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
 877                /* An empty acquisition! */
 878                s->async->events |= COMEDI_CB_EOA;
 879                subpriv->intr.active = 0;
 880                return 1;
 881        } else {
 882                unsigned bits = 0, pol_bits = 0, n;
 883                int nports, firstport, asic, port;
 884                struct comedi_cmd *cmd = &s->async->cmd;
 885
 886                asic = subpriv->intr.asic;
 887                if (asic < 0)
 888                        return 1;       /* not an interrupt
 889                                           subdev */
 890                subpriv->intr.enabled_mask = 0;
 891                subpriv->intr.active = 1;
 892                nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
 893                firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
 894                if (cmd->chanlist) {
 895                        for (n = 0; n < cmd->chanlist_len; n++) {
 896                                bits |= (1U << CR_CHAN(cmd->chanlist[n]));
 897                                pol_bits |= (CR_AREF(cmd->chanlist[n])
 898                                             || CR_RANGE(cmd->
 899                                                         chanlist[n]) ? 1U : 0U)
 900                                    << CR_CHAN(cmd->chanlist[n]);
 901                        }
 902                }
 903                bits &= ((0x1 << subpriv->intr.num_asic_chans) -
 904                         1) << subpriv->intr.first_chan;
 905                subpriv->intr.enabled_mask = bits;
 906
 907                switch_page(dev, asic, PAGE_ENAB);
 908                for (port = firstport; port < firstport + nports; ++port) {
 909                        unsigned enab =
 910                            bits >> (subpriv->intr.first_chan + (port -
 911                                                                 firstport) *
 912                                     8) & 0xff, pol =
 913                            pol_bits >> (subpriv->intr.first_chan +
 914                                         (port - firstport) * 8) & 0xff;
 915                        /* set enab intrs for this subdev.. */
 916                        outb(enab,
 917                             devpriv->asics[asic].iobase + REG_ENAB0 + port);
 918                        switch_page(dev, asic, PAGE_POL);
 919                        outb(pol,
 920                             devpriv->asics[asic].iobase + REG_ENAB0 + port);
 921                }
 922        }
 923        return 0;
 924}
 925
 926static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 927{
 928        unsigned long flags;
 929
 930        spin_lock_irqsave(&subpriv->intr.spinlock, flags);
 931        if (subpriv->intr.active)
 932                pcmuio_stop_intr(dev, s);
 933        spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
 934
 935        return 0;
 936}
 937
 938/*
 939 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
 940 */
 941static int
 942pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
 943                          unsigned int trignum)
 944{
 945        unsigned long flags;
 946        int event = 0;
 947
 948        if (trignum != 0)
 949                return -EINVAL;
 950
 951        spin_lock_irqsave(&subpriv->intr.spinlock, flags);
 952        s->async->inttrig = 0;
 953        if (subpriv->intr.active) {
 954                event = pcmuio_start_intr(dev, s);
 955        }
 956        spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
 957
 958        if (event) {
 959                comedi_event(dev, s);
 960        }
 961
 962        return 1;
 963}
 964
 965/*
 966 * 'do_cmd' function for an 'INTERRUPT' subdevice.
 967 */
 968static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 969{
 970        struct comedi_cmd *cmd = &s->async->cmd;
 971        unsigned long flags;
 972        int event = 0;
 973
 974        spin_lock_irqsave(&subpriv->intr.spinlock, flags);
 975        subpriv->intr.active = 1;
 976
 977        /* Set up end of acquisition. */
 978        switch (cmd->stop_src) {
 979        case TRIG_COUNT:
 980                subpriv->intr.continuous = 0;
 981                subpriv->intr.stop_count = cmd->stop_arg;
 982                break;
 983        default:
 984                /* TRIG_NONE */
 985                subpriv->intr.continuous = 1;
 986                subpriv->intr.stop_count = 0;
 987                break;
 988        }
 989
 990        /* Set up start of acquisition. */
 991        switch (cmd->start_src) {
 992        case TRIG_INT:
 993                s->async->inttrig = pcmuio_inttrig_start_intr;
 994                break;
 995        default:
 996                /* TRIG_NOW */
 997                event = pcmuio_start_intr(dev, s);
 998                break;
 999        }
1000        spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
1001
1002        if (event) {
1003                comedi_event(dev, s);
1004        }
1005
1006        return 0;
1007}
1008
1009static int
1010pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1011               struct comedi_cmd *cmd)
1012{
1013        return comedi_pcm_cmdtest(dev, s, cmd);
1014}
1015
1016/*
1017 * A convenient macro that defines init_module() and cleanup_module(),
1018 * as necessary.
1019 */
1020COMEDI_INITCLEANUP(driver);
1021