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