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
88struct das800_board {
89	const char *name;
90	int ai_speed;
91	const struct comedi_lrange *ai_range;
92	int resolution;
93};
94
95static 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
109static 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
123static 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
137static 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
150enum 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
160static 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
205struct das800_private {
206	unsigned int do_bits;	/* digital output bits */
207};
208
209static 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
220static 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
230static 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
247static 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
257static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
258{
259	das800_disable(dev);
260	return 0;
261}
262
263static 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
291static 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
364static 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
411static 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
419static 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
506static 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
519static 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
564static 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
574static 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
596static 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
645static 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
729static 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};
738module_comedi_driver(driver_das800);
739
740MODULE_AUTHOR("Comedi https://www.comedi.org");
741MODULE_DESCRIPTION("Comedi low-level driver");
742MODULE_LICENSE("GPL");
743