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