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