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