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