9a560a36f1f512e4cc50689e20ebe78fad634d7c
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / pcmuio.c
1 /*
2  * pcmuio.c
3  * Comedi driver for Winsystems PC-104 based 48/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
19 /*
20  * Driver: pcmuio
21  * Description: Winsystems PC-104 based 48/96-channel DIO boards.
22  * Devices: (Winsystems) PCM-UIO48A [pcmuio48]
23  *          (Winsystems) PCM-UIO96A [pcmuio96]
24  * Author: Calin Culianu <calin@ajvar.org>
25  * Updated: Fri, 13 Jan 2006 12:01:01 -0500
26  * Status: works
27  *
28  * A driver for the relatively straightforward-to-program PCM-UIO48A and
29  * PCM-UIO96A boards from Winsystems. These boards use either one or two
30  * (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO). This
31  * chip is interesting in that each I/O line is individually programmable
32  * for INPUT or OUTPUT (thus comedi_dio_config can be done on a per-channel
33  * basis). Also, each chip supports edge-triggered interrupts for the first
34  * 24 I/O lines. Of course, since the 96-channel version of the board has
35  * two ASICs, it can detect polarity changes on up to 48 I/O lines. Since
36  * this is essentially an (non-PnP) ISA board, I/O Address and IRQ selection
37  * are done through jumpers on the board. You need to pass that information
38  * to this driver as the first and second comedi_config option, respectively.
39  * Note that the 48-channel version uses 16 bytes of IO memory and the 96-
40  * channel version uses 32-bytes (in case you are worried about conflicts).
41  * The 48-channel board is split into two 24-channel comedi subdevices. The
42  * 96-channel board is split into 4 24-channel DIO subdevices.
43  *
44  * Note that IRQ support has been added, but it is untested.
45  *
46  * To use edge-detection IRQ support, pass the IRQs of both ASICS (for the
47  * 96 channel version) or just 1 ASIC (for 48-channel version). Then, use
48  * comedi_commands with TRIG_NOW. Your callback will be called each time an
49  * edge is triggered, and the data values will be two sample_t's, which
50  * should be concatenated to form one 32-bit unsigned int.  This value is
51  * the mask of channels that had edges detected from your channel list. Note
52  * that the bits positions in the mask correspond to positions in your
53  * chanlist when you specified the command and *not* channel id's!
54  *
55  * To set the polarity of the edge-detection interrupts pass a nonzero value
56  * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for
57  * both CR_RANGE and CR_AREF if you want edge-down polarity.
58  *
59  * In the 48-channel version:
60  *
61  * On subdev 0, the first 24 channels channels are edge-detect channels.
62  *
63  * In the 96-channel board you have the following channels that can do edge
64  * detection:
65  *
66  * subdev 0, channels 0-24  (first 24 channels of 1st ASIC)
67  * subdev 2, channels 0-24  (first 24 channels of 2nd ASIC)
68  *
69  * Configuration Options:
70  *  [0] - I/O port base address
71  *  [1] - IRQ (for first ASIC, or first 24 channels)
72  *  [2] - IRQ (for second ASIC, pcmuio96 only - IRQ for chans 48-72
73  *             can be the same as first irq!)
74  */
75
76 #include <linux/interrupt.h>
77 #include <linux/slab.h>
78
79 #include "../comedidev.h"
80
81 #include "comedi_fc.h"
82
83 #define CHANS_PER_PORT          8
84 #define PORTS_PER_ASIC          6
85 #define INTR_PORTS_PER_ASIC     3
86 /* number of channels per comedi subdevice */
87 #define MAX_CHANS_PER_SUBDEV    24
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
95 /* IO Memory sizes */
96 #define ASIC_IOSIZE             0x10
97 #define PCMUIO48_IOSIZE         ASIC_IOSIZE
98 #define PCMUIO96_IOSIZE         (ASIC_IOSIZE * 2)
99
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    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 /*
122  * page selector register
123  * Upper 2 bits select a page and bits 0-5 are used to
124  * 'lock down' a particular port above to make it readonly.
125  */
126 #define REG_PAGELOCK            0x7
127 #define REG_POL0                0x8
128 #define REG_POL1                0x9
129 #define REG_POL2                0xa
130 #define REG_ENAB0               0x8
131 #define REG_ENAB1               0x9
132 #define REG_ENAB2               0xa
133 #define REG_INT_ID0             0x8
134 #define REG_INT_ID1             0x9
135 #define REG_INT_ID2             0xa
136
137 #define NUM_PAGED_REGS          3
138 #define NUM_PAGES               4
139 #define FIRST_PAGED_REG         0x8
140 #define REG_PAGE_BITOFFSET      6
141 #define REG_LOCK_BITOFFSET      0
142 #define REG_PAGE_MASK           (~((0x1 << REG_PAGE_BITOFFSET) - 1))
143 #define REG_LOCK_MASK           ~(REG_PAGE_MASK)
144 #define PAGE_POL                1
145 #define PAGE_ENAB               2
146 #define PAGE_INT_ID             3
147
148 struct pcmuio_board {
149         const char *name;
150         const int num_asics;
151         const int num_channels_per_port;
152         const int num_ports;
153 };
154
155 static const struct pcmuio_board pcmuio_boards[] = {
156         {
157                 .name           = "pcmuio48",
158                 .num_asics      = 1,
159                 .num_ports      = 6,
160         }, {
161                 .name           = "pcmuio96",
162                 .num_asics      = 2,
163                 .num_ports      = 12,
164         },
165 };
166
167 struct pcmuio_subdev_private {
168         /* mapping of halfwords (bytes) in port/chanarray to iobase */
169         unsigned long iobases[PORTS_PER_SUBDEV];
170
171         /* The below is only used for intr subdevices */
172         struct {
173                 /* if non-negative, this subdev has an interrupt asic */
174                 int asic;
175                 /* if nonnegative, the first channel id for interrupts */
176                 int first_chan;
177                 /*
178                  * the number of asic channels in this
179                  * subdev that have interrutps
180                  */
181                 int num_asic_chans;
182                 /*
183                  * if nonnegative, the first channel id with
184                  * respect to the asic that has interrupts
185                  */
186                 int asic_chan;
187                 /*
188                  * subdev-relative channel mask for channels
189                  * we are interested in
190                  */
191                 int enabled_mask;
192                 int active;
193                 int stop_count;
194                 int continuous;
195                 spinlock_t spinlock;
196         } intr;
197 };
198
199 struct pcmuio_private {
200         struct {
201                 /* current page and lock */
202                 unsigned char pagelock;
203                 /* shadow of POLx registers */
204                 unsigned char pol[NUM_PAGED_REGS];
205                 /* shadow of ENABx registers */
206                 unsigned char enab[NUM_PAGED_REGS];
207                 int num;
208                 unsigned long iobase;
209                 unsigned int irq;
210                 spinlock_t spinlock;
211         } asics[MAX_ASICS];
212         struct pcmuio_subdev_private *sprivs;
213 };
214
215 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
216                                 struct comedi_subdevice *s,
217                                 struct comedi_insn *insn, unsigned int *data)
218 {
219         struct pcmuio_subdev_private *subpriv = s->private;
220         int byte_no;
221
222         /* NOTE:
223            reading a 0 means this channel was high
224            writine a 0 sets the channel high
225            reading a 1 means this channel was low
226            writing a 1 means set this channel low
227
228            Therefore everything is always inverted. */
229
230         /* The insn data is a mask in data[0] and the new data
231          * in data[1], each channel cooresponding to a bit. */
232
233         s->state = 0;
234
235         for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
236                 /* address of 8-bit port */
237                 unsigned long ioaddr = subpriv->iobases[byte_no],
238                     /* bit offset of port in 32-bit doubleword */
239                     offset = byte_no * 8;
240                 /* this 8-bit port's data */
241                 unsigned char byte = 0,
242                     /* The write mask for this port (if any) */
243                     write_mask_byte = (data[0] >> offset) & 0xff,
244                     /* The data byte for this port */
245                     data_byte = (data[1] >> offset) & 0xff;
246
247                 byte = inb(ioaddr);     /* read all 8-bits for this port */
248
249                 if (write_mask_byte) {
250                         byte &= ~write_mask_byte;
251                         byte |= ~data_byte & write_mask_byte;
252                         outb(byte, ioaddr);
253                 }
254                 /* save the digital input lines for this byte.. */
255                 s->state |= ((unsigned int)byte) << offset;
256         }
257
258         /* now return the DIO lines to data[1] - note they came inverted! */
259         data[1] = ~s->state;
260
261         return insn->n;
262 }
263
264 static int pcmuio_dio_insn_config(struct comedi_device *dev,
265                                   struct comedi_subdevice *s,
266                                   struct comedi_insn *insn, unsigned int *data)
267 {
268         struct pcmuio_subdev_private *subpriv = s->private;
269         int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
270             chan % 8;
271         unsigned long ioaddr;
272         unsigned char byte;
273
274         /* Compute ioaddr for this channel */
275         ioaddr = subpriv->iobases[byte_no];
276
277         /* NOTE:
278            writing a 0 an IO channel's bit sets the channel to INPUT
279            and pulls the line high as well
280
281            writing a 1 to an IO channel's  bit pulls the line low
282
283            All channels are implicitly always in OUTPUT mode -- but when
284            they are high they can be considered to be in INPUT mode..
285
286            Thus, we only force channels low if the config request was INPUT,
287            otherwise we do nothing to the hardware.    */
288
289         switch (data[0]) {
290         case INSN_CONFIG_DIO_OUTPUT:
291                 /* save to io_bits -- don't actually do anything since
292                    all input channels are also output channels... */
293                 s->io_bits |= 1 << chan;
294                 break;
295         case INSN_CONFIG_DIO_INPUT:
296                 /* write a 0 to the actual register representing the channel
297                    to set it to 'input'.  0 means "float high". */
298                 byte = inb(ioaddr);
299                 byte &= ~(1 << bit_no);
300                                 /**< set input channel to '0' */
301
302                 /*
303                  * write out byte
304                  * This is the only time we actually affect the hardware
305                  * as all channels are implicitly output -- but input
306                  * channels are set to float-high.
307                  */
308                 outb(byte, ioaddr);
309
310                 /* save to io_bits */
311                 s->io_bits &= ~(1 << chan);
312                 break;
313
314         case INSN_CONFIG_DIO_QUERY:
315                 /* retrieve from shadow register */
316                 data[1] =
317                     (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
318                 return insn->n;
319                 break;
320
321         default:
322                 return -EINVAL;
323                 break;
324         }
325
326         return insn->n;
327 }
328
329 static void switch_page(struct comedi_device *dev, int asic, int page)
330 {
331         const struct pcmuio_board *board = comedi_board(dev);
332         struct pcmuio_private *devpriv = dev->private;
333
334         if (asic < 0 || asic >= board->num_asics)
335                 return;         /* paranoia */
336         if (page < 0 || page >= NUM_PAGES)
337                 return;         /* more paranoia */
338
339         devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
340         devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
341
342         /* now write out the shadow register */
343         outb(devpriv->asics[asic].pagelock,
344              dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
345 }
346
347 static void init_asics(struct comedi_device *dev)
348 {                               /* sets up an
349                                    ASIC chip to defaults */
350         const struct pcmuio_board *board = comedi_board(dev);
351         int asic;
352
353         for (asic = 0; asic < board->num_asics; ++asic) {
354                 int port, page;
355                 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
356
357                 switch_page(dev, asic, 0);      /* switch back to page 0 */
358
359                 /* first, clear all the DIO port bits */
360                 for (port = 0; port < PORTS_PER_ASIC; ++port)
361                         outb(0, baseaddr + REG_PORT0 + port);
362
363                 /* Next, clear all the paged registers for each page */
364                 for (page = 1; page < NUM_PAGES; ++page) {
365                         int reg;
366                         /* now clear all the paged registers */
367                         switch_page(dev, asic, page);
368                         for (reg = FIRST_PAGED_REG;
369                              reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
370                                 outb(0, baseaddr + reg);
371                 }
372
373                 /* DEBUG  set rising edge interrupts on port0 of both asics */
374                 /*switch_page(dev, asic, PAGE_POL);
375                    outb(0xff, baseaddr + REG_POL0);
376                    switch_page(dev, asic, PAGE_ENAB);
377                    outb(0xff, baseaddr + REG_ENAB0); */
378                 /* END DEBUG */
379
380                 /* switch back to default page 0 */
381                 switch_page(dev, asic, 0);
382         }
383 }
384
385 #ifdef notused
386 static void lock_port(struct comedi_device *dev, int asic, int port)
387 {
388         const struct pcmuio_board *board = comedi_board(dev);
389         struct pcmuio_private *devpriv = dev->private;
390
391         if (asic < 0 || asic >= board->num_asics)
392                 return;         /* paranoia */
393         if (port < 0 || port >= PORTS_PER_ASIC)
394                 return;         /* more paranoia */
395
396         devpriv->asics[asic].pagelock |= 0x1 << port;
397         /* now write out the shadow register */
398         outb(devpriv->asics[asic].pagelock,
399              dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
400 }
401
402 static void unlock_port(struct comedi_device *dev, int asic, int port)
403 {
404         const struct pcmuio_board *board = comedi_board(dev);
405         struct pcmuio_private *devpriv = dev->private;
406
407         if (asic < 0 || asic >= board->num_asics)
408                 return;         /* paranoia */
409         if (port < 0 || port >= PORTS_PER_ASIC)
410                 return;         /* more paranoia */
411         devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
412         /* now write out the shadow register */
413         outb(devpriv->asics[asic].pagelock,
414              dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
415 }
416 #endif /* notused */
417
418 static void pcmuio_stop_intr(struct comedi_device *dev,
419                              struct comedi_subdevice *s)
420 {
421         struct pcmuio_private *devpriv = dev->private;
422         struct pcmuio_subdev_private *subpriv = s->private;
423         int nports, firstport, asic, port;
424
425         asic = subpriv->intr.asic;
426         if (asic < 0)
427                 return;         /* not an interrupt subdev */
428
429         subpriv->intr.enabled_mask = 0;
430         subpriv->intr.active = 0;
431         s->async->inttrig = NULL;
432         nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
433         firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
434         switch_page(dev, asic, PAGE_ENAB);
435         for (port = firstport; port < firstport + nports; ++port) {
436                 /* disable all intrs for this subdev.. */
437                 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
438         }
439 }
440
441 static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
442                                       struct comedi_subdevice *s,
443                                       unsigned triggered)
444 {
445         struct pcmuio_subdev_private *subpriv = s->private;
446         unsigned int len = s->async->cmd.chanlist_len;
447         unsigned oldevents = s->async->events;
448         unsigned int val = 0;
449         unsigned long flags;
450         unsigned mytrig;
451         unsigned int i;
452
453         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
454
455         if (!subpriv->intr.active)
456                 goto done;
457
458         mytrig = triggered >> subpriv->intr.asic_chan;
459         mytrig &= ((0x1 << subpriv->intr.num_asic_chans) - 1);
460         mytrig <<= subpriv->intr.first_chan;
461
462         if (!(mytrig & subpriv->intr.enabled_mask))
463                 goto done;
464
465         for (i = 0; i < len; i++) {
466                 unsigned int chan = CR_CHAN(s->async->cmd.chanlist[i]);
467                 if (mytrig & (1U << chan))
468                         val |= (1U << i);
469         }
470
471         /* Write the scan to the buffer. */
472         if (comedi_buf_put(s->async, ((short *)&val)[0]) &&
473             comedi_buf_put(s->async, ((short *)&val)[1])) {
474                 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
475         } else {
476                 /* Overflow! Stop acquisition!! */
477                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
478                 pcmuio_stop_intr(dev, s);
479         }
480
481         /* Check for end of acquisition. */
482         if (!subpriv->intr.continuous) {
483                 /* stop_src == TRIG_COUNT */
484                 if (subpriv->intr.stop_count > 0) {
485                         subpriv->intr.stop_count--;
486                         if (subpriv->intr.stop_count == 0) {
487                                 s->async->events |= COMEDI_CB_EOA;
488                                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
489                                 pcmuio_stop_intr(dev, s);
490                         }
491                 }
492         }
493
494 done:
495         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
496
497         if (oldevents != s->async->events)
498                 comedi_event(dev, s);
499 }
500
501 static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
502 {
503         struct pcmuio_private *devpriv = dev->private;
504         struct pcmuio_subdev_private *subpriv;
505         unsigned long iobase = devpriv->asics[asic].iobase;
506         unsigned triggered = 0;
507         int got1 = 0;
508         unsigned long flags;
509         unsigned char int_pend;
510         int i;
511
512         spin_lock_irqsave(&devpriv->asics[asic].spinlock, flags);
513
514         int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
515         if (int_pend) {
516                 for (i = 0; i < INTR_PORTS_PER_ASIC; ++i) {
517                         if (int_pend & (0x1 << i)) {
518                                 unsigned char val;
519
520                                 switch_page(dev, asic, PAGE_INT_ID);
521                                 val = inb(iobase + REG_INT_ID0 + i);
522                                 if (val)
523                                         /* clear pending interrupt */
524                                         outb(0, iobase + REG_INT_ID0 + i);
525
526                                         triggered |= (val << (i * 8));
527                         }
528                 }
529
530                 ++got1;
531         }
532
533         spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, flags);
534
535         if (triggered) {
536                 struct comedi_subdevice *s;
537                 /* TODO here: dispatch io lines to subdevs with commands.. */
538                 for (i = 0; i < dev->n_subdevices; i++) {
539                         s = &dev->subdevices[i];
540                         subpriv = s->private;
541                         if (subpriv->intr.asic == asic) {
542                                 /*
543                                  * This is an interrupt subdev, and it
544                                  * matches this asic!
545                                  */
546                                 pcmuio_handle_intr_subdev(dev, s,
547                                                           triggered);
548                         }
549                 }
550         }
551         return got1;
552 }
553
554 static irqreturn_t interrupt_pcmuio(int irq, void *d)
555 {
556         struct comedi_device *dev = d;
557         struct pcmuio_private *devpriv = dev->private;
558         int got1 = 0;
559         int asic;
560
561         for (asic = 0; asic < MAX_ASICS; ++asic) {
562                 if (irq == devpriv->asics[asic].irq) {
563                         /* it is an interrupt for ASIC #asic */
564                         if (pcmuio_handle_asic_interrupt(dev, asic))
565                                 got1++;
566                 }
567         }
568         if (!got1)
569                 return IRQ_NONE;        /* interrupt from other source */
570         return IRQ_HANDLED;
571 }
572
573 static int pcmuio_start_intr(struct comedi_device *dev,
574                              struct comedi_subdevice *s)
575 {
576         struct pcmuio_private *devpriv = dev->private;
577         struct pcmuio_subdev_private *subpriv = s->private;
578
579         if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
580                 /* An empty acquisition! */
581                 s->async->events |= COMEDI_CB_EOA;
582                 subpriv->intr.active = 0;
583                 return 1;
584         } else {
585                 unsigned bits = 0, pol_bits = 0, n;
586                 int nports, firstport, asic, port;
587                 struct comedi_cmd *cmd = &s->async->cmd;
588
589                 asic = subpriv->intr.asic;
590                 if (asic < 0)
591                         return 1;       /* not an interrupt
592                                            subdev */
593                 subpriv->intr.enabled_mask = 0;
594                 subpriv->intr.active = 1;
595                 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
596                 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
597                 if (cmd->chanlist) {
598                         for (n = 0; n < cmd->chanlist_len; n++) {
599                                 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
600                                 pol_bits |= (CR_AREF(cmd->chanlist[n])
601                                              || CR_RANGE(cmd->
602                                                          chanlist[n]) ? 1U : 0U)
603                                     << CR_CHAN(cmd->chanlist[n]);
604                         }
605                 }
606                 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
607                          1) << subpriv->intr.first_chan;
608                 subpriv->intr.enabled_mask = bits;
609
610                 switch_page(dev, asic, PAGE_ENAB);
611                 for (port = firstport; port < firstport + nports; ++port) {
612                         unsigned enab =
613                             bits >> (subpriv->intr.first_chan + (port -
614                                                                  firstport) *
615                                      8) & 0xff, pol =
616                             pol_bits >> (subpriv->intr.first_chan +
617                                          (port - firstport) * 8) & 0xff;
618                         /* set enab intrs for this subdev.. */
619                         outb(enab,
620                              devpriv->asics[asic].iobase + REG_ENAB0 + port);
621                         switch_page(dev, asic, PAGE_POL);
622                         outb(pol,
623                              devpriv->asics[asic].iobase + REG_ENAB0 + port);
624                 }
625         }
626         return 0;
627 }
628
629 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
630 {
631         struct pcmuio_subdev_private *subpriv = s->private;
632         unsigned long flags;
633
634         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
635         if (subpriv->intr.active)
636                 pcmuio_stop_intr(dev, s);
637         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
638
639         return 0;
640 }
641
642 /*
643  * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
644  */
645 static int
646 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
647                           unsigned int trignum)
648 {
649         struct pcmuio_subdev_private *subpriv = s->private;
650         unsigned long flags;
651         int event = 0;
652
653         if (trignum != 0)
654                 return -EINVAL;
655
656         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
657         s->async->inttrig = NULL;
658         if (subpriv->intr.active)
659                 event = pcmuio_start_intr(dev, s);
660
661         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
662
663         if (event)
664                 comedi_event(dev, s);
665
666         return 1;
667 }
668
669 /*
670  * 'do_cmd' function for an 'INTERRUPT' subdevice.
671  */
672 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
673 {
674         struct pcmuio_subdev_private *subpriv = s->private;
675         struct comedi_cmd *cmd = &s->async->cmd;
676         unsigned long flags;
677         int event = 0;
678
679         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
680         subpriv->intr.active = 1;
681
682         /* Set up end of acquisition. */
683         switch (cmd->stop_src) {
684         case TRIG_COUNT:
685                 subpriv->intr.continuous = 0;
686                 subpriv->intr.stop_count = cmd->stop_arg;
687                 break;
688         default:
689                 /* TRIG_NONE */
690                 subpriv->intr.continuous = 1;
691                 subpriv->intr.stop_count = 0;
692                 break;
693         }
694
695         /* Set up start of acquisition. */
696         switch (cmd->start_src) {
697         case TRIG_INT:
698                 s->async->inttrig = pcmuio_inttrig_start_intr;
699                 break;
700         default:
701                 /* TRIG_NOW */
702                 event = pcmuio_start_intr(dev, s);
703                 break;
704         }
705         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
706
707         if (event)
708                 comedi_event(dev, s);
709
710         return 0;
711 }
712
713 static int pcmuio_cmdtest(struct comedi_device *dev,
714                           struct comedi_subdevice *s,
715                           struct comedi_cmd *cmd)
716 {
717         int err = 0;
718
719         /* Step 1 : check if triggers are trivially valid */
720
721         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
722         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
723         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
724         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
725         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
726
727         if (err)
728                 return 1;
729
730         /* Step 2a : make sure trigger sources are unique */
731
732         err |= cfc_check_trigger_is_unique(cmd->start_src);
733         err |= cfc_check_trigger_is_unique(cmd->stop_src);
734
735         /* Step 2b : and mutually compatible */
736
737         if (err)
738                 return 2;
739
740         /* Step 3: check if arguments are trivially valid */
741
742         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
743         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
744         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
745         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
746
747         switch (cmd->stop_src) {
748         case TRIG_COUNT:
749                 /* any count allowed */
750                 break;
751         case TRIG_NONE:
752                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
753                 break;
754         default:
755                 break;
756         }
757
758         if (err)
759                 return 3;
760
761         /* step 4: fix up any arguments */
762
763         /* if (err) return 4; */
764
765         return 0;
766 }
767
768 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
769 {
770         const struct pcmuio_board *board = comedi_board(dev);
771         struct comedi_subdevice *s;
772         struct pcmuio_private *devpriv;
773         struct pcmuio_subdev_private *subpriv;
774         int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
775         unsigned int irq[MAX_ASICS];
776         int ret;
777
778         irq[0] = it->options[1];
779         irq[1] = it->options[2];
780
781         ret = comedi_request_region(dev, it->options[0],
782                                     board->num_asics * ASIC_IOSIZE);
783         if (ret)
784                 return ret;
785
786         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
787         if (!devpriv)
788                 return -ENOMEM;
789         dev->private = devpriv;
790
791         for (asic = 0; asic < MAX_ASICS; ++asic) {
792                 devpriv->asics[asic].num = asic;
793                 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
794                 spin_lock_init(&devpriv->asics[asic].spinlock);
795         }
796
797         chans_left = CHANS_PER_ASIC * board->num_asics;
798         n_subdevs = (chans_left / MAX_CHANS_PER_SUBDEV) +
799                     (!!(chans_left % MAX_CHANS_PER_SUBDEV));
800         devpriv->sprivs = kcalloc(n_subdevs,
801                                   sizeof(struct pcmuio_subdev_private),
802                                   GFP_KERNEL);
803         if (!devpriv->sprivs)
804                 return -ENOMEM;
805
806         ret = comedi_alloc_subdevices(dev, n_subdevs);
807         if (ret)
808                 return ret;
809
810         port = 0;
811         asic = 0;
812         for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
813                 int byte_no;
814
815                 s = &dev->subdevices[sdev_no];
816                 subpriv = &devpriv->sprivs[sdev_no];
817                 s->private = subpriv;
818                 s->maxdata = 1;
819                 s->range_table = &range_digital;
820                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
821                 s->type = COMEDI_SUBD_DIO;
822                 s->insn_bits = pcmuio_dio_insn_bits;
823                 s->insn_config = pcmuio_dio_insn_config;
824                 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
825                 subpriv->intr.asic = -1;
826                 subpriv->intr.first_chan = -1;
827                 subpriv->intr.asic_chan = -1;
828                 subpriv->intr.num_asic_chans = -1;
829                 subpriv->intr.active = 0;
830                 s->len_chanlist = 1;
831
832                 /* save the ioport address for each 'port' of 8 channels in the
833                    subdevice */
834                 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV;
835                      ++byte_no, ++port) {
836                         if (port >= PORTS_PER_ASIC) {
837                                 port = 0;
838                                 ++asic;
839                                 thisasic_chanct = 0;
840                         }
841                         subpriv->iobases[byte_no] =
842                             devpriv->asics[asic].iobase + port;
843
844                         if (thisasic_chanct <
845                             CHANS_PER_PORT * INTR_PORTS_PER_ASIC
846                             && subpriv->intr.asic < 0) {
847                                 /* setup the interrupt subdevice */
848                                 subpriv->intr.asic = asic;
849                                 subpriv->intr.active = 0;
850                                 subpriv->intr.stop_count = 0;
851                                 subpriv->intr.first_chan = byte_no * 8;
852                                 subpriv->intr.asic_chan = thisasic_chanct;
853                                 subpriv->intr.num_asic_chans =
854                                     s->n_chan - subpriv->intr.first_chan;
855                                 dev->read_subdev = s;
856                                 s->subdev_flags |= SDF_CMD_READ;
857                                 s->cancel = pcmuio_cancel;
858                                 s->do_cmd = pcmuio_cmd;
859                                 s->do_cmdtest = pcmuio_cmdtest;
860                                 s->len_chanlist = subpriv->intr.num_asic_chans;
861                         }
862                         thisasic_chanct += CHANS_PER_PORT;
863                 }
864                 spin_lock_init(&subpriv->intr.spinlock);
865
866                 chans_left -= s->n_chan;
867
868                 if (!chans_left) {
869                         /* reset to our first asic, to do intr subdevs */
870                         asic = 0;
871                         port = 0;
872                 }
873
874         }
875
876         init_asics(dev);        /* clear out all the registers, basically */
877
878         for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
879                 if (irq[asic]
880                     && request_irq(irq[asic], interrupt_pcmuio,
881                                    IRQF_SHARED, board->name, dev)) {
882                         int i;
883                         /* unroll the allocated irqs.. */
884                         for (i = asic - 1; i >= 0; --i) {
885                                 free_irq(irq[i], dev);
886                                 devpriv->asics[i].irq = irq[i] = 0;
887                         }
888                         irq[asic] = 0;
889                 }
890                 devpriv->asics[asic].irq = irq[asic];
891         }
892
893         if (irq[0]) {
894                 dev_dbg(dev->class_dev, "irq: %u\n", irq[0]);
895                 if (irq[1] && board->num_asics == 2)
896                         dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
897                                 irq[1]);
898         } else {
899                 dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
900         }
901
902
903         return 1;
904 }
905
906 static void pcmuio_detach(struct comedi_device *dev)
907 {
908         struct pcmuio_private *devpriv = dev->private;
909         int i;
910
911         for (i = 0; i < MAX_ASICS; ++i) {
912                 if (devpriv->asics[i].irq)
913                         free_irq(devpriv->asics[i].irq, dev);
914         }
915         if (devpriv && devpriv->sprivs)
916                 kfree(devpriv->sprivs);
917         comedi_legacy_detach(dev);
918 }
919
920 static struct comedi_driver pcmuio_driver = {
921         .driver_name    = "pcmuio",
922         .module         = THIS_MODULE,
923         .attach         = pcmuio_attach,
924         .detach         = pcmuio_detach,
925         .board_name     = &pcmuio_boards[0].name,
926         .offset         = sizeof(struct pcmuio_board),
927         .num_names      = ARRAY_SIZE(pcmuio_boards),
928 };
929 module_comedi_driver(pcmuio_driver);
930
931 MODULE_AUTHOR("Comedi http://www.comedi.org");
932 MODULE_DESCRIPTION("Comedi low-level driver");
933 MODULE_LICENSE("GPL");