1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <linux/module.h>
34#include <linux/interrupt.h>
35#include "../comedidev.h"
36
37#include "8255.h"
38
39
40#define COM_REG_1 0x00
41#define STAT_REG 0x00
42#define COM_REG_2 0x02
43
44#define START_CONVERT_REG 0x08
45#define START_DAQ_REG 0x0A
46#define AD_CLEAR_REG 0x0C
47#define EXT_STROBE_REG 0x0E
48
49#define DAC0_REG 0x10
50#define DAC1_REG 0x12
51#define INT2CLR_REG 0x14
52
53#define MUX_CNTR_REG 0x04
54#define MUX_GAIN_REG 0x06
55#define AD_FIFO_REG 0x16
56#define DMA_TC_INT_CLR_REG 0x16
57
58#define AM9513A_DATA_REG 0x18
59#define AM9513A_COM_REG 0x1A
60#define AM9513A_STAT_REG 0x1A
61
62#define MIO_16_DIG_IN_REG 0x1C
63#define MIO_16_DIG_OUT_REG 0x1C
64
65#define RTSI_SW_SHIFT_REG 0x1E
66#define RTSI_SW_STROBE_REG 0x1F
67
68#define DIO_24_PORTA_REG 0x00
69#define DIO_24_PORTB_REG 0x01
70#define DIO_24_PORTC_REG 0x02
71#define DIO_24_CNFG_REG 0x03
72
73
74#define COMREG1_2SCADC 0x0001
75#define COMREG1_1632CNT 0x0002
76#define COMREG1_SCANEN 0x0008
77#define COMREG1_DAQEN 0x0010
78#define COMREG1_DMAEN 0x0020
79#define COMREG1_CONVINTEN 0x0080
80#define COMREG2_SCN2 0x0010
81#define COMREG2_INTEN 0x0080
82#define COMREG2_DOUTEN0 0x0100
83#define COMREG2_DOUTEN1 0x0200
84
85#define STAT_AD_OVERRUN 0x0100
86#define STAT_AD_OVERFLOW 0x0200
87#define STAT_AD_DAQPROG 0x0800
88#define STAT_AD_CONVAVAIL 0x2000
89#define STAT_AD_DAQSTOPINT 0x4000
90
91#define CLOCK_1_MHZ 0x8B25
92#define CLOCK_100_KHZ 0x8C25
93#define CLOCK_10_KHZ 0x8D25
94#define CLOCK_1_KHZ 0x8E25
95#define CLOCK_100_HZ 0x8F25
96
97struct atmio16_board_t {
98 const char *name;
99 int has_8255;
100};
101
102
103static const struct comedi_lrange range_atmio16d_ai_10_bipolar = {
104 4, {
105 BIP_RANGE(10),
106 BIP_RANGE(1),
107 BIP_RANGE(0.1),
108 BIP_RANGE(0.02)
109 }
110};
111
112static const struct comedi_lrange range_atmio16d_ai_5_bipolar = {
113 4, {
114 BIP_RANGE(5),
115 BIP_RANGE(0.5),
116 BIP_RANGE(0.05),
117 BIP_RANGE(0.01)
118 }
119};
120
121static const struct comedi_lrange range_atmio16d_ai_unipolar = {
122 4, {
123 UNI_RANGE(10),
124 UNI_RANGE(1),
125 UNI_RANGE(0.1),
126 UNI_RANGE(0.02)
127 }
128};
129
130
131struct atmio16d_private {
132 enum { adc_diff, adc_singleended } adc_mux;
133 enum { adc_bipolar10, adc_bipolar5, adc_unipolar10 } adc_range;
134 enum { adc_2comp, adc_straight } adc_coding;
135 enum { dac_bipolar, dac_unipolar } dac0_range, dac1_range;
136 enum { dac_internal, dac_external } dac0_reference, dac1_reference;
137 enum { dac_2comp, dac_straight } dac0_coding, dac1_coding;
138 const struct comedi_lrange *ao_range_type_list[2];
139 unsigned int com_reg_1_state;
140 unsigned int com_reg_2_state;
141};
142
143static void reset_counters(struct comedi_device *dev)
144{
145
146 outw(0xFFC2, dev->iobase + AM9513A_COM_REG);
147 outw(0xFF02, dev->iobase + AM9513A_COM_REG);
148 outw(0x4, dev->iobase + AM9513A_DATA_REG);
149 outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
150 outw(0x3, dev->iobase + AM9513A_DATA_REG);
151 outw(0xFF42, dev->iobase + AM9513A_COM_REG);
152 outw(0xFF42, dev->iobase + AM9513A_COM_REG);
153
154 outw(0xFFC4, dev->iobase + AM9513A_COM_REG);
155 outw(0xFF03, dev->iobase + AM9513A_COM_REG);
156 outw(0x4, dev->iobase + AM9513A_DATA_REG);
157 outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
158 outw(0x3, dev->iobase + AM9513A_DATA_REG);
159 outw(0xFF44, dev->iobase + AM9513A_COM_REG);
160 outw(0xFF44, dev->iobase + AM9513A_COM_REG);
161
162 outw(0xFFC8, dev->iobase + AM9513A_COM_REG);
163 outw(0xFF04, dev->iobase + AM9513A_COM_REG);
164 outw(0x4, dev->iobase + AM9513A_DATA_REG);
165 outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
166 outw(0x3, dev->iobase + AM9513A_DATA_REG);
167 outw(0xFF48, dev->iobase + AM9513A_COM_REG);
168 outw(0xFF48, dev->iobase + AM9513A_COM_REG);
169
170 outw(0xFFD0, dev->iobase + AM9513A_COM_REG);
171 outw(0xFF05, dev->iobase + AM9513A_COM_REG);
172 outw(0x4, dev->iobase + AM9513A_DATA_REG);
173 outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
174 outw(0x3, dev->iobase + AM9513A_DATA_REG);
175 outw(0xFF50, dev->iobase + AM9513A_COM_REG);
176 outw(0xFF50, dev->iobase + AM9513A_COM_REG);
177
178 outw(0, dev->iobase + AD_CLEAR_REG);
179}
180
181static void reset_atmio16d(struct comedi_device *dev)
182{
183 struct atmio16d_private *devpriv = dev->private;
184 int i;
185
186
187 outw(0, dev->iobase + COM_REG_1);
188 outw(0, dev->iobase + COM_REG_2);
189 outw(0, dev->iobase + MUX_GAIN_REG);
190
191 outw(0xFFFF, dev->iobase + AM9513A_COM_REG);
192 outw(0xFFEF, dev->iobase + AM9513A_COM_REG);
193 outw(0xFF17, dev->iobase + AM9513A_COM_REG);
194 outw(0xF000, dev->iobase + AM9513A_DATA_REG);
195 for (i = 1; i <= 5; ++i) {
196 outw(0xFF00 + i, dev->iobase + AM9513A_COM_REG);
197 outw(0x0004, dev->iobase + AM9513A_DATA_REG);
198 outw(0xFF08 + i, dev->iobase + AM9513A_COM_REG);
199 outw(0x3, dev->iobase + AM9513A_DATA_REG);
200 }
201 outw(0xFF5F, dev->iobase + AM9513A_COM_REG);
202
203 outw(0, dev->iobase + AD_CLEAR_REG);
204 outw(0, dev->iobase + INT2CLR_REG);
205
206 devpriv->com_reg_1_state |= 1;
207 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
208 devpriv->adc_coding = adc_straight;
209
210 outw(2048, dev->iobase + DAC0_REG);
211 outw(2048, dev->iobase + DAC1_REG);
212}
213
214static irqreturn_t atmio16d_interrupt(int irq, void *d)
215{
216 struct comedi_device *dev = d;
217 struct comedi_subdevice *s = dev->read_subdev;
218 unsigned short val;
219
220 val = inw(dev->iobase + AD_FIFO_REG);
221 comedi_buf_write_samples(s, &val, 1);
222 comedi_handle_events(dev, s);
223
224 return IRQ_HANDLED;
225}
226
227static int atmio16d_ai_cmdtest(struct comedi_device *dev,
228 struct comedi_subdevice *s,
229 struct comedi_cmd *cmd)
230{
231 int err = 0;
232
233
234
235 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
236 err |= comedi_check_trigger_src(&cmd->scan_begin_src,
237 TRIG_FOLLOW | TRIG_TIMER);
238 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
239 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
240 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
241
242 if (err)
243 return 1;
244
245
246
247 err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
248 err |= comedi_check_trigger_is_unique(cmd->stop_src);
249
250
251
252 if (err)
253 return 2;
254
255
256
257 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
258
259 if (cmd->scan_begin_src == TRIG_FOLLOW) {
260
261 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
262 } else {
263#if 0
264
265
266 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
267#endif
268 }
269
270 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
271#if 0
272 err |= comedi_check_trigger_arg_max(&cmd->convert_arg, SLOWEST_TIMER);
273#endif
274
275 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
276 cmd->chanlist_len);
277
278 if (cmd->stop_src == TRIG_COUNT)
279 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
280 else
281 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
282
283 if (err)
284 return 3;
285
286 return 0;
287}
288
289static int atmio16d_ai_cmd(struct comedi_device *dev,
290 struct comedi_subdevice *s)
291{
292 struct atmio16d_private *devpriv = dev->private;
293 struct comedi_cmd *cmd = &s->async->cmd;
294 unsigned int timer, base_clock;
295 unsigned int sample_count, tmp, chan, gain;
296 int i;
297
298
299
300
301 reset_counters(dev);
302
303
304 if (cmd->chanlist_len < 2) {
305 devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
306 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
307 } else {
308 devpriv->com_reg_1_state |= COMREG1_SCANEN;
309 devpriv->com_reg_2_state |= COMREG2_SCN2;
310 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
311 outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
312 }
313
314
315 for (i = 0; i < cmd->chanlist_len; ++i) {
316 chan = CR_CHAN(cmd->chanlist[i]);
317 gain = CR_RANGE(cmd->chanlist[i]);
318 outw(i, dev->iobase + MUX_CNTR_REG);
319 tmp = chan | (gain << 6);
320 if (i == cmd->scan_end_arg - 1)
321 tmp |= 0x0010;
322 outw(tmp, dev->iobase + MUX_GAIN_REG);
323 }
324
325
326
327
328 if (cmd->convert_arg < 65536000) {
329 base_clock = CLOCK_1_MHZ;
330 timer = cmd->convert_arg / 1000;
331 } else if (cmd->convert_arg < 655360000) {
332 base_clock = CLOCK_100_KHZ;
333 timer = cmd->convert_arg / 10000;
334 } else {
335 base_clock = CLOCK_10_KHZ;
336 timer = cmd->convert_arg / 100000;
337 }
338 outw(0xFF03, dev->iobase + AM9513A_COM_REG);
339 outw(base_clock, dev->iobase + AM9513A_DATA_REG);
340 outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
341 outw(0x2, dev->iobase + AM9513A_DATA_REG);
342 outw(0xFF44, dev->iobase + AM9513A_COM_REG);
343 outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
344 outw(timer, dev->iobase + AM9513A_DATA_REG);
345 outw(0xFF24, dev->iobase + AM9513A_COM_REG);
346
347
348
349 sample_count = cmd->stop_arg * cmd->scan_end_arg;
350 outw(0xFF04, dev->iobase + AM9513A_COM_REG);
351 outw(0x1025, dev->iobase + AM9513A_DATA_REG);
352 outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
353 if (sample_count < 65536) {
354
355 outw(sample_count, dev->iobase + AM9513A_DATA_REG);
356 outw(0xFF48, dev->iobase + AM9513A_COM_REG);
357 outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
358 outw(0xFF28, dev->iobase + AM9513A_COM_REG);
359 devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
360 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
361 } else {
362
363
364 tmp = sample_count & 0xFFFF;
365 if (tmp)
366 outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
367 else
368 outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
369
370 outw(0xFF48, dev->iobase + AM9513A_COM_REG);
371 outw(0, dev->iobase + AM9513A_DATA_REG);
372 outw(0xFF28, dev->iobase + AM9513A_COM_REG);
373 outw(0xFF05, dev->iobase + AM9513A_COM_REG);
374 outw(0x25, dev->iobase + AM9513A_DATA_REG);
375 outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
376 tmp = sample_count & 0xFFFF;
377 if ((tmp == 0) || (tmp == 1)) {
378 outw((sample_count >> 16) & 0xFFFF,
379 dev->iobase + AM9513A_DATA_REG);
380 } else {
381 outw(((sample_count >> 16) & 0xFFFF) + 1,
382 dev->iobase + AM9513A_DATA_REG);
383 }
384 outw(0xFF70, dev->iobase + AM9513A_COM_REG);
385 devpriv->com_reg_1_state |= COMREG1_1632CNT;
386 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
387 }
388
389
390
391
392 if (cmd->chanlist_len > 1) {
393 if (cmd->scan_begin_arg < 65536000) {
394 base_clock = CLOCK_1_MHZ;
395 timer = cmd->scan_begin_arg / 1000;
396 } else if (cmd->scan_begin_arg < 655360000) {
397 base_clock = CLOCK_100_KHZ;
398 timer = cmd->scan_begin_arg / 10000;
399 } else {
400 base_clock = CLOCK_10_KHZ;
401 timer = cmd->scan_begin_arg / 100000;
402 }
403 outw(0xFF02, dev->iobase + AM9513A_COM_REG);
404 outw(base_clock, dev->iobase + AM9513A_DATA_REG);
405 outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
406 outw(0x2, dev->iobase + AM9513A_DATA_REG);
407 outw(0xFF42, dev->iobase + AM9513A_COM_REG);
408 outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
409 outw(timer, dev->iobase + AM9513A_DATA_REG);
410 outw(0xFF22, dev->iobase + AM9513A_COM_REG);
411 }
412
413
414 outw(0, dev->iobase + AD_CLEAR_REG);
415 outw(0, dev->iobase + MUX_CNTR_REG);
416 outw(0, dev->iobase + INT2CLR_REG);
417
418 devpriv->com_reg_1_state |= COMREG1_DAQEN;
419 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
420
421 devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
422 devpriv->com_reg_2_state |= COMREG2_INTEN;
423 outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
424 outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
425
426 outw(0, dev->iobase + START_DAQ_REG);
427
428 return 0;
429}
430
431
432static int atmio16d_ai_cancel(struct comedi_device *dev,
433 struct comedi_subdevice *s)
434{
435 reset_atmio16d(dev);
436
437 return 0;
438}
439
440static int atmio16d_ai_eoc(struct comedi_device *dev,
441 struct comedi_subdevice *s,
442 struct comedi_insn *insn,
443 unsigned long context)
444{
445 unsigned int status;
446
447 status = inw(dev->iobase + STAT_REG);
448 if (status & STAT_AD_CONVAVAIL)
449 return 0;
450 if (status & STAT_AD_OVERFLOW) {
451 outw(0, dev->iobase + AD_CLEAR_REG);
452 return -EOVERFLOW;
453 }
454 return -EBUSY;
455}
456
457static int atmio16d_ai_insn_read(struct comedi_device *dev,
458 struct comedi_subdevice *s,
459 struct comedi_insn *insn, unsigned int *data)
460{
461 struct atmio16d_private *devpriv = dev->private;
462 int i;
463 int chan;
464 int gain;
465 int ret;
466
467 chan = CR_CHAN(insn->chanspec);
468 gain = CR_RANGE(insn->chanspec);
469
470
471
472
473
474
475
476 outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
477
478 for (i = 0; i < insn->n; i++) {
479
480 outw(0, dev->iobase + START_CONVERT_REG);
481
482
483 ret = comedi_timeout(dev, s, insn, atmio16d_ai_eoc, 0);
484 if (ret)
485 return ret;
486
487
488 data[i] = inw(dev->iobase + AD_FIFO_REG);
489
490 if (devpriv->adc_coding == adc_2comp)
491 data[i] ^= 0x800;
492 }
493
494 return i;
495}
496
497static int atmio16d_ao_insn_write(struct comedi_device *dev,
498 struct comedi_subdevice *s,
499 struct comedi_insn *insn,
500 unsigned int *data)
501{
502 struct atmio16d_private *devpriv = dev->private;
503 unsigned int chan = CR_CHAN(insn->chanspec);
504 unsigned int reg = (chan) ? DAC1_REG : DAC0_REG;
505 bool munge = false;
506 int i;
507
508 if (chan == 0 && devpriv->dac0_coding == dac_2comp)
509 munge = true;
510 if (chan == 1 && devpriv->dac1_coding == dac_2comp)
511 munge = true;
512
513 for (i = 0; i < insn->n; i++) {
514 unsigned int val = data[i];
515
516 s->readback[chan] = val;
517
518 if (munge)
519 val ^= 0x800;
520
521 outw(val, dev->iobase + reg);
522 }
523
524 return insn->n;
525}
526
527static int atmio16d_dio_insn_bits(struct comedi_device *dev,
528 struct comedi_subdevice *s,
529 struct comedi_insn *insn,
530 unsigned int *data)
531{
532 if (comedi_dio_update_state(s, data))
533 outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
534
535 data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
536
537 return insn->n;
538}
539
540static int atmio16d_dio_insn_config(struct comedi_device *dev,
541 struct comedi_subdevice *s,
542 struct comedi_insn *insn,
543 unsigned int *data)
544{
545 struct atmio16d_private *devpriv = dev->private;
546 unsigned int chan = CR_CHAN(insn->chanspec);
547 unsigned int mask;
548 int ret;
549
550 if (chan < 4)
551 mask = 0x0f;
552 else
553 mask = 0xf0;
554
555 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
556 if (ret)
557 return ret;
558
559 devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
560 if (s->io_bits & 0x0f)
561 devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
562 if (s->io_bits & 0xf0)
563 devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
564 outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
565
566 return insn->n;
567}
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601static int atmio16d_attach(struct comedi_device *dev,
602 struct comedi_devconfig *it)
603{
604 const struct atmio16_board_t *board = dev->board_ptr;
605 struct atmio16d_private *devpriv;
606 struct comedi_subdevice *s;
607 int ret;
608
609 ret = comedi_request_region(dev, it->options[0], 0x20);
610 if (ret)
611 return ret;
612
613 ret = comedi_alloc_subdevices(dev, 4);
614 if (ret)
615 return ret;
616
617 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
618 if (!devpriv)
619 return -ENOMEM;
620
621
622 reset_atmio16d(dev);
623
624 if (it->options[1]) {
625 ret = request_irq(it->options[1], atmio16d_interrupt, 0,
626 dev->board_name, dev);
627 if (ret == 0)
628 dev->irq = it->options[1];
629 }
630
631
632 devpriv->adc_mux = it->options[5];
633 devpriv->adc_range = it->options[6];
634
635 devpriv->dac0_range = it->options[7];
636 devpriv->dac0_reference = it->options[8];
637 devpriv->dac0_coding = it->options[9];
638 devpriv->dac1_range = it->options[10];
639 devpriv->dac1_reference = it->options[11];
640 devpriv->dac1_coding = it->options[12];
641
642
643 s = &dev->subdevices[0];
644
645 s->type = COMEDI_SUBD_AI;
646 s->subdev_flags = SDF_READABLE | SDF_GROUND;
647 s->n_chan = (devpriv->adc_mux ? 16 : 8);
648 s->insn_read = atmio16d_ai_insn_read;
649 s->maxdata = 0xfff;
650 switch (devpriv->adc_range) {
651 case adc_bipolar10:
652 s->range_table = &range_atmio16d_ai_10_bipolar;
653 break;
654 case adc_bipolar5:
655 s->range_table = &range_atmio16d_ai_5_bipolar;
656 break;
657 case adc_unipolar10:
658 s->range_table = &range_atmio16d_ai_unipolar;
659 break;
660 }
661 if (dev->irq) {
662 dev->read_subdev = s;
663 s->subdev_flags |= SDF_CMD_READ;
664 s->len_chanlist = 16;
665 s->do_cmdtest = atmio16d_ai_cmdtest;
666 s->do_cmd = atmio16d_ai_cmd;
667 s->cancel = atmio16d_ai_cancel;
668 }
669
670
671 s = &dev->subdevices[1];
672 s->type = COMEDI_SUBD_AO;
673 s->subdev_flags = SDF_WRITABLE;
674 s->n_chan = 2;
675 s->maxdata = 0xfff;
676 s->range_table_list = devpriv->ao_range_type_list;
677 switch (devpriv->dac0_range) {
678 case dac_bipolar:
679 devpriv->ao_range_type_list[0] = &range_bipolar10;
680 break;
681 case dac_unipolar:
682 devpriv->ao_range_type_list[0] = &range_unipolar10;
683 break;
684 }
685 switch (devpriv->dac1_range) {
686 case dac_bipolar:
687 devpriv->ao_range_type_list[1] = &range_bipolar10;
688 break;
689 case dac_unipolar:
690 devpriv->ao_range_type_list[1] = &range_unipolar10;
691 break;
692 }
693 s->insn_write = atmio16d_ao_insn_write;
694
695 ret = comedi_alloc_subdev_readback(s);
696 if (ret)
697 return ret;
698
699
700 s = &dev->subdevices[2];
701 s->type = COMEDI_SUBD_DIO;
702 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
703 s->n_chan = 8;
704 s->insn_bits = atmio16d_dio_insn_bits;
705 s->insn_config = atmio16d_dio_insn_config;
706 s->maxdata = 1;
707 s->range_table = &range_digital;
708
709
710 s = &dev->subdevices[3];
711 if (board->has_8255) {
712 ret = subdev_8255_init(dev, s, NULL, 0x00);
713 if (ret)
714 return ret;
715 } else {
716 s->type = COMEDI_SUBD_UNUSED;
717 }
718
719
720#if 0
721 s = &dev->subdevices[4];
722
723 s->type = COMEDI_SUBD_TIMER;
724 s->n_chan = 0;
725 s->maxdata = 0
726#endif
727
728 return 0;
729}
730
731static void atmio16d_detach(struct comedi_device *dev)
732{
733 reset_atmio16d(dev);
734 comedi_legacy_detach(dev);
735}
736
737static const struct atmio16_board_t atmio16_boards[] = {
738 {
739 .name = "atmio16",
740 .has_8255 = 0,
741 }, {
742 .name = "atmio16d",
743 .has_8255 = 1,
744 },
745};
746
747static struct comedi_driver atmio16d_driver = {
748 .driver_name = "atmio16",
749 .module = THIS_MODULE,
750 .attach = atmio16d_attach,
751 .detach = atmio16d_detach,
752 .board_name = &atmio16_boards[0].name,
753 .num_names = ARRAY_SIZE(atmio16_boards),
754 .offset = sizeof(struct atmio16_board_t),
755};
756module_comedi_driver(atmio16d_driver);
757
758MODULE_AUTHOR("Comedi http://www.comedi.org");
759MODULE_DESCRIPTION("Comedi low-level driver");
760MODULE_LICENSE("GPL");
761