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