Merge tag 'for-5.14-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[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
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
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
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
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
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
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
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
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
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
590 static int apci3xxx_ai_cancel(struct comedi_device *dev,
591                               struct comedi_subdevice *s)
592 {
593         return 0;
594 }
595
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
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
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
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
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
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
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
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
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
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");