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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69#include <linux/module.h>
70#include <linux/interrupt.h>
71
72#include "../comedi_pci.h"
73#include "addi_tcw.h"
74#include "addi_watchdog.h"
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90#define APCI1564_EEPROM_REG 0x00
91#define APCI1564_EEPROM_VCC_STATUS BIT(8)
92#define APCI1564_EEPROM_TO_REV(x) (((x) >> 4) & 0xf)
93#define APCI1564_EEPROM_DI BIT(3)
94#define APCI1564_EEPROM_DO BIT(2)
95#define APCI1564_EEPROM_CS BIT(1)
96#define APCI1564_EEPROM_CLK BIT(0)
97#define APCI1564_REV1_TIMER_IOBASE 0x04
98#define APCI1564_REV2_MAIN_IOBASE 0x04
99#define APCI1564_REV2_TIMER_IOBASE 0x48
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114#define APCI1564_REV1_MAIN_IOBASE 0x00
115
116
117
118
119
120
121#define APCI1564_DI_REG 0x00
122#define APCI1564_DI_INT_MODE1_REG 0x04
123#define APCI1564_DI_INT_MODE2_REG 0x08
124#define APCI1564_DI_INT_MODE_MASK 0x000ffff0
125#define APCI1564_DI_INT_STATUS_REG 0x0c
126#define APCI1564_DI_IRQ_REG 0x10
127#define APCI1564_DI_IRQ_ENA BIT(2)
128#define APCI1564_DI_IRQ_MODE BIT(1)
129#define APCI1564_DO_REG 0x14
130#define APCI1564_DO_INT_CTRL_REG 0x18
131#define APCI1564_DO_INT_CTRL_CC_INT_ENA BIT(1)
132#define APCI1564_DO_INT_CTRL_VCC_INT_ENA BIT(0)
133#define APCI1564_DO_INT_STATUS_REG 0x1c
134#define APCI1564_DO_INT_STATUS_CC BIT(1)
135#define APCI1564_DO_INT_STATUS_VCC BIT(0)
136#define APCI1564_DO_IRQ_REG 0x20
137#define APCI1564_DO_IRQ_INTR BIT(0)
138#define APCI1564_WDOG_IOBASE 0x24
139
140
141
142
143
144
145
146
147
148
149
150#define APCI1564_COUNTER(x) ((x) * 0x20)
151
152
153
154
155
156#define APCI1564_EVENT_COS BIT(31)
157#define APCI1564_EVENT_TIMER BIT(30)
158#define APCI1564_EVENT_COUNTER(x) BIT(27 + (x))
159#define APCI1564_EVENT_MASK 0xfff0000f
160
161struct apci1564_private {
162 unsigned long eeprom;
163 unsigned long timer;
164 unsigned long counters;
165 unsigned int mode1;
166 unsigned int mode2;
167 unsigned int ctrl;
168};
169
170static int apci1564_reset(struct comedi_device *dev)
171{
172 struct apci1564_private *devpriv = dev->private;
173
174
175 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
176 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
177 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
178 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
179
180
181 outl(0x0, dev->iobase + APCI1564_DO_REG);
182 outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG);
183
184
185 addi_watchdog_reset(dev->iobase + APCI1564_WDOG_IOBASE);
186
187
188 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
189 outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG);
190
191 if (devpriv->counters) {
192 unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG;
193
194
195 outl(0x0, iobase + APCI1564_COUNTER(0));
196 outl(0x0, iobase + APCI1564_COUNTER(1));
197 outl(0x0, iobase + APCI1564_COUNTER(2));
198 }
199
200 return 0;
201}
202
203static irqreturn_t apci1564_interrupt(int irq, void *d)
204{
205 struct comedi_device *dev = d;
206 struct apci1564_private *devpriv = dev->private;
207 struct comedi_subdevice *s = dev->read_subdev;
208 unsigned int status;
209 unsigned int ctrl;
210 unsigned int chan;
211
212 s->state &= ~APCI1564_EVENT_MASK;
213
214 status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
215 if (status & APCI1564_DI_IRQ_ENA) {
216
217 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
218 s->state &= APCI1564_DI_INT_MODE_MASK;
219 s->state |= APCI1564_EVENT_COS;
220
221
222 outl(status & ~APCI1564_DI_IRQ_ENA,
223 dev->iobase + APCI1564_DI_IRQ_REG);
224 outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
225 }
226
227 status = inl(devpriv->timer + ADDI_TCW_IRQ_REG);
228 if (status & ADDI_TCW_IRQ) {
229 s->state |= APCI1564_EVENT_TIMER;
230
231
232 ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
233 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
234 outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
235 }
236
237 if (devpriv->counters) {
238 for (chan = 0; chan < 3; chan++) {
239 unsigned long iobase;
240
241 iobase = devpriv->counters + APCI1564_COUNTER(chan);
242
243 status = inl(iobase + ADDI_TCW_IRQ_REG);
244 if (status & ADDI_TCW_IRQ) {
245 s->state |= APCI1564_EVENT_COUNTER(chan);
246
247
248 ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
249 outl(0x0, iobase + ADDI_TCW_CTRL_REG);
250 outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
251 }
252 }
253 }
254
255 if (s->state & APCI1564_EVENT_MASK) {
256 comedi_buf_write_samples(s, &s->state, 1);
257 comedi_handle_events(dev, s);
258 }
259
260 return IRQ_HANDLED;
261}
262
263static int apci1564_di_insn_bits(struct comedi_device *dev,
264 struct comedi_subdevice *s,
265 struct comedi_insn *insn,
266 unsigned int *data)
267{
268 data[1] = inl(dev->iobase + APCI1564_DI_REG);
269
270 return insn->n;
271}
272
273static int apci1564_do_insn_bits(struct comedi_device *dev,
274 struct comedi_subdevice *s,
275 struct comedi_insn *insn,
276 unsigned int *data)
277{
278 s->state = inl(dev->iobase + APCI1564_DO_REG);
279
280 if (comedi_dio_update_state(s, data))
281 outl(s->state, dev->iobase + APCI1564_DO_REG);
282
283 data[1] = s->state;
284
285 return insn->n;
286}
287
288static int apci1564_diag_insn_bits(struct comedi_device *dev,
289 struct comedi_subdevice *s,
290 struct comedi_insn *insn,
291 unsigned int *data)
292{
293 data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3;
294
295 return insn->n;
296}
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328static int apci1564_cos_insn_config(struct comedi_device *dev,
329 struct comedi_subdevice *s,
330 struct comedi_insn *insn,
331 unsigned int *data)
332{
333 struct apci1564_private *devpriv = dev->private;
334 unsigned int shift, oldmask, himask, lomask;
335
336 switch (data[0]) {
337 case INSN_CONFIG_DIGITAL_TRIG:
338 if (data[1] != 0)
339 return -EINVAL;
340 shift = data[3];
341 if (shift < 32) {
342 oldmask = (1U << shift) - 1;
343 himask = data[4] << shift;
344 lomask = data[5] << shift;
345 } else {
346 oldmask = 0xffffffffu;
347 himask = 0;
348 lomask = 0;
349 }
350 switch (data[2]) {
351 case COMEDI_DIGITAL_TRIG_DISABLE:
352 devpriv->ctrl = 0;
353 devpriv->mode1 = 0;
354 devpriv->mode2 = 0;
355 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
356 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
357 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
358 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
359 break;
360 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
361 if (devpriv->ctrl != APCI1564_DI_IRQ_ENA) {
362
363 devpriv->ctrl = APCI1564_DI_IRQ_ENA;
364
365 devpriv->mode1 = 0;
366 devpriv->mode2 = 0;
367 } else {
368
369 devpriv->mode1 &= oldmask;
370 devpriv->mode2 &= oldmask;
371 }
372
373 devpriv->mode1 |= himask;
374 devpriv->mode2 |= lomask;
375 break;
376 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
377 if (devpriv->ctrl != (APCI1564_DI_IRQ_ENA |
378 APCI1564_DI_IRQ_MODE)) {
379
380 devpriv->ctrl = APCI1564_DI_IRQ_ENA |
381 APCI1564_DI_IRQ_MODE;
382
383 devpriv->mode1 = 0;
384 devpriv->mode2 = 0;
385 } else {
386
387 devpriv->mode1 &= oldmask;
388 devpriv->mode2 &= oldmask;
389 }
390
391 devpriv->mode1 |= himask;
392 devpriv->mode2 |= lomask;
393 break;
394 default:
395 return -EINVAL;
396 }
397
398
399 devpriv->mode1 &= APCI1564_DI_INT_MODE_MASK;
400 devpriv->mode2 &= APCI1564_DI_INT_MODE_MASK;
401 break;
402 default:
403 return -EINVAL;
404 }
405 return insn->n;
406}
407
408static int apci1564_cos_insn_bits(struct comedi_device *dev,
409 struct comedi_subdevice *s,
410 struct comedi_insn *insn,
411 unsigned int *data)
412{
413 data[1] = s->state;
414
415 return 0;
416}
417
418static int apci1564_cos_cmdtest(struct comedi_device *dev,
419 struct comedi_subdevice *s,
420 struct comedi_cmd *cmd)
421{
422 int err = 0;
423
424
425
426 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
427 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
428 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
429 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
430 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
431
432 if (err)
433 return 1;
434
435
436
437
438
439
440 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
441 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
442 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
443 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
444 cmd->chanlist_len);
445 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
446
447 if (err)
448 return 3;
449
450
451
452
453
454 return 0;
455}
456
457
458
459
460
461
462static int apci1564_cos_cmd(struct comedi_device *dev,
463 struct comedi_subdevice *s)
464{
465 struct apci1564_private *devpriv = dev->private;
466
467 if (!devpriv->ctrl && !(devpriv->mode1 || devpriv->mode2)) {
468 dev_warn(dev->class_dev,
469 "Interrupts disabled due to mode configuration!\n");
470 return -EINVAL;
471 }
472
473 outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
474 outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
475 outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
476
477 return 0;
478}
479
480static int apci1564_cos_cancel(struct comedi_device *dev,
481 struct comedi_subdevice *s)
482{
483 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
484 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
485 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
486 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
487
488 return 0;
489}
490
491static int apci1564_timer_insn_config(struct comedi_device *dev,
492 struct comedi_subdevice *s,
493 struct comedi_insn *insn,
494 unsigned int *data)
495{
496 struct apci1564_private *devpriv = dev->private;
497 unsigned int val;
498
499 switch (data[0]) {
500 case INSN_CONFIG_ARM:
501 if (data[1] > s->maxdata)
502 return -EINVAL;
503 outl(data[1], devpriv->timer + ADDI_TCW_RELOAD_REG);
504 outl(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_TIMER_ENA,
505 devpriv->timer + ADDI_TCW_CTRL_REG);
506 break;
507 case INSN_CONFIG_DISARM:
508 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
509 break;
510 case INSN_CONFIG_GET_COUNTER_STATUS:
511 data[1] = 0;
512 val = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
513 if (val & ADDI_TCW_CTRL_IRQ_ENA)
514 data[1] |= COMEDI_COUNTER_ARMED;
515 if (val & ADDI_TCW_CTRL_TIMER_ENA)
516 data[1] |= COMEDI_COUNTER_COUNTING;
517 val = inl(devpriv->timer + ADDI_TCW_STATUS_REG);
518 if (val & ADDI_TCW_STATUS_OVERFLOW)
519 data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
520 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
521 COMEDI_COUNTER_TERMINAL_COUNT;
522 break;
523 case INSN_CONFIG_SET_CLOCK_SRC:
524 if (data[2] > s->maxdata)
525 return -EINVAL;
526 outl(data[1], devpriv->timer + ADDI_TCW_TIMEBASE_REG);
527 outl(data[2], devpriv->timer + ADDI_TCW_RELOAD_REG);
528 break;
529 case INSN_CONFIG_GET_CLOCK_SRC:
530 data[1] = inl(devpriv->timer + ADDI_TCW_TIMEBASE_REG);
531 data[2] = inl(devpriv->timer + ADDI_TCW_RELOAD_REG);
532 break;
533 default:
534 return -EINVAL;
535 }
536
537 return insn->n;
538}
539
540static int apci1564_timer_insn_write(struct comedi_device *dev,
541 struct comedi_subdevice *s,
542 struct comedi_insn *insn,
543 unsigned int *data)
544{
545 struct apci1564_private *devpriv = dev->private;
546
547
548 if (insn->n) {
549 unsigned int val = data[insn->n - 1];
550
551 outl(val, devpriv->timer + ADDI_TCW_RELOAD_REG);
552 }
553
554 return insn->n;
555}
556
557static int apci1564_timer_insn_read(struct comedi_device *dev,
558 struct comedi_subdevice *s,
559 struct comedi_insn *insn,
560 unsigned int *data)
561{
562 struct apci1564_private *devpriv = dev->private;
563 int i;
564
565
566 for (i = 0; i < insn->n; i++)
567 data[i] = inl(devpriv->timer + ADDI_TCW_VAL_REG);
568
569 return insn->n;
570}
571
572static int apci1564_counter_insn_config(struct comedi_device *dev,
573 struct comedi_subdevice *s,
574 struct comedi_insn *insn,
575 unsigned int *data)
576{
577 struct apci1564_private *devpriv = dev->private;
578 unsigned int chan = CR_CHAN(insn->chanspec);
579 unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
580 unsigned int val;
581
582 switch (data[0]) {
583 case INSN_CONFIG_ARM:
584 val = inl(iobase + ADDI_TCW_CTRL_REG);
585 val |= ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA;
586 outl(data[1], iobase + ADDI_TCW_RELOAD_REG);
587 outl(val, iobase + ADDI_TCW_CTRL_REG);
588 break;
589 case INSN_CONFIG_DISARM:
590 val = inl(iobase + ADDI_TCW_CTRL_REG);
591 val &= ~(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA);
592 outl(val, iobase + ADDI_TCW_CTRL_REG);
593 break;
594 case INSN_CONFIG_SET_COUNTER_MODE:
595
596
597
598
599
600 outl(data[1], iobase + ADDI_TCW_CTRL_REG);
601 break;
602 case INSN_CONFIG_GET_COUNTER_STATUS:
603 data[1] = 0;
604 val = inl(iobase + ADDI_TCW_CTRL_REG);
605 if (val & ADDI_TCW_CTRL_IRQ_ENA)
606 data[1] |= COMEDI_COUNTER_ARMED;
607 if (val & ADDI_TCW_CTRL_CNTR_ENA)
608 data[1] |= COMEDI_COUNTER_COUNTING;
609 val = inl(iobase + ADDI_TCW_STATUS_REG);
610 if (val & ADDI_TCW_STATUS_OVERFLOW)
611 data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
612 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
613 COMEDI_COUNTER_TERMINAL_COUNT;
614 break;
615 default:
616 return -EINVAL;
617 }
618
619 return insn->n;
620}
621
622static int apci1564_counter_insn_write(struct comedi_device *dev,
623 struct comedi_subdevice *s,
624 struct comedi_insn *insn,
625 unsigned int *data)
626{
627 struct apci1564_private *devpriv = dev->private;
628 unsigned int chan = CR_CHAN(insn->chanspec);
629 unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
630
631
632 if (insn->n) {
633 unsigned int val = data[insn->n - 1];
634
635 outl(val, iobase + ADDI_TCW_RELOAD_REG);
636 }
637
638 return insn->n;
639}
640
641static int apci1564_counter_insn_read(struct comedi_device *dev,
642 struct comedi_subdevice *s,
643 struct comedi_insn *insn,
644 unsigned int *data)
645{
646 struct apci1564_private *devpriv = dev->private;
647 unsigned int chan = CR_CHAN(insn->chanspec);
648 unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
649 int i;
650
651
652 for (i = 0; i < insn->n; i++)
653 data[i] = inl(iobase + ADDI_TCW_VAL_REG);
654
655 return insn->n;
656}
657
658static int apci1564_auto_attach(struct comedi_device *dev,
659 unsigned long context_unused)
660{
661 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
662 struct apci1564_private *devpriv;
663 struct comedi_subdevice *s;
664 unsigned int val;
665 int ret;
666
667 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
668 if (!devpriv)
669 return -ENOMEM;
670
671 ret = comedi_pci_enable(dev);
672 if (ret)
673 return ret;
674
675
676 devpriv->eeprom = pci_resource_start(pcidev, 0);
677 val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
678 if (APCI1564_EEPROM_TO_REV(val) == 0) {
679
680 dev->iobase = pci_resource_start(pcidev, 1) +
681 APCI1564_REV1_MAIN_IOBASE;
682 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
683 } else {
684
685 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
686 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
687 devpriv->counters = pci_resource_start(pcidev, 1);
688 }
689
690 apci1564_reset(dev);
691
692 if (pcidev->irq > 0) {
693 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
694 dev->board_name, dev);
695 if (ret == 0)
696 dev->irq = pcidev->irq;
697 }
698
699 ret = comedi_alloc_subdevices(dev, 7);
700 if (ret)
701 return ret;
702
703
704 s = &dev->subdevices[0];
705 s->type = COMEDI_SUBD_DI;
706 s->subdev_flags = SDF_READABLE;
707 s->n_chan = 32;
708 s->maxdata = 1;
709 s->range_table = &range_digital;
710 s->insn_bits = apci1564_di_insn_bits;
711
712
713 s = &dev->subdevices[1];
714 s->type = COMEDI_SUBD_DO;
715 s->subdev_flags = SDF_WRITABLE;
716 s->n_chan = 32;
717 s->maxdata = 1;
718 s->range_table = &range_digital;
719 s->insn_bits = apci1564_do_insn_bits;
720
721
722 s = &dev->subdevices[2];
723 if (dev->irq) {
724 dev->read_subdev = s;
725 s->type = COMEDI_SUBD_DI;
726 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_LSAMPL;
727 s->n_chan = 1;
728 s->maxdata = 1;
729 s->range_table = &range_digital;
730 s->len_chanlist = 1;
731 s->insn_config = apci1564_cos_insn_config;
732 s->insn_bits = apci1564_cos_insn_bits;
733 s->do_cmdtest = apci1564_cos_cmdtest;
734 s->do_cmd = apci1564_cos_cmd;
735 s->cancel = apci1564_cos_cancel;
736 } else {
737 s->type = COMEDI_SUBD_UNUSED;
738 }
739
740
741 s = &dev->subdevices[3];
742 s->type = COMEDI_SUBD_TIMER;
743 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
744 s->n_chan = 1;
745 s->maxdata = 0x0fff;
746 s->range_table = &range_digital;
747 s->insn_config = apci1564_timer_insn_config;
748 s->insn_write = apci1564_timer_insn_write;
749 s->insn_read = apci1564_timer_insn_read;
750
751
752 s = &dev->subdevices[4];
753 if (devpriv->counters) {
754 s->type = COMEDI_SUBD_COUNTER;
755 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
756 s->n_chan = 3;
757 s->maxdata = 0xffffffff;
758 s->range_table = &range_digital;
759 s->insn_config = apci1564_counter_insn_config;
760 s->insn_write = apci1564_counter_insn_write;
761 s->insn_read = apci1564_counter_insn_read;
762 } else {
763 s->type = COMEDI_SUBD_UNUSED;
764 }
765
766
767 s = &dev->subdevices[5];
768 ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_IOBASE);
769 if (ret)
770 return ret;
771
772
773 s = &dev->subdevices[6];
774 s->type = COMEDI_SUBD_DI;
775 s->subdev_flags = SDF_READABLE;
776 s->n_chan = 2;
777 s->maxdata = 1;
778 s->range_table = &range_digital;
779 s->insn_bits = apci1564_diag_insn_bits;
780
781 return 0;
782}
783
784static void apci1564_detach(struct comedi_device *dev)
785{
786 if (dev->iobase)
787 apci1564_reset(dev);
788 comedi_pci_detach(dev);
789}
790
791static struct comedi_driver apci1564_driver = {
792 .driver_name = "addi_apci_1564",
793 .module = THIS_MODULE,
794 .auto_attach = apci1564_auto_attach,
795 .detach = apci1564_detach,
796};
797
798static int apci1564_pci_probe(struct pci_dev *dev,
799 const struct pci_device_id *id)
800{
801 return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
802}
803
804static const struct pci_device_id apci1564_pci_table[] = {
805 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
806 { 0 }
807};
808MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
809
810static struct pci_driver apci1564_pci_driver = {
811 .name = "addi_apci_1564",
812 .id_table = apci1564_pci_table,
813 .probe = apci1564_pci_probe,
814 .remove = comedi_pci_auto_unconfig,
815};
816module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
817
818MODULE_AUTHOR("Comedi https://www.comedi.org");
819MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
820MODULE_LICENSE("GPL");
821