Merge branch 'x86-microcode-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / dmm32at.c
1 /*
2     comedi/drivers/dmm32at.c
3     Diamond Systems mm32at code for a Comedi driver
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 */
18 /*
19 Driver: dmm32at
20 Description: Diamond Systems mm32at driver.
21 Devices:
22 Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
23 Updated: Fri Jun  4 09:13:24 CDT 2004
24 Status: experimental
25
26 This driver is for the Diamond Systems MM-32-AT board
27 http://www.diamondsystems.com/products/diamondmm32at It is being used
28 on serveral projects inside NASA, without problems so far. For analog
29 input commands, TRIG_EXT is not yet supported at all..
30
31 Configuration Options:
32   comedi_config /dev/comedi0 dmm32at baseaddr,irq
33 */
34
35 #include <linux/interrupt.h>
36 #include "../comedidev.h"
37 #include <linux/ioport.h>
38
39 #include "comedi_fc.h"
40
41 /* Board register addresses */
42
43 #define DMM32AT_MEMSIZE 0x10
44
45 #define DMM32AT_CONV 0x00
46 #define DMM32AT_AILSB 0x00
47 #define DMM32AT_AUXDOUT 0x01
48 #define DMM32AT_AIMSB 0x01
49 #define DMM32AT_AILOW 0x02
50 #define DMM32AT_AIHIGH 0x03
51
52 #define DMM32AT_DACLSB 0x04
53 #define DMM32AT_DACSTAT 0x04
54 #define DMM32AT_DACMSB 0x05
55
56 #define DMM32AT_FIFOCNTRL 0x07
57 #define DMM32AT_FIFOSTAT 0x07
58
59 #define DMM32AT_CNTRL 0x08
60 #define DMM32AT_AISTAT 0x08
61
62 #define DMM32AT_INTCLOCK 0x09
63
64 #define DMM32AT_CNTRDIO 0x0a
65
66 #define DMM32AT_AICONF 0x0b
67 #define DMM32AT_AIRBACK 0x0b
68
69 #define DMM32AT_CLK1 0x0d
70 #define DMM32AT_CLK2 0x0e
71 #define DMM32AT_CLKCT 0x0f
72
73 #define DMM32AT_DIOA 0x0c
74 #define DMM32AT_DIOB 0x0d
75 #define DMM32AT_DIOC 0x0e
76 #define DMM32AT_DIOCONF 0x0f
77
78 /* Board register values. */
79
80 /* DMM32AT_DACSTAT 0x04 */
81 #define DMM32AT_DACBUSY 0x80
82
83 /* DMM32AT_FIFOCNTRL 0x07 */
84 #define DMM32AT_FIFORESET 0x02
85 #define DMM32AT_SCANENABLE 0x04
86
87 /* DMM32AT_CNTRL 0x08 */
88 #define DMM32AT_RESET 0x20
89 #define DMM32AT_INTRESET 0x08
90 #define DMM32AT_CLKACC 0x00
91 #define DMM32AT_DIOACC 0x01
92
93 /* DMM32AT_AISTAT 0x08 */
94 #define DMM32AT_STATUS 0x80
95
96 /* DMM32AT_INTCLOCK 0x09 */
97 #define DMM32AT_ADINT 0x80
98 #define DMM32AT_CLKSEL 0x03
99
100 /* DMM32AT_CNTRDIO 0x0a */
101 #define DMM32AT_FREQ12 0x80
102
103 /* DMM32AT_AICONF 0x0b */
104 #define DMM32AT_RANGE_U10 0x0c
105 #define DMM32AT_RANGE_U5 0x0d
106 #define DMM32AT_RANGE_B10 0x08
107 #define DMM32AT_RANGE_B5 0x00
108 #define DMM32AT_SCINT_20 0x00
109 #define DMM32AT_SCINT_15 0x10
110 #define DMM32AT_SCINT_10 0x20
111 #define DMM32AT_SCINT_5 0x30
112
113 /* DMM32AT_CLKCT 0x0f */
114 #define DMM32AT_CLKCT1 0x56     /* mode3 counter 1 - write low byte only */
115 #define DMM32AT_CLKCT2 0xb6     /*  mode3 counter 2 - write high and low byte */
116
117 /* DMM32AT_DIOCONF 0x0f */
118 #define DMM32AT_DIENABLE 0x80
119 #define DMM32AT_DIRA 0x10
120 #define DMM32AT_DIRB 0x02
121 #define DMM32AT_DIRCL 0x01
122 #define DMM32AT_DIRCH 0x08
123
124 /* board AI ranges in comedi structure */
125 static const struct comedi_lrange dmm32at_airanges = {
126         4,
127         {
128          UNI_RANGE(10),
129          UNI_RANGE(5),
130          BIP_RANGE(10),
131          BIP_RANGE(5),
132          }
133 };
134
135 /* register values for above ranges */
136 static const unsigned char dmm32at_rangebits[] = {
137         DMM32AT_RANGE_U10,
138         DMM32AT_RANGE_U5,
139         DMM32AT_RANGE_B10,
140         DMM32AT_RANGE_B5,
141 };
142
143 /* only one of these ranges is valid, as set by a jumper on the
144  * board. The application should only use the range set by the jumper
145  */
146 static const struct comedi_lrange dmm32at_aoranges = {
147         4,
148         {
149          UNI_RANGE(10),
150          UNI_RANGE(5),
151          BIP_RANGE(10),
152          BIP_RANGE(5),
153          }
154 };
155
156 struct dmm32at_private {
157
158         int data;
159         int ai_inuse;
160         unsigned int ai_scans_left;
161
162         /* Used for AO readback */
163         unsigned int ao_readback[4];
164         unsigned char dio_config;
165
166 };
167
168 static int dmm32at_ai_rinsn(struct comedi_device *dev,
169                             struct comedi_subdevice *s,
170                             struct comedi_insn *insn, unsigned int *data)
171 {
172         int n, i;
173         unsigned int d;
174         unsigned char status;
175         unsigned short msb, lsb;
176         unsigned char chan;
177         int range;
178
179         /* get the channel and range number */
180
181         chan = CR_CHAN(insn->chanspec) & (s->n_chan - 1);
182         range = CR_RANGE(insn->chanspec);
183
184         /* printk("channel=0x%02x, range=%d\n",chan,range); */
185
186         /* zero scan and fifo control and reset fifo */
187         outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL);
188
189         /* write the ai channel range regs */
190         outb(chan, dev->iobase + DMM32AT_AILOW);
191         outb(chan, dev->iobase + DMM32AT_AIHIGH);
192         /* set the range bits */
193         outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF);
194
195         /* wait for circuit to settle */
196         for (i = 0; i < 40000; i++) {
197                 status = inb(dev->iobase + DMM32AT_AIRBACK);
198                 if ((status & DMM32AT_STATUS) == 0)
199                         break;
200         }
201         if (i == 40000) {
202                 printk(KERN_WARNING "dmm32at: timeout\n");
203                 return -ETIMEDOUT;
204         }
205
206         /* convert n samples */
207         for (n = 0; n < insn->n; n++) {
208                 /* trigger conversion */
209                 outb(0xff, dev->iobase + DMM32AT_CONV);
210                 /* wait for conversion to end */
211                 for (i = 0; i < 40000; i++) {
212                         status = inb(dev->iobase + DMM32AT_AISTAT);
213                         if ((status & DMM32AT_STATUS) == 0)
214                                 break;
215                 }
216                 if (i == 40000) {
217                         printk(KERN_WARNING "dmm32at: timeout\n");
218                         return -ETIMEDOUT;
219                 }
220
221                 /* read data */
222                 lsb = inb(dev->iobase + DMM32AT_AILSB);
223                 msb = inb(dev->iobase + DMM32AT_AIMSB);
224
225                 /* invert sign bit to make range unsigned, this is an
226                    idiosyncrasy of the diamond board, it return
227                    conversions as a signed value, i.e. -32768 to
228                    32767, flipping the bit and interpreting it as
229                    signed gives you a range of 0 to 65535 which is
230                    used by comedi */
231                 d = ((msb ^ 0x0080) << 8) + lsb;
232
233                 data[n] = d;
234         }
235
236         /* return the number of samples read/written */
237         return n;
238 }
239
240 static int dmm32at_ns_to_timer(unsigned int *ns, int round)
241 {
242         /* trivial timer */
243         return *ns;
244 }
245
246 static int dmm32at_ai_cmdtest(struct comedi_device *dev,
247                               struct comedi_subdevice *s,
248                               struct comedi_cmd *cmd)
249 {
250         int err = 0;
251         int tmp;
252         int start_chan, gain, i;
253
254         /* Step 1 : check if triggers are trivially valid */
255
256         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
257         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
258                                         TRIG_TIMER /*| TRIG_EXT */);
259         err |= cfc_check_trigger_src(&cmd->convert_src,
260                                         TRIG_TIMER /*| TRIG_EXT */);
261         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
262         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
263
264         if (err)
265                 return 1;
266
267         /* Step 2a : make sure trigger sources are unique */
268
269         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
270         err |= cfc_check_trigger_is_unique(cmd->convert_src);
271         err |= cfc_check_trigger_is_unique(cmd->stop_src);
272
273         /* Step 2b : and mutually compatible */
274
275         if (err)
276                 return 2;
277
278         /* Step 3: check if arguments are trivially valid */
279
280         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
281
282 #define MAX_SCAN_SPEED  1000000 /* in nanoseconds */
283 #define MIN_SCAN_SPEED  1000000000      /* in nanoseconds */
284
285         if (cmd->scan_begin_src == TRIG_TIMER) {
286                 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
287                                                  MAX_SCAN_SPEED);
288                 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg,
289                                                  MIN_SCAN_SPEED);
290         } else {
291                 /* external trigger */
292                 /* should be level/edge, hi/lo specification here */
293                 /* should specify multiple external triggers */
294                 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 9);
295         }
296
297         if (cmd->convert_src == TRIG_TIMER) {
298                 if (cmd->convert_arg >= 17500)
299                         cmd->convert_arg = 20000;
300                 else if (cmd->convert_arg >= 12500)
301                         cmd->convert_arg = 15000;
302                 else if (cmd->convert_arg >= 7500)
303                         cmd->convert_arg = 10000;
304                 else
305                         cmd->convert_arg = 5000;
306         } else {
307                 /* external trigger */
308                 /* see above */
309                 err |= cfc_check_trigger_arg_max(&cmd->convert_arg, 9);
310         }
311
312         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
313
314         if (cmd->stop_src == TRIG_COUNT) {
315                 err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0xfffffff0);
316                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
317         } else {
318                 /* TRIG_NONE */
319                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
320         }
321
322         if (err)
323                 return 3;
324
325         /* step 4: fix up any arguments */
326
327         if (cmd->scan_begin_src == TRIG_TIMER) {
328                 tmp = cmd->scan_begin_arg;
329                 dmm32at_ns_to_timer(&cmd->scan_begin_arg,
330                                     cmd->flags & TRIG_ROUND_MASK);
331                 if (tmp != cmd->scan_begin_arg)
332                         err++;
333         }
334         if (cmd->convert_src == TRIG_TIMER) {
335                 tmp = cmd->convert_arg;
336                 dmm32at_ns_to_timer(&cmd->convert_arg,
337                                     cmd->flags & TRIG_ROUND_MASK);
338                 if (tmp != cmd->convert_arg)
339                         err++;
340                 if (cmd->scan_begin_src == TRIG_TIMER &&
341                     cmd->scan_begin_arg <
342                     cmd->convert_arg * cmd->scan_end_arg) {
343                         cmd->scan_begin_arg =
344                             cmd->convert_arg * cmd->scan_end_arg;
345                         err++;
346                 }
347         }
348
349         if (err)
350                 return 4;
351
352         /* step 5 check the channel list, the channel list for this
353            board must be consecutive and gains must be the same */
354
355         if (cmd->chanlist) {
356                 gain = CR_RANGE(cmd->chanlist[0]);
357                 start_chan = CR_CHAN(cmd->chanlist[0]);
358                 for (i = 1; i < cmd->chanlist_len; i++) {
359                         if (CR_CHAN(cmd->chanlist[i]) !=
360                             (start_chan + i) % s->n_chan) {
361                                 comedi_error(dev,
362                                              "entries in chanlist must be consecutive channels, counting upwards\n");
363                                 err++;
364                         }
365                         if (CR_RANGE(cmd->chanlist[i]) != gain) {
366                                 comedi_error(dev,
367                                              "entries in chanlist must all have the same gain\n");
368                                 err++;
369                         }
370                 }
371         }
372
373         if (err)
374                 return 5;
375
376         return 0;
377 }
378
379 static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
380 {
381         unsigned char lo1, lo2, hi2;
382         unsigned short both2;
383
384         /* based on 10mhz clock */
385         lo1 = 200;
386         both2 = nansec / 20000;
387         hi2 = (both2 & 0xff00) >> 8;
388         lo2 = both2 & 0x00ff;
389
390         /* set the counter frequency to 10mhz */
391         outb(0, dev->iobase + DMM32AT_CNTRDIO);
392
393         /* get access to the clock regs */
394         outb(DMM32AT_CLKACC, dev->iobase + DMM32AT_CNTRL);
395
396         /* write the counter 1 control word and low byte to counter */
397         outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
398         outb(lo1, dev->iobase + DMM32AT_CLK1);
399
400         /* write the counter 2 control word and low byte then to counter */
401         outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
402         outb(lo2, dev->iobase + DMM32AT_CLK2);
403         outb(hi2, dev->iobase + DMM32AT_CLK2);
404
405         /* enable the ai conversion interrupt and the clock to start scans */
406         outb(DMM32AT_ADINT | DMM32AT_CLKSEL, dev->iobase + DMM32AT_INTCLOCK);
407 }
408
409 static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
410 {
411         struct dmm32at_private *devpriv = dev->private;
412         struct comedi_cmd *cmd = &s->async->cmd;
413         int i, range;
414         unsigned char chanlo, chanhi, status;
415
416         if (!cmd->chanlist)
417                 return -EINVAL;
418
419         /* get the channel list and range */
420         chanlo = CR_CHAN(cmd->chanlist[0]) & (s->n_chan - 1);
421         chanhi = chanlo + cmd->chanlist_len - 1;
422         if (chanhi >= s->n_chan)
423                 return -EINVAL;
424         range = CR_RANGE(cmd->chanlist[0]);
425
426         /* reset fifo */
427         outb(DMM32AT_FIFORESET, dev->iobase + DMM32AT_FIFOCNTRL);
428
429         /* set scan enable */
430         outb(DMM32AT_SCANENABLE, dev->iobase + DMM32AT_FIFOCNTRL);
431
432         /* write the ai channel range regs */
433         outb(chanlo, dev->iobase + DMM32AT_AILOW);
434         outb(chanhi, dev->iobase + DMM32AT_AIHIGH);
435
436         /* set the range bits */
437         outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AICONF);
438
439         /* reset the interrupt just in case */
440         outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL);
441
442         if (cmd->stop_src == TRIG_COUNT)
443                 devpriv->ai_scans_left = cmd->stop_arg;
444         else {                  /* TRIG_NONE */
445                 devpriv->ai_scans_left = 0xffffffff; /* indicates TRIG_NONE to
446                                                       * isr */
447         }
448
449         /* wait for circuit to settle */
450         for (i = 0; i < 40000; i++) {
451                 status = inb(dev->iobase + DMM32AT_AIRBACK);
452                 if ((status & DMM32AT_STATUS) == 0)
453                         break;
454         }
455         if (i == 40000) {
456                 printk(KERN_WARNING "dmm32at: timeout\n");
457                 return -ETIMEDOUT;
458         }
459
460         if (devpriv->ai_scans_left > 1) {
461                 /* start the clock and enable the interrupts */
462                 dmm32at_setaitimer(dev, cmd->scan_begin_arg);
463         } else {
464                 /* start the interrups and initiate a single scan */
465                 outb(DMM32AT_ADINT, dev->iobase + DMM32AT_INTCLOCK);
466                 outb(0xff, dev->iobase + DMM32AT_CONV);
467         }
468
469 /*      printk("dmmat32 in command\n"); */
470
471 /*      for(i=0;i<cmd->chanlist_len;i++) */
472 /*              comedi_buf_put(s->async,i*100); */
473
474 /*      s->async->events |= COMEDI_CB_EOA; */
475 /*      comedi_event(dev, s); */
476
477         return 0;
478
479 }
480
481 static int dmm32at_ai_cancel(struct comedi_device *dev,
482                              struct comedi_subdevice *s)
483 {
484         struct dmm32at_private *devpriv = dev->private;
485
486         devpriv->ai_scans_left = 1;
487         return 0;
488 }
489
490 static irqreturn_t dmm32at_isr(int irq, void *d)
491 {
492         struct comedi_device *dev = d;
493         struct dmm32at_private *devpriv = dev->private;
494         unsigned char intstat;
495         unsigned int samp;
496         unsigned short msb, lsb;
497         int i;
498
499         if (!dev->attached) {
500                 comedi_error(dev, "spurious interrupt");
501                 return IRQ_HANDLED;
502         }
503
504         intstat = inb(dev->iobase + DMM32AT_INTCLOCK);
505
506         if (intstat & DMM32AT_ADINT) {
507                 struct comedi_subdevice *s = dev->read_subdev;
508                 struct comedi_cmd *cmd = &s->async->cmd;
509
510                 for (i = 0; i < cmd->chanlist_len; i++) {
511                         /* read data */
512                         lsb = inb(dev->iobase + DMM32AT_AILSB);
513                         msb = inb(dev->iobase + DMM32AT_AIMSB);
514
515                         /* invert sign bit to make range unsigned */
516                         samp = ((msb ^ 0x0080) << 8) + lsb;
517                         comedi_buf_put(s->async, samp);
518                 }
519
520                 if (devpriv->ai_scans_left != 0xffffffff) {     /* TRIG_COUNT */
521                         devpriv->ai_scans_left--;
522                         if (devpriv->ai_scans_left == 0) {
523                                 /* disable further interrupts and clocks */
524                                 outb(0x0, dev->iobase + DMM32AT_INTCLOCK);
525                                 /* set the buffer to be flushed with an EOF */
526                                 s->async->events |= COMEDI_CB_EOA;
527                         }
528
529                 }
530                 /* flush the buffer */
531                 comedi_event(dev, s);
532         }
533
534         /* reset the interrupt */
535         outb(DMM32AT_INTRESET, dev->iobase + DMM32AT_CNTRL);
536         return IRQ_HANDLED;
537 }
538
539 static int dmm32at_ao_winsn(struct comedi_device *dev,
540                             struct comedi_subdevice *s,
541                             struct comedi_insn *insn, unsigned int *data)
542 {
543         struct dmm32at_private *devpriv = dev->private;
544         int i;
545         int chan = CR_CHAN(insn->chanspec);
546         unsigned char hi, lo, status;
547
548         /* Writing a list of values to an AO channel is probably not
549          * very useful, but that's how the interface is defined. */
550         for (i = 0; i < insn->n; i++) {
551
552                 devpriv->ao_readback[chan] = data[i];
553
554                 /* get the low byte */
555                 lo = data[i] & 0x00ff;
556                 /* high byte also contains channel number */
557                 hi = (data[i] >> 8) + chan * (1 << 6);
558                 /* printk("writing 0x%02x  0x%02x\n",hi,lo); */
559                 /* write the low and high values to the board */
560                 outb(lo, dev->iobase + DMM32AT_DACLSB);
561                 outb(hi, dev->iobase + DMM32AT_DACMSB);
562
563                 /* wait for circuit to settle */
564                 for (i = 0; i < 40000; i++) {
565                         status = inb(dev->iobase + DMM32AT_DACSTAT);
566                         if ((status & DMM32AT_DACBUSY) == 0)
567                                 break;
568                 }
569                 if (i == 40000) {
570                         printk(KERN_WARNING "dmm32at: timeout\n");
571                         return -ETIMEDOUT;
572                 }
573                 /* dummy read to update trigger the output */
574                 status = inb(dev->iobase + DMM32AT_DACMSB);
575
576         }
577
578         /* return the number of samples read/written */
579         return i;
580 }
581
582 static int dmm32at_ao_rinsn(struct comedi_device *dev,
583                             struct comedi_subdevice *s,
584                             struct comedi_insn *insn, unsigned int *data)
585 {
586         struct dmm32at_private *devpriv = dev->private;
587         int i;
588         int chan = CR_CHAN(insn->chanspec);
589
590         for (i = 0; i < insn->n; i++)
591                 data[i] = devpriv->ao_readback[chan];
592
593         return i;
594 }
595
596 static int dmm32at_dio_insn_bits(struct comedi_device *dev,
597                                  struct comedi_subdevice *s,
598                                  struct comedi_insn *insn, unsigned int *data)
599 {
600         struct dmm32at_private *devpriv = dev->private;
601         unsigned char diobits;
602
603         /* The insn data is a mask in data[0] and the new data
604          * in data[1], each channel cooresponding to a bit. */
605         if (data[0]) {
606                 s->state &= ~data[0];
607                 s->state |= data[0] & data[1];
608                 /* Write out the new digital output lines */
609                 /* outw(s->state,dev->iobase + DMM32AT_DIO); */
610         }
611
612         /* get access to the DIO regs */
613         outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
614
615         /* if either part of dio is set for output */
616         if (((devpriv->dio_config & DMM32AT_DIRCL) == 0) ||
617             ((devpriv->dio_config & DMM32AT_DIRCH) == 0)) {
618                 diobits = (s->state & 0x00ff0000) >> 16;
619                 outb(diobits, dev->iobase + DMM32AT_DIOC);
620         }
621         if ((devpriv->dio_config & DMM32AT_DIRB) == 0) {
622                 diobits = (s->state & 0x0000ff00) >> 8;
623                 outb(diobits, dev->iobase + DMM32AT_DIOB);
624         }
625         if ((devpriv->dio_config & DMM32AT_DIRA) == 0) {
626                 diobits = (s->state & 0x000000ff);
627                 outb(diobits, dev->iobase + DMM32AT_DIOA);
628         }
629
630         /* now read the state back in */
631         s->state = inb(dev->iobase + DMM32AT_DIOC);
632         s->state <<= 8;
633         s->state |= inb(dev->iobase + DMM32AT_DIOB);
634         s->state <<= 8;
635         s->state |= inb(dev->iobase + DMM32AT_DIOA);
636         data[1] = s->state;
637
638         /* on return, data[1] contains the value of the digital
639          * input and output lines. */
640         /* data[1]=inw(dev->iobase + DMM32AT_DIO); */
641         /* or we could just return the software copy of the output values if
642          * it was a purely digital output subdevice */
643         /* data[1]=s->state; */
644
645         return insn->n;
646 }
647
648 static int dmm32at_dio_insn_config(struct comedi_device *dev,
649                                    struct comedi_subdevice *s,
650                                    struct comedi_insn *insn, unsigned int *data)
651 {
652         struct dmm32at_private *devpriv = dev->private;
653         unsigned char chanbit;
654         int chan = CR_CHAN(insn->chanspec);
655
656         if (insn->n != 1)
657                 return -EINVAL;
658
659         if (chan < 8)
660                 chanbit = DMM32AT_DIRA;
661         else if (chan < 16)
662                 chanbit = DMM32AT_DIRB;
663         else if (chan < 20)
664                 chanbit = DMM32AT_DIRCL;
665         else
666                 chanbit = DMM32AT_DIRCH;
667
668         /* The input or output configuration of each digital line is
669          * configured by a special insn_config instruction.  chanspec
670          * contains the channel to be changed, and data[0] contains the
671          * value COMEDI_INPUT or COMEDI_OUTPUT. */
672
673         /* if output clear the bit, otherwise set it */
674         if (data[0] == COMEDI_OUTPUT)
675                 devpriv->dio_config &= ~chanbit;
676         else
677                 devpriv->dio_config |= chanbit;
678         /* get access to the DIO regs */
679         outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
680         /* set the DIO's to the new configuration setting */
681         outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF);
682
683         return 1;
684 }
685
686 static int dmm32at_attach(struct comedi_device *dev,
687                           struct comedi_devconfig *it)
688 {
689         struct dmm32at_private *devpriv;
690         int ret;
691         struct comedi_subdevice *s;
692         unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
693         unsigned int irq;
694
695         irq = it->options[1];
696
697         ret = comedi_request_region(dev, it->options[0], DMM32AT_MEMSIZE);
698         if (ret)
699                 return ret;
700
701         /* the following just makes sure the board is there and gets
702            it to a known state */
703
704         /* reset the board */
705         outb(DMM32AT_RESET, dev->iobase + DMM32AT_CNTRL);
706
707         /* allow a millisecond to reset */
708         udelay(1000);
709
710         /* zero scan and fifo control */
711         outb(0x0, dev->iobase + DMM32AT_FIFOCNTRL);
712
713         /* zero interrupt and clock control */
714         outb(0x0, dev->iobase + DMM32AT_INTCLOCK);
715
716         /* write a test channel range, the high 3 bits should drop */
717         outb(0x80, dev->iobase + DMM32AT_AILOW);
718         outb(0xff, dev->iobase + DMM32AT_AIHIGH);
719
720         /* set the range at 10v unipolar */
721         outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AICONF);
722
723         /* should take 10 us to settle, here's a hundred */
724         udelay(100);
725
726         /* read back the values */
727         ailo = inb(dev->iobase + DMM32AT_AILOW);
728         aihi = inb(dev->iobase + DMM32AT_AIHIGH);
729         fifostat = inb(dev->iobase + DMM32AT_FIFOSTAT);
730         aistat = inb(dev->iobase + DMM32AT_AISTAT);
731         intstat = inb(dev->iobase + DMM32AT_INTCLOCK);
732         airback = inb(dev->iobase + DMM32AT_AIRBACK);
733
734         printk(KERN_DEBUG "dmm32at: lo=0x%02x hi=0x%02x fifostat=0x%02x\n",
735                ailo, aihi, fifostat);
736         printk(KERN_DEBUG
737                "dmm32at: aistat=0x%02x intstat=0x%02x airback=0x%02x\n",
738                aistat, intstat, airback);
739
740         if ((ailo != 0x00) || (aihi != 0x1f) || (fifostat != 0x80) ||
741             (aistat != 0x60 || (intstat != 0x00) || airback != 0x0c)) {
742                 printk(KERN_ERR "dmmat32: board detection failed\n");
743                 return -EIO;
744         }
745
746         /* board is there, register interrupt */
747         if (irq) {
748                 ret = request_irq(irq, dmm32at_isr, 0, dev->board_name, dev);
749                 if (ret < 0) {
750                         printk(KERN_ERR "dmm32at: irq conflict\n");
751                         return ret;
752                 }
753                 dev->irq = irq;
754         }
755
756         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
757         if (!devpriv)
758                 return -ENOMEM;
759         dev->private = devpriv;
760
761         ret = comedi_alloc_subdevices(dev, 3);
762         if (ret)
763                 return ret;
764
765         s = &dev->subdevices[0];
766         dev->read_subdev = s;
767         /* analog input subdevice */
768         s->type = COMEDI_SUBD_AI;
769         /* we support single-ended (ground) and differential */
770         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
771         s->n_chan = 32;
772         s->maxdata = 0xffff;
773         s->range_table = &dmm32at_airanges;
774         s->len_chanlist = 32;   /* This is the maximum chanlist length that
775                                    the board can handle */
776         s->insn_read = dmm32at_ai_rinsn;
777         s->do_cmd = dmm32at_ai_cmd;
778         s->do_cmdtest = dmm32at_ai_cmdtest;
779         s->cancel = dmm32at_ai_cancel;
780
781         s = &dev->subdevices[1];
782         /* analog output subdevice */
783         s->type = COMEDI_SUBD_AO;
784         s->subdev_flags = SDF_WRITABLE;
785         s->n_chan = 4;
786         s->maxdata = 0x0fff;
787         s->range_table = &dmm32at_aoranges;
788         s->insn_write = dmm32at_ao_winsn;
789         s->insn_read = dmm32at_ao_rinsn;
790
791         s = &dev->subdevices[2];
792         /* digital i/o subdevice */
793
794         /* get access to the DIO regs */
795         outb(DMM32AT_DIOACC, dev->iobase + DMM32AT_CNTRL);
796         /* set the DIO's to the defualt input setting */
797         devpriv->dio_config = DMM32AT_DIRA | DMM32AT_DIRB |
798                 DMM32AT_DIRCL | DMM32AT_DIRCH | DMM32AT_DIENABLE;
799         outb(devpriv->dio_config, dev->iobase + DMM32AT_DIOCONF);
800
801         /* set up the subdevice */
802         s->type = COMEDI_SUBD_DIO;
803         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
804         s->n_chan = 24;
805         s->maxdata = 1;
806         s->state = 0;
807         s->range_table = &range_digital;
808         s->insn_bits = dmm32at_dio_insn_bits;
809         s->insn_config = dmm32at_dio_insn_config;
810
811         /* success */
812         printk(KERN_INFO "comedi%d: dmm32at: attached\n", dev->minor);
813
814         return 1;
815
816 }
817
818 static struct comedi_driver dmm32at_driver = {
819         .driver_name    = "dmm32at",
820         .module         = THIS_MODULE,
821         .attach         = dmm32at_attach,
822         .detach         = comedi_legacy_detach,
823 };
824 module_comedi_driver(dmm32at_driver);
825
826 MODULE_AUTHOR("Comedi http://www.comedi.org");
827 MODULE_DESCRIPTION("Comedi low-level driver");
828 MODULE_LICENSE("GPL");