Commit | Line | Data |
---|---|---|
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 |
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 | ||
dbae4575 HS |
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 | ||
e053b241 | 85 | struct 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 | ||
98 | static 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 | 355 | struct apci3xxx_private { |
f32376e9 HS |
356 | unsigned int ai_timer; |
357 | unsigned char ai_time_base; | |
1867add9 HS |
358 | }; |
359 | ||
6c5b0fff HS |
360 | static 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 | ||
384 | static 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 | ||
392 | static 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 |
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 | { | |
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 |
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 | { | |
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 | ||
472 | static 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 |
522 | static 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 | ||
578 | static 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 | ||
601 | static int apci3xxx_ai_cancel(struct comedi_device *dev, | |
602 | struct comedi_subdevice *s) | |
603 | { | |
604 | return 0; | |
605 | } | |
606 | ||
67562be5 HS |
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 | { | |
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 |
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 | { | |
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 |
650 | static 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 |
660 | static 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 |
675 | static 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 | ||
708 | static 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 | 736 | static 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 |
764 | static 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 | ||
911 | static 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 |
918 | static 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 | 925 | static 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 | 931 | static 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 | 959 | MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table); |
317285d7 | 960 | |
20a22b70 HS |
961 | static 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 | }; |
967 | module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver); | |
90f703d3 AT |
968 | |
969 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
970 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
971 | MODULE_LICENSE("GPL"); |