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