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