Merge existing fixes from regulator/for-5.14
[linux-2.6-microblaze.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
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
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
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
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
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
259 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
260 {
261         das800_disable(dev);
262         return 0;
263 }
264
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
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
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
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
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 short 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
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
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
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
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
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
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, "das800",
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 https://www.comedi.org");
743 MODULE_DESCRIPTION("Comedi low-level driver");
744 MODULE_LICENSE("GPL");