]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/pcmuio.c
staging: comedi: pcmuio: remove 'CALC_N_SUBDEVS' macro
[karo-tx-linux.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 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
87 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
88 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
89 #define INTR_CHANS_PER_ASIC 24
90 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
91 #define MAX_DIO_CHANS   (PORTS_PER_ASIC*2*CHANS_PER_PORT)
92 #define MAX_ASICS       (MAX_DIO_CHANS/CHANS_PER_ASIC)
93 /* IO Memory sizes */
94 #define ASIC_IOSIZE (0x10)
95 #define PCMUIO48_IOSIZE ASIC_IOSIZE
96 #define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
97
98 /* Some offsets - these are all in the 16byte IO memory offset from
99    the base address.  Note that there is a paging scheme to swap out
100    offsets 0x8-0xA using the PAGELOCK register.  See the table below.
101
102   Register(s)       Pages        R/W?        Description
103   --------------------------------------------------------------
104   REG_PORTx         All          R/W         Read/Write/Configure IO
105   REG_INT_PENDING   All          ReadOnly    Quickly see which INT_IDx has int.
106   REG_PAGELOCK      All          WriteOnly   Select a page
107   REG_POLx          Pg. 1 only   WriteOnly   Select edge-detection polarity
108   REG_ENABx         Pg. 2 only   WriteOnly   Enable/Disable edge-detect. int.
109   REG_INT_IDx       Pg. 3 only   R/W         See which ports/bits have ints.
110  */
111 #define REG_PORT0 0x0
112 #define REG_PORT1 0x1
113 #define REG_PORT2 0x2
114 #define REG_PORT3 0x3
115 #define REG_PORT4 0x4
116 #define REG_PORT5 0x5
117 #define REG_INT_PENDING 0x6
118 #define REG_PAGELOCK 0x7        /* page selector register, upper 2 bits select a page
119                                    and bits 0-5 are used to 'lock down' a particular
120                                    port above to make it readonly.  */
121 #define REG_POL0 0x8
122 #define REG_POL1 0x9
123 #define REG_POL2 0xA
124 #define REG_ENAB0 0x8
125 #define REG_ENAB1 0x9
126 #define REG_ENAB2 0xA
127 #define REG_INT_ID0 0x8
128 #define REG_INT_ID1 0x9
129 #define REG_INT_ID2 0xA
130
131 #define NUM_PAGED_REGS 3
132 #define NUM_PAGES 4
133 #define FIRST_PAGED_REG 0x8
134 #define REG_PAGE_BITOFFSET 6
135 #define REG_LOCK_BITOFFSET 0
136 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
137 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
138 #define PAGE_POL 1
139 #define PAGE_ENAB 2
140 #define PAGE_INT_ID 3
141
142 /*
143  * Board descriptions for two imaginary boards.  Describing the
144  * boards in this way is optional, and completely driver-dependent.
145  * Some drivers use arrays such as this, other do not.
146  */
147 struct pcmuio_board {
148         const char *name;
149         const int num_asics;
150         const int num_channels_per_port;
151         const int num_ports;
152 };
153
154 /* this structure is for data unique to this subdevice.  */
155 struct pcmuio_subdev_private {
156         /* mapping of halfwords (bytes) in port/chanarray to iobase */
157         unsigned long iobases[PORTS_PER_SUBDEV];
158
159         /* The below is only used for intr subdevices */
160         struct {
161                 int asic;       /* if non-negative, this subdev has an interrupt asic */
162                 int first_chan; /* if nonnegative, the first channel id for
163                                    interrupts. */
164                 int num_asic_chans;     /* the number of asic channels in this subdev
165                                            that have interrutps */
166                 int asic_chan;  /* if nonnegative, the first channel id with
167                                    respect to the asic that has interrupts */
168                 int enabled_mask;       /* subdev-relative channel mask for channels
169                                            we are interested in */
170                 int active;
171                 int stop_count;
172                 int continuous;
173                 spinlock_t spinlock;
174         } intr;
175 };
176
177 /* this structure is for data unique to this hardware driver.  If
178    several hardware drivers keep similar information in this structure,
179    feel free to suggest moving the variable to the struct comedi_device struct.  */
180 struct pcmuio_private {
181         struct {
182                 unsigned char pagelock; /* current page and lock */
183                 unsigned char pol[NUM_PAGED_REGS];      /* shadow of POLx registers */
184                 unsigned char enab[NUM_PAGED_REGS];     /* shadow of ENABx registers */
185                 int num;
186                 unsigned long iobase;
187                 unsigned int irq;
188                 spinlock_t spinlock;
189         } asics[MAX_ASICS];
190         struct pcmuio_subdev_private *sprivs;
191 };
192
193 /* DIO devices are slightly special.  Although it is possible to
194  * implement the insn_read/insn_write interface, it is much more
195  * useful to applications if you implement the insn_bits interface.
196  * This allows packed reading/writing of the DIO channels.  The
197  * comedi core can convert between insn_bits and insn_read/write */
198 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
199                                 struct comedi_subdevice *s,
200                                 struct comedi_insn *insn, unsigned int *data)
201 {
202         struct pcmuio_subdev_private *subpriv = s->private;
203         int byte_no;
204
205         /* NOTE:
206            reading a 0 means this channel was high
207            writine a 0 sets the channel high
208            reading a 1 means this channel was low
209            writing a 1 means set this channel low
210
211            Therefore everything is always inverted. */
212
213         /* The insn data is a mask in data[0] and the new data
214          * in data[1], each channel cooresponding to a bit. */
215
216 #ifdef DAMMIT_ITS_BROKEN
217         /* DEBUG */
218         dev_dbg(dev->class_dev, "write mask: %08x  data: %08x\n", data[0],
219                 data[1]);
220 #endif
221
222         s->state = 0;
223
224         for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
225                 /* address of 8-bit port */
226                 unsigned long ioaddr = subpriv->iobases[byte_no],
227                     /* bit offset of port in 32-bit doubleword */
228                     offset = byte_no * 8;
229                 /* this 8-bit port's data */
230                 unsigned char byte = 0,
231                     /* The write mask for this port (if any) */
232                     write_mask_byte = (data[0] >> offset) & 0xff,
233                     /* The data byte for this port */
234                     data_byte = (data[1] >> offset) & 0xff;
235
236                 byte = inb(ioaddr);     /* read all 8-bits for this port */
237
238 #ifdef DAMMIT_ITS_BROKEN
239                 /* DEBUG */
240                 printk
241                     ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
242                      byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
243                      offset, ioaddr, (unsigned)byte);
244 #endif
245
246                 if (write_mask_byte) {
247                         /* this byte has some write_bits -- so set the output lines */
248                         byte &= ~write_mask_byte;       /* clear bits for write mask */
249                         byte |= ~data_byte & write_mask_byte;   /* set to inverted data_byte */
250                         /* Write out the new digital output state */
251                         outb(byte, ioaddr);
252                 }
253 #ifdef DAMMIT_ITS_BROKEN
254                 /* DEBUG */
255                 dev_dbg(dev->class_dev, "data_out_byte %02x\n", (unsigned)byte);
256 #endif
257                 /* save the digital input lines for this byte.. */
258                 s->state |= ((unsigned int)byte) << offset;
259         }
260
261         /* now return the DIO lines to data[1] - note they came inverted! */
262         data[1] = ~s->state;
263
264 #ifdef DAMMIT_ITS_BROKEN
265         /* DEBUG */
266         dev_dbg(dev->class_dev, "s->state %08x data_out %08x\n", s->state,
267                 data[1]);
268 #endif
269
270         return insn->n;
271 }
272
273 /* The input or output configuration of each digital line is
274  * configured by a special insn_config instruction.  chanspec
275  * contains the channel to be changed, and data[0] contains the
276  * value COMEDI_INPUT or COMEDI_OUTPUT. */
277 static int pcmuio_dio_insn_config(struct comedi_device *dev,
278                                   struct comedi_subdevice *s,
279                                   struct comedi_insn *insn, unsigned int *data)
280 {
281         struct pcmuio_subdev_private *subpriv = s->private;
282         int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
283             chan % 8;
284         unsigned long ioaddr;
285         unsigned char byte;
286
287         /* Compute ioaddr for this channel */
288         ioaddr = subpriv->iobases[byte_no];
289
290         /* NOTE:
291            writing a 0 an IO channel's bit sets the channel to INPUT
292            and pulls the line high as well
293
294            writing a 1 to an IO channel's  bit pulls the line low
295
296            All channels are implicitly always in OUTPUT mode -- but when
297            they are high they can be considered to be in INPUT mode..
298
299            Thus, we only force channels low if the config request was INPUT,
300            otherwise we do nothing to the hardware.    */
301
302         switch (data[0]) {
303         case INSN_CONFIG_DIO_OUTPUT:
304                 /* save to io_bits -- don't actually do anything since
305                    all input channels are also output channels... */
306                 s->io_bits |= 1 << chan;
307                 break;
308         case INSN_CONFIG_DIO_INPUT:
309                 /* write a 0 to the actual register representing the channel
310                    to set it to 'input'.  0 means "float high". */
311                 byte = inb(ioaddr);
312                 byte &= ~(1 << bit_no);
313                                 /**< set input channel to '0' */
314
315                 /* write out byte -- this is the only time we actually affect the
316                    hardware as all channels are implicitly output -- but input
317                    channels are set to float-high */
318                 outb(byte, ioaddr);
319
320                 /* save to io_bits */
321                 s->io_bits &= ~(1 << chan);
322                 break;
323
324         case INSN_CONFIG_DIO_QUERY:
325                 /* retrieve from shadow register */
326                 data[1] =
327                     (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
328                 return insn->n;
329                 break;
330
331         default:
332                 return -EINVAL;
333                 break;
334         }
335
336         return insn->n;
337 }
338
339 static void switch_page(struct comedi_device *dev, int asic, int page)
340 {
341         const struct pcmuio_board *board = comedi_board(dev);
342         struct pcmuio_private *devpriv = dev->private;
343
344         if (asic < 0 || asic >= board->num_asics)
345                 return;         /* paranoia */
346         if (page < 0 || page >= NUM_PAGES)
347                 return;         /* more paranoia */
348
349         devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
350         devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
351
352         /* now write out the shadow register */
353         outb(devpriv->asics[asic].pagelock,
354              dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
355 }
356
357 static void init_asics(struct comedi_device *dev)
358 {                               /* sets up an
359                                    ASIC chip to defaults */
360         const struct pcmuio_board *board = comedi_board(dev);
361         int asic;
362
363         for (asic = 0; asic < board->num_asics; ++asic) {
364                 int port, page;
365                 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
366
367                 switch_page(dev, asic, 0);      /* switch back to page 0 */
368
369                 /* first, clear all the DIO port bits */
370                 for (port = 0; port < PORTS_PER_ASIC; ++port)
371                         outb(0, baseaddr + REG_PORT0 + port);
372
373                 /* Next, clear all the paged registers for each page */
374                 for (page = 1; page < NUM_PAGES; ++page) {
375                         int reg;
376                         /* now clear all the paged registers */
377                         switch_page(dev, asic, page);
378                         for (reg = FIRST_PAGED_REG;
379                              reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
380                                 outb(0, baseaddr + reg);
381                 }
382
383                 /* DEBUG  set rising edge interrupts on port0 of both asics */
384                 /*switch_page(dev, asic, PAGE_POL);
385                    outb(0xff, baseaddr + REG_POL0);
386                    switch_page(dev, asic, PAGE_ENAB);
387                    outb(0xff, baseaddr + REG_ENAB0); */
388                 /* END DEBUG */
389
390                 switch_page(dev, asic, 0);      /* switch back to default page 0 */
391
392         }
393 }
394
395 #ifdef notused
396 static void lock_port(struct comedi_device *dev, int asic, int port)
397 {
398         const struct pcmuio_board *board = comedi_board(dev);
399         struct pcmuio_private *devpriv = dev->private;
400
401         if (asic < 0 || asic >= board->num_asics)
402                 return;         /* paranoia */
403         if (port < 0 || port >= PORTS_PER_ASIC)
404                 return;         /* more paranoia */
405
406         devpriv->asics[asic].pagelock |= 0x1 << port;
407         /* now write out the shadow register */
408         outb(devpriv->asics[asic].pagelock,
409              dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
410 }
411
412 static void unlock_port(struct comedi_device *dev, int asic, int port)
413 {
414         const struct pcmuio_board *board = comedi_board(dev);
415         struct pcmuio_private *devpriv = dev->private;
416
417         if (asic < 0 || asic >= board->num_asics)
418                 return;         /* paranoia */
419         if (port < 0 || port >= PORTS_PER_ASIC)
420                 return;         /* more paranoia */
421         devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
422         /* now write out the shadow register */
423         outb(devpriv->asics[asic].pagelock,
424              dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
425 }
426 #endif /* notused */
427
428 static void pcmuio_stop_intr(struct comedi_device *dev,
429                              struct comedi_subdevice *s)
430 {
431         struct pcmuio_private *devpriv = dev->private;
432         struct pcmuio_subdev_private *subpriv = s->private;
433         int nports, firstport, asic, port;
434
435         asic = subpriv->intr.asic;
436         if (asic < 0)
437                 return;         /* not an interrupt subdev */
438
439         subpriv->intr.enabled_mask = 0;
440         subpriv->intr.active = 0;
441         s->async->inttrig = NULL;
442         nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
443         firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
444         switch_page(dev, asic, PAGE_ENAB);
445         for (port = firstport; port < firstport + nports; ++port) {
446                 /* disable all intrs for this subdev.. */
447                 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
448         }
449 }
450
451 static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
452                                       struct comedi_subdevice *s,
453                                       unsigned triggered)
454 {
455         struct pcmuio_subdev_private *subpriv = s->private;
456         unsigned int len = s->async->cmd.chanlist_len;
457         unsigned oldevents = s->async->events;
458         unsigned int val = 0;
459         unsigned long flags;
460         unsigned mytrig;
461         unsigned int i;
462
463         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
464
465         if (!subpriv->intr.active)
466                 goto done;
467
468         mytrig = triggered >> subpriv->intr.asic_chan;
469         mytrig &= ((0x1 << subpriv->intr.num_asic_chans) - 1);
470         mytrig <<= subpriv->intr.first_chan;
471
472         if (!(mytrig & subpriv->intr.enabled_mask))
473                 goto done;
474
475         for (i = 0; i < len; i++) {
476                 unsigned int chan = CR_CHAN(s->async->cmd.chanlist[i]);
477                 if (mytrig & (1U << chan))
478                         val |= (1U << i);
479         }
480
481         /* Write the scan to the buffer. */
482         if (comedi_buf_put(s->async, ((short *)&val)[0]) &&
483             comedi_buf_put(s->async, ((short *)&val)[1])) {
484                 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
485         } else {
486                 /* Overflow! Stop acquisition!! */
487                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
488                 pcmuio_stop_intr(dev, s);
489         }
490
491         /* Check for end of acquisition. */
492         if (!subpriv->intr.continuous) {
493                 /* stop_src == TRIG_COUNT */
494                 if (subpriv->intr.stop_count > 0) {
495                         subpriv->intr.stop_count--;
496                         if (subpriv->intr.stop_count == 0) {
497                                 s->async->events |= COMEDI_CB_EOA;
498                                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
499                                 pcmuio_stop_intr(dev, s);
500                         }
501                 }
502         }
503
504 done:
505         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
506
507         if (oldevents != s->async->events)
508                 comedi_event(dev, s);
509 }
510
511 static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
512 {
513         struct pcmuio_private *devpriv = dev->private;
514         struct pcmuio_subdev_private *subpriv;
515         unsigned long iobase = devpriv->asics[asic].iobase;
516         unsigned triggered = 0;
517         int got1 = 0;
518         unsigned long flags;
519         unsigned char int_pend;
520         int i;
521
522         spin_lock_irqsave(&devpriv->asics[asic].spinlock, flags);
523
524         int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
525         if (int_pend) {
526                 for (i = 0; i < INTR_PORTS_PER_ASIC; ++i) {
527                         if (int_pend & (0x1 << i)) {
528                                 unsigned char val;
529
530                                 switch_page(dev, asic, PAGE_INT_ID);
531                                 val = inb(iobase + REG_INT_ID0 + i);
532                                 if (val)
533                                         /* clear pending interrupt */
534                                         outb(0, iobase + REG_INT_ID0 + i);
535
536                                         triggered |= (val << (i * 8));
537                         }
538                 }
539
540                 ++got1;
541         }
542
543         spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, flags);
544
545         if (triggered) {
546                 struct comedi_subdevice *s;
547                 /* TODO here: dispatch io lines to subdevs with commands.. */
548                 for (i = 0; i < dev->n_subdevices; i++) {
549                         s = &dev->subdevices[i];
550                         subpriv = s->private;
551                         if (subpriv->intr.asic == asic) {
552                                 /*
553                                  * This is an interrupt subdev, and it
554                                  * matches this asic!
555                                  */
556                                 pcmuio_handle_intr_subdev(dev, s,
557                                                           triggered);
558                         }
559                 }
560         }
561         return got1;
562 }
563
564 static irqreturn_t interrupt_pcmuio(int irq, void *d)
565 {
566         struct comedi_device *dev = d;
567         struct pcmuio_private *devpriv = dev->private;
568         int got1 = 0;
569         int asic;
570
571         for (asic = 0; asic < MAX_ASICS; ++asic) {
572                 if (irq == devpriv->asics[asic].irq) {
573                         /* it is an interrupt for ASIC #asic */
574                         if (pcmuio_handle_asic_interrupt(dev, asic))
575                                 got1++;
576                 }
577         }
578         if (!got1)
579                 return IRQ_NONE;        /* interrupt from other source */
580         return IRQ_HANDLED;
581 }
582
583 static int pcmuio_start_intr(struct comedi_device *dev,
584                              struct comedi_subdevice *s)
585 {
586         struct pcmuio_private *devpriv = dev->private;
587         struct pcmuio_subdev_private *subpriv = s->private;
588
589         if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
590                 /* An empty acquisition! */
591                 s->async->events |= COMEDI_CB_EOA;
592                 subpriv->intr.active = 0;
593                 return 1;
594         } else {
595                 unsigned bits = 0, pol_bits = 0, n;
596                 int nports, firstport, asic, port;
597                 struct comedi_cmd *cmd = &s->async->cmd;
598
599                 asic = subpriv->intr.asic;
600                 if (asic < 0)
601                         return 1;       /* not an interrupt
602                                            subdev */
603                 subpriv->intr.enabled_mask = 0;
604                 subpriv->intr.active = 1;
605                 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
606                 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
607                 if (cmd->chanlist) {
608                         for (n = 0; n < cmd->chanlist_len; n++) {
609                                 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
610                                 pol_bits |= (CR_AREF(cmd->chanlist[n])
611                                              || CR_RANGE(cmd->
612                                                          chanlist[n]) ? 1U : 0U)
613                                     << CR_CHAN(cmd->chanlist[n]);
614                         }
615                 }
616                 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
617                          1) << subpriv->intr.first_chan;
618                 subpriv->intr.enabled_mask = bits;
619
620                 switch_page(dev, asic, PAGE_ENAB);
621                 for (port = firstport; port < firstport + nports; ++port) {
622                         unsigned enab =
623                             bits >> (subpriv->intr.first_chan + (port -
624                                                                  firstport) *
625                                      8) & 0xff, pol =
626                             pol_bits >> (subpriv->intr.first_chan +
627                                          (port - firstport) * 8) & 0xff;
628                         /* set enab intrs for this subdev.. */
629                         outb(enab,
630                              devpriv->asics[asic].iobase + REG_ENAB0 + port);
631                         switch_page(dev, asic, PAGE_POL);
632                         outb(pol,
633                              devpriv->asics[asic].iobase + REG_ENAB0 + port);
634                 }
635         }
636         return 0;
637 }
638
639 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
640 {
641         struct pcmuio_subdev_private *subpriv = s->private;
642         unsigned long flags;
643
644         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
645         if (subpriv->intr.active)
646                 pcmuio_stop_intr(dev, s);
647         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
648
649         return 0;
650 }
651
652 /*
653  * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
654  */
655 static int
656 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
657                           unsigned int trignum)
658 {
659         struct pcmuio_subdev_private *subpriv = s->private;
660         unsigned long flags;
661         int event = 0;
662
663         if (trignum != 0)
664                 return -EINVAL;
665
666         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
667         s->async->inttrig = NULL;
668         if (subpriv->intr.active)
669                 event = pcmuio_start_intr(dev, s);
670
671         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
672
673         if (event)
674                 comedi_event(dev, s);
675
676         return 1;
677 }
678
679 /*
680  * 'do_cmd' function for an 'INTERRUPT' subdevice.
681  */
682 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
683 {
684         struct pcmuio_subdev_private *subpriv = s->private;
685         struct comedi_cmd *cmd = &s->async->cmd;
686         unsigned long flags;
687         int event = 0;
688
689         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
690         subpriv->intr.active = 1;
691
692         /* Set up end of acquisition. */
693         switch (cmd->stop_src) {
694         case TRIG_COUNT:
695                 subpriv->intr.continuous = 0;
696                 subpriv->intr.stop_count = cmd->stop_arg;
697                 break;
698         default:
699                 /* TRIG_NONE */
700                 subpriv->intr.continuous = 1;
701                 subpriv->intr.stop_count = 0;
702                 break;
703         }
704
705         /* Set up start of acquisition. */
706         switch (cmd->start_src) {
707         case TRIG_INT:
708                 s->async->inttrig = pcmuio_inttrig_start_intr;
709                 break;
710         default:
711                 /* TRIG_NOW */
712                 event = pcmuio_start_intr(dev, s);
713                 break;
714         }
715         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
716
717         if (event)
718                 comedi_event(dev, s);
719
720         return 0;
721 }
722
723 static int pcmuio_cmdtest(struct comedi_device *dev,
724                           struct comedi_subdevice *s,
725                           struct comedi_cmd *cmd)
726 {
727         int err = 0;
728
729         /* Step 1 : check if triggers are trivially valid */
730
731         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
732         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
733         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
734         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
735         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
736
737         if (err)
738                 return 1;
739
740         /* Step 2a : make sure trigger sources are unique */
741
742         err |= cfc_check_trigger_is_unique(cmd->start_src);
743         err |= cfc_check_trigger_is_unique(cmd->stop_src);
744
745         /* Step 2b : and mutually compatible */
746
747         if (err)
748                 return 2;
749
750         /* Step 3: check if arguments are trivially valid */
751
752         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
753         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
754         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
755         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
756
757         switch (cmd->stop_src) {
758         case TRIG_COUNT:
759                 /* any count allowed */
760                 break;
761         case TRIG_NONE:
762                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
763                 break;
764         default:
765                 break;
766         }
767
768         if (err)
769                 return 3;
770
771         /* step 4: fix up any arguments */
772
773         /* if (err) return 4; */
774
775         return 0;
776 }
777
778 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
779 {
780         const struct pcmuio_board *board = comedi_board(dev);
781         struct comedi_subdevice *s;
782         struct pcmuio_private *devpriv;
783         struct pcmuio_subdev_private *subpriv;
784         int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
785         unsigned int irq[MAX_ASICS];
786         int ret;
787
788         irq[0] = it->options[1];
789         irq[1] = it->options[2];
790
791         ret = comedi_request_region(dev, it->options[0],
792                                     board->num_asics * ASIC_IOSIZE);
793         if (ret)
794                 return ret;
795
796         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
797         if (!devpriv)
798                 return -ENOMEM;
799         dev->private = devpriv;
800
801         for (asic = 0; asic < MAX_ASICS; ++asic) {
802                 devpriv->asics[asic].num = asic;
803                 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
804                 devpriv->asics[asic].irq = 0;   /* this gets actually set at the end of
805                                                    this function when we
806                                                    request_irqs */
807                 spin_lock_init(&devpriv->asics[asic].spinlock);
808         }
809
810         chans_left = CHANS_PER_ASIC * board->num_asics;
811         n_subdevs = (chans_left / MAX_CHANS_PER_SUBDEV) +
812                     (!!(chans_left % MAX_CHANS_PER_SUBDEV));
813         devpriv->sprivs = kcalloc(n_subdevs,
814                                   sizeof(struct pcmuio_subdev_private),
815                                   GFP_KERNEL);
816         if (!devpriv->sprivs)
817                 return -ENOMEM;
818
819         ret = comedi_alloc_subdevices(dev, n_subdevs);
820         if (ret)
821                 return ret;
822
823         port = 0;
824         asic = 0;
825         for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
826                 int byte_no;
827
828                 s = &dev->subdevices[sdev_no];
829                 subpriv = &devpriv->sprivs[sdev_no];
830                 s->private = subpriv;
831                 s->maxdata = 1;
832                 s->range_table = &range_digital;
833                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
834                 s->type = COMEDI_SUBD_DIO;
835                 s->insn_bits = pcmuio_dio_insn_bits;
836                 s->insn_config = pcmuio_dio_insn_config;
837                 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
838                 subpriv->intr.asic = -1;
839                 subpriv->intr.first_chan = -1;
840                 subpriv->intr.asic_chan = -1;
841                 subpriv->intr.num_asic_chans = -1;
842                 subpriv->intr.active = 0;
843                 s->len_chanlist = 1;
844
845                 /* save the ioport address for each 'port' of 8 channels in the
846                    subdevice */
847                 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
848                         if (port >= PORTS_PER_ASIC) {
849                                 port = 0;
850                                 ++asic;
851                                 thisasic_chanct = 0;
852                         }
853                         subpriv->iobases[byte_no] =
854                             devpriv->asics[asic].iobase + port;
855
856                         if (thisasic_chanct <
857                             CHANS_PER_PORT * INTR_PORTS_PER_ASIC
858                             && subpriv->intr.asic < 0) {
859                                 /* this is an interrupt subdevice, so setup the struct */
860                                 subpriv->intr.asic = asic;
861                                 subpriv->intr.active = 0;
862                                 subpriv->intr.stop_count = 0;
863                                 subpriv->intr.first_chan = byte_no * 8;
864                                 subpriv->intr.asic_chan = thisasic_chanct;
865                                 subpriv->intr.num_asic_chans =
866                                     s->n_chan - subpriv->intr.first_chan;
867                                 dev->read_subdev = s;
868                                 s->subdev_flags |= SDF_CMD_READ;
869                                 s->cancel = pcmuio_cancel;
870                                 s->do_cmd = pcmuio_cmd;
871                                 s->do_cmdtest = pcmuio_cmdtest;
872                                 s->len_chanlist = subpriv->intr.num_asic_chans;
873                         }
874                         thisasic_chanct += CHANS_PER_PORT;
875                 }
876                 spin_lock_init(&subpriv->intr.spinlock);
877
878                 chans_left -= s->n_chan;
879
880                 if (!chans_left) {
881                         asic = 0;       /* reset the asic to our first asic, to do intr subdevs */
882                         port = 0;
883                 }
884
885         }
886
887         init_asics(dev);        /* clear out all the registers, basically */
888
889         for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
890                 if (irq[asic]
891                     && request_irq(irq[asic], interrupt_pcmuio,
892                                    IRQF_SHARED, board->name, dev)) {
893                         int i;
894                         /* unroll the allocated irqs.. */
895                         for (i = asic - 1; i >= 0; --i) {
896                                 free_irq(irq[i], dev);
897                                 devpriv->asics[i].irq = irq[i] = 0;
898                         }
899                         irq[asic] = 0;
900                 }
901                 devpriv->asics[asic].irq = irq[asic];
902         }
903
904         if (irq[0]) {
905                 dev_dbg(dev->class_dev, "irq: %u\n", irq[0]);
906                 if (irq[1] && board->num_asics == 2)
907                         dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
908                                 irq[1]);
909         } else {
910                 dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
911         }
912
913
914         return 1;
915 }
916
917 static void pcmuio_detach(struct comedi_device *dev)
918 {
919         struct pcmuio_private *devpriv = dev->private;
920         int i;
921
922         for (i = 0; i < MAX_ASICS; ++i) {
923                 if (devpriv->asics[i].irq)
924                         free_irq(devpriv->asics[i].irq, dev);
925         }
926         if (devpriv && devpriv->sprivs)
927                 kfree(devpriv->sprivs);
928         comedi_legacy_detach(dev);
929 }
930
931 static const struct pcmuio_board pcmuio_boards[] = {
932         {
933                 .name           = "pcmuio48",
934                 .num_asics      = 1,
935                 .num_ports      = 6,
936         }, {
937                 .name           = "pcmuio96",
938                 .num_asics      = 2,
939                 .num_ports      = 12,
940         },
941 };
942
943 static struct comedi_driver pcmuio_driver = {
944         .driver_name    = "pcmuio",
945         .module         = THIS_MODULE,
946         .attach         = pcmuio_attach,
947         .detach         = pcmuio_detach,
948         .board_name     = &pcmuio_boards[0].name,
949         .offset         = sizeof(struct pcmuio_board),
950         .num_names      = ARRAY_SIZE(pcmuio_boards),
951 };
952 module_comedi_driver(pcmuio_driver);
953
954 MODULE_AUTHOR("Comedi http://www.comedi.org");
955 MODULE_DESCRIPTION("Comedi low-level driver");
956 MODULE_LICENSE("GPL");