Merge tag 'for-linus-20140808' of git://git.infradead.org/linux-mtd
[cascardo/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/module.h>
77 #include <linux/interrupt.h>
78
79 #include "../comedidev.h"
80
81 #include "comedi_fc.h"
82
83 /*
84  * Register I/O map
85  *
86  * Offset    Page 0       Page 1       Page 2       Page 3
87  * ------  -----------  -----------  -----------  -----------
88  *  0x00   Port 0 I/O   Port 0 I/O   Port 0 I/O   Port 0 I/O
89  *  0x01   Port 1 I/O   Port 1 I/O   Port 1 I/O   Port 1 I/O
90  *  0x02   Port 2 I/O   Port 2 I/O   Port 2 I/O   Port 2 I/O
91  *  0x03   Port 3 I/O   Port 3 I/O   Port 3 I/O   Port 3 I/O
92  *  0x04   Port 4 I/O   Port 4 I/O   Port 4 I/O   Port 4 I/O
93  *  0x05   Port 5 I/O   Port 5 I/O   Port 5 I/O   Port 5 I/O
94  *  0x06   INT_PENDING  INT_PENDING  INT_PENDING  INT_PENDING
95  *  0x07    Page/Lock    Page/Lock    Page/Lock    Page/Lock
96  *  0x08       N/A         POL_0       ENAB_0       INT_ID0
97  *  0x09       N/A         POL_1       ENAB_1       INT_ID1
98  *  0x0a       N/A         POL_2       ENAB_2       INT_ID2
99  */
100 #define PCMUIO_PORT_REG(x)              (0x00 + (x))
101 #define PCMUIO_INT_PENDING_REG          0x06
102 #define PCMUIO_PAGE_LOCK_REG            0x07
103 #define PCMUIO_LOCK_PORT(x)             ((1 << (x)) & 0x3f)
104 #define PCMUIO_PAGE(x)                  (((x) & 0x3) << 6)
105 #define PCMUIO_PAGE_MASK                PCMUIO_PAGE(3)
106 #define PCMUIO_PAGE_POL                 1
107 #define PCMUIO_PAGE_ENAB                2
108 #define PCMUIO_PAGE_INT_ID              3
109 #define PCMUIO_PAGE_REG(x)              (0x08 + (x))
110
111 #define PCMUIO_ASIC_IOSIZE              0x10
112 #define PCMUIO_MAX_ASICS                2
113
114 struct pcmuio_board {
115         const char *name;
116         const int num_asics;
117 };
118
119 static const struct pcmuio_board pcmuio_boards[] = {
120         {
121                 .name           = "pcmuio48",
122                 .num_asics      = 1,
123         }, {
124                 .name           = "pcmuio96",
125                 .num_asics      = 2,
126         },
127 };
128
129 struct pcmuio_asic {
130         spinlock_t pagelock;    /* protects the page registers */
131         spinlock_t spinlock;    /* protects member variables */
132         unsigned int enabled_mask;
133         unsigned int stop_count;
134         unsigned int active:1;
135         unsigned int continuous:1;
136 };
137
138 struct pcmuio_private {
139         struct pcmuio_asic asics[PCMUIO_MAX_ASICS];
140         unsigned int irq2;
141 };
142
143 static inline unsigned long pcmuio_asic_iobase(struct comedi_device *dev,
144                                                int asic)
145 {
146         return dev->iobase + (asic * PCMUIO_ASIC_IOSIZE);
147 }
148
149 static inline int pcmuio_subdevice_to_asic(struct comedi_subdevice *s)
150 {
151         /*
152          * subdevice 0 and 1 are handled by the first asic
153          * subdevice 2 and 3 are handled by the second asic
154          */
155         return s->index / 2;
156 }
157
158 static inline int pcmuio_subdevice_to_port(struct comedi_subdevice *s)
159 {
160         /*
161          * subdevice 0 and 2 use port registers 0-2
162          * subdevice 1 and 3 use port registers 3-5
163          */
164         return (s->index % 2) ? 3 : 0;
165 }
166
167 static void pcmuio_write(struct comedi_device *dev, unsigned int val,
168                          int asic, int page, int port)
169 {
170         struct pcmuio_private *devpriv = dev->private;
171         struct pcmuio_asic *chip = &devpriv->asics[asic];
172         unsigned long iobase = pcmuio_asic_iobase(dev, asic);
173         unsigned long flags;
174
175         spin_lock_irqsave(&chip->pagelock, flags);
176         if (page == 0) {
177                 /* Port registers are valid for any page */
178                 outb(val & 0xff, iobase + PCMUIO_PORT_REG(port + 0));
179                 outb((val >> 8) & 0xff, iobase + PCMUIO_PORT_REG(port + 1));
180                 outb((val >> 16) & 0xff, iobase + PCMUIO_PORT_REG(port + 2));
181         } else {
182                 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
183                 outb(val & 0xff, iobase + PCMUIO_PAGE_REG(0));
184                 outb((val >> 8) & 0xff, iobase + PCMUIO_PAGE_REG(1));
185                 outb((val >> 16) & 0xff, iobase + PCMUIO_PAGE_REG(2));
186         }
187         spin_unlock_irqrestore(&chip->pagelock, flags);
188 }
189
190 static unsigned int pcmuio_read(struct comedi_device *dev,
191                                 int asic, int page, int port)
192 {
193         struct pcmuio_private *devpriv = dev->private;
194         struct pcmuio_asic *chip = &devpriv->asics[asic];
195         unsigned long iobase = pcmuio_asic_iobase(dev, asic);
196         unsigned long flags;
197         unsigned int val;
198
199         spin_lock_irqsave(&chip->pagelock, flags);
200         if (page == 0) {
201                 /* Port registers are valid for any page */
202                 val = inb(iobase + PCMUIO_PORT_REG(port + 0));
203                 val |= (inb(iobase + PCMUIO_PORT_REG(port + 1)) << 8);
204                 val |= (inb(iobase + PCMUIO_PORT_REG(port + 2)) << 16);
205         } else {
206                 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
207                 val = inb(iobase + PCMUIO_PAGE_REG(0));
208                 val |= (inb(iobase + PCMUIO_PAGE_REG(1)) << 8);
209                 val |= (inb(iobase + PCMUIO_PAGE_REG(2)) << 16);
210         }
211         spin_unlock_irqrestore(&chip->pagelock, flags);
212
213         return val;
214 }
215
216 /*
217  * Each channel can be individually programmed for input or output.
218  * Writing a '0' to a channel causes the corresponding output pin
219  * to go to a high-z state (pulled high by an external 10K resistor).
220  * This allows it to be used as an input. When used in the input mode,
221  * a read reflects the inverted state of the I/O pin, such that a
222  * high on the pin will read as a '0' in the register. Writing a '1'
223  * to a bit position causes the pin to sink current (up to 12mA),
224  * effectively pulling it low.
225  */
226 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
227                                 struct comedi_subdevice *s,
228                                 struct comedi_insn *insn,
229                                 unsigned int *data)
230 {
231         int asic = pcmuio_subdevice_to_asic(s);
232         int port = pcmuio_subdevice_to_port(s);
233         unsigned int chanmask = (1 << s->n_chan) - 1;
234         unsigned int mask;
235         unsigned int val;
236
237         mask = comedi_dio_update_state(s, data);
238         if (mask) {
239                 /*
240                  * Outputs are inverted, invert the state and
241                  * update the channels.
242                  *
243                  * The s->io_bits mask makes sure the input channels
244                  * are '0' so that the outputs pins stay in a high
245                  * z-state.
246                  */
247                 val = ~s->state & chanmask;
248                 val &= s->io_bits;
249                 pcmuio_write(dev, val, asic, 0, port);
250         }
251
252         /* get inverted state of the channels from the port */
253         val = pcmuio_read(dev, asic, 0, port);
254
255         /* return the true state of the channels */
256         data[1] = ~val & chanmask;
257
258         return insn->n;
259 }
260
261 static int pcmuio_dio_insn_config(struct comedi_device *dev,
262                                   struct comedi_subdevice *s,
263                                   struct comedi_insn *insn,
264                                   unsigned int *data)
265 {
266         int asic = pcmuio_subdevice_to_asic(s);
267         int port = pcmuio_subdevice_to_port(s);
268         int ret;
269
270         ret = comedi_dio_insn_config(dev, s, insn, data, 0);
271         if (ret)
272                 return ret;
273
274         if (data[0] == INSN_CONFIG_DIO_INPUT)
275                 pcmuio_write(dev, s->io_bits, asic, 0, port);
276
277         return insn->n;
278 }
279
280 static void pcmuio_reset(struct comedi_device *dev)
281 {
282         const struct pcmuio_board *board = comedi_board(dev);
283         int asic;
284
285         for (asic = 0; asic < board->num_asics; ++asic) {
286                 /* first, clear all the DIO port bits */
287                 pcmuio_write(dev, 0, asic, 0, 0);
288                 pcmuio_write(dev, 0, asic, 0, 3);
289
290                 /* Next, clear all the paged registers for each page */
291                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_POL, 0);
292                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
293                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
294         }
295 }
296
297 /* chip->spinlock is already locked */
298 static void pcmuio_stop_intr(struct comedi_device *dev,
299                              struct comedi_subdevice *s)
300 {
301         struct pcmuio_private *devpriv = dev->private;
302         int asic = pcmuio_subdevice_to_asic(s);
303         struct pcmuio_asic *chip = &devpriv->asics[asic];
304
305         chip->enabled_mask = 0;
306         chip->active = 0;
307         s->async->inttrig = NULL;
308
309         /* disable all intrs for this subdev.. */
310         pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
311 }
312
313 static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
314                                       struct comedi_subdevice *s,
315                                       unsigned triggered)
316 {
317         struct pcmuio_private *devpriv = dev->private;
318         int asic = pcmuio_subdevice_to_asic(s);
319         struct pcmuio_asic *chip = &devpriv->asics[asic];
320         struct comedi_cmd *cmd = &s->async->cmd;
321         unsigned oldevents = s->async->events;
322         unsigned int val = 0;
323         unsigned long flags;
324         unsigned int i;
325
326         spin_lock_irqsave(&chip->spinlock, flags);
327
328         if (!chip->active)
329                 goto done;
330
331         if (!(triggered & chip->enabled_mask))
332                 goto done;
333
334         for (i = 0; i < cmd->chanlist_len; i++) {
335                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
336
337                 if (triggered & (1 << chan))
338                         val |= (1 << i);
339         }
340
341         /* Write the scan to the buffer. */
342         if (comedi_buf_put(s, val) &&
343             comedi_buf_put(s, val >> 16)) {
344                 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
345         } else {
346                 /* Overflow! Stop acquisition!! */
347                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
348                 pcmuio_stop_intr(dev, s);
349         }
350
351         /* Check for end of acquisition. */
352         if (!chip->continuous) {
353                 /* stop_src == TRIG_COUNT */
354                 if (chip->stop_count > 0) {
355                         chip->stop_count--;
356                         if (chip->stop_count == 0) {
357                                 s->async->events |= COMEDI_CB_EOA;
358                                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
359                                 pcmuio_stop_intr(dev, s);
360                         }
361                 }
362         }
363
364 done:
365         spin_unlock_irqrestore(&chip->spinlock, flags);
366
367         if (oldevents != s->async->events)
368                 comedi_event(dev, s);
369 }
370
371 static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
372 {
373         /* there are could be two asics so we can't use dev->read_subdev */
374         struct comedi_subdevice *s = &dev->subdevices[asic * 2];
375         unsigned long iobase = pcmuio_asic_iobase(dev, asic);
376         unsigned int val;
377
378         /* are there any interrupts pending */
379         val = inb(iobase + PCMUIO_INT_PENDING_REG) & 0x07;
380         if (!val)
381                 return 0;
382
383         /* get, and clear, the pending interrupts */
384         val = pcmuio_read(dev, asic, PCMUIO_PAGE_INT_ID, 0);
385         pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
386
387         /* handle the pending interrupts */
388         pcmuio_handle_intr_subdev(dev, s, val);
389
390         return 1;
391 }
392
393 static irqreturn_t pcmuio_interrupt(int irq, void *d)
394 {
395         struct comedi_device *dev = d;
396         struct pcmuio_private *devpriv = dev->private;
397         int handled = 0;
398
399         if (irq == dev->irq)
400                 handled += pcmuio_handle_asic_interrupt(dev, 0);
401         if (irq == devpriv->irq2)
402                 handled += pcmuio_handle_asic_interrupt(dev, 1);
403
404         return handled ? IRQ_HANDLED : IRQ_NONE;
405 }
406
407 /* chip->spinlock is already locked */
408 static int pcmuio_start_intr(struct comedi_device *dev,
409                              struct comedi_subdevice *s)
410 {
411         struct pcmuio_private *devpriv = dev->private;
412         int asic = pcmuio_subdevice_to_asic(s);
413         struct pcmuio_asic *chip = &devpriv->asics[asic];
414         struct comedi_cmd *cmd = &s->async->cmd;
415         unsigned int bits = 0;
416         unsigned int pol_bits = 0;
417         int i;
418
419         if (!chip->continuous && chip->stop_count == 0) {
420                 /* An empty acquisition! */
421                 s->async->events |= COMEDI_CB_EOA;
422                 chip->active = 0;
423                 return 1;
424         }
425
426         chip->enabled_mask = 0;
427         chip->active = 1;
428         if (cmd->chanlist) {
429                 for (i = 0; i < cmd->chanlist_len; i++) {
430                         unsigned int chanspec = cmd->chanlist[i];
431                         unsigned int chan = CR_CHAN(chanspec);
432                         unsigned int range = CR_RANGE(chanspec);
433                         unsigned int aref = CR_AREF(chanspec);
434
435                         bits |= (1 << chan);
436                         pol_bits |= ((aref || range) ? 1 : 0) << chan;
437                 }
438         }
439         bits &= ((1 << s->n_chan) - 1);
440         chip->enabled_mask = bits;
441
442         /* set pol and enab intrs for this subdev.. */
443         pcmuio_write(dev, pol_bits, asic, PCMUIO_PAGE_POL, 0);
444         pcmuio_write(dev, bits, asic, PCMUIO_PAGE_ENAB, 0);
445
446         return 0;
447 }
448
449 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
450 {
451         struct pcmuio_private *devpriv = dev->private;
452         int asic = pcmuio_subdevice_to_asic(s);
453         struct pcmuio_asic *chip = &devpriv->asics[asic];
454         unsigned long flags;
455
456         spin_lock_irqsave(&chip->spinlock, flags);
457         if (chip->active)
458                 pcmuio_stop_intr(dev, s);
459         spin_unlock_irqrestore(&chip->spinlock, flags);
460
461         return 0;
462 }
463
464 static int pcmuio_inttrig_start_intr(struct comedi_device *dev,
465                                      struct comedi_subdevice *s,
466                                      unsigned int trig_num)
467 {
468         struct pcmuio_private *devpriv = dev->private;
469         struct comedi_cmd *cmd = &s->async->cmd;
470         int asic = pcmuio_subdevice_to_asic(s);
471         struct pcmuio_asic *chip = &devpriv->asics[asic];
472         unsigned long flags;
473         int event = 0;
474
475         if (trig_num != cmd->start_arg)
476                 return -EINVAL;
477
478         spin_lock_irqsave(&chip->spinlock, flags);
479         s->async->inttrig = NULL;
480         if (chip->active)
481                 event = pcmuio_start_intr(dev, s);
482
483         spin_unlock_irqrestore(&chip->spinlock, flags);
484
485         if (event)
486                 comedi_event(dev, s);
487
488         return 1;
489 }
490
491 /*
492  * 'do_cmd' function for an 'INTERRUPT' subdevice.
493  */
494 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
495 {
496         struct pcmuio_private *devpriv = dev->private;
497         struct comedi_cmd *cmd = &s->async->cmd;
498         int asic = pcmuio_subdevice_to_asic(s);
499         struct pcmuio_asic *chip = &devpriv->asics[asic];
500         unsigned long flags;
501         int event = 0;
502
503         spin_lock_irqsave(&chip->spinlock, flags);
504         chip->active = 1;
505
506         /* Set up end of acquisition. */
507         switch (cmd->stop_src) {
508         case TRIG_COUNT:
509                 chip->continuous = 0;
510                 chip->stop_count = cmd->stop_arg;
511                 break;
512         default:
513                 /* TRIG_NONE */
514                 chip->continuous = 1;
515                 chip->stop_count = 0;
516                 break;
517         }
518
519         /* Set up start of acquisition. */
520         if (cmd->start_src == TRIG_INT)
521                 s->async->inttrig = pcmuio_inttrig_start_intr;
522         else    /* TRIG_NOW */
523                 event = pcmuio_start_intr(dev, s);
524
525         spin_unlock_irqrestore(&chip->spinlock, flags);
526
527         if (event)
528                 comedi_event(dev, s);
529
530         return 0;
531 }
532
533 static int pcmuio_cmdtest(struct comedi_device *dev,
534                           struct comedi_subdevice *s,
535                           struct comedi_cmd *cmd)
536 {
537         int err = 0;
538
539         /* Step 1 : check if triggers are trivially valid */
540
541         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
542         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
543         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
544         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
545         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
546
547         if (err)
548                 return 1;
549
550         /* Step 2a : make sure trigger sources are unique */
551
552         err |= cfc_check_trigger_is_unique(cmd->start_src);
553         err |= cfc_check_trigger_is_unique(cmd->stop_src);
554
555         /* Step 2b : and mutually compatible */
556
557         if (err)
558                 return 2;
559
560         /* Step 3: check if arguments are trivially valid */
561
562         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
563         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
564         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
565         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
566
567         switch (cmd->stop_src) {
568         case TRIG_COUNT:
569                 /* any count allowed */
570                 break;
571         case TRIG_NONE:
572                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
573                 break;
574         default:
575                 break;
576         }
577
578         if (err)
579                 return 3;
580
581         /* step 4: fix up any arguments */
582
583         /* if (err) return 4; */
584
585         return 0;
586 }
587
588 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
589 {
590         const struct pcmuio_board *board = comedi_board(dev);
591         struct comedi_subdevice *s;
592         struct pcmuio_private *devpriv;
593         int ret;
594         int i;
595
596         ret = comedi_request_region(dev, it->options[0],
597                                     board->num_asics * PCMUIO_ASIC_IOSIZE);
598         if (ret)
599                 return ret;
600
601         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
602         if (!devpriv)
603                 return -ENOMEM;
604
605         for (i = 0; i < PCMUIO_MAX_ASICS; ++i) {
606                 struct pcmuio_asic *chip = &devpriv->asics[i];
607
608                 spin_lock_init(&chip->pagelock);
609                 spin_lock_init(&chip->spinlock);
610         }
611
612         pcmuio_reset(dev);
613
614         if (it->options[1]) {
615                 /* request the irq for the 1st asic */
616                 ret = request_irq(it->options[1], pcmuio_interrupt, 0,
617                                   dev->board_name, dev);
618                 if (ret == 0)
619                         dev->irq = it->options[1];
620         }
621
622         if (board->num_asics == 2) {
623                 if (it->options[2] == dev->irq) {
624                         /* the same irq (or none) is used by both asics */
625                         devpriv->irq2 = it->options[2];
626                 } else if (it->options[2]) {
627                         /* request the irq for the 2nd asic */
628                         ret = request_irq(it->options[2], pcmuio_interrupt, 0,
629                                         dev->board_name, dev);
630                         if (ret == 0)
631                                 devpriv->irq2 = it->options[2];
632                 }
633         }
634
635         ret = comedi_alloc_subdevices(dev, board->num_asics * 2);
636         if (ret)
637                 return ret;
638
639         for (i = 0; i < dev->n_subdevices; ++i) {
640                 s = &dev->subdevices[i];
641                 s->type         = COMEDI_SUBD_DIO;
642                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
643                 s->n_chan       = 24;
644                 s->maxdata      = 1;
645                 s->range_table  = &range_digital;
646                 s->insn_bits    = pcmuio_dio_insn_bits;
647                 s->insn_config  = pcmuio_dio_insn_config;
648
649                 /* subdevices 0 and 2 can suppport interrupts */
650                 if ((i == 0 && dev->irq) || (i == 2 && devpriv->irq2)) {
651                         /* setup the interrupt subdevice */
652                         dev->read_subdev = s;
653                         s->subdev_flags |= SDF_CMD_READ;
654                         s->len_chanlist = s->n_chan;
655                         s->cancel       = pcmuio_cancel;
656                         s->do_cmd       = pcmuio_cmd;
657                         s->do_cmdtest   = pcmuio_cmdtest;
658                 }
659         }
660
661         return 0;
662 }
663
664 static void pcmuio_detach(struct comedi_device *dev)
665 {
666         struct pcmuio_private *devpriv = dev->private;
667
668         if (devpriv) {
669                 pcmuio_reset(dev);
670
671                 /* free the 2nd irq if used, the core will free the 1st one */
672                 if (devpriv->irq2 && devpriv->irq2 != dev->irq)
673                         free_irq(devpriv->irq2, dev);
674         }
675         comedi_legacy_detach(dev);
676 }
677
678 static struct comedi_driver pcmuio_driver = {
679         .driver_name    = "pcmuio",
680         .module         = THIS_MODULE,
681         .attach         = pcmuio_attach,
682         .detach         = pcmuio_detach,
683         .board_name     = &pcmuio_boards[0].name,
684         .offset         = sizeof(struct pcmuio_board),
685         .num_names      = ARRAY_SIZE(pcmuio_boards),
686 };
687 module_comedi_driver(pcmuio_driver);
688
689 MODULE_AUTHOR("Comedi http://www.comedi.org");
690 MODULE_DESCRIPTION("Comedi low-level driver");
691 MODULE_LICENSE("GPL");