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