pcmuio.c revision 976fe5ab27e682d3dbfec26517a8a248638af07d
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	dev_dbg(dev->hw_dev, "comedi%d: %s: io: %lx attached\n", dev->minor,
299		driver.driver_name, iobase);
300
301	dev->iobase = iobase;
302
303	if (!iobase || !request_region(iobase,
304				       thisboard->num_asics * ASIC_IOSIZE,
305				       driver.driver_name)) {
306		dev_err(dev->hw_dev, "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		dev_warn(dev->hw_dev, "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		dev_warn(dev->hw_dev, "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		dev_dbg(dev->hw_dev, "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		dev_dbg(dev->hw_dev, "irq: %u\n", irq[0]);
440		if (irq[1] && thisboard->num_asics == 2)
441			dev_dbg(dev->hw_dev, "second ASIC irq: %u\n", irq[1]);
442	} else {
443		dev_dbg(dev->hw_dev, "(IRQ mode disabled)\n");
444	}
445
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	dev_dbg(dev->hw_dev, "comedi%d: %s: remove\n", dev->minor,
463		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	dev_dbg(dev->hw_dev, "write mask: %08x  data: %08x\n", data[0],
505		data[1]);
506#endif
507
508	s->state = 0;
509
510	for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
511		/* address of 8-bit port */
512		unsigned long ioaddr = subpriv->iobases[byte_no],
513		    /* bit offset of port in 32-bit doubleword */
514		    offset = byte_no * 8;
515		/* this 8-bit port's data */
516		unsigned char byte = 0,
517		    /* The write mask for this port (if any) */
518		    write_mask_byte = (data[0] >> offset) & 0xff,
519		    /* The data byte for this port */
520		    data_byte = (data[1] >> offset) & 0xff;
521
522		byte = inb(ioaddr);	/* read all 8-bits for this port */
523
524#ifdef DAMMIT_ITS_BROKEN
525		/* DEBUG */
526		printk
527		    ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
528		     byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
529		     offset, ioaddr, (unsigned)byte);
530#endif
531
532		if (write_mask_byte) {
533			/* this byte has some write_bits -- so set the output lines */
534			byte &= ~write_mask_byte;	/* clear bits for write mask */
535			byte |= ~data_byte & write_mask_byte;	/* set to inverted data_byte */
536			/* Write out the new digital output state */
537			outb(byte, ioaddr);
538		}
539#ifdef DAMMIT_ITS_BROKEN
540		/* DEBUG */
541		dev_dbg(dev->hw_dev, "data_out_byte %02x\n", (unsigned)byte);
542#endif
543		/* save the digital input lines for this byte.. */
544		s->state |= ((unsigned int)byte) << offset;
545	}
546
547	/* now return the DIO lines to data[1] - note they came inverted! */
548	data[1] = ~s->state;
549
550#ifdef DAMMIT_ITS_BROKEN
551	/* DEBUG */
552	dev_dbg(dev->hw_dev, "s->state %08x data_out %08x\n", s->state,
553		data[1]);
554#endif
555
556	return 2;
557}
558
559/* The input or output configuration of each digital line is
560 * configured by a special insn_config instruction.  chanspec
561 * contains the channel to be changed, and data[0] contains the
562 * value COMEDI_INPUT or COMEDI_OUTPUT. */
563static int pcmuio_dio_insn_config(struct comedi_device *dev,
564				  struct comedi_subdevice *s,
565				  struct comedi_insn *insn, unsigned int *data)
566{
567	int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
568	    chan % 8;
569	unsigned long ioaddr;
570	unsigned char byte;
571
572	/* Compute ioaddr for this channel */
573	ioaddr = subpriv->iobases[byte_no];
574
575	/* NOTE:
576	   writing a 0 an IO channel's bit sets the channel to INPUT
577	   and pulls the line high as well
578
579	   writing a 1 to an IO channel's  bit pulls the line low
580
581	   All channels are implicitly always in OUTPUT mode -- but when
582	   they are high they can be considered to be in INPUT mode..
583
584	   Thus, we only force channels low if the config request was INPUT,
585	   otherwise we do nothing to the hardware.    */
586
587	switch (data[0]) {
588	case INSN_CONFIG_DIO_OUTPUT:
589		/* save to io_bits -- don't actually do anything since
590		   all input channels are also output channels... */
591		s->io_bits |= 1 << chan;
592		break;
593	case INSN_CONFIG_DIO_INPUT:
594		/* write a 0 to the actual register representing the channel
595		   to set it to 'input'.  0 means "float high". */
596		byte = inb(ioaddr);
597		byte &= ~(1 << bit_no);
598				/**< set input channel to '0' */
599
600		/* write out byte -- this is the only time we actually affect the
601		   hardware as all channels are implicitly output -- but input
602		   channels are set to float-high */
603		outb(byte, ioaddr);
604
605		/* save to io_bits */
606		s->io_bits &= ~(1 << chan);
607		break;
608
609	case INSN_CONFIG_DIO_QUERY:
610		/* retrieve from shadow register */
611		data[1] =
612		    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
613		return insn->n;
614		break;
615
616	default:
617		return -EINVAL;
618		break;
619	}
620
621	return insn->n;
622}
623
624static void init_asics(struct comedi_device *dev)
625{				/* sets up an
626				   ASIC chip to defaults */
627	int asic;
628
629	for (asic = 0; asic < thisboard->num_asics; ++asic) {
630		int port, page;
631		unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
632
633		switch_page(dev, asic, 0);	/* switch back to page 0 */
634
635		/* first, clear all the DIO port bits */
636		for (port = 0; port < PORTS_PER_ASIC; ++port)
637			outb(0, baseaddr + REG_PORT0 + port);
638
639		/* Next, clear all the paged registers for each page */
640		for (page = 1; page < NUM_PAGES; ++page) {
641			int reg;
642			/* now clear all the paged registers */
643			switch_page(dev, asic, page);
644			for (reg = FIRST_PAGED_REG;
645			     reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
646				outb(0, baseaddr + reg);
647		}
648
649		/* DEBUG  set rising edge interrupts on port0 of both asics */
650		/*switch_page(dev, asic, PAGE_POL);
651		   outb(0xff, baseaddr + REG_POL0);
652		   switch_page(dev, asic, PAGE_ENAB);
653		   outb(0xff, baseaddr + REG_ENAB0); */
654		/* END DEBUG */
655
656		switch_page(dev, asic, 0);	/* switch back to default page 0 */
657
658	}
659}
660
661static void switch_page(struct comedi_device *dev, int asic, int page)
662{
663	if (asic < 0 || asic >= thisboard->num_asics)
664		return;		/* paranoia */
665	if (page < 0 || page >= NUM_PAGES)
666		return;		/* more paranoia */
667
668	devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
669	devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
670
671	/* now write out the shadow register */
672	outb(devpriv->asics[asic].pagelock,
673	     dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
674}
675
676#ifdef notused
677static void lock_port(struct comedi_device *dev, int asic, int port)
678{
679	if (asic < 0 || asic >= thisboard->num_asics)
680		return;		/* paranoia */
681	if (port < 0 || port >= PORTS_PER_ASIC)
682		return;		/* more paranoia */
683
684	devpriv->asics[asic].pagelock |= 0x1 << port;
685	/* now write out the shadow register */
686	outb(devpriv->asics[asic].pagelock,
687	     dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
688}
689
690static void unlock_port(struct comedi_device *dev, int asic, int port)
691{
692	if (asic < 0 || asic >= thisboard->num_asics)
693		return;		/* paranoia */
694	if (port < 0 || port >= PORTS_PER_ASIC)
695		return;		/* more paranoia */
696	devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
697	/* now write out the shadow register */
698	outb(devpriv->asics[asic].pagelock,
699	     dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
700}
701#endif /* notused */
702
703static irqreturn_t interrupt_pcmuio(int irq, void *d)
704{
705	int asic, got1 = 0;
706	struct comedi_device *dev = (struct comedi_device *)d;
707
708	for (asic = 0; asic < MAX_ASICS; ++asic) {
709		if (irq == devpriv->asics[asic].irq) {
710			unsigned long flags;
711			unsigned triggered = 0;
712			unsigned long iobase = devpriv->asics[asic].iobase;
713			/* it is an interrupt for ASIC #asic */
714			unsigned char int_pend;
715
716			spin_lock_irqsave(&devpriv->asics[asic].spinlock,
717					  flags);
718
719			int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
720
721			if (int_pend) {
722				int port;
723				for (port = 0; port < INTR_PORTS_PER_ASIC;
724				     ++port) {
725					if (int_pend & (0x1 << port)) {
726						unsigned char
727						    io_lines_with_edges = 0;
728						switch_page(dev, asic,
729							    PAGE_INT_ID);
730						io_lines_with_edges =
731						    inb(iobase +
732							REG_INT_ID0 + port);
733
734						if (io_lines_with_edges)
735							/* clear pending interrupt */
736							outb(0, iobase +
737							     REG_INT_ID0 +
738							     port);
739
740						triggered |=
741						    io_lines_with_edges <<
742						    port * 8;
743					}
744				}
745
746				++got1;
747			}
748
749			spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
750					       flags);
751
752			if (triggered) {
753				struct comedi_subdevice *s;
754				/* TODO here: dispatch io lines to subdevs with commands.. */
755				printk
756				    ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
757				     irq, asic, triggered);
758				for (s = dev->subdevices;
759				     s < dev->subdevices + dev->n_subdevices;
760				     ++s) {
761					if (subpriv->intr.asic == asic) {	/* this is an interrupt subdev, and it matches this asic! */
762						unsigned long flags;
763						unsigned oldevents;
764
765						spin_lock_irqsave(&subpriv->
766								  intr.spinlock,
767								  flags);
768
769						oldevents = s->async->events;
770
771						if (subpriv->intr.active) {
772							unsigned mytrig =
773							    ((triggered >>
774							      subpriv->intr.asic_chan)
775							     &
776							     ((0x1 << subpriv->
777							       intr.
778							       num_asic_chans) -
779							      1)) << subpriv->
780							    intr.first_chan;
781							if (mytrig &
782							    subpriv->intr.enabled_mask)
783							{
784								unsigned int val
785								    = 0;
786								unsigned int n,
787								    ch, len;
788
789								len =
790								    s->
791								    async->cmd.chanlist_len;
792								for (n = 0;
793								     n < len;
794								     n++) {
795									ch = CR_CHAN(s->async->cmd.chanlist[n]);
796									if (mytrig & (1U << ch)) {
797										val |= (1U << n);
798									}
799								}
800								/* Write the scan to the buffer. */
801								if (comedi_buf_put(s->async, ((short *)&val)[0])
802								    &&
803								    comedi_buf_put
804								    (s->async,
805								     ((short *)
806								      &val)[1]))
807								{
808									s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
809								} else {
810									/* Overflow! Stop acquisition!! */
811									/* TODO: STOP_ACQUISITION_CALL_HERE!! */
812									pcmuio_stop_intr
813									    (dev,
814									     s);
815								}
816
817								/* Check for end of acquisition. */
818								if (!subpriv->intr.continuous) {
819									/* stop_src == TRIG_COUNT */
820									if (subpriv->intr.stop_count > 0) {
821										subpriv->intr.stop_count--;
822										if (subpriv->intr.stop_count == 0) {
823											s->async->events |= COMEDI_CB_EOA;
824											/* TODO: STOP_ACQUISITION_CALL_HERE!! */
825											pcmuio_stop_intr
826											    (dev,
827											     s);
828										}
829									}
830								}
831							}
832						}
833
834						spin_unlock_irqrestore
835						    (&subpriv->intr.spinlock,
836						     flags);
837
838						if (oldevents !=
839						    s->async->events) {
840							comedi_event(dev, s);
841						}
842
843					}
844
845				}
846			}
847
848		}
849	}
850	if (!got1)
851		return IRQ_NONE;	/* interrupt from other source */
852	return IRQ_HANDLED;
853}
854
855static void pcmuio_stop_intr(struct comedi_device *dev,
856			     struct comedi_subdevice *s)
857{
858	int nports, firstport, asic, port;
859
860	asic = subpriv->intr.asic;
861	if (asic < 0)
862		return;		/* not an interrupt subdev */
863
864	subpriv->intr.enabled_mask = 0;
865	subpriv->intr.active = 0;
866	s->async->inttrig = 0;
867	nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
868	firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
869	switch_page(dev, asic, PAGE_ENAB);
870	for (port = firstport; port < firstport + nports; ++port) {
871		/* disable all intrs for this subdev.. */
872		outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
873	}
874}
875
876static int pcmuio_start_intr(struct comedi_device *dev,
877			     struct comedi_subdevice *s)
878{
879	if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
880		/* An empty acquisition! */
881		s->async->events |= COMEDI_CB_EOA;
882		subpriv->intr.active = 0;
883		return 1;
884	} else {
885		unsigned bits = 0, pol_bits = 0, n;
886		int nports, firstport, asic, port;
887		struct comedi_cmd *cmd = &s->async->cmd;
888
889		asic = subpriv->intr.asic;
890		if (asic < 0)
891			return 1;	/* not an interrupt
892					   subdev */
893		subpriv->intr.enabled_mask = 0;
894		subpriv->intr.active = 1;
895		nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
896		firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
897		if (cmd->chanlist) {
898			for (n = 0; n < cmd->chanlist_len; n++) {
899				bits |= (1U << CR_CHAN(cmd->chanlist[n]));
900				pol_bits |= (CR_AREF(cmd->chanlist[n])
901					     || CR_RANGE(cmd->
902							 chanlist[n]) ? 1U : 0U)
903				    << CR_CHAN(cmd->chanlist[n]);
904			}
905		}
906		bits &= ((0x1 << subpriv->intr.num_asic_chans) -
907			 1) << subpriv->intr.first_chan;
908		subpriv->intr.enabled_mask = bits;
909
910		switch_page(dev, asic, PAGE_ENAB);
911		for (port = firstport; port < firstport + nports; ++port) {
912			unsigned enab =
913			    bits >> (subpriv->intr.first_chan + (port -
914								 firstport) *
915				     8) & 0xff, pol =
916			    pol_bits >> (subpriv->intr.first_chan +
917					 (port - firstport) * 8) & 0xff;
918			/* set enab intrs for this subdev.. */
919			outb(enab,
920			     devpriv->asics[asic].iobase + REG_ENAB0 + port);
921			switch_page(dev, asic, PAGE_POL);
922			outb(pol,
923			     devpriv->asics[asic].iobase + REG_ENAB0 + port);
924		}
925	}
926	return 0;
927}
928
929static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
930{
931	unsigned long flags;
932
933	spin_lock_irqsave(&subpriv->intr.spinlock, flags);
934	if (subpriv->intr.active)
935		pcmuio_stop_intr(dev, s);
936	spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
937
938	return 0;
939}
940
941/*
942 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
943 */
944static int
945pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
946			  unsigned int trignum)
947{
948	unsigned long flags;
949	int event = 0;
950
951	if (trignum != 0)
952		return -EINVAL;
953
954	spin_lock_irqsave(&subpriv->intr.spinlock, flags);
955	s->async->inttrig = 0;
956	if (subpriv->intr.active)
957		event = pcmuio_start_intr(dev, s);
958
959	spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
960
961	if (event)
962		comedi_event(dev, s);
963
964	return 1;
965}
966
967/*
968 * 'do_cmd' function for an 'INTERRUPT' subdevice.
969 */
970static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
971{
972	struct comedi_cmd *cmd = &s->async->cmd;
973	unsigned long flags;
974	int event = 0;
975
976	spin_lock_irqsave(&subpriv->intr.spinlock, flags);
977	subpriv->intr.active = 1;
978
979	/* Set up end of acquisition. */
980	switch (cmd->stop_src) {
981	case TRIG_COUNT:
982		subpriv->intr.continuous = 0;
983		subpriv->intr.stop_count = cmd->stop_arg;
984		break;
985	default:
986		/* TRIG_NONE */
987		subpriv->intr.continuous = 1;
988		subpriv->intr.stop_count = 0;
989		break;
990	}
991
992	/* Set up start of acquisition. */
993	switch (cmd->start_src) {
994	case TRIG_INT:
995		s->async->inttrig = pcmuio_inttrig_start_intr;
996		break;
997	default:
998		/* TRIG_NOW */
999		event = pcmuio_start_intr(dev, s);
1000		break;
1001	}
1002	spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
1003
1004	if (event)
1005		comedi_event(dev, s);
1006
1007	return 0;
1008}
1009
1010static int
1011pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1012	       struct comedi_cmd *cmd)
1013{
1014	return comedi_pcm_cmdtest(dev, s, cmd);
1015}
1016
1017/*
1018 * A convenient macro that defines init_module() and cleanup_module(),
1019 * as necessary.
1020 */
1021static int __init driver_init_module(void)
1022{
1023	return comedi_driver_register(&driver);
1024}
1025
1026static void __exit driver_cleanup_module(void)
1027{
1028	comedi_driver_unregister(&driver);
1029}
1030
1031module_init(driver_init_module);
1032module_exit(driver_cleanup_module);
1033
1034MODULE_AUTHOR("Comedi http://www.comedi.org");
1035MODULE_DESCRIPTION("Comedi low-level driver");
1036MODULE_LICENSE("GPL");
1037