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