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