Version:  2.0.40 2.2.26 2.4.37 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17

Linux/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         unsigned int ai_timer;
357         unsigned char ai_time_base;
358 };
359 
360 static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
361 {
362         struct comedi_device *dev = d;
363         struct comedi_subdevice *s = dev->read_subdev;
364         unsigned int status;
365         unsigned int val;
366 
367         /* Test if interrupt occur */
368         status = readl(dev->mmio + 16);
369         if ((status & 0x2) == 0x2) {
370                 /* Reset the interrupt */
371                 writel(status, dev->mmio + 16);
372 
373                 val = readl(dev->mmio + 28);
374                 comedi_buf_put(s, val);
375 
376                 s->async->events |= COMEDI_CB_EOA;
377                 comedi_event(dev, s);
378 
379                 return IRQ_HANDLED;
380         }
381         return IRQ_NONE;
382 }
383 
384 static int apci3xxx_ai_started(struct comedi_device *dev)
385 {
386         if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
387                 return 1;
388 
389         return 0;
390 }
391 
392 static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
393 {
394         unsigned int chan = CR_CHAN(chanspec);
395         unsigned int range = CR_RANGE(chanspec);
396         unsigned int aref = CR_AREF(chanspec);
397         unsigned int delay_mode;
398         unsigned int val;
399 
400         if (apci3xxx_ai_started(dev))
401                 return -EBUSY;
402 
403         /* Clear the FIFO */
404         writel(0x10000, dev->mmio + 12);
405 
406         /* Get and save the delay mode */
407         delay_mode = readl(dev->mmio + 4);
408         delay_mode &= 0xfffffef0;
409 
410         /* Channel configuration selection */
411         writel(delay_mode, dev->mmio + 4);
412 
413         /* Make the configuration */
414         val = (range & 3) | ((range >> 2) << 6) |
415               ((aref == AREF_DIFF) << 7);
416         writel(val, dev->mmio + 0);
417 
418         /* Channel selection */
419         writel(delay_mode | 0x100, dev->mmio + 4);
420         writel(chan, dev->mmio + 0);
421 
422         /* Restore delay mode */
423         writel(delay_mode, dev->mmio + 4);
424 
425         /* Set the number of sequence to 1 */
426         writel(1, dev->mmio + 48);
427 
428         return 0;
429 }
430 
431 static int apci3xxx_ai_eoc(struct comedi_device *dev,
432                            struct comedi_subdevice *s,
433                            struct comedi_insn *insn,
434                            unsigned long context)
435 {
436         unsigned int status;
437 
438         status = readl(dev->mmio + 20);
439         if (status & 0x1)
440                 return 0;
441         return -EBUSY;
442 }
443 
444 static int apci3xxx_ai_insn_read(struct comedi_device *dev,
445                                  struct comedi_subdevice *s,
446                                  struct comedi_insn *insn,
447                                  unsigned int *data)
448 {
449         int ret;
450         int i;
451 
452         ret = apci3xxx_ai_setup(dev, insn->chanspec);
453         if (ret)
454                 return ret;
455 
456         for (i = 0; i < insn->n; i++) {
457                 /* Start the conversion */
458                 writel(0x80000, dev->mmio + 8);
459 
460                 /* Wait the EOS */
461                 ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
462                 if (ret)
463                         return ret;
464 
465                 /* Read the analog value */
466                 data[i] = readl(dev->mmio + 28);
467         }
468 
469         return insn->n;
470 }
471 
472 static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
473                                    unsigned int *ns, unsigned int flags)
474 {
475         const struct apci3xxx_boardinfo *board = comedi_board(dev);
476         struct apci3xxx_private *devpriv = dev->private;
477         unsigned int base;
478         unsigned int timer;
479         int time_base;
480 
481         /* time_base: 0 = ns, 1 = us, 2 = ms */
482         for (time_base = 0; time_base < 3; time_base++) {
483                 /* skip unsupported time bases */
484                 if (!(board->ai_conv_units & (1 << time_base)))
485                         continue;
486 
487                 switch (time_base) {
488                 case 0:
489                         base = 1;
490                         break;
491                 case 1:
492                         base = 1000;
493                         break;
494                 case 2:
495                         base = 1000000;
496                         break;
497                 }
498 
499                 switch (flags & TRIG_ROUND_MASK) {
500                 case TRIG_ROUND_NEAREST:
501                 default:
502                         timer = (*ns + base / 2) / base;
503                         break;
504                 case TRIG_ROUND_DOWN:
505                         timer = *ns / base;
506                         break;
507                 case TRIG_ROUND_UP:
508                         timer = (*ns + base - 1) / base;
509                         break;
510                 }
511 
512                 if (timer < 0x10000) {
513                         devpriv->ai_time_base = time_base;
514                         devpriv->ai_timer = timer;
515                         *ns = timer * time_base;
516                         return 0;
517                 }
518         }
519         return -EINVAL;
520 }
521 
522 static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
523                                struct comedi_subdevice *s,
524                                struct comedi_cmd *cmd)
525 {
526         const struct apci3xxx_boardinfo *board = comedi_board(dev);
527         int err = 0;
528         unsigned int arg;
529 
530         /* Step 1 : check if triggers are trivially valid */
531 
532         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
533         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
534         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
535         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
536         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
537 
538         if (err)
539                 return 1;
540 
541         /* Step 2a : make sure trigger sources are unique */
542 
543         err |= cfc_check_trigger_is_unique(cmd->stop_src);
544 
545         /* Step 2b : and mutually compatible */
546 
547         if (err)
548                 return 2;
549 
550         /* Step 3: check if arguments are trivially valid */
551 
552         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
553         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
554         err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
555                                          board->ai_min_acq_ns);
556         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
557 
558         if (cmd->stop_src == TRIG_COUNT)
559                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
560         else    /* TRIG_NONE */
561                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
562 
563         if (err)
564                 return 3;
565 
566         /* step 4: fix up any arguments */
567 
568         arg = cmd->convert_arg;
569         err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
570         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
571 
572         if (err)
573                 return 4;
574 
575         return 0;
576 }
577 
578 static int apci3xxx_ai_cmd(struct comedi_device *dev,
579                            struct comedi_subdevice *s)
580 {
581         struct apci3xxx_private *devpriv = dev->private;
582         struct comedi_cmd *cmd = &s->async->cmd;
583         int ret;
584 
585         ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
586         if (ret)
587                 return ret;
588 
589         /* Set the convert timing unit */
590         writel(devpriv->ai_time_base, dev->mmio + 36);
591 
592         /* Set the convert timing */
593         writel(devpriv->ai_timer, dev->mmio + 32);
594 
595         /* Start the conversion */
596         writel(0x180000, dev->mmio + 8);
597 
598         return 0;
599 }
600 
601 static int apci3xxx_ai_cancel(struct comedi_device *dev,
602                               struct comedi_subdevice *s)
603 {
604         return 0;
605 }
606 
607 static int apci3xxx_ao_eoc(struct comedi_device *dev,
608                            struct comedi_subdevice *s,
609                            struct comedi_insn *insn,
610                            unsigned long context)
611 {
612         unsigned int status;
613 
614         status = readl(dev->mmio + 96);
615         if (status & 0x100)
616                 return 0;
617         return -EBUSY;
618 }
619 
620 static int apci3xxx_ao_insn_write(struct comedi_device *dev,
621                                   struct comedi_subdevice *s,
622                                   struct comedi_insn *insn,
623                                   unsigned int *data)
624 {
625         unsigned int chan = CR_CHAN(insn->chanspec);
626         unsigned int range = CR_RANGE(insn->chanspec);
627         int ret;
628         int i;
629 
630         for (i = 0; i < insn->n; i++) {
631                 /* Set the range selection */
632                 writel(range, dev->mmio + 96);
633 
634                 /* Write the analog value to the selected channel */
635                 writel((data[i] << 8) | chan, dev->mmio + 100);
636 
637                 /* Wait the end of transfer */
638                 ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
639                 if (ret)
640                         return ret;
641         }
642 
643         return insn->n;
644 }
645 
646 static int apci3xxx_di_insn_bits(struct comedi_device *dev,
647                                  struct comedi_subdevice *s,
648                                  struct comedi_insn *insn,
649                                  unsigned int *data)
650 {
651         data[1] = inl(dev->iobase + 32) & 0xf;
652 
653         return insn->n;
654 }
655 
656 static int apci3xxx_do_insn_bits(struct comedi_device *dev,
657                                  struct comedi_subdevice *s,
658                                  struct comedi_insn *insn,
659                                  unsigned int *data)
660 {
661         s->state = inl(dev->iobase + 48) & 0xf;
662 
663         if (comedi_dio_update_state(s, data))
664                 outl(s->state, dev->iobase + 48);
665 
666         data[1] = s->state;
667 
668         return insn->n;
669 }
670 
671 static int apci3xxx_dio_insn_config(struct comedi_device *dev,
672                                     struct comedi_subdevice *s,
673                                     struct comedi_insn *insn,
674                                     unsigned int *data)
675 {
676         unsigned int chan = CR_CHAN(insn->chanspec);
677         unsigned int mask = 0;
678         int ret;
679 
680         /*
681          * Port 0 (channels 0-7) are always inputs
682          * Port 1 (channels 8-15) are always outputs
683          * Port 2 (channels 16-23) are programmable i/o
684          */
685         if (data[0] != INSN_CONFIG_DIO_QUERY) {
686                 /* ignore all other instructions for ports 0 and 1 */
687                 if (chan < 16)
688                         return -EINVAL;
689 
690                 /* changing any channel in port 2 changes the entire port */
691                 mask = 0xff0000;
692         }
693 
694         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
695         if (ret)
696                 return ret;
697 
698         /* update port 2 configuration */
699         outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
700 
701         return insn->n;
702 }
703 
704 static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
705                                   struct comedi_subdevice *s,
706                                   struct comedi_insn *insn,
707                                   unsigned int *data)
708 {
709         unsigned int mask;
710         unsigned int val;
711 
712         mask = comedi_dio_update_state(s, data);
713         if (mask) {
714                 if (mask & 0xff)
715                         outl(s->state & 0xff, dev->iobase + 80);
716                 if (mask & 0xff0000)
717                         outl((s->state >> 16) & 0xff, dev->iobase + 112);
718         }
719 
720         val = inl(dev->iobase + 80);
721         val |= (inl(dev->iobase + 64) << 8);
722         if (s->io_bits & 0xff0000)
723                 val |= (inl(dev->iobase + 112) << 16);
724         else
725                 val |= (inl(dev->iobase + 96) << 16);
726 
727         data[1] = val;
728 
729         return insn->n;
730 }
731 
732 static int apci3xxx_reset(struct comedi_device *dev)
733 {
734         unsigned int val;
735         int i;
736 
737         /* Disable the interrupt */
738         disable_irq(dev->irq);
739 
740         /* Clear the start command */
741         writel(0, dev->mmio + 8);
742 
743         /* Reset the interrupt flags */
744         val = readl(dev->mmio + 16);
745         writel(val, dev->mmio + 16);
746 
747         /* clear the EOS */
748         readl(dev->mmio + 20);
749 
750         /* Clear the FIFO */
751         for (i = 0; i < 16; i++)
752                 val = readl(dev->mmio + 28);
753 
754         /* Enable the interrupt */
755         enable_irq(dev->irq);
756 
757         return 0;
758 }
759 
760 static int apci3xxx_auto_attach(struct comedi_device *dev,
761                                 unsigned long context)
762 {
763         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
764         const struct apci3xxx_boardinfo *board = NULL;
765         struct apci3xxx_private *devpriv;
766         struct comedi_subdevice *s;
767         int n_subdevices;
768         int subdev;
769         int ret;
770 
771         if (context < ARRAY_SIZE(apci3xxx_boardtypes))
772                 board = &apci3xxx_boardtypes[context];
773         if (!board)
774                 return -ENODEV;
775         dev->board_ptr = board;
776         dev->board_name = board->name;
777 
778         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
779         if (!devpriv)
780                 return -ENOMEM;
781 
782         ret = comedi_pci_enable(dev);
783         if (ret)
784                 return ret;
785 
786         dev->iobase = pci_resource_start(pcidev, 2);
787         dev->mmio = pci_ioremap_bar(pcidev, 3);
788 
789         if (pcidev->irq > 0) {
790                 ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
791                                   IRQF_SHARED, dev->board_name, dev);
792                 if (ret == 0)
793                         dev->irq = pcidev->irq;
794         }
795 
796         n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
797                        board->has_dig_in + board->has_dig_out +
798                        board->has_ttl_io;
799         ret = comedi_alloc_subdevices(dev, n_subdevices);
800         if (ret)
801                 return ret;
802 
803         subdev = 0;
804 
805         /* Analog Input subdevice */
806         if (board->ai_n_chan) {
807                 s = &dev->subdevices[subdev];
808                 s->type         = COMEDI_SUBD_AI;
809                 s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
810                 s->n_chan       = board->ai_n_chan;
811                 s->maxdata      = board->ai_maxdata;
812                 s->range_table  = &apci3xxx_ai_range;
813                 s->insn_read    = apci3xxx_ai_insn_read;
814                 if (dev->irq) {
815                         /*
816                          * FIXME: The hardware supports multiple scan modes
817                          * but the original addi-data driver only supported
818                          * reading a single channel with interrupts. Need a
819                          * proper datasheet to fix this.
820                          *
821                          * The following scan modes are supported by the
822                          * hardware:
823                          *   1) Single software scan
824                          *   2) Single hardware triggered scan
825                          *   3) Continuous software scan
826                          *   4) Continuous software scan with timer delay
827                          *   5) Continuous hardware triggered scan
828                          *   6) Continuous hardware triggered scan with timer
829                          *      delay
830                          *
831                          * For now, limit the chanlist to a single channel.
832                          */
833                         dev->read_subdev = s;
834                         s->subdev_flags |= SDF_CMD_READ;
835                         s->len_chanlist = 1;
836                         s->do_cmdtest   = apci3xxx_ai_cmdtest;
837                         s->do_cmd       = apci3xxx_ai_cmd;
838                         s->cancel       = apci3xxx_ai_cancel;
839                 }
840 
841                 subdev++;
842         }
843 
844         /* Analog Output subdevice */
845         if (board->has_ao) {
846                 s = &dev->subdevices[subdev];
847                 s->type         = COMEDI_SUBD_AO;
848                 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
849                 s->n_chan       = 4;
850                 s->maxdata      = 0x0fff;
851                 s->range_table  = &apci3xxx_ao_range;
852                 s->insn_write   = apci3xxx_ao_insn_write;
853 
854                 subdev++;
855         }
856 
857         /* Digital Input subdevice */
858         if (board->has_dig_in) {
859                 s = &dev->subdevices[subdev];
860                 s->type         = COMEDI_SUBD_DI;
861                 s->subdev_flags = SDF_READABLE;
862                 s->n_chan       = 4;
863                 s->maxdata      = 1;
864                 s->range_table  = &range_digital;
865                 s->insn_bits    = apci3xxx_di_insn_bits;
866 
867                 subdev++;
868         }
869 
870         /* Digital Output subdevice */
871         if (board->has_dig_out) {
872                 s = &dev->subdevices[subdev];
873                 s->type         = COMEDI_SUBD_DO;
874                 s->subdev_flags = SDF_WRITEABLE;
875                 s->n_chan       = 4;
876                 s->maxdata      = 1;
877                 s->range_table  = &range_digital;
878                 s->insn_bits    = apci3xxx_do_insn_bits;
879 
880                 subdev++;
881         }
882 
883         /* TTL Digital I/O subdevice */
884         if (board->has_ttl_io) {
885                 s = &dev->subdevices[subdev];
886                 s->type         = COMEDI_SUBD_DIO;
887                 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
888                 s->n_chan       = 24;
889                 s->maxdata      = 1;
890                 s->io_bits      = 0xff; /* channels 0-7 are always outputs */
891                 s->range_table  = &range_digital;
892                 s->insn_config  = apci3xxx_dio_insn_config;
893                 s->insn_bits    = apci3xxx_dio_insn_bits;
894 
895                 subdev++;
896         }
897 
898         apci3xxx_reset(dev);
899         return 0;
900 }
901 
902 static void apci3xxx_detach(struct comedi_device *dev)
903 {
904         struct apci3xxx_private *devpriv = dev->private;
905 
906         if (devpriv) {
907                 if (dev->iobase)
908                         apci3xxx_reset(dev);
909                 if (dev->irq)
910                         free_irq(dev->irq, dev);
911                 if (dev->mmio)
912                         iounmap(dev->mmio);
913         }
914         comedi_pci_disable(dev);
915 }
916 
917 static struct comedi_driver apci3xxx_driver = {
918         .driver_name    = "addi_apci_3xxx",
919         .module         = THIS_MODULE,
920         .auto_attach    = apci3xxx_auto_attach,
921         .detach         = apci3xxx_detach,
922 };
923 
924 static int apci3xxx_pci_probe(struct pci_dev *dev,
925                               const struct pci_device_id *id)
926 {
927         return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
928 }
929 
930 static const struct pci_device_id apci3xxx_pci_table[] = {
931         { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
932         { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
933         { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
934         { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
935         { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
936         { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
937         { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
938         { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
939         { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
940         { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
941         { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
942         { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
943         { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
944         { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
945         { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
946         { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
947         { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
948         { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
949         { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
950         { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
951         { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
952         { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
953         { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
954         { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
955         { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
956         { 0 }
957 };
958 MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
959 
960 static struct pci_driver apci3xxx_pci_driver = {
961         .name           = "addi_apci_3xxx",
962         .id_table       = apci3xxx_pci_table,
963         .probe          = apci3xxx_pci_probe,
964         .remove         = comedi_pci_auto_unconfig,
965 };
966 module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
967 
968 MODULE_AUTHOR("Comedi http://www.comedi.org");
969 MODULE_DESCRIPTION("Comedi low-level driver");
970 MODULE_LICENSE("GPL");
971 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us