pcmuio.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
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/*
23Driver: pcmuio
24Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Fri, 13 Jan 2006 12:01:01 -0500
28Status: works
29
30A driver for the relatively straightforward-to-program PCM-UIO48A and
31PCM-UIO96A boards from Winsystems.  These boards use either one or two
32(in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33This chip is interesting in that each I/O line is individually
34programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35on a per-channel basis).  Also, each chip supports edge-triggered
36interrupts for the first 24 I/O lines.  Of course, since the
3796-channel version of the board has two ASICs, it can detect polarity
38changes on up to 48 I/O lines.  Since this is essentially an (non-PnP)
39ISA board, I/O Address and IRQ selection are done through jumpers on
40the board.  You need to pass that information to this driver as the
41first and second comedi_config option, respectively.  Note that the
4248-channel version uses 16 bytes of IO memory and the 96-channel
43version uses 32-bytes (in case you are worried about conflicts).  The
4448-channel board is split into two 24-channel comedi subdevices.
45The 96-channel board is split into 4 24-channel DIO subdevices.
46
47Note that IRQ support has been added, but it is untested.
48
49To 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).
51Then, use use comedi_commands with TRIG_NOW.
52Your callback will be called each time an edge is triggered, and the data
53values will be two sample_t's, which should be concatenated to form one
5432-bit unsigned int.  This value is the mask of channels that had
55edges detected from your channel list.  Note that the bits positions
56in the mask correspond to positions in your chanlist when you specified
57the command and *not* channel id's!
58
59To set the polarity of the edge-detection interrupts pass a nonzero value for
60either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61CR_RANGE and CR_AREF if you want edge-down polarity.
62
63In the 48-channel version:
64
65On subdev 0, the first 24 channels channels are edge-detect channels.
66
67In the 96-channel board you have the collowing channels that can do edge detection:
68
69subdev 0, channels 0-24  (first 24 channels of 1st ASIC)
70subdev 2, channels 0-24  (first 24 channels of 2nd ASIC)
71
72Configuration 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 */
150struct 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
157static 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.  */
176struct 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.  */
201struct 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 */
226static int pcmuio_attach(struct comedi_device *dev,
227			 struct comedi_devconfig *it);
228static int pcmuio_detach(struct comedi_device *dev);
229
230static struct comedi_driver driver = {
231	.driver_name = "pcmuio",
232	.module = THIS_MODULE,
233	.attach = pcmuio_attach,
234	.detach = pcmuio_detach,
235/* It is not necessary to implement the following members if you are
236 * writing a driver for a ISA PnP or PCI card */
237	/* Most drivers will support multiple types of boards by
238	 * having an array of board structures.  These were defined
239	 * in pcmuio_boards[] above.  Note that the element 'name'
240	 * was first in the structure -- Comedi uses this fact to
241	 * extract the name of the board without knowing any details
242	 * about the structure except for its length.
243	 * When a device is attached (by comedi_config), the name
244	 * of the device is given to Comedi, and Comedi tries to
245	 * match it by going through the list of board names.  If
246	 * there is a match, the address of the pointer is put
247	 * into dev->board_ptr and driver->attach() is called.
248	 *
249	 * Note that these are not necessary if you can determine
250	 * the type of board in software.  ISA PnP, PCI, and PCMCIA
251	 * devices are such boards.
252	 */
253	.board_name = &pcmuio_boards[0].name,
254	.offset = sizeof(struct pcmuio_board),
255	.num_names = ARRAY_SIZE(pcmuio_boards),
256};
257
258static int pcmuio_dio_insn_bits(struct comedi_device *dev,
259				struct comedi_subdevice *s,
260				struct comedi_insn *insn, unsigned int *data);
261static int pcmuio_dio_insn_config(struct comedi_device *dev,
262				  struct comedi_subdevice *s,
263				  struct comedi_insn *insn, unsigned int *data);
264
265static irqreturn_t interrupt_pcmuio(int irq, void *d);
266static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
267static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
268static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
269static int pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
270			  struct comedi_cmd *cmd);
271
272/* some helper functions to deal with specifics of this device's registers */
273static void init_asics(struct comedi_device *dev);	/* sets up/clears ASIC chips to defaults */
274static void switch_page(struct comedi_device *dev, int asic, int page);
275#ifdef notused
276static void lock_port(struct comedi_device *dev, int asic, int port);
277static void unlock_port(struct comedi_device *dev, int asic, int port);
278#endif
279
280/*
281 * Attach is called by the Comedi core to configure the driver
282 * for a particular board.  If you specified a board_name array
283 * in the driver structure, dev->board_ptr contains that
284 * address.
285 */
286static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
287{
288	struct comedi_subdevice *s;
289	int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
290	unsigned long iobase;
291	unsigned int irq[MAX_ASICS];
292
293	iobase = it->options[0];
294	irq[0] = it->options[1];
295	irq[1] = it->options[2];
296
297	printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
298	       iobase);
299
300	dev->iobase = iobase;
301
302	if (!iobase || !request_region(iobase,
303				       thisboard->num_asics * ASIC_IOSIZE,
304				       driver.driver_name)) {
305		printk("I/O port conflict\n");
306		return -EIO;
307	}
308
309/*
310 * Initialize dev->board_name.  Note that we can use the "thisboard"
311 * macro now, since we just initialized it in the last line.
312 */
313	dev->board_name = thisboard->name;
314
315/*
316 * Allocate the private structure area.  alloc_private() is a
317 * convenient macro defined in comedidev.h.
318 */
319	if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
320		printk("cannot allocate private data structure\n");
321		return -ENOMEM;
322	}
323
324	for (asic = 0; asic < MAX_ASICS; ++asic) {
325		devpriv->asics[asic].num = asic;
326		devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
327		devpriv->asics[asic].irq = 0;	/* this gets actually set at the end of
328						   this function when we
329						   request_irqs */
330		spin_lock_init(&devpriv->asics[asic].spinlock);
331	}
332
333	chans_left = CHANS_PER_ASIC * thisboard->num_asics;
334	n_subdevs = CALC_N_SUBDEVS(chans_left);
335	devpriv->sprivs =
336	    kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
337		    GFP_KERNEL);
338	if (!devpriv->sprivs) {
339		printk("cannot allocate subdevice private data structures\n");
340		return -ENOMEM;
341	}
342	/*
343	 * Allocate the subdevice structures.  alloc_subdevice() is a
344	 * convenient macro defined in comedidev.h.
345	 *
346	 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
347	 * 96-channel version of the board.
348	 */
349	if (alloc_subdevices(dev, n_subdevs) < 0) {
350		printk("cannot allocate subdevice data structures\n");
351		return -ENOMEM;
352	}
353
354	port = 0;
355	asic = 0;
356	for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
357		int byte_no;
358
359		s = dev->subdevices + sdev_no;
360		s->private = devpriv->sprivs + sdev_no;
361		s->maxdata = 1;
362		s->range_table = &range_digital;
363		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
364		s->type = COMEDI_SUBD_DIO;
365		s->insn_bits = pcmuio_dio_insn_bits;
366		s->insn_config = pcmuio_dio_insn_config;
367		s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
368		subpriv->intr.asic = -1;
369		subpriv->intr.first_chan = -1;
370		subpriv->intr.asic_chan = -1;
371		subpriv->intr.num_asic_chans = -1;
372		subpriv->intr.active = 0;
373		s->len_chanlist = 1;
374
375		/* save the ioport address for each 'port' of 8 channels in the
376		   subdevice */
377		for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
378			if (port >= PORTS_PER_ASIC) {
379				port = 0;
380				++asic;
381				thisasic_chanct = 0;
382			}
383			subpriv->iobases[byte_no] =
384			    devpriv->asics[asic].iobase + port;
385
386			if (thisasic_chanct <
387			    CHANS_PER_PORT * INTR_PORTS_PER_ASIC
388			    && subpriv->intr.asic < 0) {
389				/* this is an interrupt subdevice, so setup the struct */
390				subpriv->intr.asic = asic;
391				subpriv->intr.active = 0;
392				subpriv->intr.stop_count = 0;
393				subpriv->intr.first_chan = byte_no * 8;
394				subpriv->intr.asic_chan = thisasic_chanct;
395				subpriv->intr.num_asic_chans =
396				    s->n_chan - subpriv->intr.first_chan;
397				dev->read_subdev = s;
398				s->subdev_flags |= SDF_CMD_READ;
399				s->cancel = pcmuio_cancel;
400				s->do_cmd = pcmuio_cmd;
401				s->do_cmdtest = pcmuio_cmdtest;
402				s->len_chanlist = subpriv->intr.num_asic_chans;
403			}
404			thisasic_chanct += CHANS_PER_PORT;
405		}
406		spin_lock_init(&subpriv->intr.spinlock);
407
408		chans_left -= s->n_chan;
409
410		if (!chans_left) {
411			asic = 0;	/* reset the asic to our first asic, to do intr subdevs */
412			port = 0;
413		}
414
415	}
416
417	init_asics(dev);	/* clear out all the registers, basically */
418
419	for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
420		if (irq[asic]
421		    && request_irq(irq[asic], interrupt_pcmuio,
422				   IRQF_SHARED, thisboard->name, dev)) {
423			int i;
424			/* unroll the allocated irqs.. */
425			for (i = asic - 1; i >= 0; --i) {
426				free_irq(irq[i], dev);
427				devpriv->asics[i].irq = irq[i] = 0;
428			}
429			irq[asic] = 0;
430		}
431		devpriv->asics[asic].irq = irq[asic];
432	}
433
434	dev->irq = irq[0];	/* grr.. wish comedi dev struct supported multiple
435				   irqs.. */
436
437	if (irq[0]) {
438		printk("irq: %u ", irq[0]);
439		if (irq[1] && thisboard->num_asics == 2)
440			printk("second ASIC irq: %u ", irq[1]);
441	} else {
442		printk("(IRQ mode disabled) ");
443	}
444
445	printk("attached\n");
446
447	return 1;
448}
449
450/*
451 * _detach is called to deconfigure a device.  It should deallocate
452 * resources.
453 * This function is also called when _attach() fails, so it should be
454 * careful not to release resources that were not necessarily
455 * allocated by _attach().  dev->private and dev->subdevices are
456 * deallocated automatically by the core.
457 */
458static int pcmuio_detach(struct comedi_device *dev)
459{
460	int i;
461
462	printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
463	if (dev->iobase)
464		release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
465
466	for (i = 0; i < MAX_ASICS; ++i) {
467		if (devpriv->asics[i].irq)
468			free_irq(devpriv->asics[i].irq, dev);
469	}
470
471	if (devpriv && devpriv->sprivs)
472		kfree(devpriv->sprivs);
473
474	return 0;
475}
476
477/* DIO devices are slightly special.  Although it is possible to
478 * implement the insn_read/insn_write interface, it is much more
479 * useful to applications if you implement the insn_bits interface.
480 * This allows packed reading/writing of the DIO channels.  The
481 * comedi core can convert between insn_bits and insn_read/write */
482static int pcmuio_dio_insn_bits(struct comedi_device *dev,
483				struct comedi_subdevice *s,
484				struct comedi_insn *insn, unsigned int *data)
485{
486	int byte_no;
487	if (insn->n != 2)
488		return -EINVAL;
489
490	/* NOTE:
491	   reading a 0 means this channel was high
492	   writine a 0 sets the channel high
493	   reading a 1 means this channel was low
494	   writing a 1 means set this channel low
495
496	   Therefore everything is always inverted. */
497
498	/* The insn data is a mask in data[0] and the new data
499	 * in data[1], each channel cooresponding to a bit. */
500
501#ifdef DAMMIT_ITS_BROKEN
502	/* DEBUG */
503	printk("write mask: %08x  data: %08x\n", data[0], data[1]);
504#endif
505
506	s->state = 0;
507
508	for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
509		/* address of 8-bit port */
510		unsigned long ioaddr = subpriv->iobases[byte_no],
511		    /* bit offset of port in 32-bit doubleword */
512		    offset = byte_no * 8;
513		/* this 8-bit port's data */
514		unsigned char byte = 0,
515		    /* The write mask for this port (if any) */
516		    write_mask_byte = (data[0] >> offset) & 0xff,
517		    /* The data byte for this port */
518		    data_byte = (data[1] >> offset) & 0xff;
519
520		byte = inb(ioaddr);	/* read all 8-bits for this port */
521
522#ifdef DAMMIT_ITS_BROKEN
523		/* DEBUG */
524		printk
525		    ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
526		     byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
527		     offset, ioaddr, (unsigned)byte);
528#endif
529
530		if (write_mask_byte) {
531			/* this byte has some write_bits -- so set the output lines */
532			byte &= ~write_mask_byte;	/* clear bits for write mask */
533			byte |= ~data_byte & write_mask_byte;	/* set to inverted data_byte */
534			/* Write out the new digital output state */
535			outb(byte, ioaddr);
536		}
537#ifdef DAMMIT_ITS_BROKEN
538		/* DEBUG */
539		printk("data_out_byte %02x\n", (unsigned)byte);
540#endif
541		/* save the digital input lines for this byte.. */
542		s->state |= ((unsigned int)byte) << offset;
543	}
544
545	/* now return the DIO lines to data[1] - note they came inverted! */
546	data[1] = ~s->state;
547
548#ifdef DAMMIT_ITS_BROKEN
549	/* DEBUG */
550	printk("s->state %08x data_out %08x\n", s->state, data[1]);
551#endif
552
553	return 2;
554}
555
556/* The input or output configuration of each digital line is
557 * configured by a special insn_config instruction.  chanspec
558 * contains the channel to be changed, and data[0] contains the
559 * value COMEDI_INPUT or COMEDI_OUTPUT. */
560static int pcmuio_dio_insn_config(struct comedi_device *dev,
561				  struct comedi_subdevice *s,
562				  struct comedi_insn *insn, unsigned int *data)
563{
564	int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
565	    chan % 8;
566	unsigned long ioaddr;
567	unsigned char byte;
568
569	/* Compute ioaddr for this channel */
570	ioaddr = subpriv->iobases[byte_no];
571
572	/* NOTE:
573	   writing a 0 an IO channel's bit sets the channel to INPUT
574	   and pulls the line high as well
575
576	   writing a 1 to an IO channel's  bit pulls the line low
577
578	   All channels are implicitly always in OUTPUT mode -- but when
579	   they are high they can be considered to be in INPUT mode..
580
581	   Thus, we only force channels low if the config request was INPUT,
582	   otherwise we do nothing to the hardware.    */
583
584	switch (data[0]) {
585	case INSN_CONFIG_DIO_OUTPUT:
586		/* save to io_bits -- don't actually do anything since
587		   all input channels are also output channels... */
588		s->io_bits |= 1 << chan;
589		break;
590	case INSN_CONFIG_DIO_INPUT:
591		/* write a 0 to the actual register representing the channel
592		   to set it to 'input'.  0 means "float high". */
593		byte = inb(ioaddr);
594		byte &= ~(1 << bit_no);
595				/**< set input channel to '0' */
596
597		/* write out byte -- this is the only time we actually affect the
598		   hardware as all channels are implicitly output -- but input
599		   channels are set to float-high */
600		outb(byte, ioaddr);
601
602		/* save to io_bits */
603		s->io_bits &= ~(1 << chan);
604		break;
605
606	case INSN_CONFIG_DIO_QUERY:
607		/* retreive from shadow register */
608		data[1] =
609		    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
610		return insn->n;
611		break;
612
613	default:
614		return -EINVAL;
615		break;
616	}
617
618	return insn->n;
619}
620
621static void init_asics(struct comedi_device *dev)
622{				/* sets up an
623				   ASIC chip to defaults */
624	int asic;
625
626	for (asic = 0; asic < thisboard->num_asics; ++asic) {
627		int port, page;
628		unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
629
630		switch_page(dev, asic, 0);	/* switch back to page 0 */
631
632		/* first, clear all the DIO port bits */
633		for (port = 0; port < PORTS_PER_ASIC; ++port)
634			outb(0, baseaddr + REG_PORT0 + port);
635
636		/* Next, clear all the paged registers for each page */
637		for (page = 1; page < NUM_PAGES; ++page) {
638			int reg;
639			/* now clear all the paged registers */
640			switch_page(dev, asic, page);
641			for (reg = FIRST_PAGED_REG;
642			     reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
643				outb(0, baseaddr + reg);
644		}
645
646		/* DEBUG  set rising edge interrupts on port0 of both asics */
647		/*switch_page(dev, asic, PAGE_POL);
648		   outb(0xff, baseaddr + REG_POL0);
649		   switch_page(dev, asic, PAGE_ENAB);
650		   outb(0xff, baseaddr + REG_ENAB0); */
651		/* END DEBUG */
652
653		switch_page(dev, asic, 0);	/* switch back to default page 0 */
654
655	}
656}
657
658static void switch_page(struct comedi_device *dev, int asic, int page)
659{
660	if (asic < 0 || asic >= thisboard->num_asics)
661		return;		/* paranoia */
662	if (page < 0 || page >= NUM_PAGES)
663		return;		/* more paranoia */
664
665	devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
666	devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
667
668	/* now write out the shadow register */
669	outb(devpriv->asics[asic].pagelock,
670	     dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
671}
672
673#ifdef notused
674static void lock_port(struct comedi_device *dev, int asic, int port)
675{
676	if (asic < 0 || asic >= thisboard->num_asics)
677		return;		/* paranoia */
678	if (port < 0 || port >= PORTS_PER_ASIC)
679		return;		/* more paranoia */
680
681	devpriv->asics[asic].pagelock |= 0x1 << port;
682	/* now write out the shadow register */
683	outb(devpriv->asics[asic].pagelock,
684	     dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
685}
686
687static void unlock_port(struct comedi_device *dev, int asic, int port)
688{
689	if (asic < 0 || asic >= thisboard->num_asics)
690		return;		/* paranoia */
691	if (port < 0 || port >= PORTS_PER_ASIC)
692		return;		/* more paranoia */
693	devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
694	/* now write out the shadow register */
695	outb(devpriv->asics[asic].pagelock,
696	     dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
697}
698#endif /* notused */
699
700static irqreturn_t interrupt_pcmuio(int irq, void *d)
701{
702	int asic, got1 = 0;
703	struct comedi_device *dev = (struct comedi_device *)d;
704
705	for (asic = 0; asic < MAX_ASICS; ++asic) {
706		if (irq == devpriv->asics[asic].irq) {
707			unsigned long flags;
708			unsigned triggered = 0;
709			unsigned long iobase = devpriv->asics[asic].iobase;
710			/* it is an interrupt for ASIC #asic */
711			unsigned char int_pend;
712
713			spin_lock_irqsave(&devpriv->asics[asic].spinlock,
714					  flags);
715
716			int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
717
718			if (int_pend) {
719				int port;
720				for (port = 0; port < INTR_PORTS_PER_ASIC;
721				     ++port) {
722					if (int_pend & (0x1 << port)) {
723						unsigned char
724						    io_lines_with_edges = 0;
725						switch_page(dev, asic,
726							    PAGE_INT_ID);
727						io_lines_with_edges =
728						    inb(iobase +
729							REG_INT_ID0 + port);
730
731						if (io_lines_with_edges)
732							/* clear pending interrupt */
733							outb(0, iobase +
734							     REG_INT_ID0 +
735							     port);
736
737						triggered |=
738						    io_lines_with_edges <<
739						    port * 8;
740					}
741				}
742
743				++got1;
744			}
745
746			spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
747					       flags);
748
749			if (triggered) {
750				struct comedi_subdevice *s;
751				/* TODO here: dispatch io lines to subdevs with commands.. */
752				printk
753				    ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
754				     irq, asic, triggered);
755				for (s = dev->subdevices;
756				     s < dev->subdevices + dev->n_subdevices;
757				     ++s) {
758					if (subpriv->intr.asic == asic) {	/* this is an interrupt subdev, and it matches this asic! */
759						unsigned long flags;
760						unsigned oldevents;
761
762						spin_lock_irqsave(&subpriv->
763								  intr.spinlock,
764								  flags);
765
766						oldevents = s->async->events;
767
768						if (subpriv->intr.active) {
769							unsigned mytrig =
770							    ((triggered >>
771							      subpriv->intr.asic_chan)
772							     &
773							     ((0x1 << subpriv->
774							       intr.
775							       num_asic_chans) -
776							      1)) << subpriv->
777							    intr.first_chan;
778							if (mytrig &
779							    subpriv->intr.enabled_mask)
780							{
781								unsigned int val
782								    = 0;
783								unsigned int n,
784								    ch, len;
785
786								len =
787								    s->
788								    async->cmd.chanlist_len;
789								for (n = 0;
790								     n < len;
791								     n++) {
792									ch = CR_CHAN(s->async->cmd.chanlist[n]);
793									if (mytrig & (1U << ch)) {
794										val |= (1U << n);
795									}
796								}
797								/* Write the scan to the buffer. */
798								if (comedi_buf_put(s->async, ((short *)&val)[0])
799								    &&
800								    comedi_buf_put
801								    (s->async,
802								     ((short *)
803								      &val)[1]))
804								{
805									s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
806								} else {
807									/* Overflow! Stop acquisition!! */
808									/* TODO: STOP_ACQUISITION_CALL_HERE!! */
809									pcmuio_stop_intr
810									    (dev,
811									     s);
812								}
813
814								/* Check for end of acquisition. */
815								if (!subpriv->intr.continuous) {
816									/* stop_src == TRIG_COUNT */
817									if (subpriv->intr.stop_count > 0) {
818										subpriv->intr.stop_count--;
819										if (subpriv->intr.stop_count == 0) {
820											s->async->events |= COMEDI_CB_EOA;
821											/* TODO: STOP_ACQUISITION_CALL_HERE!! */
822											pcmuio_stop_intr
823											    (dev,
824											     s);
825										}
826									}
827								}
828							}
829						}
830
831						spin_unlock_irqrestore
832						    (&subpriv->intr.spinlock,
833						     flags);
834
835						if (oldevents !=
836						    s->async->events) {
837							comedi_event(dev, s);
838						}
839
840					}
841
842				}
843			}
844
845		}
846	}
847	if (!got1)
848		return IRQ_NONE;	/* interrupt from other source */
849	return IRQ_HANDLED;
850}
851
852static void pcmuio_stop_intr(struct comedi_device *dev,
853			     struct comedi_subdevice *s)
854{
855	int nports, firstport, asic, port;
856
857	asic = subpriv->intr.asic;
858	if (asic < 0)
859		return;		/* not an interrupt subdev */
860
861	subpriv->intr.enabled_mask = 0;
862	subpriv->intr.active = 0;
863	s->async->inttrig = 0;
864	nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
865	firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
866	switch_page(dev, asic, PAGE_ENAB);
867	for (port = firstport; port < firstport + nports; ++port) {
868		/* disable all intrs for this subdev.. */
869		outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
870	}
871}
872
873static int pcmuio_start_intr(struct comedi_device *dev,
874			     struct comedi_subdevice *s)
875{
876	if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
877		/* An empty acquisition! */
878		s->async->events |= COMEDI_CB_EOA;
879		subpriv->intr.active = 0;
880		return 1;
881	} else {
882		unsigned bits = 0, pol_bits = 0, n;
883		int nports, firstport, asic, port;
884		struct comedi_cmd *cmd = &s->async->cmd;
885
886		asic = subpriv->intr.asic;
887		if (asic < 0)
888			return 1;	/* not an interrupt
889					   subdev */
890		subpriv->intr.enabled_mask = 0;
891		subpriv->intr.active = 1;
892		nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
893		firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
894		if (cmd->chanlist) {
895			for (n = 0; n < cmd->chanlist_len; n++) {
896				bits |= (1U << CR_CHAN(cmd->chanlist[n]));
897				pol_bits |= (CR_AREF(cmd->chanlist[n])
898					     || CR_RANGE(cmd->
899							 chanlist[n]) ? 1U : 0U)
900				    << CR_CHAN(cmd->chanlist[n]);
901			}
902		}
903		bits &= ((0x1 << subpriv->intr.num_asic_chans) -
904			 1) << subpriv->intr.first_chan;
905		subpriv->intr.enabled_mask = bits;
906
907		switch_page(dev, asic, PAGE_ENAB);
908		for (port = firstport; port < firstport + nports; ++port) {
909			unsigned enab =
910			    bits >> (subpriv->intr.first_chan + (port -
911								 firstport) *
912				     8) & 0xff, pol =
913			    pol_bits >> (subpriv->intr.first_chan +
914					 (port - firstport) * 8) & 0xff;
915			/* set enab intrs for this subdev.. */
916			outb(enab,
917			     devpriv->asics[asic].iobase + REG_ENAB0 + port);
918			switch_page(dev, asic, PAGE_POL);
919			outb(pol,
920			     devpriv->asics[asic].iobase + REG_ENAB0 + port);
921		}
922	}
923	return 0;
924}
925
926static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
927{
928	unsigned long flags;
929
930	spin_lock_irqsave(&subpriv->intr.spinlock, flags);
931	if (subpriv->intr.active)
932		pcmuio_stop_intr(dev, s);
933	spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
934
935	return 0;
936}
937
938/*
939 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
940 */
941static int
942pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
943			  unsigned int trignum)
944{
945	unsigned long flags;
946	int event = 0;
947
948	if (trignum != 0)
949		return -EINVAL;
950
951	spin_lock_irqsave(&subpriv->intr.spinlock, flags);
952	s->async->inttrig = 0;
953	if (subpriv->intr.active) {
954		event = pcmuio_start_intr(dev, s);
955	}
956	spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
957
958	if (event) {
959		comedi_event(dev, s);
960	}
961
962	return 1;
963}
964
965/*
966 * 'do_cmd' function for an 'INTERRUPT' subdevice.
967 */
968static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
969{
970	struct comedi_cmd *cmd = &s->async->cmd;
971	unsigned long flags;
972	int event = 0;
973
974	spin_lock_irqsave(&subpriv->intr.spinlock, flags);
975	subpriv->intr.active = 1;
976
977	/* Set up end of acquisition. */
978	switch (cmd->stop_src) {
979	case TRIG_COUNT:
980		subpriv->intr.continuous = 0;
981		subpriv->intr.stop_count = cmd->stop_arg;
982		break;
983	default:
984		/* TRIG_NONE */
985		subpriv->intr.continuous = 1;
986		subpriv->intr.stop_count = 0;
987		break;
988	}
989
990	/* Set up start of acquisition. */
991	switch (cmd->start_src) {
992	case TRIG_INT:
993		s->async->inttrig = pcmuio_inttrig_start_intr;
994		break;
995	default:
996		/* TRIG_NOW */
997		event = pcmuio_start_intr(dev, s);
998		break;
999	}
1000	spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
1001
1002	if (event) {
1003		comedi_event(dev, s);
1004	}
1005
1006	return 0;
1007}
1008
1009static int
1010pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1011	       struct comedi_cmd *cmd)
1012{
1013	return comedi_pcm_cmdtest(dev, s, cmd);
1014}
1015
1016/*
1017 * A convenient macro that defines init_module() and cleanup_module(),
1018 * as necessary.
1019 */
1020COMEDI_INITCLEANUP(driver);
1021