]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/dmm32at.c
Merge branch 'for-4.8/core' of git://git.kernel.dk/linux-block
[karo-tx-linux.git] / drivers / staging / comedi / drivers / dmm32at.c
1 /*
2  * dmm32at.c
3  * Diamond Systems Diamond-MM-32-AT 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 /*
20  * Driver: dmm32at
21  * Description: Diamond Systems Diamond-MM-32-AT
22  * Devices: [Diamond Systems] Diamond-MM-32-AT (dmm32at)
23  * Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
24  * Updated: Fri Jun  4 09:13:24 CDT 2004
25  * Status: experimental
26  *
27  * Configuration Options:
28  *      comedi_config /dev/comedi0 dmm32at baseaddr,irq
29  *
30  * This driver is for the Diamond Systems MM-32-AT board
31  *      http://www.diamondsystems.com/products/diamondmm32at
32  *
33  * It is being used on several projects inside NASA, without
34  * problems so far. For analog input commands, TRIG_EXT is not
35  * yet supported.
36  */
37
38 #include <linux/module.h>
39 #include <linux/delay.h>
40 #include <linux/interrupt.h>
41 #include "../comedidev.h"
42
43 #include "8255.h"
44
45 /* Board register addresses */
46 #define DMM32AT_AI_START_CONV_REG       0x00
47 #define DMM32AT_AI_LSB_REG              0x00
48 #define DMM32AT_AUX_DOUT_REG            0x01
49 #define DMM32AT_AUX_DOUT2               BIT(2)  /* J3.42 - OUT2 (OUT2EN) */
50 #define DMM32AT_AUX_DOUT1               BIT(1)  /* J3.43 */
51 #define DMM32AT_AUX_DOUT0               BIT(0)  /* J3.44 - OUT0 (OUT0EN) */
52 #define DMM32AT_AI_MSB_REG              0x01
53 #define DMM32AT_AI_LO_CHAN_REG          0x02
54 #define DMM32AT_AI_HI_CHAN_REG          0x03
55 #define DMM32AT_AUX_DI_REG              0x04
56 #define DMM32AT_AUX_DI_DACBUSY          BIT(7)
57 #define DMM32AT_AUX_DI_CALBUSY          BIT(6)
58 #define DMM32AT_AUX_DI3                 BIT(3)  /* J3.45 - ADCLK (CLKSEL) */
59 #define DMM32AT_AUX_DI2                 BIT(2)  /* J3.46 - GATE12 (GT12EN) */
60 #define DMM32AT_AUX_DI1                 BIT(1)  /* J3.47 - GATE0 (GT0EN) */
61 #define DMM32AT_AUX_DI0                 BIT(0)  /* J3.48 - CLK0 (SRC0) */
62 #define DMM32AT_AO_LSB_REG              0x04
63 #define DMM32AT_AO_MSB_REG              0x05
64 #define DMM32AT_AO_MSB_DACH(x)          ((x) << 6)
65 #define DMM32AT_FIFO_DEPTH_REG          0x06
66 #define DMM32AT_FIFO_CTRL_REG           0x07
67 #define DMM32AT_FIFO_CTRL_FIFOEN        BIT(3)
68 #define DMM32AT_FIFO_CTRL_SCANEN        BIT(2)
69 #define DMM32AT_FIFO_CTRL_FIFORST       BIT(1)
70 #define DMM32AT_FIFO_STATUS_REG         0x07
71 #define DMM32AT_FIFO_STATUS_EF          BIT(7)
72 #define DMM32AT_FIFO_STATUS_HF          BIT(6)
73 #define DMM32AT_FIFO_STATUS_FF          BIT(5)
74 #define DMM32AT_FIFO_STATUS_OVF         BIT(4)
75 #define DMM32AT_FIFO_STATUS_FIFOEN      BIT(3)
76 #define DMM32AT_FIFO_STATUS_SCANEN      BIT(2)
77 #define DMM32AT_FIFO_STATUS_PAGE_MASK   (3 << 0)
78 #define DMM32AT_CTRL_REG                0x08
79 #define DMM32AT_CTRL_RESETA             BIT(5)
80 #define DMM32AT_CTRL_RESETD             BIT(4)
81 #define DMM32AT_CTRL_INTRST             BIT(3)
82 #define DMM32AT_CTRL_PAGE(x)            ((x) << 0)
83 #define DMM32AT_CTRL_PAGE_8254          DMM32AT_CTRL_PAGE(0)
84 #define DMM32AT_CTRL_PAGE_8255          DMM32AT_CTRL_PAGE(1)
85 #define DMM32AT_CTRL_PAGE_CALIB         DMM32AT_CTRL_PAGE(3)
86 #define DMM32AT_AI_STATUS_REG           0x08
87 #define DMM32AT_AI_STATUS_STS           BIT(7)
88 #define DMM32AT_AI_STATUS_SD1           BIT(6)
89 #define DMM32AT_AI_STATUS_SD0           BIT(5)
90 #define DMM32AT_AI_STATUS_ADCH_MASK     (0x1f << 0)
91 #define DMM32AT_INTCLK_REG              0x09
92 #define DMM32AT_INTCLK_ADINT            BIT(7)
93 #define DMM32AT_INTCLK_DINT             BIT(6)
94 #define DMM32AT_INTCLK_TINT             BIT(5)
95 #define DMM32AT_INTCLK_CLKEN            BIT(1)  /* 1=see below  0=software */
96 #define DMM32AT_INTCLK_CLKSEL           BIT(0)  /* 1=OUT2  0=EXTCLK */
97 #define DMM32AT_CTRDIO_CFG_REG          0x0a
98 #define DMM32AT_CTRDIO_CFG_FREQ12       BIT(7)  /* CLK12 1=100KHz 0=10MHz */
99 #define DMM32AT_CTRDIO_CFG_FREQ0        BIT(6)  /* CLK0  1=10KHz  0=10MHz */
100 #define DMM32AT_CTRDIO_CFG_OUT2EN       BIT(5)  /* J3.42 1=OUT2 is DOUT2 */
101 #define DMM32AT_CTRDIO_CFG_OUT0EN       BIT(4)  /* J3,44 1=OUT0 is DOUT0 */
102 #define DMM32AT_CTRDIO_CFG_GT0EN        BIT(2)  /* J3.47 1=DIN1 is GATE0 */
103 #define DMM32AT_CTRDIO_CFG_SRC0         BIT(1)  /* CLK0 is 0=FREQ0 1=J3.48 */
104 #define DMM32AT_CTRDIO_CFG_GT12EN       BIT(0)  /* J3.46 1=DIN2 is GATE12 */
105 #define DMM32AT_AI_CFG_REG              0x0b
106 #define DMM32AT_AI_CFG_SCINT(x)         ((x) << 4)
107 #define DMM32AT_AI_CFG_SCINT_20US       DMM32AT_AI_CFG_SCINT(0)
108 #define DMM32AT_AI_CFG_SCINT_15US       DMM32AT_AI_CFG_SCINT(1)
109 #define DMM32AT_AI_CFG_SCINT_10US       DMM32AT_AI_CFG_SCINT(2)
110 #define DMM32AT_AI_CFG_SCINT_5US        DMM32AT_AI_CFG_SCINT(3)
111 #define DMM32AT_AI_CFG_RANGE            BIT(3)  /* 0=5V  1=10V */
112 #define DMM32AT_AI_CFG_ADBU             BIT(2)  /* 0=bipolar  1=unipolar */
113 #define DMM32AT_AI_CFG_GAIN(x)          ((x) << 0)
114 #define DMM32AT_AI_READBACK_REG         0x0b
115 #define DMM32AT_AI_READBACK_WAIT        BIT(7)  /* DMM32AT_AI_STATUS_STS */
116 #define DMM32AT_AI_READBACK_RANGE       BIT(3)
117 #define DMM32AT_AI_READBACK_ADBU        BIT(2)
118 #define DMM32AT_AI_READBACK_GAIN_MASK   (3 << 0)
119
120 #define DMM32AT_CLK1 0x0d
121 #define DMM32AT_CLK2 0x0e
122 #define DMM32AT_CLKCT 0x0f
123
124 #define DMM32AT_8255_IOBASE             0x0c  /* Page 1 registers */
125
126 /* Board register values. */
127
128 /* DMM32AT_AI_CFG_REG 0x0b */
129 #define DMM32AT_RANGE_U10 0x0c
130 #define DMM32AT_RANGE_U5 0x0d
131 #define DMM32AT_RANGE_B10 0x08
132 #define DMM32AT_RANGE_B5 0x00
133
134 /* DMM32AT_CLKCT 0x0f */
135 #define DMM32AT_CLKCT1 0x56     /* mode3 counter 1 - write low byte only */
136 #define DMM32AT_CLKCT2 0xb6     /*  mode3 counter 2 - write high and low byte */
137
138 /* board AI ranges in comedi structure */
139 static const struct comedi_lrange dmm32at_airanges = {
140         4, {
141                 UNI_RANGE(10),
142                 UNI_RANGE(5),
143                 BIP_RANGE(10),
144                 BIP_RANGE(5)
145         }
146 };
147
148 /* register values for above ranges */
149 static const unsigned char dmm32at_rangebits[] = {
150         DMM32AT_RANGE_U10,
151         DMM32AT_RANGE_U5,
152         DMM32AT_RANGE_B10,
153         DMM32AT_RANGE_B5,
154 };
155
156 /* only one of these ranges is valid, as set by a jumper on the
157  * board. The application should only use the range set by the jumper
158  */
159 static const struct comedi_lrange dmm32at_aoranges = {
160         4, {
161                 UNI_RANGE(10),
162                 UNI_RANGE(5),
163                 BIP_RANGE(10),
164                 BIP_RANGE(5)
165         }
166 };
167
168 static void dmm32at_ai_set_chanspec(struct comedi_device *dev,
169                                     struct comedi_subdevice *s,
170                                     unsigned int chanspec, int nchan)
171 {
172         unsigned int chan = CR_CHAN(chanspec);
173         unsigned int range = CR_RANGE(chanspec);
174         unsigned int last_chan = (chan + nchan - 1) % s->n_chan;
175
176         outb(DMM32AT_FIFO_CTRL_FIFORST, dev->iobase + DMM32AT_FIFO_CTRL_REG);
177
178         if (nchan > 1)
179                 outb(DMM32AT_FIFO_CTRL_SCANEN,
180                      dev->iobase + DMM32AT_FIFO_CTRL_REG);
181
182         outb(chan, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
183         outb(last_chan, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
184         outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AI_CFG_REG);
185 }
186
187 static unsigned int dmm32at_ai_get_sample(struct comedi_device *dev,
188                                           struct comedi_subdevice *s)
189 {
190         unsigned int val;
191
192         val = inb(dev->iobase + DMM32AT_AI_LSB_REG);
193         val |= (inb(dev->iobase + DMM32AT_AI_MSB_REG) << 8);
194
195         /* munge two's complement value to offset binary */
196         return comedi_offset_munge(s, val);
197 }
198
199 static int dmm32at_ai_status(struct comedi_device *dev,
200                              struct comedi_subdevice *s,
201                              struct comedi_insn *insn,
202                              unsigned long context)
203 {
204         unsigned char status;
205
206         status = inb(dev->iobase + context);
207         if ((status & DMM32AT_AI_STATUS_STS) == 0)
208                 return 0;
209         return -EBUSY;
210 }
211
212 static int dmm32at_ai_insn_read(struct comedi_device *dev,
213                                 struct comedi_subdevice *s,
214                                 struct comedi_insn *insn,
215                                 unsigned int *data)
216 {
217         int ret;
218         int i;
219
220         dmm32at_ai_set_chanspec(dev, s, insn->chanspec, 1);
221
222         /* wait for circuit to settle */
223         ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
224                              DMM32AT_AI_READBACK_REG);
225         if (ret)
226                 return ret;
227
228         for (i = 0; i < insn->n; i++) {
229                 outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
230
231                 ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
232                                      DMM32AT_AI_STATUS_REG);
233                 if (ret)
234                         return ret;
235
236                 data[i] = dmm32at_ai_get_sample(dev, s);
237         }
238
239         return insn->n;
240 }
241
242 static int dmm32at_ai_check_chanlist(struct comedi_device *dev,
243                                      struct comedi_subdevice *s,
244                                      struct comedi_cmd *cmd)
245 {
246         unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
247         unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
248         int i;
249
250         for (i = 1; i < cmd->chanlist_len; i++) {
251                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
252                 unsigned int range = CR_RANGE(cmd->chanlist[i]);
253
254                 if (chan != (chan0 + i) % s->n_chan) {
255                         dev_dbg(dev->class_dev,
256                                 "entries in chanlist must be consecutive channels, counting upwards\n");
257                         return -EINVAL;
258                 }
259                 if (range != range0) {
260                         dev_dbg(dev->class_dev,
261                                 "entries in chanlist must all have the same gain\n");
262                         return -EINVAL;
263                 }
264         }
265
266         return 0;
267 }
268
269 static int dmm32at_ai_cmdtest(struct comedi_device *dev,
270                               struct comedi_subdevice *s,
271                               struct comedi_cmd *cmd)
272 {
273         int err = 0;
274         unsigned int arg;
275
276         /* Step 1 : check if triggers are trivially valid */
277
278         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
279         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
280         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
281         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
282         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
283
284         if (err)
285                 return 1;
286
287         /* Step 2a : make sure trigger sources are unique */
288
289         err |= comedi_check_trigger_is_unique(cmd->stop_src);
290
291         /* Step 2b : and mutually compatible */
292
293         if (err)
294                 return 2;
295
296         /* Step 3: check if arguments are trivially valid */
297
298         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
299
300         err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 1000000);
301         err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
302
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
312         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
313                                            cmd->chanlist_len);
314
315         if (cmd->stop_src == TRIG_COUNT)
316                 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
317         else /* TRIG_NONE */
318                 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
319
320         if (err)
321                 return 3;
322
323         /* Step 4: fix up any arguments */
324
325         arg = cmd->convert_arg * cmd->scan_end_arg;
326         err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
327
328         if (err)
329                 return 4;
330
331         /* Step 5: check channel list if it exists */
332         if (cmd->chanlist && cmd->chanlist_len > 0)
333                 err |= dmm32at_ai_check_chanlist(dev, s, cmd);
334
335         if (err)
336                 return 5;
337
338         return 0;
339 }
340
341 static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
342 {
343         unsigned char lo1, lo2, hi2;
344         unsigned short both2;
345
346         /* based on 10mhz clock */
347         lo1 = 200;
348         both2 = nansec / 20000;
349         hi2 = (both2 & 0xff00) >> 8;
350         lo2 = both2 & 0x00ff;
351
352         /* set counter clocks to 10MHz, disable all aux dio */
353         outb(0, dev->iobase + DMM32AT_CTRDIO_CFG_REG);
354
355         /* get access to the clock regs */
356         outb(DMM32AT_CTRL_PAGE_8254, dev->iobase + DMM32AT_CTRL_REG);
357
358         /* write the counter 1 control word and low byte to counter */
359         outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
360         outb(lo1, dev->iobase + DMM32AT_CLK1);
361
362         /* write the counter 2 control word and low byte then to counter */
363         outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
364         outb(lo2, dev->iobase + DMM32AT_CLK2);
365         outb(hi2, dev->iobase + DMM32AT_CLK2);
366
367         /* enable the ai conversion interrupt and the clock to start scans */
368         outb(DMM32AT_INTCLK_ADINT |
369              DMM32AT_INTCLK_CLKEN | DMM32AT_INTCLK_CLKSEL,
370              dev->iobase + DMM32AT_INTCLK_REG);
371 }
372
373 static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
374 {
375         struct comedi_cmd *cmd = &s->async->cmd;
376         int ret;
377
378         dmm32at_ai_set_chanspec(dev, s, cmd->chanlist[0], cmd->chanlist_len);
379
380         /* reset the interrupt just in case */
381         outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
382
383         /*
384          * wait for circuit to settle
385          * we don't have the 'insn' here but it's not needed
386          */
387         ret = comedi_timeout(dev, s, NULL, dmm32at_ai_status,
388                              DMM32AT_AI_READBACK_REG);
389         if (ret)
390                 return ret;
391
392         if (cmd->stop_src == TRIG_NONE || cmd->stop_arg > 1) {
393                 /* start the clock and enable the interrupts */
394                 dmm32at_setaitimer(dev, cmd->scan_begin_arg);
395         } else {
396                 /* start the interrupts and initiate a single scan */
397                 outb(DMM32AT_INTCLK_ADINT, dev->iobase + DMM32AT_INTCLK_REG);
398                 outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
399         }
400
401         return 0;
402 }
403
404 static int dmm32at_ai_cancel(struct comedi_device *dev,
405                              struct comedi_subdevice *s)
406 {
407         /* disable further interrupts and clocks */
408         outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
409         return 0;
410 }
411
412 static irqreturn_t dmm32at_isr(int irq, void *d)
413 {
414         struct comedi_device *dev = d;
415         unsigned char intstat;
416         unsigned int val;
417         int i;
418
419         if (!dev->attached) {
420                 dev_err(dev->class_dev, "spurious interrupt\n");
421                 return IRQ_HANDLED;
422         }
423
424         intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
425
426         if (intstat & DMM32AT_INTCLK_ADINT) {
427                 struct comedi_subdevice *s = dev->read_subdev;
428                 struct comedi_cmd *cmd = &s->async->cmd;
429
430                 for (i = 0; i < cmd->chanlist_len; i++) {
431                         val = dmm32at_ai_get_sample(dev, s);
432                         comedi_buf_write_samples(s, &val, 1);
433                 }
434
435                 if (cmd->stop_src == TRIG_COUNT &&
436                     s->async->scans_done >= cmd->stop_arg)
437                         s->async->events |= COMEDI_CB_EOA;
438
439                 comedi_handle_events(dev, s);
440         }
441
442         /* reset the interrupt */
443         outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
444         return IRQ_HANDLED;
445 }
446
447 static int dmm32at_ao_eoc(struct comedi_device *dev,
448                           struct comedi_subdevice *s,
449                           struct comedi_insn *insn,
450                           unsigned long context)
451 {
452         unsigned char status;
453
454         status = inb(dev->iobase + DMM32AT_AUX_DI_REG);
455         if ((status & DMM32AT_AUX_DI_DACBUSY) == 0)
456                 return 0;
457         return -EBUSY;
458 }
459
460 static int dmm32at_ao_insn_write(struct comedi_device *dev,
461                                  struct comedi_subdevice *s,
462                                  struct comedi_insn *insn,
463                                  unsigned int *data)
464 {
465         unsigned int chan = CR_CHAN(insn->chanspec);
466         int i;
467
468         for (i = 0; i < insn->n; i++) {
469                 unsigned int val = data[i];
470                 int ret;
471
472                 /* write LSB then MSB + chan to load DAC */
473                 outb(val & 0xff, dev->iobase + DMM32AT_AO_LSB_REG);
474                 outb((val >> 8) | DMM32AT_AO_MSB_DACH(chan),
475                      dev->iobase + DMM32AT_AO_MSB_REG);
476
477                 /* wait for circuit to settle */
478                 ret = comedi_timeout(dev, s, insn, dmm32at_ao_eoc, 0);
479                 if (ret)
480                         return ret;
481
482                 /* dummy read to update DAC */
483                 inb(dev->iobase + DMM32AT_AO_MSB_REG);
484
485                 s->readback[chan] = val;
486         }
487
488         return insn->n;
489 }
490
491 static int dmm32at_8255_io(struct comedi_device *dev,
492                            int dir, int port, int data, unsigned long regbase)
493 {
494         /* get access to the DIO regs */
495         outb(DMM32AT_CTRL_PAGE_8255, dev->iobase + DMM32AT_CTRL_REG);
496
497         if (dir) {
498                 outb(data, dev->iobase + regbase + port);
499                 return 0;
500         }
501         return inb(dev->iobase + regbase + port);
502 }
503
504 /* Make sure the board is there and put it to a known state */
505 static int dmm32at_reset(struct comedi_device *dev)
506 {
507         unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
508
509         /* reset the board */
510         outb(DMM32AT_CTRL_RESETA, dev->iobase + DMM32AT_CTRL_REG);
511
512         /* allow a millisecond to reset */
513         udelay(1000);
514
515         /* zero scan and fifo control */
516         outb(0x0, dev->iobase + DMM32AT_FIFO_CTRL_REG);
517
518         /* zero interrupt and clock control */
519         outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
520
521         /* write a test channel range, the high 3 bits should drop */
522         outb(0x80, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
523         outb(0xff, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
524
525         /* set the range at 10v unipolar */
526         outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AI_CFG_REG);
527
528         /* should take 10 us to settle, here's a hundred */
529         udelay(100);
530
531         /* read back the values */
532         ailo = inb(dev->iobase + DMM32AT_AI_LO_CHAN_REG);
533         aihi = inb(dev->iobase + DMM32AT_AI_HI_CHAN_REG);
534         fifostat = inb(dev->iobase + DMM32AT_FIFO_STATUS_REG);
535         aistat = inb(dev->iobase + DMM32AT_AI_STATUS_REG);
536         intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
537         airback = inb(dev->iobase + DMM32AT_AI_READBACK_REG);
538
539         /*
540          * NOTE: The (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0)
541          * test makes this driver only work if the board is configured
542          * with all A/D channels set for single-ended operation.
543          */
544         if (ailo != 0x00 || aihi != 0x1f ||
545             fifostat != DMM32AT_FIFO_STATUS_EF ||
546             aistat != (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0) ||
547             intstat != 0x00 || airback != 0x0c)
548                 return -EIO;
549
550         return 0;
551 }
552
553 static int dmm32at_attach(struct comedi_device *dev,
554                           struct comedi_devconfig *it)
555 {
556         struct comedi_subdevice *s;
557         int ret;
558
559         ret = comedi_request_region(dev, it->options[0], 0x10);
560         if (ret)
561                 return ret;
562
563         ret = dmm32at_reset(dev);
564         if (ret) {
565                 dev_err(dev->class_dev, "board detection failed\n");
566                 return ret;
567         }
568
569         if (it->options[1]) {
570                 ret = request_irq(it->options[1], dmm32at_isr, 0,
571                                   dev->board_name, dev);
572                 if (ret == 0)
573                         dev->irq = it->options[1];
574         }
575
576         ret = comedi_alloc_subdevices(dev, 3);
577         if (ret)
578                 return ret;
579
580         /* Analog Input subdevice */
581         s = &dev->subdevices[0];
582         s->type         = COMEDI_SUBD_AI;
583         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
584         s->n_chan       = 32;
585         s->maxdata      = 0xffff;
586         s->range_table  = &dmm32at_airanges;
587         s->insn_read    = dmm32at_ai_insn_read;
588         if (dev->irq) {
589                 dev->read_subdev = s;
590                 s->subdev_flags |= SDF_CMD_READ;
591                 s->len_chanlist = s->n_chan;
592                 s->do_cmd       = dmm32at_ai_cmd;
593                 s->do_cmdtest   = dmm32at_ai_cmdtest;
594                 s->cancel       = dmm32at_ai_cancel;
595         }
596
597         /* Analog Output subdevice */
598         s = &dev->subdevices[1];
599         s->type         = COMEDI_SUBD_AO;
600         s->subdev_flags = SDF_WRITABLE;
601         s->n_chan       = 4;
602         s->maxdata      = 0x0fff;
603         s->range_table  = &dmm32at_aoranges;
604         s->insn_write   = dmm32at_ao_insn_write;
605
606         ret = comedi_alloc_subdev_readback(s);
607         if (ret)
608                 return ret;
609
610         /* Digital I/O subdevice */
611         s = &dev->subdevices[2];
612         return subdev_8255_init(dev, s, dmm32at_8255_io, DMM32AT_8255_IOBASE);
613 }
614
615 static struct comedi_driver dmm32at_driver = {
616         .driver_name    = "dmm32at",
617         .module         = THIS_MODULE,
618         .attach         = dmm32at_attach,
619         .detach         = comedi_legacy_detach,
620 };
621 module_comedi_driver(dmm32at_driver);
622
623 MODULE_AUTHOR("Comedi http://www.comedi.org");
624 MODULE_DESCRIPTION("Comedi: Diamond Systems Diamond-MM-32-AT");
625 MODULE_LICENSE("GPL");