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