Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[deliverable/linux.git] / drivers / staging / comedi / drivers / addi_apci_3xxx.c
CommitLineData
5c70cbfb
HS
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
ce157f80 25#include <linux/module.h>
33782dd5 26#include <linux/pci.h>
1867add9 27#include <linux/interrupt.h>
33782dd5 28
3d41c443 29#include "../comedidev.h"
3d41c443 30
f32376e9
HS
31#include "comedi_fc.h"
32
a67e0cc7
HS
33#define CONV_UNIT_NS (1 << 0)
34#define CONV_UNIT_US (1 << 1)
35#define CONV_UNIT_MS (1 << 2)
36
98a85621
HS
37static 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
50static const struct comedi_lrange apci3xxx_ao_range = {
51 2, {
52 BIP_RANGE(10),
53 UNI_RANGE(10)
54 }
55};
56
dbae4575
HS
57enum 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
e053b241 85struct apci3xxx_boardinfo {
6abeba09 86 const char *name;
8a1ec30d 87 int ai_subdev_flags;
47bd1ad8 88 int ai_n_chan;
5469d929 89 unsigned int ai_maxdata;
a67e0cc7 90 unsigned char ai_conv_units;
8edd4425 91 unsigned int ai_min_acq_ns;
fa81e2f1 92 unsigned int has_ao:1;
4aab8bfd
HS
93 unsigned int has_dig_in:1;
94 unsigned int has_dig_out:1;
0ed9f25f 95 unsigned int has_ttl_io:1;
e053b241
HS
96};
97
98static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
dbae4575 99 [BOARD_APCI3000_16] = {
6abeba09 100 .name = "apci3000-16",
8a1ec30d 101 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 102 .ai_n_chan = 16,
5469d929 103 .ai_maxdata = 0x0fff,
a67e0cc7 104 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 105 .ai_min_acq_ns = 10000,
0ed9f25f 106 .has_ttl_io = 1,
dbae4575
HS
107 },
108 [BOARD_APCI3000_8] = {
6abeba09 109 .name = "apci3000-8",
8a1ec30d 110 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 111 .ai_n_chan = 8,
5469d929 112 .ai_maxdata = 0x0fff,
a67e0cc7 113 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 114 .ai_min_acq_ns = 10000,
0ed9f25f 115 .has_ttl_io = 1,
dbae4575
HS
116 },
117 [BOARD_APCI3000_4] = {
6abeba09 118 .name = "apci3000-4",
8a1ec30d 119 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 120 .ai_n_chan = 4,
5469d929 121 .ai_maxdata = 0x0fff,
a67e0cc7 122 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 123 .ai_min_acq_ns = 10000,
0ed9f25f 124 .has_ttl_io = 1,
dbae4575
HS
125 },
126 [BOARD_APCI3006_16] = {
6abeba09 127 .name = "apci3006-16",
8a1ec30d 128 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 129 .ai_n_chan = 16,
5469d929 130 .ai_maxdata = 0xffff,
a67e0cc7 131 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 132 .ai_min_acq_ns = 10000,
0ed9f25f 133 .has_ttl_io = 1,
dbae4575
HS
134 },
135 [BOARD_APCI3006_8] = {
6abeba09 136 .name = "apci3006-8",
8a1ec30d 137 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 138 .ai_n_chan = 8,
5469d929 139 .ai_maxdata = 0xffff,
a67e0cc7 140 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 141 .ai_min_acq_ns = 10000,
0ed9f25f 142 .has_ttl_io = 1,
dbae4575
HS
143 },
144 [BOARD_APCI3006_4] = {
6abeba09 145 .name = "apci3006-4",
8a1ec30d 146 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 147 .ai_n_chan = 4,
5469d929 148 .ai_maxdata = 0xffff,
a67e0cc7 149 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 150 .ai_min_acq_ns = 10000,
0ed9f25f 151 .has_ttl_io = 1,
dbae4575
HS
152 },
153 [BOARD_APCI3010_16] = {
6abeba09 154 .name = "apci3010-16",
8a1ec30d 155 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 156 .ai_n_chan = 16,
5469d929 157 .ai_maxdata = 0x0fff,
a67e0cc7 158 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 159 .ai_min_acq_ns = 5000,
4aab8bfd
HS
160 .has_dig_in = 1,
161 .has_dig_out = 1,
0ed9f25f 162 .has_ttl_io = 1,
dbae4575
HS
163 },
164 [BOARD_APCI3010_8] = {
6abeba09 165 .name = "apci3010-8",
8a1ec30d 166 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 167 .ai_n_chan = 8,
5469d929 168 .ai_maxdata = 0x0fff,
a67e0cc7 169 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 170 .ai_min_acq_ns = 5000,
4aab8bfd
HS
171 .has_dig_in = 1,
172 .has_dig_out = 1,
0ed9f25f 173 .has_ttl_io = 1,
dbae4575
HS
174 },
175 [BOARD_APCI3010_4] = {
6abeba09 176 .name = "apci3010-4",
8a1ec30d 177 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 178 .ai_n_chan = 4,
5469d929 179 .ai_maxdata = 0x0fff,
a67e0cc7 180 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 181 .ai_min_acq_ns = 5000,
4aab8bfd
HS
182 .has_dig_in = 1,
183 .has_dig_out = 1,
0ed9f25f 184 .has_ttl_io = 1,
dbae4575
HS
185 },
186 [BOARD_APCI3016_16] = {
6abeba09 187 .name = "apci3016-16",
8a1ec30d 188 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 189 .ai_n_chan = 16,
5469d929 190 .ai_maxdata = 0xffff,
a67e0cc7 191 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 192 .ai_min_acq_ns = 5000,
4aab8bfd
HS
193 .has_dig_in = 1,
194 .has_dig_out = 1,
0ed9f25f 195 .has_ttl_io = 1,
dbae4575
HS
196 },
197 [BOARD_APCI3016_8] = {
6abeba09 198 .name = "apci3016-8",
8a1ec30d 199 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 200 .ai_n_chan = 8,
5469d929 201 .ai_maxdata = 0xffff,
a67e0cc7 202 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 203 .ai_min_acq_ns = 5000,
4aab8bfd
HS
204 .has_dig_in = 1,
205 .has_dig_out = 1,
0ed9f25f 206 .has_ttl_io = 1,
dbae4575
HS
207 },
208 [BOARD_APCI3016_4] = {
6abeba09 209 .name = "apci3016-4",
8a1ec30d 210 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 211 .ai_n_chan = 4,
5469d929 212 .ai_maxdata = 0xffff,
a67e0cc7 213 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 214 .ai_min_acq_ns = 5000,
4aab8bfd
HS
215 .has_dig_in = 1,
216 .has_dig_out = 1,
0ed9f25f 217 .has_ttl_io = 1,
dbae4575
HS
218 },
219 [BOARD_APCI3100_16_4] = {
6abeba09 220 .name = "apci3100-16-4",
8a1ec30d 221 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 222 .ai_n_chan = 16,
5469d929 223 .ai_maxdata = 0x0fff,
a67e0cc7 224 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 225 .ai_min_acq_ns = 10000,
fa81e2f1 226 .has_ao = 1,
0ed9f25f 227 .has_ttl_io = 1,
dbae4575
HS
228 },
229 [BOARD_APCI3100_8_4] = {
6abeba09 230 .name = "apci3100-8-4",
8a1ec30d 231 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 232 .ai_n_chan = 8,
5469d929 233 .ai_maxdata = 0x0fff,
a67e0cc7 234 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 235 .ai_min_acq_ns = 10000,
fa81e2f1 236 .has_ao = 1,
0ed9f25f 237 .has_ttl_io = 1,
dbae4575
HS
238 },
239 [BOARD_APCI3106_16_4] = {
6abeba09 240 .name = "apci3106-16-4",
8a1ec30d 241 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 242 .ai_n_chan = 16,
5469d929 243 .ai_maxdata = 0xffff,
a67e0cc7 244 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 245 .ai_min_acq_ns = 10000,
fa81e2f1 246 .has_ao = 1,
0ed9f25f 247 .has_ttl_io = 1,
dbae4575
HS
248 },
249 [BOARD_APCI3106_8_4] = {
6abeba09 250 .name = "apci3106-8-4",
8a1ec30d 251 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 252 .ai_n_chan = 8,
5469d929 253 .ai_maxdata = 0xffff,
a67e0cc7 254 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 255 .ai_min_acq_ns = 10000,
fa81e2f1 256 .has_ao = 1,
0ed9f25f 257 .has_ttl_io = 1,
dbae4575
HS
258 },
259 [BOARD_APCI3110_16_4] = {
6abeba09 260 .name = "apci3110-16-4",
8a1ec30d 261 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 262 .ai_n_chan = 16,
5469d929 263 .ai_maxdata = 0x0fff,
a67e0cc7 264 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 265 .ai_min_acq_ns = 5000,
fa81e2f1 266 .has_ao = 1,
4aab8bfd
HS
267 .has_dig_in = 1,
268 .has_dig_out = 1,
0ed9f25f 269 .has_ttl_io = 1,
dbae4575
HS
270 },
271 [BOARD_APCI3110_8_4] = {
6abeba09 272 .name = "apci3110-8-4",
8a1ec30d 273 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 274 .ai_n_chan = 8,
5469d929 275 .ai_maxdata = 0x0fff,
a67e0cc7 276 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 277 .ai_min_acq_ns = 5000,
fa81e2f1 278 .has_ao = 1,
4aab8bfd
HS
279 .has_dig_in = 1,
280 .has_dig_out = 1,
0ed9f25f 281 .has_ttl_io = 1,
dbae4575
HS
282 },
283 [BOARD_APCI3116_16_4] = {
6abeba09 284 .name = "apci3116-16-4",
8a1ec30d 285 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 286 .ai_n_chan = 16,
5469d929 287 .ai_maxdata = 0xffff,
a67e0cc7 288 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 289 .ai_min_acq_ns = 5000,
fa81e2f1 290 .has_ao = 1,
4aab8bfd
HS
291 .has_dig_in = 1,
292 .has_dig_out = 1,
0ed9f25f 293 .has_ttl_io = 1,
dbae4575
HS
294 },
295 [BOARD_APCI3116_8_4] = {
6abeba09 296 .name = "apci3116-8-4",
8a1ec30d 297 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
47bd1ad8 298 .ai_n_chan = 8,
5469d929 299 .ai_maxdata = 0xffff,
a67e0cc7 300 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 301 .ai_min_acq_ns = 5000,
fa81e2f1 302 .has_ao = 1,
4aab8bfd
HS
303 .has_dig_in = 1,
304 .has_dig_out = 1,
0ed9f25f 305 .has_ttl_io = 1,
dbae4575
HS
306 },
307 [BOARD_APCI3003] = {
6abeba09 308 .name = "apci3003",
8a1ec30d 309 .ai_subdev_flags = SDF_DIFF,
8d472906 310 .ai_n_chan = 4,
5469d929 311 .ai_maxdata = 0xffff,
a67e0cc7
HS
312 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US |
313 CONV_UNIT_NS,
8edd4425 314 .ai_min_acq_ns = 2500,
4aab8bfd
HS
315 .has_dig_in = 1,
316 .has_dig_out = 1,
dbae4575
HS
317 },
318 [BOARD_APCI3002_16] = {
6abeba09 319 .name = "apci3002-16",
8a1ec30d 320 .ai_subdev_flags = SDF_DIFF,
8d472906 321 .ai_n_chan = 16,
5469d929 322 .ai_maxdata = 0xffff,
a67e0cc7 323 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 324 .ai_min_acq_ns = 5000,
4aab8bfd
HS
325 .has_dig_in = 1,
326 .has_dig_out = 1,
dbae4575
HS
327 },
328 [BOARD_APCI3002_8] = {
6abeba09 329 .name = "apci3002-8",
8a1ec30d 330 .ai_subdev_flags = SDF_DIFF,
8d472906 331 .ai_n_chan = 8,
5469d929 332 .ai_maxdata = 0xffff,
a67e0cc7 333 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 334 .ai_min_acq_ns = 5000,
4aab8bfd
HS
335 .has_dig_in = 1,
336 .has_dig_out = 1,
dbae4575
HS
337 },
338 [BOARD_APCI3002_4] = {
6abeba09 339 .name = "apci3002-4",
8a1ec30d 340 .ai_subdev_flags = SDF_DIFF,
8d472906 341 .ai_n_chan = 4,
5469d929 342 .ai_maxdata = 0xffff,
a67e0cc7 343 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
8edd4425 344 .ai_min_acq_ns = 5000,
4aab8bfd
HS
345 .has_dig_in = 1,
346 .has_dig_out = 1,
dbae4575
HS
347 },
348 [BOARD_APCI3500] = {
6abeba09 349 .name = "apci3500",
fa81e2f1 350 .has_ao = 1,
0ed9f25f 351 .has_ttl_io = 1,
c0a053b8
HS
352 },
353};
354
1867add9 355struct apci3xxx_private {
f32376e9
HS
356 unsigned int ai_timer;
357 unsigned char ai_time_base;
1867add9
HS
358};
359
6c5b0fff
HS
360static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
361{
362 struct comedi_device *dev = d;
f32376e9 363 struct comedi_subdevice *s = dev->read_subdev;
6c5b0fff 364 unsigned int status;
f32376e9 365 unsigned int val;
6c5b0fff
HS
366
367 /* Test if interrupt occur */
24a44612 368 status = readl(dev->mmio + 16);
6c5b0fff
HS
369 if ((status & 0x2) == 0x2) {
370 /* Reset the interrupt */
24a44612 371 writel(status, dev->mmio + 16);
6c5b0fff 372
24a44612 373 val = readl(dev->mmio + 28);
3672effd 374 comedi_buf_put(s, val);
f32376e9
HS
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
384static int apci3xxx_ai_started(struct comedi_device *dev)
385{
24a44612 386 if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
f32376e9 387 return 1;
f32376e9 388
9ed221fa 389 return 0;
f32376e9
HS
390}
391
392static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
393{
f32376e9
HS
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 */
24a44612 404 writel(0x10000, dev->mmio + 12);
f32376e9
HS
405
406 /* Get and save the delay mode */
24a44612 407 delay_mode = readl(dev->mmio + 4);
f32376e9
HS
408 delay_mode &= 0xfffffef0;
409
410 /* Channel configuration selection */
24a44612 411 writel(delay_mode, dev->mmio + 4);
f32376e9
HS
412
413 /* Make the configuration */
414 val = (range & 3) | ((range >> 2) << 6) |
415 ((aref == AREF_DIFF) << 7);
24a44612 416 writel(val, dev->mmio + 0);
f32376e9
HS
417
418 /* Channel selection */
24a44612
HS
419 writel(delay_mode | 0x100, dev->mmio + 4);
420 writel(chan, dev->mmio + 0);
f32376e9
HS
421
422 /* Restore delay mode */
24a44612 423 writel(delay_mode, dev->mmio + 4);
f32376e9
HS
424
425 /* Set the number of sequence to 1 */
24a44612 426 writel(1, dev->mmio + 48);
f32376e9
HS
427
428 return 0;
429}
430
67562be5
HS
431static int apci3xxx_ai_eoc(struct comedi_device *dev,
432 struct comedi_subdevice *s,
433 struct comedi_insn *insn,
434 unsigned long context)
435{
67562be5
HS
436 unsigned int status;
437
24a44612 438 status = readl(dev->mmio + 20);
67562be5
HS
439 if (status & 0x1)
440 return 0;
441 return -EBUSY;
442}
443
f32376e9
HS
444static int apci3xxx_ai_insn_read(struct comedi_device *dev,
445 struct comedi_subdevice *s,
446 struct comedi_insn *insn,
447 unsigned int *data)
448{
f32376e9
HS
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 */
24a44612 458 writel(0x80000, dev->mmio + 8);
f32376e9
HS
459
460 /* Wait the EOS */
67562be5
HS
461 ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
462 if (ret)
463 return ret;
f32376e9
HS
464
465 /* Read the analog value */
24a44612 466 data[i] = readl(dev->mmio + 28);
f32376e9 467 }
6c5b0fff 468
f32376e9
HS
469 return insn->n;
470}
471
472static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
a207c12f 473 unsigned int *ns, unsigned int flags)
f32376e9 474{
ef97126f 475 const struct apci3xxx_boardinfo *board = dev->board_ptr;
f32376e9
HS
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 }
6c5b0fff 498
74eede61
IA
499 switch (flags & CMDF_ROUND_MASK) {
500 case CMDF_ROUND_NEAREST:
f32376e9
HS
501 default:
502 timer = (*ns + base / 2) / base;
503 break;
74eede61 504 case CMDF_ROUND_DOWN:
f32376e9
HS
505 timer = *ns / base;
506 break;
74eede61 507 case CMDF_ROUND_UP:
f32376e9
HS
508 timer = (*ns + base - 1) / base;
509 break;
510 }
6c5b0fff 511
f32376e9
HS
512 if (timer < 0x10000) {
513 devpriv->ai_time_base = time_base;
514 devpriv->ai_timer = timer;
515 *ns = timer * time_base;
516 return 0;
6c5b0fff
HS
517 }
518 }
f32376e9 519 return -EINVAL;
6c5b0fff
HS
520}
521
66573991
HS
522static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
523 struct comedi_subdevice *s,
524 struct comedi_cmd *cmd)
525{
ef97126f 526 const struct apci3xxx_boardinfo *board = dev->board_ptr;
f32376e9 527 int err = 0;
66e3015f 528 unsigned int arg;
f32376e9
HS
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
66e3015f 568 arg = cmd->convert_arg;
a207c12f 569 err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
66e3015f 570 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
f32376e9
HS
571
572 if (err)
573 return 4;
574
66573991
HS
575 return 0;
576}
577
578static int apci3xxx_ai_cmd(struct comedi_device *dev,
579 struct comedi_subdevice *s)
580{
f32376e9
HS
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 */
24a44612 590 writel(devpriv->ai_time_base, dev->mmio + 36);
f32376e9
HS
591
592 /* Set the convert timing */
24a44612 593 writel(devpriv->ai_timer, dev->mmio + 32);
f32376e9
HS
594
595 /* Start the conversion */
24a44612 596 writel(0x180000, dev->mmio + 8);
f32376e9 597
66573991
HS
598 return 0;
599}
600
601static int apci3xxx_ai_cancel(struct comedi_device *dev,
602 struct comedi_subdevice *s)
603{
604 return 0;
605}
606
67562be5
HS
607static int apci3xxx_ao_eoc(struct comedi_device *dev,
608 struct comedi_subdevice *s,
609 struct comedi_insn *insn,
610 unsigned long context)
611{
67562be5
HS
612 unsigned int status;
613
24a44612 614 status = readl(dev->mmio + 96);
67562be5
HS
615 if (status & 0x100)
616 return 0;
617 return -EBUSY;
618}
619
0e771e49
HS
620static int apci3xxx_ao_insn_write(struct comedi_device *dev,
621 struct comedi_subdevice *s,
622 struct comedi_insn *insn,
623 unsigned int *data)
624{
0e771e49
HS
625 unsigned int chan = CR_CHAN(insn->chanspec);
626 unsigned int range = CR_RANGE(insn->chanspec);
67562be5 627 int ret;
0e771e49
HS
628 int i;
629
630 for (i = 0; i < insn->n; i++) {
b38d6494
HS
631 unsigned int val = data[i];
632
0e771e49 633 /* Set the range selection */
24a44612 634 writel(range, dev->mmio + 96);
0e771e49
HS
635
636 /* Write the analog value to the selected channel */
b38d6494 637 writel((val << 8) | chan, dev->mmio + 100);
0e771e49
HS
638
639 /* Wait the end of transfer */
67562be5
HS
640 ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
641 if (ret)
642 return ret;
b38d6494
HS
643
644 s->readback[chan] = val;
0e771e49
HS
645 }
646
647 return insn->n;
648}
649
ae57b696
HS
650static int apci3xxx_di_insn_bits(struct comedi_device *dev,
651 struct comedi_subdevice *s,
652 struct comedi_insn *insn,
653 unsigned int *data)
654{
dc633646 655 data[1] = inl(dev->iobase + 32) & 0xf;
ae57b696
HS
656
657 return insn->n;
658}
659
c3d8605f
HS
660static int apci3xxx_do_insn_bits(struct comedi_device *dev,
661 struct comedi_subdevice *s,
662 struct comedi_insn *insn,
663 unsigned int *data)
664{
dc633646 665 s->state = inl(dev->iobase + 48) & 0xf;
c3d8605f 666
97f4289a 667 if (comedi_dio_update_state(s, data))
dc633646 668 outl(s->state, dev->iobase + 48);
c3d8605f
HS
669
670 data[1] = s->state;
671
672 return insn->n;
673}
674
da6578ab
HS
675static int apci3xxx_dio_insn_config(struct comedi_device *dev,
676 struct comedi_subdevice *s,
677 struct comedi_insn *insn,
678 unsigned int *data)
679{
da6578ab 680 unsigned int chan = CR_CHAN(insn->chanspec);
b0377d4b 681 unsigned int mask = 0;
5dacadcc 682 int ret;
da6578ab
HS
683
684 /*
685 * Port 0 (channels 0-7) are always inputs
686 * Port 1 (channels 8-15) are always outputs
687 * Port 2 (channels 16-23) are programmable i/o
da6578ab 688 */
b0377d4b
CS
689 if (data[0] != INSN_CONFIG_DIO_QUERY) {
690 /* ignore all other instructions for ports 0 and 1 */
691 if (chan < 16)
5dacadcc 692 return -EINVAL;
9ed221fa
HS
693
694 /* changing any channel in port 2 changes the entire port */
695 mask = 0xff0000;
da6578ab
HS
696 }
697
5dacadcc
HS
698 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
699 if (ret)
700 return ret;
701
da6578ab 702 /* update port 2 configuration */
5dacadcc 703 outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
da6578ab
HS
704
705 return insn->n;
706}
707
708static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
709 struct comedi_subdevice *s,
710 struct comedi_insn *insn,
711 unsigned int *data)
712{
ca5d4a20 713 unsigned int mask;
da6578ab
HS
714 unsigned int val;
715
ca5d4a20 716 mask = comedi_dio_update_state(s, data);
da6578ab 717 if (mask) {
da6578ab 718 if (mask & 0xff)
dc633646 719 outl(s->state & 0xff, dev->iobase + 80);
da6578ab 720 if (mask & 0xff0000)
dc633646 721 outl((s->state >> 16) & 0xff, dev->iobase + 112);
da6578ab
HS
722 }
723
dc633646
HS
724 val = inl(dev->iobase + 80);
725 val |= (inl(dev->iobase + 64) << 8);
da6578ab 726 if (s->io_bits & 0xff0000)
dc633646 727 val |= (inl(dev->iobase + 112) << 16);
da6578ab 728 else
dc633646 729 val |= (inl(dev->iobase + 96) << 16);
da6578ab
HS
730
731 data[1] = val;
732
733 return insn->n;
734}
735
5e72c7a5 736static int apci3xxx_reset(struct comedi_device *dev)
98d3385d 737{
5e72c7a5
HS
738 unsigned int val;
739 int i;
740
741 /* Disable the interrupt */
742 disable_irq(dev->irq);
743
5e72c7a5 744 /* Clear the start command */
24a44612 745 writel(0, dev->mmio + 8);
5e72c7a5
HS
746
747 /* Reset the interrupt flags */
24a44612
HS
748 val = readl(dev->mmio + 16);
749 writel(val, dev->mmio + 16);
5e72c7a5
HS
750
751 /* clear the EOS */
24a44612 752 readl(dev->mmio + 20);
5e72c7a5
HS
753
754 /* Clear the FIFO */
755 for (i = 0; i < 16; i++)
24a44612 756 val = readl(dev->mmio + 28);
5e72c7a5
HS
757
758 /* Enable the interrupt */
759 enable_irq(dev->irq);
98d3385d 760
98d3385d
HS
761 return 0;
762}
763
dbae4575
HS
764static int apci3xxx_auto_attach(struct comedi_device *dev,
765 unsigned long context)
766{
98d3385d 767 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
e053b241 768 const struct apci3xxx_boardinfo *board = NULL;
1867add9 769 struct apci3xxx_private *devpriv;
98d3385d 770 struct comedi_subdevice *s;
656e39fe
HS
771 int n_subdevices;
772 int subdev;
773 int ret;
dbae4575
HS
774
775 if (context < ARRAY_SIZE(apci3xxx_boardtypes))
776 board = &apci3xxx_boardtypes[context];
777 if (!board)
778 return -ENODEV;
779 dev->board_ptr = board;
6abeba09 780 dev->board_name = board->name;
98d3385d 781
0bdab509 782 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
98d3385d
HS
783 if (!devpriv)
784 return -ENOMEM;
98d3385d
HS
785
786 ret = comedi_pci_enable(dev);
787 if (ret)
788 return ret;
789
21073473 790 dev->iobase = pci_resource_start(pcidev, 2);
24a44612 791 dev->mmio = pci_ioremap_bar(pcidev, 3);
98d3385d 792
98d3385d 793 if (pcidev->irq > 0) {
6c5b0fff
HS
794 ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
795 IRQF_SHARED, dev->board_name, dev);
98d3385d
HS
796 if (ret == 0)
797 dev->irq = pcidev->irq;
798 }
799
656e39fe
HS
800 n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
801 board->has_dig_in + board->has_dig_out +
802 board->has_ttl_io;
98d3385d
HS
803 ret = comedi_alloc_subdevices(dev, n_subdevices);
804 if (ret)
805 return ret;
806
656e39fe
HS
807 subdev = 0;
808
308380e6 809 /* Analog Input subdevice */
8d472906 810 if (board->ai_n_chan) {
656e39fe 811 s = &dev->subdevices[subdev];
308380e6
HS
812 s->type = COMEDI_SUBD_AI;
813 s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
814 s->n_chan = board->ai_n_chan;
815 s->maxdata = board->ai_maxdata;
308380e6 816 s->range_table = &apci3xxx_ai_range;
308380e6 817 s->insn_read = apci3xxx_ai_insn_read;
66573991 818 if (dev->irq) {
7c9fc34e
HS
819 /*
820 * FIXME: The hardware supports multiple scan modes
821 * but the original addi-data driver only supported
822 * reading a single channel with interrupts. Need a
823 * proper datasheet to fix this.
824 *
825 * The following scan modes are supported by the
826 * hardware:
827 * 1) Single software scan
828 * 2) Single hardware triggered scan
829 * 3) Continuous software scan
830 * 4) Continuous software scan with timer delay
831 * 5) Continuous hardware triggered scan
832 * 6) Continuous hardware triggered scan with timer
833 * delay
834 *
835 * For now, limit the chanlist to a single channel.
836 */
66573991
HS
837 dev->read_subdev = s;
838 s->subdev_flags |= SDF_CMD_READ;
7c9fc34e 839 s->len_chanlist = 1;
66573991
HS
840 s->do_cmdtest = apci3xxx_ai_cmdtest;
841 s->do_cmd = apci3xxx_ai_cmd;
842 s->cancel = apci3xxx_ai_cancel;
843 }
656e39fe
HS
844
845 subdev++;
98d3385d
HS
846 }
847
0e771e49 848 /* Analog Output subdevice */
fa81e2f1 849 if (board->has_ao) {
656e39fe 850 s = &dev->subdevices[subdev];
0e771e49
HS
851 s->type = COMEDI_SUBD_AO;
852 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
853 s->n_chan = 4;
854 s->maxdata = 0x0fff;
855 s->range_table = &apci3xxx_ao_range;
856 s->insn_write = apci3xxx_ao_insn_write;
b38d6494
HS
857 s->insn_read = comedi_readback_insn_read;
858
859 ret = comedi_alloc_subdev_readback(s);
860 if (ret)
861 return ret;
656e39fe
HS
862
863 subdev++;
98d3385d 864 }
ca1cc85b
HS
865
866 /* Digital Input subdevice */
4aab8bfd 867 if (board->has_dig_in) {
656e39fe 868 s = &dev->subdevices[subdev];
ca1cc85b
HS
869 s->type = COMEDI_SUBD_DI;
870 s->subdev_flags = SDF_READABLE;
871 s->n_chan = 4;
872 s->maxdata = 1;
873 s->range_table = &range_digital;
874 s->insn_bits = apci3xxx_di_insn_bits;
656e39fe
HS
875
876 subdev++;
98d3385d 877 }
ca1cc85b
HS
878
879 /* Digital Output subdevice */
4aab8bfd 880 if (board->has_dig_out) {
656e39fe 881 s = &dev->subdevices[subdev];
ca1cc85b
HS
882 s->type = COMEDI_SUBD_DO;
883 s->subdev_flags = SDF_WRITEABLE;
884 s->n_chan = 4;
885 s->maxdata = 1;
886 s->range_table = &range_digital;
887 s->insn_bits = apci3xxx_do_insn_bits;
98d3385d 888
656e39fe
HS
889 subdev++;
890 }
98d3385d 891
383390cf 892 /* TTL Digital I/O subdevice */
0ed9f25f 893 if (board->has_ttl_io) {
656e39fe 894 s = &dev->subdevices[subdev];
383390cf
HS
895 s->type = COMEDI_SUBD_DIO;
896 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
897 s->n_chan = 24;
898 s->maxdata = 1;
899 s->io_bits = 0xff; /* channels 0-7 are always outputs */
900 s->range_table = &range_digital;
da6578ab
HS
901 s->insn_config = apci3xxx_dio_insn_config;
902 s->insn_bits = apci3xxx_dio_insn_bits;
98d3385d 903
656e39fe
HS
904 subdev++;
905 }
98d3385d 906
5e72c7a5 907 apci3xxx_reset(dev);
98d3385d
HS
908 return 0;
909}
910
911static void apci3xxx_detach(struct comedi_device *dev)
912{
aac307f9
HS
913 if (dev->iobase)
914 apci3xxx_reset(dev);
915 comedi_pci_detach(dev);
dbae4575
HS
916}
917
20a22b70
HS
918static struct comedi_driver apci3xxx_driver = {
919 .driver_name = "addi_apci_3xxx",
920 .module = THIS_MODULE,
dbae4575 921 .auto_attach = apci3xxx_auto_attach,
98d3385d 922 .detach = apci3xxx_detach,
20a22b70
HS
923};
924
a690b7e5 925static int apci3xxx_pci_probe(struct pci_dev *dev,
b8f4ac23 926 const struct pci_device_id *id)
20a22b70 927{
b8f4ac23 928 return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
20a22b70
HS
929}
930
41e043fc 931static const struct pci_device_id apci3xxx_pci_table[] = {
dbae4575
HS
932 { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
933 { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
934 { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
935 { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
936 { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
937 { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
938 { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
939 { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
940 { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
941 { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
942 { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
943 { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
944 { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
945 { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
946 { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
947 { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
948 { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
949 { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
950 { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
951 { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
952 { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
953 { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
954 { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
955 { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
956 { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
317285d7
HS
957 { 0 }
958};
20a22b70 959MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
317285d7 960
20a22b70
HS
961static struct pci_driver apci3xxx_pci_driver = {
962 .name = "addi_apci_3xxx",
963 .id_table = apci3xxx_pci_table,
964 .probe = apci3xxx_pci_probe,
9901a4d7 965 .remove = comedi_pci_auto_unconfig,
20a22b70
HS
966};
967module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
90f703d3
AT
968
969MODULE_AUTHOR("Comedi http://www.comedi.org");
970MODULE_DESCRIPTION("Comedi low-level driver");
971MODULE_LICENSE("GPL");
This page took 0.566265 seconds and 5 git commands to generate.