staging: comedi: pcmuio: introduce pcmuio_write()
[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 /*
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 CHANS_PER_PORT          8
112 #define PORTS_PER_ASIC          6
113 #define INTR_PORTS_PER_ASIC     3
114 /* number of channels per comedi subdevice */
115 #define MAX_CHANS_PER_SUBDEV    24
116 #define PORTS_PER_SUBDEV        (MAX_CHANS_PER_SUBDEV / CHANS_PER_PORT)
117 #define CHANS_PER_ASIC          (CHANS_PER_PORT * PORTS_PER_ASIC)
118 #define INTR_CHANS_PER_ASIC     24
119 #define INTR_PORTS_PER_SUBDEV   (INTR_CHANS_PER_ASIC / CHANS_PER_PORT)
120 #define MAX_DIO_CHANS           (PORTS_PER_ASIC * 2 * CHANS_PER_PORT)
121 #define MAX_ASICS               (MAX_DIO_CHANS / CHANS_PER_ASIC)
122
123 /* IO Memory sizes */
124 #define ASIC_IOSIZE             0x10
125 #define PCMUIO48_IOSIZE         ASIC_IOSIZE
126 #define PCMUIO96_IOSIZE         (ASIC_IOSIZE * 2)
127
128 #define NUM_PAGED_REGS          3
129
130 struct pcmuio_board {
131         const char *name;
132         const int num_asics;
133         const int num_channels_per_port;
134         const int num_ports;
135 };
136
137 static const struct pcmuio_board pcmuio_boards[] = {
138         {
139                 .name           = "pcmuio48",
140                 .num_asics      = 1,
141                 .num_ports      = 6,
142         }, {
143                 .name           = "pcmuio96",
144                 .num_asics      = 2,
145                 .num_ports      = 12,
146         },
147 };
148
149 struct pcmuio_subdev_private {
150         /* mapping of halfwords (bytes) in port/chanarray to iobase */
151         unsigned long iobases[PORTS_PER_SUBDEV];
152
153         /* The below is only used for intr subdevices */
154         struct {
155                 /* if non-negative, this subdev has an interrupt asic */
156                 int asic;
157                 /* if nonnegative, the first channel id for interrupts */
158                 int first_chan;
159                 /*
160                  * the number of asic channels in this
161                  * subdev that have interrutps
162                  */
163                 int num_asic_chans;
164                 /*
165                  * if nonnegative, the first channel id with
166                  * respect to the asic that has interrupts
167                  */
168                 int asic_chan;
169                 /*
170                  * subdev-relative channel mask for channels
171                  * we are interested in
172                  */
173                 int enabled_mask;
174                 int active;
175                 int stop_count;
176                 int continuous;
177                 spinlock_t spinlock;
178         } intr;
179 };
180
181 struct pcmuio_private {
182         struct {
183                 /* shadow of POLx registers */
184                 unsigned char pol[NUM_PAGED_REGS];
185                 /* shadow of ENABx registers */
186                 unsigned char enab[NUM_PAGED_REGS];
187                 int num;
188                 unsigned long iobase;
189                 unsigned int irq;
190                 spinlock_t spinlock;
191         } asics[MAX_ASICS];
192         struct pcmuio_subdev_private *sprivs;
193 };
194
195 static void pcmuio_write(struct comedi_device *dev, unsigned int val,
196                          int asic, int page, int port)
197 {
198         unsigned long iobase = dev->iobase + (asic * ASIC_IOSIZE);
199
200         if (page == 0) {
201                 /* Port registers are valid for any page */
202                 outb(val & 0xff, iobase + PCMUIO_PORT_REG(port + 0));
203                 outb((val >> 8) & 0xff, iobase + PCMUIO_PORT_REG(port + 1));
204                 outb((val >> 16) & 0xff, iobase + PCMUIO_PORT_REG(port + 2));
205         } else {
206                 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
207                 outb(val & 0xff, iobase + PCMUIO_PAGE_REG(0));
208                 outb((val >> 8) & 0xff, iobase + PCMUIO_PAGE_REG(1));
209                 outb((val >> 16) & 0xff, iobase + PCMUIO_PAGE_REG(2));
210         }
211 }
212
213 static int pcmuio_dio_insn_bits(struct comedi_device *dev,
214                                 struct comedi_subdevice *s,
215                                 struct comedi_insn *insn, unsigned int *data)
216 {
217         struct pcmuio_subdev_private *subpriv = s->private;
218         int byte_no;
219
220         /* NOTE:
221            reading a 0 means this channel was high
222            writine a 0 sets the channel high
223            reading a 1 means this channel was low
224            writing a 1 means set this channel low
225
226            Therefore everything is always inverted. */
227
228         /* The insn data is a mask in data[0] and the new data
229          * in data[1], each channel cooresponding to a bit. */
230
231         s->state = 0;
232
233         for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
234                 /* address of 8-bit port */
235                 unsigned long ioaddr = subpriv->iobases[byte_no],
236                     /* bit offset of port in 32-bit doubleword */
237                     offset = byte_no * 8;
238                 /* this 8-bit port's data */
239                 unsigned char byte = 0,
240                     /* The write mask for this port (if any) */
241                     write_mask_byte = (data[0] >> offset) & 0xff,
242                     /* The data byte for this port */
243                     data_byte = (data[1] >> offset) & 0xff;
244
245                 byte = inb(ioaddr);     /* read all 8-bits for this port */
246
247                 if (write_mask_byte) {
248                         byte &= ~write_mask_byte;
249                         byte |= ~data_byte & write_mask_byte;
250                         outb(byte, ioaddr);
251                 }
252                 /* save the digital input lines for this byte.. */
253                 s->state |= ((unsigned int)byte) << offset;
254         }
255
256         /* now return the DIO lines to data[1] - note they came inverted! */
257         data[1] = ~s->state;
258
259         return insn->n;
260 }
261
262 static int pcmuio_dio_insn_config(struct comedi_device *dev,
263                                   struct comedi_subdevice *s,
264                                   struct comedi_insn *insn, unsigned int *data)
265 {
266         struct pcmuio_subdev_private *subpriv = s->private;
267         int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
268             chan % 8;
269         unsigned long ioaddr;
270         unsigned char byte;
271
272         /* Compute ioaddr for this channel */
273         ioaddr = subpriv->iobases[byte_no];
274
275         /* NOTE:
276            writing a 0 an IO channel's bit sets the channel to INPUT
277            and pulls the line high as well
278
279            writing a 1 to an IO channel's  bit pulls the line low
280
281            All channels are implicitly always in OUTPUT mode -- but when
282            they are high they can be considered to be in INPUT mode..
283
284            Thus, we only force channels low if the config request was INPUT,
285            otherwise we do nothing to the hardware.    */
286
287         switch (data[0]) {
288         case INSN_CONFIG_DIO_OUTPUT:
289                 /* save to io_bits -- don't actually do anything since
290                    all input channels are also output channels... */
291                 s->io_bits |= 1 << chan;
292                 break;
293         case INSN_CONFIG_DIO_INPUT:
294                 /* write a 0 to the actual register representing the channel
295                    to set it to 'input'.  0 means "float high". */
296                 byte = inb(ioaddr);
297                 byte &= ~(1 << bit_no);
298                                 /**< set input channel to '0' */
299
300                 /*
301                  * write out byte
302                  * This is the only time we actually affect the hardware
303                  * as all channels are implicitly output -- but input
304                  * channels are set to float-high.
305                  */
306                 outb(byte, ioaddr);
307
308                 /* save to io_bits */
309                 s->io_bits &= ~(1 << chan);
310                 break;
311
312         case INSN_CONFIG_DIO_QUERY:
313                 /* retrieve from shadow register */
314                 data[1] =
315                     (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
316                 return insn->n;
317                 break;
318
319         default:
320                 return -EINVAL;
321                 break;
322         }
323
324         return insn->n;
325 }
326
327 static void switch_page(struct comedi_device *dev, int asic, int page)
328 {
329         outb(PCMUIO_PAGE(page),
330              dev->iobase + ASIC_IOSIZE * asic + PCMUIO_PAGE_LOCK_REG);
331 }
332
333 static void init_asics(struct comedi_device *dev)
334 {
335         const struct pcmuio_board *board = comedi_board(dev);
336         int asic;
337
338         for (asic = 0; asic < board->num_asics; ++asic) {
339                 /* first, clear all the DIO port bits */
340                 pcmuio_write(dev, 0, asic, 0, 0);
341                 pcmuio_write(dev, 0, asic, 0, 3);
342
343                 /* Next, clear all the paged registers for each page */
344                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_POL, 0);
345                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
346                 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
347         }
348 }
349
350 static void pcmuio_stop_intr(struct comedi_device *dev,
351                              struct comedi_subdevice *s)
352 {
353         struct pcmuio_subdev_private *subpriv = s->private;
354         int asic;
355
356         asic = subpriv->intr.asic;
357         if (asic < 0)
358                 return;         /* not an interrupt subdev */
359
360         subpriv->intr.enabled_mask = 0;
361         subpriv->intr.active = 0;
362         s->async->inttrig = NULL;
363
364         /* disable all intrs for this subdev.. */
365         pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
366 }
367
368 static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
369                                       struct comedi_subdevice *s,
370                                       unsigned triggered)
371 {
372         struct pcmuio_subdev_private *subpriv = s->private;
373         unsigned int len = s->async->cmd.chanlist_len;
374         unsigned oldevents = s->async->events;
375         unsigned int val = 0;
376         unsigned long flags;
377         unsigned mytrig;
378         unsigned int i;
379
380         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
381
382         if (!subpriv->intr.active)
383                 goto done;
384
385         mytrig = triggered >> subpriv->intr.asic_chan;
386         mytrig &= ((0x1 << subpriv->intr.num_asic_chans) - 1);
387         mytrig <<= subpriv->intr.first_chan;
388
389         if (!(mytrig & subpriv->intr.enabled_mask))
390                 goto done;
391
392         for (i = 0; i < len; i++) {
393                 unsigned int chan = CR_CHAN(s->async->cmd.chanlist[i]);
394                 if (mytrig & (1U << chan))
395                         val |= (1U << i);
396         }
397
398         /* Write the scan to the buffer. */
399         if (comedi_buf_put(s->async, ((short *)&val)[0]) &&
400             comedi_buf_put(s->async, ((short *)&val)[1])) {
401                 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
402         } else {
403                 /* Overflow! Stop acquisition!! */
404                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
405                 pcmuio_stop_intr(dev, s);
406         }
407
408         /* Check for end of acquisition. */
409         if (!subpriv->intr.continuous) {
410                 /* stop_src == TRIG_COUNT */
411                 if (subpriv->intr.stop_count > 0) {
412                         subpriv->intr.stop_count--;
413                         if (subpriv->intr.stop_count == 0) {
414                                 s->async->events |= COMEDI_CB_EOA;
415                                 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
416                                 pcmuio_stop_intr(dev, s);
417                         }
418                 }
419         }
420
421 done:
422         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
423
424         if (oldevents != s->async->events)
425                 comedi_event(dev, s);
426 }
427
428 static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
429 {
430         struct pcmuio_private *devpriv = dev->private;
431         struct pcmuio_subdev_private *subpriv;
432         unsigned long iobase = devpriv->asics[asic].iobase;
433         unsigned triggered = 0;
434         int got1 = 0;
435         unsigned long flags;
436         unsigned char int_pend;
437         int i;
438
439         spin_lock_irqsave(&devpriv->asics[asic].spinlock, flags);
440
441         int_pend = inb(iobase + PCMUIO_INT_PENDING_REG) & 0x07;
442         if (int_pend) {
443                 for (i = 0; i < INTR_PORTS_PER_ASIC; ++i) {
444                         if (int_pend & (0x1 << i)) {
445                                 unsigned char val;
446
447                                 switch_page(dev, asic, PCMUIO_PAGE_INT_ID);
448                                 val = inb(iobase + PCMUIO_PAGE_REG(i));
449                                 if (val)
450                                         /* clear pending interrupt */
451                                         outb(0, iobase + PCMUIO_PAGE_REG(i));
452
453                                         triggered |= (val << (i * 8));
454                         }
455                 }
456
457                 ++got1;
458         }
459
460         spin_unlock_irqrestore(&devpriv->asics[asic].spinlock, flags);
461
462         if (triggered) {
463                 struct comedi_subdevice *s;
464                 /* TODO here: dispatch io lines to subdevs with commands.. */
465                 for (i = 0; i < dev->n_subdevices; i++) {
466                         s = &dev->subdevices[i];
467                         subpriv = s->private;
468                         if (subpriv->intr.asic == asic) {
469                                 /*
470                                  * This is an interrupt subdev, and it
471                                  * matches this asic!
472                                  */
473                                 pcmuio_handle_intr_subdev(dev, s,
474                                                           triggered);
475                         }
476                 }
477         }
478         return got1;
479 }
480
481 static irqreturn_t interrupt_pcmuio(int irq, void *d)
482 {
483         struct comedi_device *dev = d;
484         struct pcmuio_private *devpriv = dev->private;
485         int got1 = 0;
486         int asic;
487
488         for (asic = 0; asic < MAX_ASICS; ++asic) {
489                 if (irq == devpriv->asics[asic].irq) {
490                         /* it is an interrupt for ASIC #asic */
491                         if (pcmuio_handle_asic_interrupt(dev, asic))
492                                 got1++;
493                 }
494         }
495         if (!got1)
496                 return IRQ_NONE;        /* interrupt from other source */
497         return IRQ_HANDLED;
498 }
499
500 static int pcmuio_start_intr(struct comedi_device *dev,
501                              struct comedi_subdevice *s)
502 {
503         struct pcmuio_subdev_private *subpriv = s->private;
504
505         if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
506                 /* An empty acquisition! */
507                 s->async->events |= COMEDI_CB_EOA;
508                 subpriv->intr.active = 0;
509                 return 1;
510         } else {
511                 unsigned bits = 0, pol_bits = 0, n;
512                 int asic;
513                 struct comedi_cmd *cmd = &s->async->cmd;
514
515                 asic = subpriv->intr.asic;
516                 if (asic < 0)
517                         return 1;       /* not an interrupt
518                                            subdev */
519                 subpriv->intr.enabled_mask = 0;
520                 subpriv->intr.active = 1;
521                 if (cmd->chanlist) {
522                         for (n = 0; n < cmd->chanlist_len; n++) {
523                                 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
524                                 pol_bits |= (CR_AREF(cmd->chanlist[n])
525                                              || CR_RANGE(cmd->
526                                                          chanlist[n]) ? 1U : 0U)
527                                     << CR_CHAN(cmd->chanlist[n]);
528                         }
529                 }
530                 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
531                          1) << subpriv->intr.first_chan;
532                 subpriv->intr.enabled_mask = bits;
533
534                 /* set pol and enab intrs for this subdev.. */
535                 pcmuio_write(dev, pol_bits, asic, PCMUIO_PAGE_POL, 0);
536                 pcmuio_write(dev, bits, asic, PCMUIO_PAGE_ENAB, 0);
537         }
538         return 0;
539 }
540
541 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
542 {
543         struct pcmuio_subdev_private *subpriv = s->private;
544         unsigned long flags;
545
546         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
547         if (subpriv->intr.active)
548                 pcmuio_stop_intr(dev, s);
549         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
550
551         return 0;
552 }
553
554 /*
555  * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
556  */
557 static int
558 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
559                           unsigned int trignum)
560 {
561         struct pcmuio_subdev_private *subpriv = s->private;
562         unsigned long flags;
563         int event = 0;
564
565         if (trignum != 0)
566                 return -EINVAL;
567
568         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
569         s->async->inttrig = NULL;
570         if (subpriv->intr.active)
571                 event = pcmuio_start_intr(dev, s);
572
573         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
574
575         if (event)
576                 comedi_event(dev, s);
577
578         return 1;
579 }
580
581 /*
582  * 'do_cmd' function for an 'INTERRUPT' subdevice.
583  */
584 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
585 {
586         struct pcmuio_subdev_private *subpriv = s->private;
587         struct comedi_cmd *cmd = &s->async->cmd;
588         unsigned long flags;
589         int event = 0;
590
591         spin_lock_irqsave(&subpriv->intr.spinlock, flags);
592         subpriv->intr.active = 1;
593
594         /* Set up end of acquisition. */
595         switch (cmd->stop_src) {
596         case TRIG_COUNT:
597                 subpriv->intr.continuous = 0;
598                 subpriv->intr.stop_count = cmd->stop_arg;
599                 break;
600         default:
601                 /* TRIG_NONE */
602                 subpriv->intr.continuous = 1;
603                 subpriv->intr.stop_count = 0;
604                 break;
605         }
606
607         /* Set up start of acquisition. */
608         switch (cmd->start_src) {
609         case TRIG_INT:
610                 s->async->inttrig = pcmuio_inttrig_start_intr;
611                 break;
612         default:
613                 /* TRIG_NOW */
614                 event = pcmuio_start_intr(dev, s);
615                 break;
616         }
617         spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
618
619         if (event)
620                 comedi_event(dev, s);
621
622         return 0;
623 }
624
625 static int pcmuio_cmdtest(struct comedi_device *dev,
626                           struct comedi_subdevice *s,
627                           struct comedi_cmd *cmd)
628 {
629         int err = 0;
630
631         /* Step 1 : check if triggers are trivially valid */
632
633         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
634         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
635         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
636         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
637         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
638
639         if (err)
640                 return 1;
641
642         /* Step 2a : make sure trigger sources are unique */
643
644         err |= cfc_check_trigger_is_unique(cmd->start_src);
645         err |= cfc_check_trigger_is_unique(cmd->stop_src);
646
647         /* Step 2b : and mutually compatible */
648
649         if (err)
650                 return 2;
651
652         /* Step 3: check if arguments are trivially valid */
653
654         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
655         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
656         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
657         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
658
659         switch (cmd->stop_src) {
660         case TRIG_COUNT:
661                 /* any count allowed */
662                 break;
663         case TRIG_NONE:
664                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
665                 break;
666         default:
667                 break;
668         }
669
670         if (err)
671                 return 3;
672
673         /* step 4: fix up any arguments */
674
675         /* if (err) return 4; */
676
677         return 0;
678 }
679
680 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
681 {
682         const struct pcmuio_board *board = comedi_board(dev);
683         struct comedi_subdevice *s;
684         struct pcmuio_private *devpriv;
685         struct pcmuio_subdev_private *subpriv;
686         int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
687         unsigned int irq[MAX_ASICS];
688         int ret;
689
690         irq[0] = it->options[1];
691         irq[1] = it->options[2];
692
693         ret = comedi_request_region(dev, it->options[0],
694                                     board->num_asics * ASIC_IOSIZE);
695         if (ret)
696                 return ret;
697
698         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
699         if (!devpriv)
700                 return -ENOMEM;
701         dev->private = devpriv;
702
703         for (asic = 0; asic < MAX_ASICS; ++asic) {
704                 devpriv->asics[asic].num = asic;
705                 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
706                 spin_lock_init(&devpriv->asics[asic].spinlock);
707         }
708
709         chans_left = CHANS_PER_ASIC * board->num_asics;
710         n_subdevs = (chans_left / MAX_CHANS_PER_SUBDEV) +
711                     (!!(chans_left % MAX_CHANS_PER_SUBDEV));
712         devpriv->sprivs = kcalloc(n_subdevs,
713                                   sizeof(struct pcmuio_subdev_private),
714                                   GFP_KERNEL);
715         if (!devpriv->sprivs)
716                 return -ENOMEM;
717
718         ret = comedi_alloc_subdevices(dev, n_subdevs);
719         if (ret)
720                 return ret;
721
722         port = 0;
723         asic = 0;
724         for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
725                 int byte_no;
726
727                 s = &dev->subdevices[sdev_no];
728                 subpriv = &devpriv->sprivs[sdev_no];
729                 s->private = subpriv;
730                 s->maxdata = 1;
731                 s->range_table = &range_digital;
732                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
733                 s->type = COMEDI_SUBD_DIO;
734                 s->insn_bits = pcmuio_dio_insn_bits;
735                 s->insn_config = pcmuio_dio_insn_config;
736                 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
737                 subpriv->intr.asic = -1;
738                 subpriv->intr.first_chan = -1;
739                 subpriv->intr.asic_chan = -1;
740                 subpriv->intr.num_asic_chans = -1;
741                 subpriv->intr.active = 0;
742                 s->len_chanlist = 1;
743
744                 /* save the ioport address for each 'port' of 8 channels in the
745                    subdevice */
746                 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV;
747                      ++byte_no, ++port) {
748                         if (port >= PORTS_PER_ASIC) {
749                                 port = 0;
750                                 ++asic;
751                                 thisasic_chanct = 0;
752                         }
753                         subpriv->iobases[byte_no] =
754                             devpriv->asics[asic].iobase + port;
755
756                         if (thisasic_chanct <
757                             CHANS_PER_PORT * INTR_PORTS_PER_ASIC
758                             && subpriv->intr.asic < 0) {
759                                 /* setup the interrupt subdevice */
760                                 subpriv->intr.asic = asic;
761                                 subpriv->intr.active = 0;
762                                 subpriv->intr.stop_count = 0;
763                                 subpriv->intr.first_chan = byte_no * 8;
764                                 subpriv->intr.asic_chan = thisasic_chanct;
765                                 subpriv->intr.num_asic_chans =
766                                     s->n_chan - subpriv->intr.first_chan;
767                                 dev->read_subdev = s;
768                                 s->subdev_flags |= SDF_CMD_READ;
769                                 s->cancel = pcmuio_cancel;
770                                 s->do_cmd = pcmuio_cmd;
771                                 s->do_cmdtest = pcmuio_cmdtest;
772                                 s->len_chanlist = subpriv->intr.num_asic_chans;
773                         }
774                         thisasic_chanct += CHANS_PER_PORT;
775                 }
776                 spin_lock_init(&subpriv->intr.spinlock);
777
778                 chans_left -= s->n_chan;
779
780                 if (!chans_left) {
781                         /* reset to our first asic, to do intr subdevs */
782                         asic = 0;
783                         port = 0;
784                 }
785
786         }
787
788         init_asics(dev);        /* clear out all the registers, basically */
789
790         for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
791                 if (irq[asic]
792                     && request_irq(irq[asic], interrupt_pcmuio,
793                                    IRQF_SHARED, board->name, dev)) {
794                         int i;
795                         /* unroll the allocated irqs.. */
796                         for (i = asic - 1; i >= 0; --i) {
797                                 free_irq(irq[i], dev);
798                                 devpriv->asics[i].irq = irq[i] = 0;
799                         }
800                         irq[asic] = 0;
801                 }
802                 devpriv->asics[asic].irq = irq[asic];
803         }
804
805         if (irq[0]) {
806                 dev_dbg(dev->class_dev, "irq: %u\n", irq[0]);
807                 if (irq[1] && board->num_asics == 2)
808                         dev_dbg(dev->class_dev, "second ASIC irq: %u\n",
809                                 irq[1]);
810         } else {
811                 dev_dbg(dev->class_dev, "(IRQ mode disabled)\n");
812         }
813
814
815         return 1;
816 }
817
818 static void pcmuio_detach(struct comedi_device *dev)
819 {
820         struct pcmuio_private *devpriv = dev->private;
821         int i;
822
823         for (i = 0; i < MAX_ASICS; ++i) {
824                 if (devpriv->asics[i].irq)
825                         free_irq(devpriv->asics[i].irq, dev);
826         }
827         if (devpriv && devpriv->sprivs)
828                 kfree(devpriv->sprivs);
829         comedi_legacy_detach(dev);
830 }
831
832 static struct comedi_driver pcmuio_driver = {
833         .driver_name    = "pcmuio",
834         .module         = THIS_MODULE,
835         .attach         = pcmuio_attach,
836         .detach         = pcmuio_detach,
837         .board_name     = &pcmuio_boards[0].name,
838         .offset         = sizeof(struct pcmuio_board),
839         .num_names      = ARRAY_SIZE(pcmuio_boards),
840 };
841 module_comedi_driver(pcmuio_driver);
842
843 MODULE_AUTHOR("Comedi http://www.comedi.org");
844 MODULE_DESCRIPTION("Comedi low-level driver");
845 MODULE_LICENSE("GPL");