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#include <linux/module.h>
26#include <linux/interrupt.h>
27
28#include "../comedi_pci.h"
29
30#define CONV_UNIT_NS (1 << 0)
31#define CONV_UNIT_US (1 << 1)
32#define CONV_UNIT_MS (1 << 2)
33
34static const struct comedi_lrange apci3xxx_ai_range = {
35 8, {
36 BIP_RANGE(10),
37 BIP_RANGE(5),
38 BIP_RANGE(2),
39 BIP_RANGE(1),
40 UNI_RANGE(10),
41 UNI_RANGE(5),
42 UNI_RANGE(2),
43 UNI_RANGE(1)
44 }
45};
46
47static const struct comedi_lrange apci3xxx_ao_range = {
48 2, {
49 BIP_RANGE(10),
50 UNI_RANGE(10)
51 }
52};
53
54enum apci3xxx_boardid {
55 BOARD_APCI3000_16,
56 BOARD_APCI3000_8,
57 BOARD_APCI3000_4,
58 BOARD_APCI3006_16,
59 BOARD_APCI3006_8,
60 BOARD_APCI3006_4,
61 BOARD_APCI3010_16,
62 BOARD_APCI3010_8,
63 BOARD_APCI3010_4,
64 BOARD_APCI3016_16,
65 BOARD_APCI3016_8,
66 BOARD_APCI3016_4,
67 BOARD_APCI3100_16_4,
68 BOARD_APCI3100_8_4,
69 BOARD_APCI3106_16_4,
70 BOARD_APCI3106_8_4,
71 BOARD_APCI3110_16_4,
72 BOARD_APCI3110_8_4,
73 BOARD_APCI3116_16_4,
74 BOARD_APCI3116_8_4,
75 BOARD_APCI3003,
76 BOARD_APCI3002_16,
77 BOARD_APCI3002_8,
78 BOARD_APCI3002_4,
79 BOARD_APCI3500,
80};
81
82struct apci3xxx_boardinfo {
83 const char *name;
84 int ai_subdev_flags;
85 int ai_n_chan;
86 unsigned int ai_maxdata;
87 unsigned char ai_conv_units;
88 unsigned int ai_min_acq_ns;
89 unsigned int has_ao:1;
90 unsigned int has_dig_in:1;
91 unsigned int has_dig_out:1;
92 unsigned int has_ttl_io:1;
93};
94
95static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
96 [BOARD_APCI3000_16] = {
97 .name = "apci3000-16",
98 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
99 .ai_n_chan = 16,
100 .ai_maxdata = 0x0fff,
101 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
102 .ai_min_acq_ns = 10000,
103 .has_ttl_io = 1,
104 },
105 [BOARD_APCI3000_8] = {
106 .name = "apci3000-8",
107 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
108 .ai_n_chan = 8,
109 .ai_maxdata = 0x0fff,
110 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
111 .ai_min_acq_ns = 10000,
112 .has_ttl_io = 1,
113 },
114 [BOARD_APCI3000_4] = {
115 .name = "apci3000-4",
116 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
117 .ai_n_chan = 4,
118 .ai_maxdata = 0x0fff,
119 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
120 .ai_min_acq_ns = 10000,
121 .has_ttl_io = 1,
122 },
123 [BOARD_APCI3006_16] = {
124 .name = "apci3006-16",
125 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
126 .ai_n_chan = 16,
127 .ai_maxdata = 0xffff,
128 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
129 .ai_min_acq_ns = 10000,
130 .has_ttl_io = 1,
131 },
132 [BOARD_APCI3006_8] = {
133 .name = "apci3006-8",
134 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
135 .ai_n_chan = 8,
136 .ai_maxdata = 0xffff,
137 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
138 .ai_min_acq_ns = 10000,
139 .has_ttl_io = 1,
140 },
141 [BOARD_APCI3006_4] = {
142 .name = "apci3006-4",
143 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
144 .ai_n_chan = 4,
145 .ai_maxdata = 0xffff,
146 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
147 .ai_min_acq_ns = 10000,
148 .has_ttl_io = 1,
149 },
150 [BOARD_APCI3010_16] = {
151 .name = "apci3010-16",
152 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
153 .ai_n_chan = 16,
154 .ai_maxdata = 0x0fff,
155 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
156 .ai_min_acq_ns = 5000,
157 .has_dig_in = 1,
158 .has_dig_out = 1,
159 .has_ttl_io = 1,
160 },
161 [BOARD_APCI3010_8] = {
162 .name = "apci3010-8",
163 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
164 .ai_n_chan = 8,
165 .ai_maxdata = 0x0fff,
166 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
167 .ai_min_acq_ns = 5000,
168 .has_dig_in = 1,
169 .has_dig_out = 1,
170 .has_ttl_io = 1,
171 },
172 [BOARD_APCI3010_4] = {
173 .name = "apci3010-4",
174 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
175 .ai_n_chan = 4,
176 .ai_maxdata = 0x0fff,
177 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
178 .ai_min_acq_ns = 5000,
179 .has_dig_in = 1,
180 .has_dig_out = 1,
181 .has_ttl_io = 1,
182 },
183 [BOARD_APCI3016_16] = {
184 .name = "apci3016-16",
185 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
186 .ai_n_chan = 16,
187 .ai_maxdata = 0xffff,
188 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
189 .ai_min_acq_ns = 5000,
190 .has_dig_in = 1,
191 .has_dig_out = 1,
192 .has_ttl_io = 1,
193 },
194 [BOARD_APCI3016_8] = {
195 .name = "apci3016-8",
196 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
197 .ai_n_chan = 8,
198 .ai_maxdata = 0xffff,
199 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
200 .ai_min_acq_ns = 5000,
201 .has_dig_in = 1,
202 .has_dig_out = 1,
203 .has_ttl_io = 1,
204 },
205 [BOARD_APCI3016_4] = {
206 .name = "apci3016-4",
207 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
208 .ai_n_chan = 4,
209 .ai_maxdata = 0xffff,
210 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
211 .ai_min_acq_ns = 5000,
212 .has_dig_in = 1,
213 .has_dig_out = 1,
214 .has_ttl_io = 1,
215 },
216 [BOARD_APCI3100_16_4] = {
217 .name = "apci3100-16-4",
218 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
219 .ai_n_chan = 16,
220 .ai_maxdata = 0x0fff,
221 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
222 .ai_min_acq_ns = 10000,
223 .has_ao = 1,
224 .has_ttl_io = 1,
225 },
226 [BOARD_APCI3100_8_4] = {
227 .name = "apci3100-8-4",
228 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
229 .ai_n_chan = 8,
230 .ai_maxdata = 0x0fff,
231 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
232 .ai_min_acq_ns = 10000,
233 .has_ao = 1,
234 .has_ttl_io = 1,
235 },
236 [BOARD_APCI3106_16_4] = {
237 .name = "apci3106-16-4",
238 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
239 .ai_n_chan = 16,
240 .ai_maxdata = 0xffff,
241 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
242 .ai_min_acq_ns = 10000,
243 .has_ao = 1,
244 .has_ttl_io = 1,
245 },
246 [BOARD_APCI3106_8_4] = {
247 .name = "apci3106-8-4",
248 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
249 .ai_n_chan = 8,
250 .ai_maxdata = 0xffff,
251 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
252 .ai_min_acq_ns = 10000,
253 .has_ao = 1,
254 .has_ttl_io = 1,
255 },
256 [BOARD_APCI3110_16_4] = {
257 .name = "apci3110-16-4",
258 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
259 .ai_n_chan = 16,
260 .ai_maxdata = 0x0fff,
261 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
262 .ai_min_acq_ns = 5000,
263 .has_ao = 1,
264 .has_dig_in = 1,
265 .has_dig_out = 1,
266 .has_ttl_io = 1,
267 },
268 [BOARD_APCI3110_8_4] = {
269 .name = "apci3110-8-4",
270 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
271 .ai_n_chan = 8,
272 .ai_maxdata = 0x0fff,
273 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
274 .ai_min_acq_ns = 5000,
275 .has_ao = 1,
276 .has_dig_in = 1,
277 .has_dig_out = 1,
278 .has_ttl_io = 1,
279 },
280 [BOARD_APCI3116_16_4] = {
281 .name = "apci3116-16-4",
282 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
283 .ai_n_chan = 16,
284 .ai_maxdata = 0xffff,
285 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
286 .ai_min_acq_ns = 5000,
287 .has_ao = 1,
288 .has_dig_in = 1,
289 .has_dig_out = 1,
290 .has_ttl_io = 1,
291 },
292 [BOARD_APCI3116_8_4] = {
293 .name = "apci3116-8-4",
294 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
295 .ai_n_chan = 8,
296 .ai_maxdata = 0xffff,
297 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
298 .ai_min_acq_ns = 5000,
299 .has_ao = 1,
300 .has_dig_in = 1,
301 .has_dig_out = 1,
302 .has_ttl_io = 1,
303 },
304 [BOARD_APCI3003] = {
305 .name = "apci3003",
306 .ai_subdev_flags = SDF_DIFF,
307 .ai_n_chan = 4,
308 .ai_maxdata = 0xffff,
309 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US |
310 CONV_UNIT_NS,
311 .ai_min_acq_ns = 2500,
312 .has_dig_in = 1,
313 .has_dig_out = 1,
314 },
315 [BOARD_APCI3002_16] = {
316 .name = "apci3002-16",
317 .ai_subdev_flags = SDF_DIFF,
318 .ai_n_chan = 16,
319 .ai_maxdata = 0xffff,
320 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
321 .ai_min_acq_ns = 5000,
322 .has_dig_in = 1,
323 .has_dig_out = 1,
324 },
325 [BOARD_APCI3002_8] = {
326 .name = "apci3002-8",
327 .ai_subdev_flags = SDF_DIFF,
328 .ai_n_chan = 8,
329 .ai_maxdata = 0xffff,
330 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
331 .ai_min_acq_ns = 5000,
332 .has_dig_in = 1,
333 .has_dig_out = 1,
334 },
335 [BOARD_APCI3002_4] = {
336 .name = "apci3002-4",
337 .ai_subdev_flags = SDF_DIFF,
338 .ai_n_chan = 4,
339 .ai_maxdata = 0xffff,
340 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
341 .ai_min_acq_ns = 5000,
342 .has_dig_in = 1,
343 .has_dig_out = 1,
344 },
345 [BOARD_APCI3500] = {
346 .name = "apci3500",
347 .has_ao = 1,
348 .has_ttl_io = 1,
349 },
350};
351
352struct apci3xxx_private {
353 unsigned int ai_timer;
354 unsigned char ai_time_base;
355};
356
357static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
358{
359 struct comedi_device *dev = d;
360 struct comedi_subdevice *s = dev->read_subdev;
361 unsigned int status;
362 unsigned int val;
363
364
365 status = readl(dev->mmio + 16);
366 if ((status & 0x2) == 0x2) {
367
368 writel(status, dev->mmio + 16);
369
370 val = readl(dev->mmio + 28);
371 comedi_buf_write_samples(s, &val, 1);
372
373 s->async->events |= COMEDI_CB_EOA;
374 comedi_handle_events(dev, s);
375
376 return IRQ_HANDLED;
377 }
378 return IRQ_NONE;
379}
380
381static int apci3xxx_ai_started(struct comedi_device *dev)
382{
383 if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
384 return 1;
385
386 return 0;
387}
388
389static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
390{
391 unsigned int chan = CR_CHAN(chanspec);
392 unsigned int range = CR_RANGE(chanspec);
393 unsigned int aref = CR_AREF(chanspec);
394 unsigned int delay_mode;
395 unsigned int val;
396
397 if (apci3xxx_ai_started(dev))
398 return -EBUSY;
399
400
401 writel(0x10000, dev->mmio + 12);
402
403
404 delay_mode = readl(dev->mmio + 4);
405 delay_mode &= 0xfffffef0;
406
407
408 writel(delay_mode, dev->mmio + 4);
409
410
411 val = (range & 3) | ((range >> 2) << 6) |
412 ((aref == AREF_DIFF) << 7);
413 writel(val, dev->mmio + 0);
414
415
416 writel(delay_mode | 0x100, dev->mmio + 4);
417 writel(chan, dev->mmio + 0);
418
419
420 writel(delay_mode, dev->mmio + 4);
421
422
423 writel(1, dev->mmio + 48);
424
425 return 0;
426}
427
428static int apci3xxx_ai_eoc(struct comedi_device *dev,
429 struct comedi_subdevice *s,
430 struct comedi_insn *insn,
431 unsigned long context)
432{
433 unsigned int status;
434
435 status = readl(dev->mmio + 20);
436 if (status & 0x1)
437 return 0;
438 return -EBUSY;
439}
440
441static int apci3xxx_ai_insn_read(struct comedi_device *dev,
442 struct comedi_subdevice *s,
443 struct comedi_insn *insn,
444 unsigned int *data)
445{
446 int ret;
447 int i;
448
449 ret = apci3xxx_ai_setup(dev, insn->chanspec);
450 if (ret)
451 return ret;
452
453 for (i = 0; i < insn->n; i++) {
454
455 writel(0x80000, dev->mmio + 8);
456
457
458 ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
459 if (ret)
460 return ret;
461
462
463 data[i] = readl(dev->mmio + 28);
464 }
465
466 return insn->n;
467}
468
469static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
470 unsigned int *ns, unsigned int flags)
471{
472 const struct apci3xxx_boardinfo *board = dev->board_ptr;
473 struct apci3xxx_private *devpriv = dev->private;
474 unsigned int base;
475 unsigned int timer;
476 int time_base;
477
478
479 for (time_base = 0; time_base < 3; time_base++) {
480
481 if (!(board->ai_conv_units & (1 << time_base)))
482 continue;
483
484 switch (time_base) {
485 case 0:
486 base = 1;
487 break;
488 case 1:
489 base = 1000;
490 break;
491 case 2:
492 base = 1000000;
493 break;
494 }
495
496 switch (flags & CMDF_ROUND_MASK) {
497 case CMDF_ROUND_NEAREST:
498 default:
499 timer = (*ns + base / 2) / base;
500 break;
501 case CMDF_ROUND_DOWN:
502 timer = *ns / base;
503 break;
504 case CMDF_ROUND_UP:
505 timer = (*ns + base - 1) / base;
506 break;
507 }
508
509 if (timer < 0x10000) {
510 devpriv->ai_time_base = time_base;
511 devpriv->ai_timer = timer;
512 *ns = timer * time_base;
513 return 0;
514 }
515 }
516 return -EINVAL;
517}
518
519static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
520 struct comedi_subdevice *s,
521 struct comedi_cmd *cmd)
522{
523 const struct apci3xxx_boardinfo *board = dev->board_ptr;
524 int err = 0;
525 unsigned int arg;
526
527
528
529 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
530 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
531 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
532 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
533 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
534
535 if (err)
536 return 1;
537
538
539
540 err |= comedi_check_trigger_is_unique(cmd->stop_src);
541
542
543
544 if (err)
545 return 2;
546
547
548
549 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
550 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
551 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
552 board->ai_min_acq_ns);
553 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
554 cmd->chanlist_len);
555
556 if (cmd->stop_src == TRIG_COUNT)
557 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
558 else
559 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
560
561 if (err)
562 return 3;
563
564
565
566 arg = cmd->convert_arg;
567 err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
568 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
569
570 if (err)
571 return 4;
572
573 return 0;
574}
575
576static int apci3xxx_ai_cmd(struct comedi_device *dev,
577 struct comedi_subdevice *s)
578{
579 struct apci3xxx_private *devpriv = dev->private;
580 struct comedi_cmd *cmd = &s->async->cmd;
581 int ret;
582
583 ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
584 if (ret)
585 return ret;
586
587
588 writel(devpriv->ai_time_base, dev->mmio + 36);
589
590
591 writel(devpriv->ai_timer, dev->mmio + 32);
592
593
594 writel(0x180000, dev->mmio + 8);
595
596 return 0;
597}
598
599static int apci3xxx_ai_cancel(struct comedi_device *dev,
600 struct comedi_subdevice *s)
601{
602 return 0;
603}
604
605static int apci3xxx_ao_eoc(struct comedi_device *dev,
606 struct comedi_subdevice *s,
607 struct comedi_insn *insn,
608 unsigned long context)
609{
610 unsigned int status;
611
612 status = readl(dev->mmio + 96);
613 if (status & 0x100)
614 return 0;
615 return -EBUSY;
616}
617
618static int apci3xxx_ao_insn_write(struct comedi_device *dev,
619 struct comedi_subdevice *s,
620 struct comedi_insn *insn,
621 unsigned int *data)
622{
623 unsigned int chan = CR_CHAN(insn->chanspec);
624 unsigned int range = CR_RANGE(insn->chanspec);
625 int ret;
626 int i;
627
628 for (i = 0; i < insn->n; i++) {
629 unsigned int val = data[i];
630
631
632 writel(range, dev->mmio + 96);
633
634
635 writel((val << 8) | chan, dev->mmio + 100);
636
637
638 ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
639 if (ret)
640 return ret;
641
642 s->readback[chan] = val;
643 }
644
645 return insn->n;
646}
647
648static int apci3xxx_di_insn_bits(struct comedi_device *dev,
649 struct comedi_subdevice *s,
650 struct comedi_insn *insn,
651 unsigned int *data)
652{
653 data[1] = inl(dev->iobase + 32) & 0xf;
654
655 return insn->n;
656}
657
658static int apci3xxx_do_insn_bits(struct comedi_device *dev,
659 struct comedi_subdevice *s,
660 struct comedi_insn *insn,
661 unsigned int *data)
662{
663 s->state = inl(dev->iobase + 48) & 0xf;
664
665 if (comedi_dio_update_state(s, data))
666 outl(s->state, dev->iobase + 48);
667
668 data[1] = s->state;
669
670 return insn->n;
671}
672
673static int apci3xxx_dio_insn_config(struct comedi_device *dev,
674 struct comedi_subdevice *s,
675 struct comedi_insn *insn,
676 unsigned int *data)
677{
678 unsigned int chan = CR_CHAN(insn->chanspec);
679 unsigned int mask = 0;
680 int ret;
681
682
683
684
685
686
687 if (data[0] != INSN_CONFIG_DIO_QUERY) {
688
689 if (chan < 16)
690 return -EINVAL;
691
692
693 mask = 0xff0000;
694 }
695
696 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
697 if (ret)
698 return ret;
699
700
701 outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
702
703 return insn->n;
704}
705
706static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
707 struct comedi_subdevice *s,
708 struct comedi_insn *insn,
709 unsigned int *data)
710{
711 unsigned int mask;
712 unsigned int val;
713
714 mask = comedi_dio_update_state(s, data);
715 if (mask) {
716 if (mask & 0xff)
717 outl(s->state & 0xff, dev->iobase + 80);
718 if (mask & 0xff0000)
719 outl((s->state >> 16) & 0xff, dev->iobase + 112);
720 }
721
722 val = inl(dev->iobase + 80);
723 val |= (inl(dev->iobase + 64) << 8);
724 if (s->io_bits & 0xff0000)
725 val |= (inl(dev->iobase + 112) << 16);
726 else
727 val |= (inl(dev->iobase + 96) << 16);
728
729 data[1] = val;
730
731 return insn->n;
732}
733
734static int apci3xxx_reset(struct comedi_device *dev)
735{
736 unsigned int val;
737 int i;
738
739
740 disable_irq(dev->irq);
741
742
743 writel(0, dev->mmio + 8);
744
745
746 val = readl(dev->mmio + 16);
747 writel(val, dev->mmio + 16);
748
749
750 readl(dev->mmio + 20);
751
752
753 for (i = 0; i < 16; i++)
754 val = readl(dev->mmio + 28);
755
756
757 enable_irq(dev->irq);
758
759 return 0;
760}
761
762static int apci3xxx_auto_attach(struct comedi_device *dev,
763 unsigned long context)
764{
765 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
766 const struct apci3xxx_boardinfo *board = NULL;
767 struct apci3xxx_private *devpriv;
768 struct comedi_subdevice *s;
769 int n_subdevices;
770 int subdev;
771 int ret;
772
773 if (context < ARRAY_SIZE(apci3xxx_boardtypes))
774 board = &apci3xxx_boardtypes[context];
775 if (!board)
776 return -ENODEV;
777 dev->board_ptr = board;
778 dev->board_name = board->name;
779
780 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
781 if (!devpriv)
782 return -ENOMEM;
783
784 ret = comedi_pci_enable(dev);
785 if (ret)
786 return ret;
787
788 dev->iobase = pci_resource_start(pcidev, 2);
789 dev->mmio = pci_ioremap_bar(pcidev, 3);
790
791 if (pcidev->irq > 0) {
792 ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
793 IRQF_SHARED, dev->board_name, dev);
794 if (ret == 0)
795 dev->irq = pcidev->irq;
796 }
797
798 n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
799 board->has_dig_in + board->has_dig_out +
800 board->has_ttl_io;
801 ret = comedi_alloc_subdevices(dev, n_subdevices);
802 if (ret)
803 return ret;
804
805 subdev = 0;
806
807
808 if (board->ai_n_chan) {
809 s = &dev->subdevices[subdev];
810 s->type = COMEDI_SUBD_AI;
811 s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
812 s->n_chan = board->ai_n_chan;
813 s->maxdata = board->ai_maxdata;
814 s->range_table = &apci3xxx_ai_range;
815 s->insn_read = apci3xxx_ai_insn_read;
816 if (dev->irq) {
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835 dev->read_subdev = s;
836 s->subdev_flags |= SDF_CMD_READ;
837 s->len_chanlist = 1;
838 s->do_cmdtest = apci3xxx_ai_cmdtest;
839 s->do_cmd = apci3xxx_ai_cmd;
840 s->cancel = apci3xxx_ai_cancel;
841 }
842
843 subdev++;
844 }
845
846
847 if (board->has_ao) {
848 s = &dev->subdevices[subdev];
849 s->type = COMEDI_SUBD_AO;
850 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
851 s->n_chan = 4;
852 s->maxdata = 0x0fff;
853 s->range_table = &apci3xxx_ao_range;
854 s->insn_write = apci3xxx_ao_insn_write;
855
856 ret = comedi_alloc_subdev_readback(s);
857 if (ret)
858 return ret;
859
860 subdev++;
861 }
862
863
864 if (board->has_dig_in) {
865 s = &dev->subdevices[subdev];
866 s->type = COMEDI_SUBD_DI;
867 s->subdev_flags = SDF_READABLE;
868 s->n_chan = 4;
869 s->maxdata = 1;
870 s->range_table = &range_digital;
871 s->insn_bits = apci3xxx_di_insn_bits;
872
873 subdev++;
874 }
875
876
877 if (board->has_dig_out) {
878 s = &dev->subdevices[subdev];
879 s->type = COMEDI_SUBD_DO;
880 s->subdev_flags = SDF_WRITABLE;
881 s->n_chan = 4;
882 s->maxdata = 1;
883 s->range_table = &range_digital;
884 s->insn_bits = apci3xxx_do_insn_bits;
885
886 subdev++;
887 }
888
889
890 if (board->has_ttl_io) {
891 s = &dev->subdevices[subdev];
892 s->type = COMEDI_SUBD_DIO;
893 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
894 s->n_chan = 24;
895 s->maxdata = 1;
896 s->io_bits = 0xff;
897 s->range_table = &range_digital;
898 s->insn_config = apci3xxx_dio_insn_config;
899 s->insn_bits = apci3xxx_dio_insn_bits;
900
901 subdev++;
902 }
903
904 apci3xxx_reset(dev);
905 return 0;
906}
907
908static void apci3xxx_detach(struct comedi_device *dev)
909{
910 if (dev->iobase)
911 apci3xxx_reset(dev);
912 comedi_pci_detach(dev);
913}
914
915static struct comedi_driver apci3xxx_driver = {
916 .driver_name = "addi_apci_3xxx",
917 .module = THIS_MODULE,
918 .auto_attach = apci3xxx_auto_attach,
919 .detach = apci3xxx_detach,
920};
921
922static int apci3xxx_pci_probe(struct pci_dev *dev,
923 const struct pci_device_id *id)
924{
925 return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
926}
927
928static const struct pci_device_id apci3xxx_pci_table[] = {
929 { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
930 { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
931 { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
932 { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
933 { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
934 { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
935 { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
936 { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
937 { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
938 { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
939 { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
940 { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
941 { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
942 { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
943 { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
944 { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
945 { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
946 { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
947 { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
948 { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
949 { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
950 { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
951 { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
952 { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
953 { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
954 { 0 }
955};
956MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
957
958static struct pci_driver apci3xxx_pci_driver = {
959 .name = "addi_apci_3xxx",
960 .id_table = apci3xxx_pci_table,
961 .probe = apci3xxx_pci_probe,
962 .remove = comedi_pci_auto_unconfig,
963};
964module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
965
966MODULE_AUTHOR("Comedi http://www.comedi.org");
967MODULE_DESCRIPTION("Comedi low-level driver");
968MODULE_LICENSE("GPL");
969