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