1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * comedi/drivers/das800.c
4  * Driver for Keitley das800 series boards and compatibles
5  * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
6  *
7  * COMEDI - Linux Control and Measurement Device Interface
8  * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9  */
10 /*
11  * Driver: das800
12  * Description: Keithley Metrabyte DAS800 (& compatibles)
13  * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
14  * Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
15  * DAS-802 (das-802),
16  * [Measurement Computing] CIO-DAS800 (cio-das800),
17  * CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
18  * CIO-DAS802/16 (cio-das802/16)
19  * Status: works, cio-das802/16 untested - email me if you have tested it
20  *
21  * Configuration options:
22  * [0] - I/O port base address
23  * [1] - IRQ (optional, required for timed or externally triggered conversions)
24  *
25  * Notes:
26  *	IRQ can be omitted, although the cmd interface will not work without it.
27  *
28  *	All entries in the channel/gain list must use the same gain and be
29  *	consecutive channels counting upwards in channel number (these are
30  *	hardware limitations.)
31  *
32  *	I've never tested the gain setting stuff since I only have a
33  *	DAS-800 board with fixed gain.
34  *
35  *	The cio-das802/16 does not have a fifo-empty status bit!  Therefore
36  *	only fifo-half-full transfers are possible with this card.
37  *
38  * cmd triggers supported:
39  *	start_src:      TRIG_NOW | TRIG_EXT
40  *	scan_begin_src: TRIG_FOLLOW
41  *	scan_end_src:   TRIG_COUNT
42  *	convert_src:    TRIG_TIMER | TRIG_EXT
43  *	stop_src:       TRIG_NONE | TRIG_COUNT
44  */
45 
46 #include <linux/module.h>
47 #include <linux/interrupt.h>
48 #include <linux/delay.h>
49 
50 #include "../comedidev.h"
51 
52 #include "comedi_8254.h"
53 
54 #define N_CHAN_AI             8	/*  number of analog input channels */
55 
56 /* Registers for the das800 */
57 
58 #define DAS800_LSB            0
59 #define   FIFO_EMPTY            0x1
60 #define   FIFO_OVF              0x2
61 #define DAS800_MSB            1
62 #define DAS800_CONTROL1       2
63 #define   CONTROL1_INTE         0x8
64 #define DAS800_CONV_CONTROL   2
65 #define   ITE                   0x1
66 #define   CASC                  0x2
67 #define   DTEN                  0x4
68 #define   IEOC                  0x8
69 #define   EACS                  0x10
70 #define   CONV_HCEN             0x80
71 #define DAS800_SCAN_LIMITS    2
72 #define DAS800_STATUS         2
73 #define   IRQ                   0x8
74 #define   BUSY                  0x80
75 #define DAS800_GAIN           3
76 #define   CIO_FFOV              0x8   /* cio-das802/16 fifo overflow */
77 #define   CIO_ENHF              0x90  /* cio-das802/16 fifo half full int ena */
78 #define   CONTROL1              0x80
79 #define   CONV_CONTROL          0xa0
80 #define   SCAN_LIMITS           0xc0
81 #define   ID                    0xe0
82 #define DAS800_8254           4
83 #define DAS800_STATUS2        7
84 #define   STATUS2_HCEN          0x80
85 #define   STATUS2_INTE          0X20
86 #define DAS800_ID             7
87 
88 #define DAS802_16_HALF_FIFO_SZ	128
89 
90 struct das800_board {
91 	const char *name;
92 	int ai_speed;
93 	const struct comedi_lrange *ai_range;
94 	int resolution;
95 };
96 
97 static const struct comedi_lrange range_das801_ai = {
98 	9, {
99 		BIP_RANGE(5),
100 		BIP_RANGE(10),
101 		UNI_RANGE(10),
102 		BIP_RANGE(0.5),
103 		UNI_RANGE(1),
104 		BIP_RANGE(0.05),
105 		UNI_RANGE(0.1),
106 		BIP_RANGE(0.01),
107 		UNI_RANGE(0.02)
108 	}
109 };
110 
111 static const struct comedi_lrange range_cio_das801_ai = {
112 	9, {
113 		BIP_RANGE(5),
114 		BIP_RANGE(10),
115 		UNI_RANGE(10),
116 		BIP_RANGE(0.5),
117 		UNI_RANGE(1),
118 		BIP_RANGE(0.05),
119 		UNI_RANGE(0.1),
120 		BIP_RANGE(0.005),
121 		UNI_RANGE(0.01)
122 	}
123 };
124 
125 static const struct comedi_lrange range_das802_ai = {
126 	9, {
127 		BIP_RANGE(5),
128 		BIP_RANGE(10),
129 		UNI_RANGE(10),
130 		BIP_RANGE(2.5),
131 		UNI_RANGE(5),
132 		BIP_RANGE(1.25),
133 		UNI_RANGE(2.5),
134 		BIP_RANGE(0.625),
135 		UNI_RANGE(1.25)
136 	}
137 };
138 
139 static const struct comedi_lrange range_das80216_ai = {
140 	8, {
141 		BIP_RANGE(10),
142 		UNI_RANGE(10),
143 		BIP_RANGE(5),
144 		UNI_RANGE(5),
145 		BIP_RANGE(2.5),
146 		UNI_RANGE(2.5),
147 		BIP_RANGE(1.25),
148 		UNI_RANGE(1.25)
149 	}
150 };
151 
152 enum das800_boardinfo {
153 	BOARD_DAS800,
154 	BOARD_CIODAS800,
155 	BOARD_DAS801,
156 	BOARD_CIODAS801,
157 	BOARD_DAS802,
158 	BOARD_CIODAS802,
159 	BOARD_CIODAS80216,
160 };
161 
162 static const struct das800_board das800_boards[] = {
163 	[BOARD_DAS800] = {
164 		.name		= "das-800",
165 		.ai_speed	= 25000,
166 		.ai_range	= &range_bipolar5,
167 		.resolution	= 12,
168 	},
169 	[BOARD_CIODAS800] = {
170 		.name		= "cio-das800",
171 		.ai_speed	= 20000,
172 		.ai_range	= &range_bipolar5,
173 		.resolution	= 12,
174 	},
175 	[BOARD_DAS801] = {
176 		.name		= "das-801",
177 		.ai_speed	= 25000,
178 		.ai_range	= &range_das801_ai,
179 		.resolution	= 12,
180 	},
181 	[BOARD_CIODAS801] = {
182 		.name		= "cio-das801",
183 		.ai_speed	= 20000,
184 		.ai_range	= &range_cio_das801_ai,
185 		.resolution	= 12,
186 	},
187 	[BOARD_DAS802] = {
188 		.name		= "das-802",
189 		.ai_speed	= 25000,
190 		.ai_range	= &range_das802_ai,
191 		.resolution	= 12,
192 	},
193 	[BOARD_CIODAS802] = {
194 		.name		= "cio-das802",
195 		.ai_speed	= 20000,
196 		.ai_range	= &range_das802_ai,
197 		.resolution	= 12,
198 	},
199 	[BOARD_CIODAS80216] = {
200 		.name		= "cio-das802/16",
201 		.ai_speed	= 10000,
202 		.ai_range	= &range_das80216_ai,
203 		.resolution	= 16,
204 	},
205 };
206 
207 struct das800_private {
208 	unsigned int do_bits;	/* digital output bits */
209 };
210 
das800_ind_write(struct comedi_device * dev,unsigned int val,unsigned int reg)211 static void das800_ind_write(struct comedi_device *dev,
212 			     unsigned int val, unsigned int reg)
213 {
214 	/*
215 	 * Select dev->iobase + 2 to be desired register
216 	 * then write to that register.
217 	 */
218 	outb(reg, dev->iobase + DAS800_GAIN);
219 	outb(val, dev->iobase + 2);
220 }
221 
das800_ind_read(struct comedi_device * dev,unsigned int reg)222 static unsigned int das800_ind_read(struct comedi_device *dev, unsigned int reg)
223 {
224 	/*
225 	 * Select dev->iobase + 7 to be desired register
226 	 * then read from that register.
227 	 */
228 	outb(reg, dev->iobase + DAS800_GAIN);
229 	return inb(dev->iobase + 7);
230 }
231 
das800_enable(struct comedi_device * dev)232 static void das800_enable(struct comedi_device *dev)
233 {
234 	const struct das800_board *board = dev->board_ptr;
235 	struct das800_private *devpriv = dev->private;
236 	unsigned long irq_flags;
237 
238 	spin_lock_irqsave(&dev->spinlock, irq_flags);
239 	/*  enable fifo-half full interrupts for cio-das802/16 */
240 	if (board->resolution == 16)
241 		outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
242 	/* enable hardware triggering */
243 	das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
244 	/* enable card's interrupt */
245 	das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
246 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
247 }
248 
das800_disable(struct comedi_device * dev)249 static void das800_disable(struct comedi_device *dev)
250 {
251 	unsigned long irq_flags;
252 
253 	spin_lock_irqsave(&dev->spinlock, irq_flags);
254 	/* disable hardware triggering of conversions */
255 	das800_ind_write(dev, 0x0, CONV_CONTROL);
256 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
257 }
258 
das800_cancel(struct comedi_device * dev,struct comedi_subdevice * s)259 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
260 {
261 	das800_disable(dev);
262 	return 0;
263 }
264 
das800_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)265 static int das800_ai_check_chanlist(struct comedi_device *dev,
266 				    struct comedi_subdevice *s,
267 				    struct comedi_cmd *cmd)
268 {
269 	unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
270 	unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
271 	int i;
272 
273 	for (i = 1; i < cmd->chanlist_len; i++) {
274 		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
275 		unsigned int range = CR_RANGE(cmd->chanlist[i]);
276 
277 		if (chan != (chan0 + i) % s->n_chan) {
278 			dev_dbg(dev->class_dev,
279 				"chanlist must be consecutive, counting upwards\n");
280 			return -EINVAL;
281 		}
282 
283 		if (range != range0) {
284 			dev_dbg(dev->class_dev,
285 				"chanlist must all have the same gain\n");
286 			return -EINVAL;
287 		}
288 	}
289 
290 	return 0;
291 }
292 
das800_ai_do_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)293 static int das800_ai_do_cmdtest(struct comedi_device *dev,
294 				struct comedi_subdevice *s,
295 				struct comedi_cmd *cmd)
296 {
297 	const struct das800_board *board = dev->board_ptr;
298 	int err = 0;
299 
300 	/* Step 1 : check if triggers are trivially valid */
301 
302 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
303 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
304 	err |= comedi_check_trigger_src(&cmd->convert_src,
305 					TRIG_TIMER | TRIG_EXT);
306 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
307 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
308 
309 	if (err)
310 		return 1;
311 
312 	/* Step 2a : make sure trigger sources are unique */
313 
314 	err |= comedi_check_trigger_is_unique(cmd->start_src);
315 	err |= comedi_check_trigger_is_unique(cmd->convert_src);
316 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
317 
318 	/* Step 2b : and mutually compatible */
319 
320 	if (err)
321 		return 2;
322 
323 	/* Step 3: check if arguments are trivially valid */
324 
325 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
326 
327 	if (cmd->convert_src == TRIG_TIMER) {
328 		err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
329 						    board->ai_speed);
330 	}
331 
332 	err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
333 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
334 					   cmd->chanlist_len);
335 
336 	if (cmd->stop_src == TRIG_COUNT)
337 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
338 	else	/* TRIG_NONE */
339 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
340 
341 	if (err)
342 		return 3;
343 
344 	/* step 4: fix up any arguments */
345 
346 	if (cmd->convert_src == TRIG_TIMER) {
347 		unsigned int arg = cmd->convert_arg;
348 
349 		comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
350 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
351 	}
352 
353 	if (err)
354 		return 4;
355 
356 	/* Step 5: check channel list if it exists */
357 	if (cmd->chanlist && cmd->chanlist_len > 0)
358 		err |= das800_ai_check_chanlist(dev, s, cmd);
359 
360 	if (err)
361 		return 5;
362 
363 	return 0;
364 }
365 
das800_ai_do_cmd(struct comedi_device * dev,struct comedi_subdevice * s)366 static int das800_ai_do_cmd(struct comedi_device *dev,
367 			    struct comedi_subdevice *s)
368 {
369 	const struct das800_board *board = dev->board_ptr;
370 	struct comedi_async *async = s->async;
371 	struct comedi_cmd *cmd = &async->cmd;
372 	unsigned int gain = CR_RANGE(cmd->chanlist[0]);
373 	unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
374 	unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
375 	unsigned int scan_chans = (end_chan << 3) | start_chan;
376 	int conv_bits;
377 	unsigned long irq_flags;
378 
379 	das800_disable(dev);
380 
381 	spin_lock_irqsave(&dev->spinlock, irq_flags);
382 	/* set scan limits */
383 	das800_ind_write(dev, scan_chans, SCAN_LIMITS);
384 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
385 
386 	/* set gain */
387 	if (board->resolution == 12 && gain > 0)
388 		gain += 0x7;
389 	gain &= 0xf;
390 	outb(gain, dev->iobase + DAS800_GAIN);
391 
392 	/* enable auto channel scan, send interrupts on end of conversion
393 	 * and set clock source to internal or external
394 	 */
395 	conv_bits = 0;
396 	conv_bits |= EACS | IEOC;
397 	if (cmd->start_src == TRIG_EXT)
398 		conv_bits |= DTEN;
399 	if (cmd->convert_src == TRIG_TIMER) {
400 		conv_bits |= CASC | ITE;
401 		comedi_8254_update_divisors(dev->pacer);
402 		comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
403 	}
404 
405 	spin_lock_irqsave(&dev->spinlock, irq_flags);
406 	das800_ind_write(dev, conv_bits, CONV_CONTROL);
407 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
408 
409 	das800_enable(dev);
410 	return 0;
411 }
412 
das800_ai_get_sample(struct comedi_device * dev)413 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
414 {
415 	unsigned int lsb = inb(dev->iobase + DAS800_LSB);
416 	unsigned int msb = inb(dev->iobase + DAS800_MSB);
417 
418 	return (msb << 8) | lsb;
419 }
420 
das800_interrupt(int irq,void * d)421 static irqreturn_t das800_interrupt(int irq, void *d)
422 {
423 	struct comedi_device *dev = d;
424 	struct das800_private *devpriv = dev->private;
425 	struct comedi_subdevice *s = dev->read_subdev;
426 	struct comedi_async *async;
427 	struct comedi_cmd *cmd;
428 	unsigned long irq_flags;
429 	unsigned int status;
430 	unsigned int val;
431 	bool fifo_empty;
432 	bool fifo_overflow;
433 	int i;
434 
435 	status = inb(dev->iobase + DAS800_STATUS);
436 	if (!(status & IRQ))
437 		return IRQ_NONE;
438 	if (!dev->attached)
439 		return IRQ_HANDLED;
440 
441 	async = s->async;
442 	cmd = &async->cmd;
443 
444 	spin_lock_irqsave(&dev->spinlock, irq_flags);
445 	status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
446 	/*
447 	 * Don't release spinlock yet since we want to make sure
448 	 * no one else disables hardware conversions.
449 	 */
450 
451 	/* if hardware conversions are not enabled, then quit */
452 	if (status == 0) {
453 		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
454 		return IRQ_HANDLED;
455 	}
456 
457 	for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
458 		val = das800_ai_get_sample(dev);
459 		if (s->maxdata == 0x0fff) {
460 			fifo_empty = !!(val & FIFO_EMPTY);
461 			fifo_overflow = !!(val & FIFO_OVF);
462 		} else {
463 			/* cio-das802/16 has no fifo empty status bit */
464 			fifo_empty = false;
465 			fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
466 						CIO_FFOV);
467 		}
468 		if (fifo_empty || fifo_overflow)
469 			break;
470 
471 		if (s->maxdata == 0x0fff)
472 			val >>= 4;	/* 12-bit sample */
473 
474 		val &= s->maxdata;
475 		comedi_buf_write_samples(s, &val, 1);
476 
477 		if (cmd->stop_src == TRIG_COUNT &&
478 		    async->scans_done >= cmd->stop_arg) {
479 			async->events |= COMEDI_CB_EOA;
480 			break;
481 		}
482 	}
483 
484 	if (fifo_overflow) {
485 		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
486 		async->events |= COMEDI_CB_ERROR;
487 		comedi_handle_events(dev, s);
488 		return IRQ_HANDLED;
489 	}
490 
491 	if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
492 		/*
493 		 * Re-enable card's interrupt.
494 		 * We already have spinlock, so indirect addressing is safe
495 		 */
496 		das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
497 				 CONTROL1);
498 		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
499 	} else {
500 		/* otherwise, stop taking data */
501 		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
502 		das800_disable(dev);
503 	}
504 	comedi_handle_events(dev, s);
505 	return IRQ_HANDLED;
506 }
507 
das800_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)508 static int das800_ai_eoc(struct comedi_device *dev,
509 			 struct comedi_subdevice *s,
510 			 struct comedi_insn *insn,
511 			 unsigned long context)
512 {
513 	unsigned int status;
514 
515 	status = inb(dev->iobase + DAS800_STATUS);
516 	if ((status & BUSY) == 0)
517 		return 0;
518 	return -EBUSY;
519 }
520 
das800_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)521 static int das800_ai_insn_read(struct comedi_device *dev,
522 			       struct comedi_subdevice *s,
523 			       struct comedi_insn *insn,
524 			       unsigned int *data)
525 {
526 	struct das800_private *devpriv = dev->private;
527 	unsigned int chan = CR_CHAN(insn->chanspec);
528 	unsigned int range = CR_RANGE(insn->chanspec);
529 	unsigned long irq_flags;
530 	unsigned int val;
531 	int ret;
532 	int i;
533 
534 	das800_disable(dev);
535 
536 	/* set multiplexer */
537 	spin_lock_irqsave(&dev->spinlock, irq_flags);
538 	das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
539 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
540 
541 	/* set gain / range */
542 	if (s->maxdata == 0x0fff && range)
543 		range += 0x7;
544 	range &= 0xf;
545 	outb(range, dev->iobase + DAS800_GAIN);
546 
547 	udelay(5);
548 
549 	for (i = 0; i < insn->n; i++) {
550 		/* trigger conversion */
551 		outb_p(0, dev->iobase + DAS800_MSB);
552 
553 		ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
554 		if (ret)
555 			return ret;
556 
557 		val = das800_ai_get_sample(dev);
558 		if (s->maxdata == 0x0fff)
559 			val >>= 4;	/* 12-bit sample */
560 		data[i] = val & s->maxdata;
561 	}
562 
563 	return insn->n;
564 }
565 
das800_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)566 static int das800_di_insn_bits(struct comedi_device *dev,
567 			       struct comedi_subdevice *s,
568 			       struct comedi_insn *insn,
569 			       unsigned int *data)
570 {
571 	data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
572 
573 	return insn->n;
574 }
575 
das800_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)576 static int das800_do_insn_bits(struct comedi_device *dev,
577 			       struct comedi_subdevice *s,
578 			       struct comedi_insn *insn,
579 			       unsigned int *data)
580 {
581 	struct das800_private *devpriv = dev->private;
582 	unsigned long irq_flags;
583 
584 	if (comedi_dio_update_state(s, data)) {
585 		devpriv->do_bits = s->state << 4;
586 
587 		spin_lock_irqsave(&dev->spinlock, irq_flags);
588 		das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
589 				 CONTROL1);
590 		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
591 	}
592 
593 	data[1] = s->state;
594 
595 	return insn->n;
596 }
597 
das800_probe(struct comedi_device * dev)598 static const struct das800_board *das800_probe(struct comedi_device *dev)
599 {
600 	const struct das800_board *board = dev->board_ptr;
601 	int index = board ? board - das800_boards : -EINVAL;
602 	int id_bits;
603 	unsigned long irq_flags;
604 
605 	/*
606 	 * The dev->board_ptr will be set by comedi_device_attach() if the
607 	 * board name provided by the user matches a board->name in this
608 	 * driver. If so, this function sanity checks the id_bits to verify
609 	 * that the board is correct.
610 	 *
611 	 * If the dev->board_ptr is not set, the user is trying to attach
612 	 * an unspecified board to this driver. In this case the id_bits
613 	 * are used to 'probe' for the correct dev->board_ptr.
614 	 */
615 	spin_lock_irqsave(&dev->spinlock, irq_flags);
616 	id_bits = das800_ind_read(dev, ID) & 0x3;
617 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
618 
619 	switch (id_bits) {
620 	case 0x0:
621 		if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
622 			return board;
623 		index = BOARD_DAS800;
624 		break;
625 	case 0x2:
626 		if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
627 			return board;
628 		index = BOARD_DAS801;
629 		break;
630 	case 0x3:
631 		if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
632 		    index == BOARD_CIODAS80216)
633 			return board;
634 		index = BOARD_DAS802;
635 		break;
636 	default:
637 		dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
638 			id_bits);
639 		return NULL;
640 	}
641 	dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
642 		das800_boards[index].name);
643 
644 	return &das800_boards[index];
645 }
646 
das800_attach(struct comedi_device * dev,struct comedi_devconfig * it)647 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
648 {
649 	const struct das800_board *board;
650 	struct das800_private *devpriv;
651 	struct comedi_subdevice *s;
652 	unsigned int irq = it->options[1];
653 	unsigned long irq_flags;
654 	int ret;
655 
656 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
657 	if (!devpriv)
658 		return -ENOMEM;
659 
660 	ret = comedi_request_region(dev, it->options[0], 0x8);
661 	if (ret)
662 		return ret;
663 
664 	board = das800_probe(dev);
665 	if (!board)
666 		return -ENODEV;
667 	dev->board_ptr = board;
668 	dev->board_name = board->name;
669 
670 	if (irq > 1 && irq <= 7) {
671 		ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
672 				  dev);
673 		if (ret == 0)
674 			dev->irq = irq;
675 	}
676 
677 	dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
678 				      I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
679 	if (!dev->pacer)
680 		return -ENOMEM;
681 
682 	ret = comedi_alloc_subdevices(dev, 3);
683 	if (ret)
684 		return ret;
685 
686 	/* Analog Input subdevice */
687 	s = &dev->subdevices[0];
688 	dev->read_subdev = s;
689 	s->type		= COMEDI_SUBD_AI;
690 	s->subdev_flags	= SDF_READABLE | SDF_GROUND;
691 	s->n_chan	= 8;
692 	s->maxdata	= (1 << board->resolution) - 1;
693 	s->range_table	= board->ai_range;
694 	s->insn_read	= das800_ai_insn_read;
695 	if (dev->irq) {
696 		s->subdev_flags	|= SDF_CMD_READ;
697 		s->len_chanlist	= 8;
698 		s->do_cmdtest	= das800_ai_do_cmdtest;
699 		s->do_cmd	= das800_ai_do_cmd;
700 		s->cancel	= das800_cancel;
701 	}
702 
703 	/* Digital Input subdevice */
704 	s = &dev->subdevices[1];
705 	s->type		= COMEDI_SUBD_DI;
706 	s->subdev_flags	= SDF_READABLE;
707 	s->n_chan	= 3;
708 	s->maxdata	= 1;
709 	s->range_table	= &range_digital;
710 	s->insn_bits	= das800_di_insn_bits;
711 
712 	/* Digital Output subdevice */
713 	s = &dev->subdevices[2];
714 	s->type		= COMEDI_SUBD_DO;
715 	s->subdev_flags	= SDF_WRITABLE;
716 	s->n_chan	= 4;
717 	s->maxdata	= 1;
718 	s->range_table	= &range_digital;
719 	s->insn_bits	= das800_do_insn_bits;
720 
721 	das800_disable(dev);
722 
723 	/* initialize digital out channels */
724 	spin_lock_irqsave(&dev->spinlock, irq_flags);
725 	das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
726 	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
727 
728 	return 0;
729 };
730 
731 static struct comedi_driver driver_das800 = {
732 	.driver_name	= "das800",
733 	.module		= THIS_MODULE,
734 	.attach		= das800_attach,
735 	.detach		= comedi_legacy_detach,
736 	.num_names	= ARRAY_SIZE(das800_boards),
737 	.board_name	= &das800_boards[0].name,
738 	.offset		= sizeof(struct das800_board),
739 };
740 module_comedi_driver(driver_das800);
741 
742 MODULE_AUTHOR("Comedi http://www.comedi.org");
743 MODULE_DESCRIPTION("Comedi low-level driver");
744 MODULE_LICENSE("GPL");
745