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