das16m1.c revision 25436dc9d84f1be60ff549c9ab712bba2835f284
1/*
2    comedi/drivers/das16m1.c
3    CIO-DAS16/M1 driver
4    Author: Frank Mori Hess, based on code from the das16
5      driver.
6    Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
7
8    COMEDI - Linux Control and Measurement Device Interface
9    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25************************************************************************
26*/
27/*
28Driver: das16m1
29Description: CIO-DAS16/M1
30Author: Frank Mori Hess <fmhess@users.sourceforge.net>
31Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1)
32Status: works
33
34This driver supports a single board - the CIO-DAS16/M1.
35As far as I know, there are no other boards that have
36the same register layout.  Even the CIO-DAS16/M1/16 is
37significantly different.
38
39I was _barely_ able to reach the full 1 MHz capability
40of this board, using a hard real-time interrupt
41(set the TRIG_RT flag in your struct comedi_cmd and use
42rtlinux or RTAI).  The board can't do dma, so the bottleneck is
43pulling the data across the ISA bus.  I timed the interrupt
44handler, and it took my computer ~470 microseconds to pull 512
45samples from the board.  So at 1 Mhz sampling rate,
46expect your CPU to be spending almost all of its
47time in the interrupt handler.
48
49This board has some unusual restrictions for its channel/gain list.  If the
50list has 2 or more channels in it, then two conditions must be satisfied:
51(1) - even/odd channels must appear at even/odd indices in the list
52(2) - the list must have an even number of entries.
53
54Options:
55        [0] - base io address
56        [1] - irq (optional, but you probably want it)
57
58irq can be omitted, although the cmd interface will not work without it.
59*/
60
61#include <linux/ioport.h>
62#include <linux/interrupt.h>
63#include "../comedidev.h"
64
65#include "8255.h"
66#include "8253.h"
67#include "comedi_fc.h"
68
69#define DAS16M1_SIZE 16
70#define DAS16M1_SIZE2 8
71
72#define DAS16M1_XTAL 100	/* 10 MHz master clock */
73
74#define FIFO_SIZE 1024		/*  1024 sample fifo */
75
76/*
77    CIO-DAS16_M1.pdf
78
79    "cio-das16/m1"
80
81  0	a/d bits 0-3, mux		start 12 bit
82  1	a/d bits 4-11		unused
83  2	status		control
84  3	di 4 bit		do 4 bit
85  4	unused			clear interrupt
86  5	interrupt, pacer
87  6	channel/gain queue address
88  7	channel/gain queue data
89  89ab	8254
90  cdef	8254
91  400	8255
92  404-407 	8254
93
94*/
95
96#define DAS16M1_AI             0	/*  16-bit wide register */
97#define   AI_CHAN(x)             ((x) & 0xf)
98#define DAS16M1_CS             2
99#define   EXT_TRIG_BIT           0x1
100#define   OVRUN                  0x20
101#define   IRQDATA                0x80
102#define DAS16M1_DIO            3
103#define DAS16M1_CLEAR_INTR     4
104#define DAS16M1_INTR_CONTROL   5
105#define   EXT_PACER              0x2
106#define   INT_PACER              0x3
107#define   PACER_MASK             0x3
108#define   INTE                   0x80
109#define DAS16M1_QUEUE_ADDR     6
110#define DAS16M1_QUEUE_DATA     7
111#define   Q_CHAN(x)              ((x) & 0x7)
112#define   Q_RANGE(x)             (((x) & 0xf) << 4)
113#define   UNIPOLAR               0x40
114#define DAS16M1_8254_FIRST             0x8
115#define DAS16M1_8254_FIRST_CNTRL       0xb
116#define   TOTAL_CLEAR                    0x30
117#define DAS16M1_8254_SECOND            0xc
118#define DAS16M1_82C55                  0x400
119#define DAS16M1_8254_THIRD             0x404
120
121static const struct comedi_lrange range_das16m1 = { 9,
122	{
123			BIP_RANGE(5),
124			BIP_RANGE(2.5),
125			BIP_RANGE(1.25),
126			BIP_RANGE(0.625),
127			UNI_RANGE(10),
128			UNI_RANGE(5),
129			UNI_RANGE(2.5),
130			UNI_RANGE(1.25),
131			BIP_RANGE(10),
132		}
133};
134
135static int das16m1_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
136	struct comedi_insn *insn, unsigned int *data);
137static int das16m1_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
138	struct comedi_insn *insn, unsigned int *data);
139static int das16m1_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
140	struct comedi_insn *insn, unsigned int *data);
141
142static int das16m1_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
143	struct comedi_cmd *cmd);
144static int das16m1_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s);
145static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
146
147static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s);
148static irqreturn_t das16m1_interrupt(int irq, void *d);
149static void das16m1_handler(struct comedi_device *dev, unsigned int status);
150
151static unsigned int das16m1_set_pacer(struct comedi_device *dev, unsigned int ns,
152	int round_flag);
153
154static int das16m1_irq_bits(unsigned int irq);
155
156struct das16m1_board {
157	const char *name;
158	unsigned int ai_speed;
159};
160
161static const struct das16m1_board das16m1_boards[] = {
162	{
163	.name = "cio-das16/m1",	/*  CIO-DAS16_M1.pdf */
164	.ai_speed = 1000,	/*  1MHz max speed */
165		},
166};
167
168static int das16m1_attach(struct comedi_device *dev, struct comedi_devconfig *it);
169static int das16m1_detach(struct comedi_device *dev);
170static struct comedi_driver driver_das16m1 = {
171	.driver_name = "das16m1",
172	.module = THIS_MODULE,
173	.attach = das16m1_attach,
174	.detach = das16m1_detach,
175	.board_name = &das16m1_boards[0].name,
176	.num_names = ARRAY_SIZE(das16m1_boards),
177	.offset = sizeof(das16m1_boards[0]),
178};
179
180struct das16m1_private_struct {
181	unsigned int control_state;
182	volatile unsigned int adc_count;	/*  number of samples completed */
183	/* initial value in lower half of hardware conversion counter,
184	 * needed to keep track of whether new count has been loaded into
185	 * counter yet (loaded by first sample conversion) */
186	u16 initial_hw_count;
187	short ai_buffer[FIFO_SIZE];
188	unsigned int do_bits;	/*  saves status of digital output bits */
189	unsigned int divisor1;	/*  divides master clock to obtain conversion speed */
190	unsigned int divisor2;	/*  divides master clock to obtain conversion speed */
191};
192#define devpriv ((struct das16m1_private_struct *)(dev->private))
193#define thisboard ((const struct das16m1_board *)(dev->board_ptr))
194
195COMEDI_INITCLEANUP(driver_das16m1);
196
197static inline short munge_sample(short data)
198{
199	return (data >> 4) & 0xfff;
200}
201
202static int das16m1_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
203	struct comedi_cmd *cmd)
204{
205	unsigned int err = 0, tmp, i;
206
207	/* make sure triggers are valid */
208	tmp = cmd->start_src;
209	cmd->start_src &= TRIG_NOW | TRIG_EXT;
210	if (!cmd->start_src || tmp != cmd->start_src)
211		err++;
212
213	tmp = cmd->scan_begin_src;
214	cmd->scan_begin_src &= TRIG_FOLLOW;
215	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
216		err++;
217
218	tmp = cmd->convert_src;
219	cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
220	if (!cmd->convert_src || tmp != cmd->convert_src)
221		err++;
222
223	tmp = cmd->scan_end_src;
224	cmd->scan_end_src &= TRIG_COUNT;
225	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
226		err++;
227
228	tmp = cmd->stop_src;
229	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
230	if (!cmd->stop_src || tmp != cmd->stop_src)
231		err++;
232
233	if (err)
234		return 1;
235
236	/* step 2: make sure trigger sources are unique and mutually compatible */
237	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
238		err++;
239	if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
240		err++;
241	if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
242		err++;
243
244	if (err)
245		return 2;
246
247	/* step 3: make sure arguments are trivially compatible */
248	if (cmd->start_arg != 0) {
249		cmd->start_arg = 0;
250		err++;
251	}
252
253	if (cmd->scan_begin_src == TRIG_FOLLOW) {
254		/* internal trigger */
255		if (cmd->scan_begin_arg != 0) {
256			cmd->scan_begin_arg = 0;
257			err++;
258		}
259	}
260
261	if (cmd->convert_src == TRIG_TIMER) {
262		if (cmd->convert_arg < thisboard->ai_speed) {
263			cmd->convert_arg = thisboard->ai_speed;
264			err++;
265		}
266	}
267
268	if (cmd->scan_end_arg != cmd->chanlist_len) {
269		cmd->scan_end_arg = cmd->chanlist_len;
270		err++;
271	}
272
273	if (cmd->stop_src == TRIG_COUNT) {
274		/* any count is allowed */
275	} else {
276		/* TRIG_NONE */
277		if (cmd->stop_arg != 0) {
278			cmd->stop_arg = 0;
279			err++;
280		}
281	}
282
283	if (err)
284		return 3;
285
286	/* step 4: fix up arguments */
287
288	if (cmd->convert_src == TRIG_TIMER) {
289		tmp = cmd->convert_arg;
290		/* calculate counter values that give desired timing */
291		i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
292			&(devpriv->divisor1), &(devpriv->divisor2),
293			&(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
294		if (tmp != cmd->convert_arg)
295			err++;
296	}
297
298	if (err)
299		return 4;
300
301	/*  check chanlist against board's peculiarities */
302	if (cmd->chanlist && cmd->chanlist_len > 1) {
303		for (i = 0; i < cmd->chanlist_len; i++) {
304			/*  even/odd channels must go into even/odd queue addresses */
305			if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
306				comedi_error(dev, "bad chanlist:\n"
307					" even/odd channels must go have even/odd chanlist indices");
308				err++;
309			}
310		}
311		if ((cmd->chanlist_len % 2) != 0) {
312			comedi_error(dev,
313				"chanlist must be of even length or length 1");
314			err++;
315		}
316	}
317
318	if (err)
319		return 5;
320
321	return 0;
322}
323
324static int das16m1_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s)
325{
326	struct comedi_async *async = s->async;
327	struct comedi_cmd *cmd = &async->cmd;
328	unsigned int byte, i;
329
330	if (dev->irq == 0) {
331		comedi_error(dev, "irq required to execute comedi_cmd");
332		return -1;
333	}
334
335	/* disable interrupts and internal pacer */
336	devpriv->control_state &= ~INTE & ~PACER_MASK;
337	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
338
339	/*  set software count */
340	devpriv->adc_count = 0;
341	/* Initialize lower half of hardware counter, used to determine how
342	 * many samples are in fifo.  Value doesn't actually load into counter
343	 * until counter's next clock (the next a/d conversion) */
344	i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
345	/* remember current reading of counter so we know when counter has
346	 * actually been loaded */
347	devpriv->initial_hw_count =
348		i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
349	/* setup channel/gain queue */
350	for (i = 0; i < cmd->chanlist_len; i++) {
351		outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
352		byte = Q_CHAN(CR_CHAN(cmd->
353				chanlist[i])) | Q_RANGE(CR_RANGE(cmd->
354				chanlist[i]));
355		outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
356	}
357
358	/* set counter mode and counts */
359	cmd->convert_arg =
360		das16m1_set_pacer(dev, cmd->convert_arg,
361		cmd->flags & TRIG_ROUND_MASK);
362
363	/*  set control & status register */
364	byte = 0;
365	/* if we are using external start trigger (also board dislikes having
366	 * both start and conversion triggers external simultaneously) */
367	if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) {
368		byte |= EXT_TRIG_BIT;
369	}
370	outb(byte, dev->iobase + DAS16M1_CS);
371	/* clear interrupt bit */
372	outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
373
374	/* enable interrupts and internal pacer */
375	devpriv->control_state &= ~PACER_MASK;
376	if (cmd->convert_src == TRIG_TIMER) {
377		devpriv->control_state |= INT_PACER;
378	} else {
379		devpriv->control_state |= EXT_PACER;
380	}
381	devpriv->control_state |= INTE;
382	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
383
384	return 0;
385}
386
387static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
388{
389	devpriv->control_state &= ~INTE & ~PACER_MASK;
390	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
391
392	return 0;
393}
394
395static int das16m1_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
396	struct comedi_insn *insn, unsigned int *data)
397{
398	int i, n;
399	int byte;
400	const int timeout = 1000;
401
402	/* disable interrupts and internal pacer */
403	devpriv->control_state &= ~INTE & ~PACER_MASK;
404	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
405
406	/* setup channel/gain queue */
407	outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
408	byte = Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->
409			chanspec));
410	outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
411
412	for (n = 0; n < insn->n; n++) {
413		/* clear IRQDATA bit */
414		outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
415		/* trigger conversion */
416		outb(0, dev->iobase);
417
418		for (i = 0; i < timeout; i++) {
419			if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
420				break;
421		}
422		if (i == timeout) {
423			comedi_error(dev, "timeout");
424			return -ETIME;
425		}
426		data[n] = munge_sample(inw(dev->iobase));
427	}
428
429	return n;
430}
431
432static int das16m1_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
433	struct comedi_insn *insn, unsigned int *data)
434{
435	unsigned int bits;
436
437	bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
438	data[1] = bits;
439	data[0] = 0;
440
441	return 2;
442}
443
444static int das16m1_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
445	struct comedi_insn *insn, unsigned int *data)
446{
447	unsigned int wbits;
448
449	/*  only set bits that have been masked */
450	data[0] &= 0xf;
451	wbits = devpriv->do_bits;
452	/*  zero bits that have been masked */
453	wbits &= ~data[0];
454	/*  set masked bits */
455	wbits |= data[0] & data[1];
456	devpriv->do_bits = wbits;
457	data[1] = wbits;
458
459	outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
460
461	return 2;
462}
463
464static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
465{
466	unsigned long flags;
467	unsigned int status;
468
469	/*  prevent race with interrupt handler */
470	spin_lock_irqsave(&dev->spinlock, flags);
471	status = inb(dev->iobase + DAS16M1_CS);
472	das16m1_handler(dev, status);
473	spin_unlock_irqrestore(&dev->spinlock, flags);
474
475	return s->async->buf_write_count - s->async->buf_read_count;
476}
477
478static irqreturn_t das16m1_interrupt(int irq, void *d)
479{
480	int status;
481	struct comedi_device *dev = d;
482
483	if (dev->attached == 0) {
484		comedi_error(dev, "premature interrupt");
485		return IRQ_HANDLED;
486	}
487	/*  prevent race with comedi_poll() */
488	spin_lock(&dev->spinlock);
489
490	status = inb(dev->iobase + DAS16M1_CS);
491
492	if ((status & (IRQDATA | OVRUN)) == 0) {
493		comedi_error(dev, "spurious interrupt");
494		spin_unlock(&dev->spinlock);
495		return IRQ_NONE;
496	}
497
498	das16m1_handler(dev, status);
499
500	/* clear interrupt */
501	outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
502
503	spin_unlock(&dev->spinlock);
504	return IRQ_HANDLED;
505}
506
507static void munge_sample_array(short *array, unsigned int num_elements)
508{
509	unsigned int i;
510
511	for (i = 0; i < num_elements; i++) {
512		array[i] = munge_sample(array[i]);
513	}
514}
515
516static void das16m1_handler(struct comedi_device *dev, unsigned int status)
517{
518	struct comedi_subdevice *s;
519	struct comedi_async *async;
520	struct comedi_cmd *cmd;
521	u16 num_samples;
522	u16 hw_counter;
523
524	s = dev->read_subdev;
525	async = s->async;
526	async->events = 0;
527	cmd = &async->cmd;
528
529	/*  figure out how many samples are in fifo */
530	hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
531	/* make sure hardware counter reading is not bogus due to initial value
532	 * not having been loaded yet */
533	if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
534		num_samples = 0;
535	} else {
536		/* The calculation of num_samples looks odd, but it uses the following facts.
537		 * 16 bit hardware counter is initialized with value of zero (which really
538		 * means 0x1000).  The counter decrements by one on each conversion
539		 * (when the counter decrements from zero it goes to 0xffff).  num_samples
540		 * is a 16 bit variable, so it will roll over in a similar fashion to the
541		 * hardware counter.  Work it out, and this is what you get. */
542		num_samples = -hw_counter - devpriv->adc_count;
543	}
544	/*  check if we only need some of the points */
545	if (cmd->stop_src == TRIG_COUNT) {
546		if (num_samples > cmd->stop_arg * cmd->chanlist_len)
547			num_samples = cmd->stop_arg * cmd->chanlist_len;
548	}
549	/*  make sure we dont try to get too many points if fifo has overrun */
550	if (num_samples > FIFO_SIZE)
551		num_samples = FIFO_SIZE;
552	insw(dev->iobase, devpriv->ai_buffer, num_samples);
553	munge_sample_array(devpriv->ai_buffer, num_samples);
554	cfc_write_array_to_buffer(s, devpriv->ai_buffer,
555		num_samples * sizeof(short));
556	devpriv->adc_count += num_samples;
557
558	if (cmd->stop_src == TRIG_COUNT) {
559		if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {	/* end of acquisition */
560			das16m1_cancel(dev, s);
561			async->events |= COMEDI_CB_EOA;
562		}
563	}
564
565	/* this probably won't catch overruns since the card doesn't generate
566	 * overrun interrupts, but we might as well try */
567	if (status & OVRUN) {
568		das16m1_cancel(dev, s);
569		async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
570		comedi_error(dev, "fifo overflow");
571	}
572
573	comedi_event(dev, s);
574
575}
576
577/* This function takes a time in nanoseconds and sets the     *
578 * 2 pacer clocks to the closest frequency possible. It also  *
579 * returns the actual sampling period.                        */
580static unsigned int das16m1_set_pacer(struct comedi_device *dev, unsigned int ns,
581	int rounding_flags)
582{
583	i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
584		&(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK);
585
586	/* Write the values of ctr1 and ctr2 into counters 1 and 2 */
587	i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
588		2);
589	i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
590		2);
591
592	return ns;
593}
594
595static int das16m1_irq_bits(unsigned int irq)
596{
597	int ret;
598
599	switch (irq) {
600	case 10:
601		ret = 0x0;
602		break;
603	case 11:
604		ret = 0x1;
605		break;
606	case 12:
607		ret = 0x2;
608		break;
609	case 15:
610		ret = 0x3;
611		break;
612	case 2:
613		ret = 0x4;
614		break;
615	case 3:
616		ret = 0x5;
617		break;
618	case 5:
619		ret = 0x6;
620		break;
621	case 7:
622		ret = 0x7;
623		break;
624	default:
625		return -1;
626		break;
627	}
628	return ret << 4;
629}
630
631/*
632 * Options list:
633 *   0  I/O base
634 *   1  IRQ
635 */
636
637static int das16m1_attach(struct comedi_device *dev, struct comedi_devconfig *it)
638{
639	struct comedi_subdevice *s;
640	int ret;
641	unsigned int irq;
642	unsigned long iobase;
643
644	iobase = it->options[0];
645
646	printk("comedi%d: das16m1:", dev->minor);
647
648	ret = alloc_private(dev, sizeof(struct das16m1_private_struct));
649	if (ret < 0)
650		return ret;
651
652	dev->board_name = thisboard->name;
653
654	printk(" io 0x%lx-0x%lx 0x%lx-0x%lx",
655		iobase, iobase + DAS16M1_SIZE,
656		iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2);
657	if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) {
658		printk(" I/O port conflict\n");
659		return -EIO;
660	}
661	if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2,
662			driver_das16m1.driver_name)) {
663		release_region(iobase, DAS16M1_SIZE);
664		printk(" I/O port conflict\n");
665		return -EIO;
666	}
667	dev->iobase = iobase;
668
669	/* now for the irq */
670	irq = it->options[1];
671	/*  make sure it is valid */
672	if (das16m1_irq_bits(irq) >= 0) {
673		ret = request_irq(irq, das16m1_interrupt, 0,
674				  driver_das16m1.driver_name, dev);
675		if (ret < 0) {
676			printk(", irq unavailable\n");
677			return ret;
678		}
679		dev->irq = irq;
680		printk(", irq %u\n", irq);
681	} else if (irq == 0) {
682		printk(", no irq\n");
683	} else {
684		printk(", invalid irq\n"
685			" valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
686		return -EINVAL;
687	}
688
689	ret = alloc_subdevices(dev, 4);
690	if (ret < 0)
691		return ret;
692
693	s = dev->subdevices + 0;
694	dev->read_subdev = s;
695	/* ai */
696	s->type = COMEDI_SUBD_AI;
697	s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
698	s->n_chan = 8;
699	s->subdev_flags = SDF_DIFF;
700	s->len_chanlist = 256;
701	s->maxdata = (1 << 12) - 1;
702	s->range_table = &range_das16m1;
703	s->insn_read = das16m1_ai_rinsn;
704	s->do_cmdtest = das16m1_cmd_test;
705	s->do_cmd = das16m1_cmd_exec;
706	s->cancel = das16m1_cancel;
707	s->poll = das16m1_poll;
708
709	s = dev->subdevices + 1;
710	/* di */
711	s->type = COMEDI_SUBD_DI;
712	s->subdev_flags = SDF_READABLE;
713	s->n_chan = 4;
714	s->maxdata = 1;
715	s->range_table = &range_digital;
716	s->insn_bits = das16m1_di_rbits;
717
718	s = dev->subdevices + 2;
719	/* do */
720	s->type = COMEDI_SUBD_DO;
721	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
722	s->n_chan = 4;
723	s->maxdata = 1;
724	s->range_table = &range_digital;
725	s->insn_bits = das16m1_do_wbits;
726
727	s = dev->subdevices + 3;
728	/* 8255 */
729	subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
730
731	/*  disable upper half of hardware conversion counter so it doesn't mess with us */
732	outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
733
734	/*  initialize digital output lines */
735	outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
736
737	/* set the interrupt level */
738	if (dev->irq)
739		devpriv->control_state = das16m1_irq_bits(dev->irq);
740	else
741		devpriv->control_state = 0;
742	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
743
744	return 0;
745}
746
747static int das16m1_detach(struct comedi_device *dev)
748{
749	printk("comedi%d: das16m1: remove\n", dev->minor);
750
751/* das16m1_reset(dev); */
752
753	if (dev->subdevices)
754		subdev_8255_cleanup(dev, dev->subdevices + 3);
755
756	if (dev->irq)
757		free_irq(dev->irq, dev);
758
759	if (dev->iobase) {
760		release_region(dev->iobase, DAS16M1_SIZE);
761		release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2);
762	}
763
764	return 0;
765}
766