amplc_dio200.c revision 669c930c8fe266917865f923db51c3b654ab78a3
1/*
2    comedi/drivers/amplc_dio200.c
3    Driver for Amplicon PC272E and PCI272 DIO boards.
4    (Support for other boards in Amplicon 200 series may be added at
5    a later date, e.g. PCI215.)
6
7    Copyright (C) 2005 MEV Ltd. <http://www.mev.co.uk/>
8
9    COMEDI - Linux Control and Measurement Device Interface
10    Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 2 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25
26*/
27/*
28Driver: amplc_dio200
29Description: Amplicon 200 Series Digital I/O
30Author: Ian Abbott <abbotti@mev.co.uk>
31Devices: [Amplicon] PC212E (pc212e), PC214E (pc214e), PC215E (pc215e),
32  PCI215 (pci215 or amplc_dio200), PC218E (pc218e), PC272E (pc272e),
33  PCI272 (pci272 or amplc_dio200)
34Updated: Wed, 22 Oct 2008 13:36:02 +0100
35Status: works
36
37Configuration options - PC212E, PC214E, PC215E, PC218E, PC272E:
38  [0] - I/O port base address
39  [1] - IRQ (optional, but commands won't work without it)
40
41Configuration options - PCI215, PCI272:
42  [0] - PCI bus of device (optional)
43  [1] - PCI slot of device (optional)
44  If bus/slot is not specified, the first available PCI device will
45  be used.
46
47Passing a zero for an option is the same as leaving it unspecified.
48
49SUBDEVICES
50
51		    PC218E         PC212E      PC215E/PCI215
52		 -------------  -------------  -------------
53  Subdevices           7              6              5
54   0                 CTR-X1         PPI-X          PPI-X
55   1                 CTR-X2         CTR-Y1         PPI-Y
56   2                 CTR-Y1         CTR-Y2         CTR-Z1
57   3                 CTR-Y2         CTR-Z1         CTR-Z2
58   4                 CTR-Z1         CTR-Z2       INTERRUPT
59   5                 CTR-Z2       INTERRUPT
60   6               INTERRUPT
61
62		    PC214E      PC272E/PCI272
63		 -------------  -------------
64  Subdevices           4              4
65   0                 PPI-X          PPI-X
66   1                 PPI-Y          PPI-Y
67   2                 CTR-Z1*        PPI-Z
68   3               INTERRUPT*     INTERRUPT
69
70Each PPI is a 8255 chip providing 24 DIO channels.  The DIO channels
71are configurable as inputs or outputs in four groups:
72
73  Port A  - channels  0 to  7
74  Port B  - channels  8 to 15
75  Port CL - channels 16 to 19
76  Port CH - channels 20 to 23
77
78Only mode 0 of the 8255 chips is supported.
79
80Each CTR is a 8254 chip providing 3 16-bit counter channels.  Each
81channel is configured individually with INSN_CONFIG instructions.  The
82specific type of configuration instruction is specified in data[0].
83Some configuration instructions expect an additional parameter in
84data[1]; others return a value in data[1].  The following configuration
85instructions are supported:
86
87  INSN_CONFIG_SET_COUNTER_MODE.  Sets the counter channel's mode and
88    BCD/binary setting specified in data[1].
89
90  INSN_CONFIG_8254_READ_STATUS.  Reads the status register value for the
91    counter channel into data[1].
92
93  INSN_CONFIG_SET_CLOCK_SRC.  Sets the counter channel's clock source as
94    specified in data[1] (this is a hardware-specific value).  Not
95    supported on PC214E.  For the other boards, valid clock sources are
96    0 to 7 as follows:
97
98      0.  CLK n, the counter channel's dedicated CLK input from the SK1
99	connector.  (N.B. for other values, the counter channel's CLKn
100	pin on the SK1 connector is an output!)
101      1.  Internal 10 MHz clock.
102      2.  Internal 1 MHz clock.
103      3.  Internal 100 kHz clock.
104      4.  Internal 10 kHz clock.
105      5.  Internal 1 kHz clock.
106      6.  OUT n-1, the output of counter channel n-1 (see note 1 below).
107      7.  Ext Clock, the counter chip's dedicated Ext Clock input from
108	the SK1 connector.  This pin is shared by all three counter
109	channels on the chip.
110
111  INSN_CONFIG_GET_CLOCK_SRC.  Returns the counter channel's current
112    clock source in data[1].  For internal clock sources, data[2] is set
113    to the period in ns.
114
115  INSN_CONFIG_SET_GATE_SRC.  Sets the counter channel's gate source as
116    specified in data[2] (this is a hardware-specific value).  Not
117    supported on PC214E.  For the other boards, valid gate sources are 0
118    to 7 as follows:
119
120      0.  VCC (internal +5V d.c.), i.e. gate permanently enabled.
121      1.  GND (internal 0V d.c.), i.e. gate permanently disabled.
122      2.  GAT n, the counter channel's dedicated GAT input from the SK1
123	connector.  (N.B. for other values, the counter channel's GATn
124	pin on the SK1 connector is an output!)
125      3.  /OUT n-2, the inverted output of counter channel n-2 (see note
126	2 below).
127      4.  Reserved.
128      5.  Reserved.
129      6.  Reserved.
130      7.  Reserved.
131
132  INSN_CONFIG_GET_GATE_SRC.  Returns the counter channel's current gate
133    source in data[2].
134
135Clock and gate interconnection notes:
136
137  1.  Clock source OUT n-1 is the output of the preceding channel on the
138  same counter subdevice if n > 0, or the output of channel 2 on the
139  preceding counter subdevice (see note 3) if n = 0.
140
141  2.  Gate source /OUT n-2 is the inverted output of channel 0 on the
142  same counter subdevice if n = 2, or the inverted output of channel n+1
143  on the preceding counter subdevice (see note 3) if n < 2.
144
145  3.  The counter subdevices are connected in a ring, so the highest
146  counter subdevice precedes the lowest.
147
148The 'INTERRUPT' subdevice pretends to be a digital input subdevice.  The
149digital inputs come from the interrupt status register.  The number of
150channels matches the number of interrupt sources.  The PC214E does not
151have an interrupt status register; see notes on 'INTERRUPT SOURCES'
152below.
153
154INTERRUPT SOURCES
155
156		    PC218E         PC212E      PC215E/PCI215
157		 -------------  -------------  -------------
158  Sources              6              6              6
159   0              CTR-X1-OUT      PPI-X-C0       PPI-X-C0
160   1              CTR-X2-OUT      PPI-X-C3       PPI-X-C3
161   2              CTR-Y1-OUT     CTR-Y1-OUT      PPI-Y-C0
162   3              CTR-Y2-OUT     CTR-Y2-OUT      PPI-Y-C3
163   4              CTR-Z1-OUT     CTR-Z1-OUT     CTR-Z1-OUT
164   5              CTR-Z2-OUT     CTR-Z2-OUT     CTR-Z2-OUT
165
166		    PC214E      PC272E/PCI272
167		 -------------  -------------
168  Sources              1              6
169   0               JUMPER-J5      PPI-X-C0
170   1                              PPI-X-C3
171   2                              PPI-Y-C0
172   3                              PPI-Y-C3
173   4                              PPI-Z-C0
174   5                              PPI-Z-C3
175
176When an interrupt source is enabled in the interrupt source enable
177register, a rising edge on the source signal latches the corresponding
178bit to 1 in the interrupt status register.
179
180When the interrupt status register value as a whole (actually, just the
1816 least significant bits) goes from zero to non-zero, the board will
182generate an interrupt.  For level-triggered hardware interrupts (PCI
183card), the interrupt will remain asserted until the interrupt status
184register is cleared to zero.  For edge-triggered hardware interrupts
185(ISA card), no further interrupts will occur until the interrupt status
186register is cleared to zero.  To clear a bit to zero in the interrupt
187status register, the corresponding interrupt source must be disabled
188in the interrupt source enable register (there is no separate interrupt
189clear register).
190
191The PC214E does not have an interrupt source enable register or an
192interrupt status register; its 'INTERRUPT' subdevice has a single
193channel and its interrupt source is selected by the position of jumper
194J5.
195
196COMMANDS
197
198The driver supports a read streaming acquisition command on the
199'INTERRUPT' subdevice.  The channel list selects the interrupt sources
200to be enabled.  All channels will be sampled together (convert_src ==
201TRIG_NOW).  The scan begins a short time after the hardware interrupt
202occurs, subject to interrupt latencies (scan_begin_src == TRIG_EXT,
203scan_begin_arg == 0).  The value read from the interrupt status register
204is packed into a short value, one bit per requested channel, in the
205order they appear in the channel list.
206*/
207
208#include <linux/interrupt.h>
209
210#include "../comedidev.h"
211
212#include "comedi_pci.h"
213
214#include "8255.h"
215#include "8253.h"
216
217#define DIO200_DRIVER_NAME	"amplc_dio200"
218
219/* PCI IDs */
220/* #define PCI_VENDOR_ID_AMPLICON 0x14dc */
221#define PCI_DEVICE_ID_AMPLICON_PCI272 0x000a
222#define PCI_DEVICE_ID_AMPLICON_PCI215 0x000b
223#define PCI_DEVICE_ID_INVALID 0xffff
224
225/* 200 series registers */
226#define DIO200_IO_SIZE		0x20
227#define DIO200_XCLK_SCE		0x18	/* Group X clock selection register */
228#define DIO200_YCLK_SCE		0x19	/* Group Y clock selection register */
229#define DIO200_ZCLK_SCE		0x1a	/* Group Z clock selection register */
230#define DIO200_XGAT_SCE		0x1b	/* Group X gate selection register */
231#define DIO200_YGAT_SCE		0x1c	/* Group Y gate selection register */
232#define DIO200_ZGAT_SCE		0x1d	/* Group Z gate selection register */
233#define DIO200_INT_SCE		0x1e	/* Interrupt enable/status register */
234
235/*
236 * Macros for constructing value for DIO_200_?CLK_SCE and
237 * DIO_200_?GAT_SCE registers:
238 *
239 * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
240 * 'chan' is the channel: 0, 1 or 2.
241 * 'source' is the signal source: 0 to 7.
242 */
243#define CLK_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
244#define GAT_SCE(which, chan, source) (((which) << 5) | ((chan) << 3) | (source))
245
246/*
247 * Periods of the internal clock sources in nanoseconds.
248 */
249static const unsigned clock_period[8] = {
250	0,			/* dedicated clock input/output pin */
251	100,			/* 10 MHz */
252	1000,			/* 1 MHz */
253	10000,			/* 100 kHz */
254	100000,			/* 10 kHz */
255	1000000,		/* 1 kHz */
256	0,			/* OUT N-1 */
257	0			/* group clock input pin */
258};
259
260/*
261 * Board descriptions.
262 */
263
264enum dio200_bustype { isa_bustype, pci_bustype };
265
266enum dio200_model {
267	pc212e_model,
268	pc214e_model,
269	pc215e_model, pci215_model,
270	pc218e_model,
271	pc272e_model, pci272_model,
272	anypci_model
273};
274
275enum dio200_layout {
276	pc212_layout,
277	pc214_layout,
278	pc215_layout,
279	pc218_layout,
280	pc272_layout
281};
282
283struct dio200_board {
284	const char *name;
285	unsigned short devid;
286	enum dio200_bustype bustype;
287	enum dio200_model model;
288	enum dio200_layout layout;
289};
290
291static const struct dio200_board dio200_boards[] = {
292	{
293	 .name = "pc212e",
294	 .bustype = isa_bustype,
295	 .model = pc212e_model,
296	 .layout = pc212_layout,
297	 },
298	{
299	 .name = "pc214e",
300	 .bustype = isa_bustype,
301	 .model = pc214e_model,
302	 .layout = pc214_layout,
303	 },
304	{
305	 .name = "pc215e",
306	 .bustype = isa_bustype,
307	 .model = pc215e_model,
308	 .layout = pc215_layout,
309	 },
310#ifdef CONFIG_COMEDI_PCI
311	{
312	 .name = "pci215",
313	 .devid = PCI_DEVICE_ID_AMPLICON_PCI215,
314	 .bustype = pci_bustype,
315	 .model = pci215_model,
316	 .layout = pc215_layout,
317	 },
318#endif
319	{
320	 .name = "pc218e",
321	 .bustype = isa_bustype,
322	 .model = pc218e_model,
323	 .layout = pc218_layout,
324	 },
325	{
326	 .name = "pc272e",
327	 .bustype = isa_bustype,
328	 .model = pc272e_model,
329	 .layout = pc272_layout,
330	 },
331#ifdef CONFIG_COMEDI_PCI
332	{
333	 .name = "pci272",
334	 .devid = PCI_DEVICE_ID_AMPLICON_PCI272,
335	 .bustype = pci_bustype,
336	 .model = pci272_model,
337	 .layout = pc272_layout,
338	 },
339#endif
340#ifdef CONFIG_COMEDI_PCI
341	{
342	 .name = DIO200_DRIVER_NAME,
343	 .devid = PCI_DEVICE_ID_INVALID,
344	 .bustype = pci_bustype,
345	 .model = anypci_model,	/* wildcard */
346	 },
347#endif
348};
349
350/*
351 * Layout descriptions - some ISA and PCI board descriptions share the same
352 * layout.
353 */
354
355enum dio200_sdtype { sd_none, sd_intr, sd_8255, sd_8254 };
356
357#define DIO200_MAX_SUBDEVS	7
358#define DIO200_MAX_ISNS		6
359
360struct dio200_layout_struct {
361	unsigned short n_subdevs;	/* number of subdevices */
362	unsigned char sdtype[DIO200_MAX_SUBDEVS];	/* enum dio200_sdtype */
363	unsigned char sdinfo[DIO200_MAX_SUBDEVS];	/* depends on sdtype */
364	char has_int_sce;	/* has interrupt enable/status register */
365	char has_clk_gat_sce;	/* has clock/gate selection registers */
366};
367
368static const struct dio200_layout_struct dio200_layouts[] = {
369	[pc212_layout] = {
370			  .n_subdevs = 6,
371			  .sdtype = {sd_8255, sd_8254, sd_8254, sd_8254,
372				     sd_8254,
373				     sd_intr},
374			  .sdinfo = {0x00, 0x08, 0x0C, 0x10, 0x14,
375				     0x3F},
376			  .has_int_sce = 1,
377			  .has_clk_gat_sce = 1,
378			  },
379	[pc214_layout] = {
380			  .n_subdevs = 4,
381			  .sdtype = {sd_8255, sd_8255, sd_8254,
382				     sd_intr},
383			  .sdinfo = {0x00, 0x08, 0x10, 0x01},
384			  .has_int_sce = 0,
385			  .has_clk_gat_sce = 0,
386			  },
387	[pc215_layout] = {
388			  .n_subdevs = 5,
389			  .sdtype = {sd_8255, sd_8255, sd_8254,
390				     sd_8254,
391				     sd_intr},
392			  .sdinfo = {0x00, 0x08, 0x10, 0x14, 0x3F},
393			  .has_int_sce = 1,
394			  .has_clk_gat_sce = 1,
395			  },
396	[pc218_layout] = {
397			  .n_subdevs = 7,
398			  .sdtype = {sd_8254, sd_8254, sd_8255, sd_8254,
399				     sd_8254,
400				     sd_intr},
401			  .sdinfo = {0x00, 0x04, 0x08, 0x0C, 0x10,
402				     0x14,
403				     0x3F},
404			  .has_int_sce = 1,
405			  .has_clk_gat_sce = 1,
406			  },
407	[pc272_layout] = {
408			  .n_subdevs = 4,
409			  .sdtype = {sd_8255, sd_8255, sd_8255,
410				     sd_intr},
411			  .sdinfo = {0x00, 0x08, 0x10, 0x3F},
412			  .has_int_sce = 1,
413			  .has_clk_gat_sce = 0,
414			  },
415};
416
417/*
418 * PCI driver table.
419 */
420
421#ifdef CONFIG_COMEDI_PCI
422static DEFINE_PCI_DEVICE_TABLE(dio200_pci_table) = {
423	{
424	PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI215,
425		    PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
426	PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI272,
427		    PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
428	0}
429};
430
431MODULE_DEVICE_TABLE(pci, dio200_pci_table);
432#endif /* CONFIG_COMEDI_PCI */
433
434/*
435 * Useful for shorthand access to the particular board structure
436 */
437#define thisboard ((const struct dio200_board *)dev->board_ptr)
438#define thislayout (&dio200_layouts[((struct dio200_board *) \
439		    dev->board_ptr)->layout])
440
441/* this structure is for data unique to this hardware driver.  If
442   several hardware drivers keep similar information in this structure,
443   feel free to suggest moving the variable to the struct comedi_device struct.
444 */
445struct dio200_private {
446#ifdef CONFIG_COMEDI_PCI
447	struct pci_dev *pci_dev;	/* PCI device */
448#endif
449	int intr_sd;
450};
451
452#define devpriv ((struct dio200_private *)dev->private)
453
454struct dio200_subdev_8254 {
455	unsigned long iobase;	/* Counter base address */
456	unsigned long clk_sce_iobase;	/* CLK_SCE base address */
457	unsigned long gat_sce_iobase;	/* GAT_SCE base address */
458	int which;		/* Bit 5 of CLK_SCE or GAT_SCE */
459	int has_clk_gat_sce;
460	unsigned clock_src[3];	/* Current clock sources */
461	unsigned gate_src[3];	/* Current gate sources */
462};
463
464struct dio200_subdev_intr {
465	unsigned long iobase;
466	spinlock_t spinlock;
467	int active;
468	int has_int_sce;
469	unsigned int valid_isns;
470	unsigned int enabled_isns;
471	unsigned int stopcount;
472	int continuous;
473};
474
475/*
476 * The struct comedi_driver structure tells the Comedi core module
477 * which functions to call to configure/deconfigure (attach/detach)
478 * the board, and also about the kernel module that contains
479 * the device code.
480 */
481static int dio200_attach(struct comedi_device *dev,
482			 struct comedi_devconfig *it);
483static int dio200_detach(struct comedi_device *dev);
484static struct comedi_driver driver_amplc_dio200 = {
485	.driver_name = DIO200_DRIVER_NAME,
486	.module = THIS_MODULE,
487	.attach = dio200_attach,
488	.detach = dio200_detach,
489	.board_name = &dio200_boards[0].name,
490	.offset = sizeof(struct dio200_board),
491	.num_names = ARRAY_SIZE(dio200_boards),
492};
493
494#ifdef CONFIG_COMEDI_PCI
495COMEDI_PCI_INITCLEANUP(driver_amplc_dio200, dio200_pci_table);
496#else
497COMEDI_INITCLEANUP(driver_amplc_dio200);
498#endif
499
500/*
501 * This function looks for a PCI device matching the requested board name,
502 * bus and slot.
503 */
504#ifdef CONFIG_COMEDI_PCI
505static int
506dio200_find_pci(struct comedi_device *dev, int bus, int slot,
507		struct pci_dev **pci_dev_p)
508{
509	struct pci_dev *pci_dev = NULL;
510
511	*pci_dev_p = NULL;
512
513	/* Look for matching PCI device. */
514	for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
515	     pci_dev != NULL;
516	     pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
517				      PCI_ANY_ID, pci_dev)) {
518		/* If bus/slot specified, check them. */
519		if (bus || slot) {
520			if (bus != pci_dev->bus->number
521			    || slot != PCI_SLOT(pci_dev->devfn))
522				continue;
523		}
524		if (thisboard->model == anypci_model) {
525			/* Match any supported model. */
526			int i;
527
528			for (i = 0; i < ARRAY_SIZE(dio200_boards); i++) {
529				if (dio200_boards[i].bustype != pci_bustype)
530					continue;
531				if (pci_dev->device == dio200_boards[i].devid) {
532					/* Change board_ptr to matched board. */
533					dev->board_ptr = &dio200_boards[i];
534					break;
535				}
536			}
537			if (i == ARRAY_SIZE(dio200_boards))
538				continue;
539		} else {
540			/* Match specific model name. */
541			if (pci_dev->device != thisboard->devid)
542				continue;
543		}
544
545		/* Found a match. */
546		*pci_dev_p = pci_dev;
547		return 0;
548	}
549	/* No match found. */
550	if (bus || slot) {
551		printk(KERN_ERR
552		       "comedi%d: error! no %s found at pci %02x:%02x!\n",
553		       dev->minor, thisboard->name, bus, slot);
554	} else {
555		printk(KERN_ERR "comedi%d: error! no %s found!\n",
556		       dev->minor, thisboard->name);
557	}
558	return -EIO;
559}
560#endif
561
562/*
563 * This function checks and requests an I/O region, reporting an error
564 * if there is a conflict.
565 */
566static int
567dio200_request_region(unsigned minor, unsigned long from, unsigned long extent)
568{
569	if (!from || !request_region(from, extent, DIO200_DRIVER_NAME)) {
570		printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
571		       minor, from, extent);
572		return -EIO;
573	}
574	return 0;
575}
576
577/*
578 * 'insn_bits' function for an 'INTERRUPT' subdevice.
579 */
580static int
581dio200_subdev_intr_insn_bits(struct comedi_device *dev,
582			     struct comedi_subdevice *s,
583			     struct comedi_insn *insn, unsigned int *data)
584{
585	struct dio200_subdev_intr *subpriv = s->private;
586
587	if (subpriv->has_int_sce) {
588		/* Just read the interrupt status register.  */
589		data[1] = inb(subpriv->iobase) & subpriv->valid_isns;
590	} else {
591		/* No interrupt status register. */
592		data[0] = 0;
593	}
594
595	return 2;
596}
597
598/*
599 * Called to stop acquisition for an 'INTERRUPT' subdevice.
600 */
601static void dio200_stop_intr(struct comedi_device *dev,
602			     struct comedi_subdevice *s)
603{
604	struct dio200_subdev_intr *subpriv = s->private;
605
606	subpriv->active = 0;
607	subpriv->enabled_isns = 0;
608	if (subpriv->has_int_sce)
609		outb(0, subpriv->iobase);
610}
611
612/*
613 * Called to start acquisition for an 'INTERRUPT' subdevice.
614 */
615static int dio200_start_intr(struct comedi_device *dev,
616			     struct comedi_subdevice *s)
617{
618	unsigned int n;
619	unsigned isn_bits;
620	struct dio200_subdev_intr *subpriv = s->private;
621	struct comedi_cmd *cmd = &s->async->cmd;
622	int retval = 0;
623
624	if (!subpriv->continuous && subpriv->stopcount == 0) {
625		/* An empty acquisition! */
626		s->async->events |= COMEDI_CB_EOA;
627		subpriv->active = 0;
628		retval = 1;
629	} else {
630		/* Determine interrupt sources to enable. */
631		isn_bits = 0;
632		if (cmd->chanlist) {
633			for (n = 0; n < cmd->chanlist_len; n++)
634				isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
635		}
636		isn_bits &= subpriv->valid_isns;
637		/* Enable interrupt sources. */
638		subpriv->enabled_isns = isn_bits;
639		if (subpriv->has_int_sce)
640			outb(isn_bits, subpriv->iobase);
641	}
642
643	return retval;
644}
645
646/*
647 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
648 */
649static int
650dio200_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
651			  unsigned int trignum)
652{
653	struct dio200_subdev_intr *subpriv;
654	unsigned long flags;
655	int event = 0;
656
657	if (trignum != 0)
658		return -EINVAL;
659
660	subpriv = s->private;
661
662	spin_lock_irqsave(&subpriv->spinlock, flags);
663	s->async->inttrig = 0;
664	if (subpriv->active)
665		event = dio200_start_intr(dev, s);
666
667	spin_unlock_irqrestore(&subpriv->spinlock, flags);
668
669	if (event)
670		comedi_event(dev, s);
671
672	return 1;
673}
674
675/*
676 * This is called from the interrupt service routine to handle a read
677 * scan on an 'INTERRUPT' subdevice.
678 */
679static int dio200_handle_read_intr(struct comedi_device *dev,
680				   struct comedi_subdevice *s)
681{
682	struct dio200_subdev_intr *subpriv = s->private;
683	unsigned triggered;
684	unsigned intstat;
685	unsigned cur_enabled;
686	unsigned int oldevents;
687	unsigned long flags;
688
689	triggered = 0;
690
691	spin_lock_irqsave(&subpriv->spinlock, flags);
692	oldevents = s->async->events;
693	if (subpriv->has_int_sce) {
694		/*
695		 * Collect interrupt sources that have triggered and disable
696		 * them temporarily.  Loop around until no extra interrupt
697		 * sources have triggered, at which point, the valid part of
698		 * the interrupt status register will read zero, clearing the
699		 * cause of the interrupt.
700		 *
701		 * Mask off interrupt sources already seen to avoid infinite
702		 * loop in case of misconfiguration.
703		 */
704		cur_enabled = subpriv->enabled_isns;
705		while ((intstat = (inb(subpriv->iobase) & subpriv->valid_isns
706				   & ~triggered)) != 0) {
707			triggered |= intstat;
708			cur_enabled &= ~triggered;
709			outb(cur_enabled, subpriv->iobase);
710		}
711	} else {
712		/*
713		 * No interrupt status register.  Assume the single interrupt
714		 * source has triggered.
715		 */
716		triggered = subpriv->enabled_isns;
717	}
718
719	if (triggered) {
720		/*
721		 * Some interrupt sources have triggered and have been
722		 * temporarily disabled to clear the cause of the interrupt.
723		 *
724		 * Reenable them NOW to minimize the time they are disabled.
725		 */
726		cur_enabled = subpriv->enabled_isns;
727		if (subpriv->has_int_sce)
728			outb(cur_enabled, subpriv->iobase);
729
730		if (subpriv->active) {
731			/*
732			 * The command is still active.
733			 *
734			 * Ignore interrupt sources that the command isn't
735			 * interested in (just in case there's a race
736			 * condition).
737			 */
738			if (triggered & subpriv->enabled_isns) {
739				/* Collect scan data. */
740				short val;
741				unsigned int n, ch, len;
742
743				val = 0;
744				len = s->async->cmd.chanlist_len;
745				for (n = 0; n < len; n++) {
746					ch = CR_CHAN(s->async->cmd.chanlist[n]);
747					if (triggered & (1U << ch))
748						val |= (1U << n);
749				}
750				/* Write the scan to the buffer. */
751				if (comedi_buf_put(s->async, val)) {
752					s->async->events |= (COMEDI_CB_BLOCK |
753							     COMEDI_CB_EOS);
754				} else {
755					/* Error!  Stop acquisition.  */
756					dio200_stop_intr(dev, s);
757					s->async->events |= COMEDI_CB_ERROR
758					    | COMEDI_CB_OVERFLOW;
759					comedi_error(dev, "buffer overflow");
760				}
761
762				/* Check for end of acquisition. */
763				if (!subpriv->continuous) {
764					/* stop_src == TRIG_COUNT */
765					if (subpriv->stopcount > 0) {
766						subpriv->stopcount--;
767						if (subpriv->stopcount == 0) {
768							s->async->events |=
769							    COMEDI_CB_EOA;
770							dio200_stop_intr(dev,
771									 s);
772						}
773					}
774				}
775			}
776		}
777	}
778	spin_unlock_irqrestore(&subpriv->spinlock, flags);
779
780	if (oldevents != s->async->events)
781		comedi_event(dev, s);
782
783	return (triggered != 0);
784}
785
786/*
787 * 'cancel' function for an 'INTERRUPT' subdevice.
788 */
789static int dio200_subdev_intr_cancel(struct comedi_device *dev,
790				     struct comedi_subdevice *s)
791{
792	struct dio200_subdev_intr *subpriv = s->private;
793	unsigned long flags;
794
795	spin_lock_irqsave(&subpriv->spinlock, flags);
796	if (subpriv->active)
797		dio200_stop_intr(dev, s);
798
799	spin_unlock_irqrestore(&subpriv->spinlock, flags);
800
801	return 0;
802}
803
804/*
805 * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
806 */
807static int
808dio200_subdev_intr_cmdtest(struct comedi_device *dev,
809			   struct comedi_subdevice *s, struct comedi_cmd *cmd)
810{
811	int err = 0;
812	unsigned int tmp;
813
814	/* step 1: make sure trigger sources are trivially valid */
815
816	tmp = cmd->start_src;
817	cmd->start_src &= (TRIG_NOW | TRIG_INT);
818	if (!cmd->start_src || tmp != cmd->start_src)
819		err++;
820
821	tmp = cmd->scan_begin_src;
822	cmd->scan_begin_src &= TRIG_EXT;
823	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
824		err++;
825
826	tmp = cmd->convert_src;
827	cmd->convert_src &= TRIG_NOW;
828	if (!cmd->convert_src || tmp != cmd->convert_src)
829		err++;
830
831	tmp = cmd->scan_end_src;
832	cmd->scan_end_src &= TRIG_COUNT;
833	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
834		err++;
835
836	tmp = cmd->stop_src;
837	cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
838	if (!cmd->stop_src || tmp != cmd->stop_src)
839		err++;
840
841	if (err)
842		return 1;
843
844	/* step 2: make sure trigger sources are unique and mutually
845		   compatible */
846
847	/* these tests are true if more than one _src bit is set */
848	if ((cmd->start_src & (cmd->start_src - 1)) != 0)
849		err++;
850	if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
851		err++;
852	if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
853		err++;
854	if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
855		err++;
856	if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
857		err++;
858
859	if (err)
860		return 2;
861
862	/* step 3: make sure arguments are trivially compatible */
863
864	/* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
865	if (cmd->start_arg != 0) {
866		cmd->start_arg = 0;
867		err++;
868	}
869
870	/* cmd->scan_begin_src == TRIG_EXT */
871	if (cmd->scan_begin_arg != 0) {
872		cmd->scan_begin_arg = 0;
873		err++;
874	}
875
876	/* cmd->convert_src == TRIG_NOW */
877	if (cmd->convert_arg != 0) {
878		cmd->convert_arg = 0;
879		err++;
880	}
881
882	/* cmd->scan_end_src == TRIG_COUNT */
883	if (cmd->scan_end_arg != cmd->chanlist_len) {
884		cmd->scan_end_arg = cmd->chanlist_len;
885		err++;
886	}
887
888	switch (cmd->stop_src) {
889	case TRIG_COUNT:
890		/* any count allowed */
891		break;
892	case TRIG_NONE:
893		if (cmd->stop_arg != 0) {
894			cmd->stop_arg = 0;
895			err++;
896		}
897		break;
898	default:
899		break;
900	}
901
902	if (err)
903		return 3;
904
905	/* step 4: fix up any arguments */
906
907	/* if (err) return 4; */
908
909	return 0;
910}
911
912/*
913 * 'do_cmd' function for an 'INTERRUPT' subdevice.
914 */
915static int dio200_subdev_intr_cmd(struct comedi_device *dev,
916				  struct comedi_subdevice *s)
917{
918	struct comedi_cmd *cmd = &s->async->cmd;
919	struct dio200_subdev_intr *subpriv = s->private;
920	unsigned long flags;
921	int event = 0;
922
923	spin_lock_irqsave(&subpriv->spinlock, flags);
924	subpriv->active = 1;
925
926	/* Set up end of acquisition. */
927	switch (cmd->stop_src) {
928	case TRIG_COUNT:
929		subpriv->continuous = 0;
930		subpriv->stopcount = cmd->stop_arg;
931		break;
932	default:
933		/* TRIG_NONE */
934		subpriv->continuous = 1;
935		subpriv->stopcount = 0;
936		break;
937	}
938
939	/* Set up start of acquisition. */
940	switch (cmd->start_src) {
941	case TRIG_INT:
942		s->async->inttrig = dio200_inttrig_start_intr;
943		break;
944	default:
945		/* TRIG_NOW */
946		event = dio200_start_intr(dev, s);
947		break;
948	}
949	spin_unlock_irqrestore(&subpriv->spinlock, flags);
950
951	if (event)
952		comedi_event(dev, s);
953
954	return 0;
955}
956
957/*
958 * This function initializes an 'INTERRUPT' subdevice.
959 */
960static int
961dio200_subdev_intr_init(struct comedi_device *dev, struct comedi_subdevice *s,
962			unsigned long iobase, unsigned valid_isns,
963			int has_int_sce)
964{
965	struct dio200_subdev_intr *subpriv;
966
967	subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
968	if (!subpriv) {
969		printk(KERN_ERR "comedi%d: error! out of memory!\n",
970		       dev->minor);
971		return -ENOMEM;
972	}
973	subpriv->iobase = iobase;
974	subpriv->has_int_sce = has_int_sce;
975	subpriv->valid_isns = valid_isns;
976	spin_lock_init(&subpriv->spinlock);
977
978	if (has_int_sce)
979		outb(0, subpriv->iobase);	/* Disable interrupt sources. */
980
981	s->private = subpriv;
982	s->type = COMEDI_SUBD_DI;
983	s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
984	if (has_int_sce) {
985		s->n_chan = DIO200_MAX_ISNS;
986		s->len_chanlist = DIO200_MAX_ISNS;
987	} else {
988		/* No interrupt source register.  Support single channel. */
989		s->n_chan = 1;
990		s->len_chanlist = 1;
991	}
992	s->range_table = &range_digital;
993	s->maxdata = 1;
994	s->insn_bits = dio200_subdev_intr_insn_bits;
995	s->do_cmdtest = dio200_subdev_intr_cmdtest;
996	s->do_cmd = dio200_subdev_intr_cmd;
997	s->cancel = dio200_subdev_intr_cancel;
998
999	return 0;
1000}
1001
1002/*
1003 * This function cleans up an 'INTERRUPT' subdevice.
1004 */
1005static void
1006dio200_subdev_intr_cleanup(struct comedi_device *dev,
1007			   struct comedi_subdevice *s)
1008{
1009	struct dio200_subdev_intr *subpriv = s->private;
1010	kfree(subpriv);
1011}
1012
1013/*
1014 * Interrupt service routine.
1015 */
1016static irqreturn_t dio200_interrupt(int irq, void *d)
1017{
1018	struct comedi_device *dev = d;
1019	int handled;
1020
1021	if (!dev->attached)
1022		return IRQ_NONE;
1023
1024	if (devpriv->intr_sd >= 0) {
1025		handled = dio200_handle_read_intr(dev,
1026						  dev->subdevices +
1027						  devpriv->intr_sd);
1028	} else {
1029		handled = 0;
1030	}
1031
1032	return IRQ_RETVAL(handled);
1033}
1034
1035/*
1036 * Handle 'insn_read' for an '8254' counter subdevice.
1037 */
1038static int
1039dio200_subdev_8254_read(struct comedi_device *dev, struct comedi_subdevice *s,
1040			struct comedi_insn *insn, unsigned int *data)
1041{
1042	struct dio200_subdev_8254 *subpriv = s->private;
1043	int chan = CR_CHAN(insn->chanspec);
1044
1045	data[0] = i8254_read(subpriv->iobase, 0, chan);
1046
1047	return 1;
1048}
1049
1050/*
1051 * Handle 'insn_write' for an '8254' counter subdevice.
1052 */
1053static int
1054dio200_subdev_8254_write(struct comedi_device *dev, struct comedi_subdevice *s,
1055			 struct comedi_insn *insn, unsigned int *data)
1056{
1057	struct dio200_subdev_8254 *subpriv = s->private;
1058	int chan = CR_CHAN(insn->chanspec);
1059
1060	i8254_write(subpriv->iobase, 0, chan, data[0]);
1061
1062	return 1;
1063}
1064
1065/*
1066 * Set gate source for an '8254' counter subdevice channel.
1067 */
1068static int
1069dio200_set_gate_src(struct dio200_subdev_8254 *subpriv,
1070		    unsigned int counter_number, unsigned int gate_src)
1071{
1072	unsigned char byte;
1073
1074	if (!subpriv->has_clk_gat_sce)
1075		return -1;
1076	if (counter_number > 2)
1077		return -1;
1078	if (gate_src > 7)
1079		return -1;
1080
1081	subpriv->gate_src[counter_number] = gate_src;
1082	byte = GAT_SCE(subpriv->which, counter_number, gate_src);
1083	outb(byte, subpriv->gat_sce_iobase);
1084
1085	return 0;
1086}
1087
1088/*
1089 * Get gate source for an '8254' counter subdevice channel.
1090 */
1091static int
1092dio200_get_gate_src(struct dio200_subdev_8254 *subpriv,
1093		    unsigned int counter_number)
1094{
1095	if (!subpriv->has_clk_gat_sce)
1096		return -1;
1097	if (counter_number > 2)
1098		return -1;
1099
1100	return subpriv->gate_src[counter_number];
1101}
1102
1103/*
1104 * Set clock source for an '8254' counter subdevice channel.
1105 */
1106static int
1107dio200_set_clock_src(struct dio200_subdev_8254 *subpriv,
1108		     unsigned int counter_number, unsigned int clock_src)
1109{
1110	unsigned char byte;
1111
1112	if (!subpriv->has_clk_gat_sce)
1113		return -1;
1114	if (counter_number > 2)
1115		return -1;
1116	if (clock_src > 7)
1117		return -1;
1118
1119	subpriv->clock_src[counter_number] = clock_src;
1120	byte = CLK_SCE(subpriv->which, counter_number, clock_src);
1121	outb(byte, subpriv->clk_sce_iobase);
1122
1123	return 0;
1124}
1125
1126/*
1127 * Get clock source for an '8254' counter subdevice channel.
1128 */
1129static int
1130dio200_get_clock_src(struct dio200_subdev_8254 *subpriv,
1131		     unsigned int counter_number, unsigned int *period_ns)
1132{
1133	unsigned clock_src;
1134
1135	if (!subpriv->has_clk_gat_sce)
1136		return -1;
1137	if (counter_number > 2)
1138		return -1;
1139
1140	clock_src = subpriv->clock_src[counter_number];
1141	*period_ns = clock_period[clock_src];
1142	return clock_src;
1143}
1144
1145/*
1146 * Handle 'insn_config' for an '8254' counter subdevice.
1147 */
1148static int
1149dio200_subdev_8254_config(struct comedi_device *dev, struct comedi_subdevice *s,
1150			  struct comedi_insn *insn, unsigned int *data)
1151{
1152	struct dio200_subdev_8254 *subpriv = s->private;
1153	int ret;
1154	int chan = CR_CHAN(insn->chanspec);
1155
1156	switch (data[0]) {
1157	case INSN_CONFIG_SET_COUNTER_MODE:
1158		ret = i8254_set_mode(subpriv->iobase, 0, chan, data[1]);
1159		if (ret < 0)
1160			return -EINVAL;
1161		break;
1162	case INSN_CONFIG_8254_READ_STATUS:
1163		data[1] = i8254_status(subpriv->iobase, 0, chan);
1164		break;
1165	case INSN_CONFIG_SET_GATE_SRC:
1166		ret = dio200_set_gate_src(subpriv, chan, data[2]);
1167		if (ret < 0)
1168			return -EINVAL;
1169		break;
1170	case INSN_CONFIG_GET_GATE_SRC:
1171		ret = dio200_get_gate_src(subpriv, chan);
1172		if (ret < 0)
1173			return -EINVAL;
1174		data[2] = ret;
1175		break;
1176	case INSN_CONFIG_SET_CLOCK_SRC:
1177		ret = dio200_set_clock_src(subpriv, chan, data[1]);
1178		if (ret < 0)
1179			return -EINVAL;
1180		break;
1181	case INSN_CONFIG_GET_CLOCK_SRC:
1182		ret = dio200_get_clock_src(subpriv, chan, &data[2]);
1183		if (ret < 0)
1184			return -EINVAL;
1185		data[1] = ret;
1186		break;
1187	default:
1188		return -EINVAL;
1189		break;
1190	}
1191	return insn->n;
1192}
1193
1194/*
1195 * This function initializes an '8254' counter subdevice.
1196 *
1197 * Note: iobase is the base address of the board, not the subdevice;
1198 * offset is the offset to the 8254 chip.
1199 */
1200static int
1201dio200_subdev_8254_init(struct comedi_device *dev, struct comedi_subdevice *s,
1202			unsigned long iobase, unsigned offset,
1203			int has_clk_gat_sce)
1204{
1205	struct dio200_subdev_8254 *subpriv;
1206	unsigned int chan;
1207
1208	subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
1209	if (!subpriv) {
1210		printk(KERN_ERR "comedi%d: error! out of memory!\n",
1211		       dev->minor);
1212		return -ENOMEM;
1213	}
1214
1215	s->private = subpriv;
1216	s->type = COMEDI_SUBD_COUNTER;
1217	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1218	s->n_chan = 3;
1219	s->maxdata = 0xFFFF;
1220	s->insn_read = dio200_subdev_8254_read;
1221	s->insn_write = dio200_subdev_8254_write;
1222	s->insn_config = dio200_subdev_8254_config;
1223
1224	subpriv->iobase = offset + iobase;
1225	subpriv->has_clk_gat_sce = has_clk_gat_sce;
1226	if (has_clk_gat_sce) {
1227		/* Derive CLK_SCE and GAT_SCE register offsets from
1228		 * 8254 offset. */
1229		subpriv->clk_sce_iobase =
1230		    DIO200_XCLK_SCE + (offset >> 3) + iobase;
1231		subpriv->gat_sce_iobase =
1232		    DIO200_XGAT_SCE + (offset >> 3) + iobase;
1233		subpriv->which = (offset >> 2) & 1;
1234	}
1235
1236	/* Initialize channels. */
1237	for (chan = 0; chan < 3; chan++) {
1238		i8254_set_mode(subpriv->iobase, 0, chan,
1239			       I8254_MODE0 | I8254_BINARY);
1240		if (subpriv->has_clk_gat_sce) {
1241			/* Gate source 0 is VCC (logic 1). */
1242			dio200_set_gate_src(subpriv, chan, 0);
1243			/* Clock source 0 is the dedicated clock input. */
1244			dio200_set_clock_src(subpriv, chan, 0);
1245		}
1246	}
1247
1248	return 0;
1249}
1250
1251/*
1252 * This function cleans up an '8254' counter subdevice.
1253 */
1254static void
1255dio200_subdev_8254_cleanup(struct comedi_device *dev,
1256			   struct comedi_subdevice *s)
1257{
1258	struct dio200_subdev_intr *subpriv = s->private;
1259	kfree(subpriv);
1260}
1261
1262/*
1263 * Attach is called by the Comedi core to configure the driver
1264 * for a particular board.  If you specified a board_name array
1265 * in the driver structure, dev->board_ptr contains that
1266 * address.
1267 */
1268static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1269{
1270	struct comedi_subdevice *s;
1271	unsigned long iobase = 0;
1272	unsigned int irq = 0;
1273#ifdef CONFIG_COMEDI_PCI
1274	struct pci_dev *pci_dev = NULL;
1275	int bus = 0, slot = 0;
1276#endif
1277	const struct dio200_layout_struct *layout;
1278	int share_irq = 0;
1279	int sdx;
1280	unsigned n;
1281	int ret;
1282
1283	printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
1284	       DIO200_DRIVER_NAME);
1285
1286	ret = alloc_private(dev, sizeof(struct dio200_private));
1287	if (ret < 0) {
1288		printk(KERN_ERR "comedi%d: error! out of memory!\n",
1289		       dev->minor);
1290		return ret;
1291	}
1292
1293	/* Process options. */
1294	switch (thisboard->bustype) {
1295	case isa_bustype:
1296		iobase = it->options[0];
1297		irq = it->options[1];
1298		share_irq = 0;
1299		break;
1300#ifdef CONFIG_COMEDI_PCI
1301	case pci_bustype:
1302		bus = it->options[0];
1303		slot = it->options[1];
1304		share_irq = 1;
1305
1306		ret = dio200_find_pci(dev, bus, slot, &pci_dev);
1307		if (ret < 0)
1308			return ret;
1309		devpriv->pci_dev = pci_dev;
1310		break;
1311#endif
1312	default:
1313		printk(KERN_ERR
1314		       "comedi%d: %s: BUG! cannot determine board type!\n",
1315		       dev->minor, DIO200_DRIVER_NAME);
1316		return -EINVAL;
1317		break;
1318	}
1319
1320	devpriv->intr_sd = -1;
1321
1322	/* Enable device and reserve I/O spaces. */
1323#ifdef CONFIG_COMEDI_PCI
1324	if (pci_dev) {
1325		ret = comedi_pci_enable(pci_dev, DIO200_DRIVER_NAME);
1326		if (ret < 0) {
1327			printk(KERN_ERR
1328			       "comedi%d: error! cannot enable PCI device and request regions!\n",
1329			       dev->minor);
1330			return ret;
1331		}
1332		iobase = pci_resource_start(pci_dev, 2);
1333		irq = pci_dev->irq;
1334	} else
1335#endif
1336	{
1337		ret = dio200_request_region(dev->minor, iobase, DIO200_IO_SIZE);
1338		if (ret < 0)
1339			return ret;
1340	}
1341	dev->iobase = iobase;
1342
1343	layout = thislayout;
1344
1345	ret = alloc_subdevices(dev, layout->n_subdevs);
1346	if (ret < 0) {
1347		printk(KERN_ERR "comedi%d: error! out of memory!\n",
1348		       dev->minor);
1349		return ret;
1350	}
1351
1352	for (n = 0; n < dev->n_subdevices; n++) {
1353		s = &dev->subdevices[n];
1354		switch (layout->sdtype[n]) {
1355		case sd_8254:
1356			/* counter subdevice (8254) */
1357			ret = dio200_subdev_8254_init(dev, s, iobase,
1358						      layout->sdinfo[n],
1359						      layout->has_clk_gat_sce);
1360			if (ret < 0)
1361				return ret;
1362
1363			break;
1364		case sd_8255:
1365			/* digital i/o subdevice (8255) */
1366			ret = subdev_8255_init(dev, s, 0,
1367					       iobase + layout->sdinfo[n]);
1368			if (ret < 0)
1369				return ret;
1370
1371			break;
1372		case sd_intr:
1373			/* 'INTERRUPT' subdevice */
1374			if (irq) {
1375				ret = dio200_subdev_intr_init(dev, s,
1376							      iobase +
1377							      DIO200_INT_SCE,
1378							      layout->sdinfo[n],
1379							      layout->
1380							      has_int_sce);
1381				if (ret < 0)
1382					return ret;
1383
1384				devpriv->intr_sd = n;
1385			} else {
1386				s->type = COMEDI_SUBD_UNUSED;
1387			}
1388			break;
1389		default:
1390			s->type = COMEDI_SUBD_UNUSED;
1391			break;
1392		}
1393	}
1394
1395	sdx = devpriv->intr_sd;
1396	if (sdx >= 0 && sdx < dev->n_subdevices)
1397		dev->read_subdev = &dev->subdevices[sdx];
1398
1399	dev->board_name = thisboard->name;
1400
1401	if (irq) {
1402		unsigned long flags = share_irq ? IRQF_SHARED : 0;
1403
1404		if (request_irq(irq, dio200_interrupt, flags,
1405				DIO200_DRIVER_NAME, dev) >= 0) {
1406			dev->irq = irq;
1407		} else {
1408			printk(KERN_WARNING
1409			       "comedi%d: warning! irq %u unavailable!\n",
1410			       dev->minor, irq);
1411		}
1412	}
1413
1414	printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
1415	if (thisboard->bustype == isa_bustype) {
1416		printk("(base %#lx) ", iobase);
1417	} else {
1418#ifdef CONFIG_COMEDI_PCI
1419		printk("(pci %s) ", pci_name(pci_dev));
1420#endif
1421	}
1422	if (irq)
1423		printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
1424	else
1425		printk("(no irq) ");
1426
1427	printk("attached\n");
1428
1429	return 1;
1430}
1431
1432/*
1433 * _detach is called to deconfigure a device.  It should deallocate
1434 * resources.
1435 * This function is also called when _attach() fails, so it should be
1436 * careful not to release resources that were not necessarily
1437 * allocated by _attach().  dev->private and dev->subdevices are
1438 * deallocated automatically by the core.
1439 */
1440static int dio200_detach(struct comedi_device *dev)
1441{
1442	const struct dio200_layout_struct *layout;
1443	unsigned n;
1444
1445	printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
1446	       DIO200_DRIVER_NAME);
1447
1448	if (dev->irq)
1449		free_irq(dev->irq, dev);
1450	if (dev->subdevices) {
1451		layout = thislayout;
1452		for (n = 0; n < dev->n_subdevices; n++) {
1453			struct comedi_subdevice *s = &dev->subdevices[n];
1454			switch (layout->sdtype[n]) {
1455			case sd_8254:
1456				dio200_subdev_8254_cleanup(dev, s);
1457				break;
1458			case sd_8255:
1459				subdev_8255_cleanup(dev, s);
1460				break;
1461			case sd_intr:
1462				dio200_subdev_intr_cleanup(dev, s);
1463				break;
1464			default:
1465				break;
1466			}
1467		}
1468	}
1469	if (devpriv) {
1470#ifdef CONFIG_COMEDI_PCI
1471		if (devpriv->pci_dev) {
1472			if (dev->iobase)
1473				comedi_pci_disable(devpriv->pci_dev);
1474			pci_dev_put(devpriv->pci_dev);
1475		} else
1476#endif
1477		{
1478			if (dev->iobase)
1479				release_region(dev->iobase, DIO200_IO_SIZE);
1480		}
1481	}
1482	if (dev->board_name)
1483		printk(KERN_INFO "comedi%d: %s removed\n",
1484		       dev->minor, dev->board_name);
1485
1486	return 0;
1487}
1488