Version:  2.0.40 2.2.26 2.4.37 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 3.18 3.19 4.0 4.1

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

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