GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / comedi / drivers / das800.c
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
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
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
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
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
257 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
258 {
259         das800_disable(dev);
260         return 0;
261 }
262
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
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
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
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
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
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
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
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
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
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
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");