Commit | Line | Data |
---|---|---|
48f16b6a | 1 | /* |
79e5e6ad HS |
2 | * das6402.c |
3 | * Comedi driver for DAS6402 compatible boards | |
4 | * Copyright(c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com> | |
5 | * | |
6 | * Rewrite of an experimental driver by: | |
7 | * Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
48f16b6a OS |
18 | */ |
19 | ||
20 | /* | |
79e5e6ad HS |
21 | * Driver: das6402 |
22 | * Description: Keithley Metrabyte DAS6402 (& compatibles) | |
23 | * Devices: (Keithley Metrabyte) DAS6402-12 (das6402-12) | |
24 | * (Keithley Metrabyte) DAS6402-16 (das6402-16) | |
25 | * Author: H Hartley Sweeten <hsweeten@visionengravers.com> | |
26 | * Updated: Fri, 14 Mar 2014 10:18:43 -0700 | |
27 | * Status: unknown | |
28 | * | |
29 | * Configuration Options: | |
30 | * [0] - I/O base address | |
31 | * [1] - IRQ (optional, needed for async command support) | |
32 | */ | |
48f16b6a | 33 | |
79e5e6ad HS |
34 | #include <linux/module.h> |
35 | #include <linux/interrupt.h> | |
48f16b6a | 36 | |
79e5e6ad HS |
37 | #include "../comedidev.h" |
38 | #include "8253.h" | |
48f16b6a | 39 | |
48f16b6a | 40 | /* |
79e5e6ad HS |
41 | * Register I/O map |
42 | */ | |
43 | #define DAS6402_AI_DATA_REG 0x00 | |
44 | #define DAS6402_AI_MUX_REG 0x02 | |
45 | #define DAS6402_AI_MUX_LO(x) (((x) & 0x3f) << 0) | |
46 | #define DAS6402_AI_MUX_HI(x) (((x) & 0x3f) << 8) | |
47 | #define DAS6402_DI_DO_REG 0x03 | |
48 | #define DAS6402_AO_DATA_REG(x) (0x04 + ((x) * 2)) | |
49 | #define DAS6402_AO_LSB_REG(x) (0x04 + ((x) * 2)) | |
50 | #define DAS6402_AO_MSB_REG(x) (0x05 + ((x) * 2)) | |
51 | #define DAS6402_STATUS_REG 0x08 | |
52 | #define DAS6402_STATUS_FFNE (1 << 0) | |
53 | #define DAS6402_STATUS_FHALF (1 << 1) | |
54 | #define DAS6402_STATUS_FFULL (1 << 2) | |
55 | #define DAS6402_STATUS_XINT (1 << 3) | |
56 | #define DAS6402_STATUS_INT (1 << 4) | |
57 | #define DAS6402_STATUS_XTRIG (1 << 5) | |
58 | #define DAS6402_STATUS_INDGT (1 << 6) | |
59 | #define DAS6402_STATUS_10MHZ (1 << 7) | |
60 | #define DAS6402_STATUS_W_CLRINT (1 << 0) | |
61 | #define DAS6402_STATUS_W_CLRXTR (1 << 1) | |
62 | #define DAS6402_STATUS_W_CLRXIN (1 << 2) | |
63 | #define DAS6402_STATUS_W_EXTEND (1 << 4) | |
64 | #define DAS6402_STATUS_W_ARMED (1 << 5) | |
65 | #define DAS6402_STATUS_W_POSTMODE (1 << 6) | |
66 | #define DAS6402_STATUS_W_10MHZ (1 << 7) | |
67 | #define DAS6402_CTRL_REG 0x09 | |
68 | #define DAS6402_CTRL_SOFT_TRIG (0 << 0) | |
69 | #define DAS6402_CTRL_EXT_FALL_TRIG (1 << 0) | |
70 | #define DAS6402_CTRL_EXT_RISE_TRIG (2 << 0) | |
71 | #define DAS6402_CTRL_PACER_TRIG (3 << 0) | |
72 | #define DAS6402_CTRL_BURSTEN (1 << 2) | |
73 | #define DAS6402_CTRL_XINTE (1 << 3) | |
74 | #define DAS6402_CTRL_IRQ(x) ((x) << 4) | |
75 | #define DAS6402_CTRL_INTE (1 << 7) | |
76 | #define DAS6402_TRIG_REG 0x0a | |
77 | #define DAS6402_TRIG_TGEN (1 << 0) | |
78 | #define DAS6402_TRIG_TGSEL (1 << 1) | |
79 | #define DAS6402_TRIG_TGPOL (1 << 2) | |
80 | #define DAS6402_TRIG_PRETRIG (1 << 3) | |
81 | #define DAS6402_AO_RANGE(_chan, _range) ((_range) << ((_chan) ? 6 : 4)) | |
82 | #define DAS6402_AO_RANGE_MASK(_chan) (3 << ((_chan) ? 6 : 4)) | |
83 | #define DAS6402_MODE_REG 0x0b | |
84 | #define DAS6402_MODE_RANGE(x) ((x) << 0) | |
85 | #define DAS6402_MODE_POLLED (0 << 2) | |
86 | #define DAS6402_MODE_FIFONEPTY (1 << 2) | |
87 | #define DAS6402_MODE_FIFOHFULL (2 << 2) | |
88 | #define DAS6402_MODE_EOB (3 << 2) | |
89 | #define DAS6402_MODE_ENHANCED (1 << 4) | |
90 | #define DAS6402_MODE_SE (1 << 5) | |
91 | #define DAS6402_MODE_UNI (1 << 6) | |
92 | #define DAS6402_MODE_DMA1 (0 << 7) | |
93 | #define DAS6402_MODE_DMA3 (1 << 7) | |
94 | #define DAS6402_TIMER_BASE 0x0c | |
95 | ||
96 | static const struct comedi_lrange das6402_ai_ranges = { | |
97 | 8, { | |
98 | BIP_RANGE(10), | |
99 | BIP_RANGE(5), | |
100 | BIP_RANGE(2.5), | |
101 | BIP_RANGE(1.25), | |
102 | UNI_RANGE(10), | |
103 | UNI_RANGE(5), | |
104 | UNI_RANGE(2.5), | |
105 | UNI_RANGE(1.25) | |
106 | } | |
107 | }; | |
48f16b6a | 108 | |
79e5e6ad HS |
109 | /* |
110 | * Analog output ranges are programmable on the DAS6402/12. | |
111 | * For the DAS6402/16 the range bits have no function, the | |
112 | * DAC ranges are selected by switches on the board. | |
113 | */ | |
114 | static const struct comedi_lrange das6402_ao_ranges = { | |
115 | 4, { | |
116 | BIP_RANGE(5), | |
117 | BIP_RANGE(10), | |
118 | UNI_RANGE(5), | |
119 | UNI_RANGE(10) | |
120 | } | |
121 | }; | |
48f16b6a | 122 | |
79e5e6ad HS |
123 | struct das6402_boardinfo { |
124 | const char *name; | |
125 | unsigned int maxdata; | |
126 | }; | |
48f16b6a | 127 | |
79e5e6ad HS |
128 | struct das6402_boardinfo das6402_boards[] = { |
129 | { | |
130 | .name = "das6402-12", | |
131 | .maxdata = 0x0fff, | |
132 | }, { | |
133 | .name = "das6402-16", | |
134 | .maxdata = 0xffff, | |
135 | }, | |
136 | }; | |
48f16b6a | 137 | |
c7b8bb98 | 138 | struct das6402_private { |
79e5e6ad HS |
139 | unsigned int irq; |
140 | ||
141 | unsigned int count; | |
142 | unsigned int divider1; | |
143 | unsigned int divider2; | |
48f16b6a | 144 | |
79e5e6ad HS |
145 | unsigned int ao_range; |
146 | unsigned int ao_readback[2]; | |
c7b8bb98 | 147 | }; |
48f16b6a | 148 | |
79e5e6ad HS |
149 | static void das6402_set_mode(struct comedi_device *dev, |
150 | unsigned int mode) | |
71e7271b | 151 | { |
79e5e6ad | 152 | outb(DAS6402_MODE_ENHANCED | mode, dev->iobase + DAS6402_MODE_REG); |
71e7271b | 153 | } |
48f16b6a | 154 | |
79e5e6ad HS |
155 | static void das6402_set_extended(struct comedi_device *dev, |
156 | unsigned int val) | |
48f16b6a | 157 | { |
79e5e6ad HS |
158 | outb(DAS6402_STATUS_W_EXTEND, dev->iobase + DAS6402_STATUS_REG); |
159 | outb(DAS6402_STATUS_W_EXTEND | val, dev->iobase + DAS6402_STATUS_REG); | |
160 | outb(val, dev->iobase + DAS6402_STATUS_REG); | |
48f16b6a OS |
161 | } |
162 | ||
79e5e6ad HS |
163 | static void das6402_clear_all_interrupts(struct comedi_device *dev) |
164 | { | |
165 | outb(DAS6402_STATUS_W_CLRINT | | |
166 | DAS6402_STATUS_W_CLRXTR | | |
167 | DAS6402_STATUS_W_CLRXIN, dev->iobase + DAS6402_STATUS_REG); | |
168 | } | |
169 | ||
170 | static void das6402_ai_clear_eoc(struct comedi_device *dev) | |
171 | { | |
172 | outb(DAS6402_STATUS_W_CLRINT, dev->iobase + DAS6402_STATUS_REG); | |
173 | } | |
174 | ||
175 | static void das6402_enable_counter(struct comedi_device *dev, bool load) | |
48f16b6a | 176 | { |
9a1a6cf8 | 177 | struct das6402_private *devpriv = dev->private; |
79e5e6ad | 178 | unsigned long timer_iobase = dev->iobase + DAS6402_TIMER_BASE; |
48f16b6a | 179 | |
79e5e6ad HS |
180 | if (load) { |
181 | i8254_set_mode(timer_iobase, 0, 0, I8254_MODE0 | I8254_BINARY); | |
182 | i8254_set_mode(timer_iobase, 0, 1, I8254_MODE2 | I8254_BINARY); | |
183 | i8254_set_mode(timer_iobase, 0, 2, I8254_MODE2 | I8254_BINARY); | |
48f16b6a | 184 | |
79e5e6ad HS |
185 | i8254_write(timer_iobase, 0, 0, devpriv->count); |
186 | i8254_write(timer_iobase, 0, 1, devpriv->divider1); | |
187 | i8254_write(timer_iobase, 0, 2, devpriv->divider2); | |
48f16b6a | 188 | |
79e5e6ad HS |
189 | } else { |
190 | i8254_set_mode(timer_iobase, 0, 0, I8254_MODE0 | I8254_BINARY); | |
191 | i8254_set_mode(timer_iobase, 0, 1, I8254_MODE0 | I8254_BINARY); | |
192 | i8254_set_mode(timer_iobase, 0, 2, I8254_MODE0 | I8254_BINARY); | |
48f16b6a | 193 | } |
79e5e6ad | 194 | } |
48f16b6a | 195 | |
79e5e6ad HS |
196 | static irqreturn_t das6402_interrupt(int irq, void *d) |
197 | { | |
198 | struct comedi_device *dev = d; | |
199 | ||
200 | das6402_clear_all_interrupts(dev); | |
48f16b6a | 201 | |
48f16b6a OS |
202 | return IRQ_HANDLED; |
203 | } | |
204 | ||
79e5e6ad HS |
205 | static int das6402_ai_cmd(struct comedi_device *dev, |
206 | struct comedi_subdevice *s) | |
48f16b6a | 207 | { |
79e5e6ad HS |
208 | return -EINVAL; |
209 | } | |
48f16b6a | 210 | |
79e5e6ad HS |
211 | static int das6402_ai_cmdtest(struct comedi_device *dev, |
212 | struct comedi_subdevice *s, | |
213 | struct comedi_cmd *cmd) | |
214 | { | |
215 | return -EINVAL; | |
48f16b6a | 216 | } |
48f16b6a | 217 | |
0a85b6f0 MT |
218 | static int das6402_ai_cancel(struct comedi_device *dev, |
219 | struct comedi_subdevice *s) | |
79e5e6ad HS |
220 | { |
221 | outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static void das6402_ai_soft_trig(struct comedi_device *dev) | |
227 | { | |
228 | outw(0, dev->iobase + DAS6402_AI_DATA_REG); | |
229 | } | |
230 | ||
231 | static int das6402_ai_eoc(struct comedi_device *dev, | |
232 | struct comedi_subdevice *s, | |
233 | struct comedi_insn *insn, | |
234 | unsigned long context) | |
235 | { | |
236 | unsigned int status; | |
237 | ||
238 | status = inb(dev->iobase + DAS6402_STATUS_REG); | |
239 | if (status & DAS6402_STATUS_FFNE) | |
240 | return 0; | |
241 | return -EBUSY; | |
242 | } | |
243 | ||
244 | static int das6402_ai_insn_read(struct comedi_device *dev, | |
245 | struct comedi_subdevice *s, | |
246 | struct comedi_insn *insn, | |
247 | unsigned int *data) | |
248 | { | |
249 | unsigned int chan = CR_CHAN(insn->chanspec); | |
250 | unsigned int range = CR_RANGE(insn->chanspec); | |
251 | unsigned int aref = CR_AREF(insn->chanspec); | |
252 | unsigned int val; | |
253 | int ret; | |
254 | int i; | |
255 | ||
256 | val = DAS6402_MODE_RANGE(range) | DAS6402_MODE_POLLED; | |
257 | if (aref == AREF_DIFF) { | |
258 | if (chan > s->n_chan / 2) | |
259 | return -EINVAL; | |
260 | } else { | |
261 | val |= DAS6402_MODE_SE; | |
262 | } | |
263 | if (comedi_range_is_unipolar(s, range)) | |
264 | val |= DAS6402_MODE_UNI; | |
265 | ||
266 | /* enable software conversion trigger */ | |
267 | outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG); | |
268 | ||
269 | das6402_set_mode(dev, val); | |
270 | ||
271 | /* load the mux for single channel conversion */ | |
272 | outw(DAS6402_AI_MUX_HI(chan) | DAS6402_AI_MUX_LO(chan), | |
273 | dev->iobase + DAS6402_AI_MUX_REG); | |
274 | ||
275 | for (i = 0; i < insn->n; i++) { | |
276 | das6402_ai_clear_eoc(dev); | |
277 | das6402_ai_soft_trig(dev); | |
278 | ||
279 | ret = comedi_timeout(dev, s, insn, das6402_ai_eoc, 0); | |
280 | if (ret) | |
281 | break; | |
282 | ||
283 | val = inw(dev->iobase + DAS6402_AI_DATA_REG); | |
284 | ||
285 | if (s->maxdata == 0x0fff) | |
286 | val >>= 4; | |
287 | ||
288 | data[i] = val; | |
289 | } | |
290 | ||
291 | das6402_ai_clear_eoc(dev); | |
292 | ||
293 | return insn->n; | |
294 | } | |
295 | ||
296 | static int das6402_ao_insn_write(struct comedi_device *dev, | |
297 | struct comedi_subdevice *s, | |
298 | struct comedi_insn *insn, | |
299 | unsigned int *data) | |
48f16b6a | 300 | { |
9a1a6cf8 | 301 | struct das6402_private *devpriv = dev->private; |
79e5e6ad HS |
302 | unsigned int chan = CR_CHAN(insn->chanspec); |
303 | unsigned int range = CR_RANGE(insn->chanspec); | |
304 | unsigned int val; | |
305 | int i; | |
306 | ||
307 | /* set the range for this channel */ | |
308 | val = devpriv->ao_range; | |
309 | val &= ~DAS6402_AO_RANGE_MASK(chan); | |
310 | val |= DAS6402_AO_RANGE(chan, range); | |
311 | if (val != devpriv->ao_range) { | |
312 | devpriv->ao_range = val; | |
313 | outb(val, dev->iobase + DAS6402_TRIG_REG); | |
314 | } | |
9a1a6cf8 | 315 | |
48f16b6a | 316 | /* |
79e5e6ad HS |
317 | * The DAS6402/16 has a jumper to select either individual |
318 | * update (UPDATE) or simultaneous updating (XFER) of both | |
319 | * DAC's. In UPDATE mode, when the MSB is written, that DAC | |
320 | * is updated. In XFER mode, after both DAC's are loaded, | |
321 | * a read cycle of any DAC register will update both DAC's | |
322 | * simultaneously. | |
323 | * | |
324 | * If you have XFER mode enabled a (*insn_read) will need | |
325 | * to be performed in order to update the DAC's with the | |
326 | * last value written. | |
48f16b6a | 327 | */ |
79e5e6ad HS |
328 | for (i = 0; i < insn->n; i++) { |
329 | val = data[i]; | |
330 | ||
331 | devpriv->ao_readback[chan] = val; | |
332 | ||
333 | if (s->maxdata == 0x0fff) { | |
334 | /* | |
335 | * DAS6402/12 has the two 8-bit DAC registers, left | |
336 | * justified (the 4 LSB bits are don't care). Data | |
337 | * can be written as one word. | |
338 | */ | |
339 | val <<= 4; | |
340 | outw(val, dev->iobase + DAS6402_AO_DATA_REG(chan)); | |
341 | } else { | |
342 | /* | |
343 | * DAS6402/16 uses both 8-bit DAC registers and needs | |
344 | * to be written LSB then MSB. | |
345 | */ | |
346 | outb(val & 0xff, | |
347 | dev->iobase + DAS6402_AO_LSB_REG(chan)); | |
348 | outb((val >> 8) & 0xff, | |
349 | dev->iobase + DAS6402_AO_LSB_REG(chan)); | |
350 | } | |
351 | } | |
48f16b6a | 352 | |
79e5e6ad | 353 | return insn->n; |
48f16b6a OS |
354 | } |
355 | ||
79e5e6ad HS |
356 | static int das6402_ao_insn_read(struct comedi_device *dev, |
357 | struct comedi_subdevice *s, | |
358 | struct comedi_insn *insn, | |
359 | unsigned int *data) | |
48f16b6a | 360 | { |
9a1a6cf8 | 361 | struct das6402_private *devpriv = dev->private; |
79e5e6ad HS |
362 | unsigned int chan = CR_CHAN(insn->chanspec); |
363 | int i; | |
9a1a6cf8 | 364 | |
79e5e6ad HS |
365 | /* |
366 | * If XFER mode is enabled, reading any DAC register | |
367 | * will update both DAC's simultaneously. | |
368 | */ | |
369 | inw(dev->iobase + DAS6402_AO_LSB_REG(chan)); | |
48f16b6a | 370 | |
79e5e6ad HS |
371 | for (i = 0; i < insn->n; i++) |
372 | data[i] = devpriv->ao_readback[chan]; | |
48f16b6a | 373 | |
79e5e6ad HS |
374 | return insn->n; |
375 | } | |
48f16b6a | 376 | |
79e5e6ad HS |
377 | static int das6402_di_insn_bits(struct comedi_device *dev, |
378 | struct comedi_subdevice *s, | |
379 | struct comedi_insn *insn, | |
380 | unsigned int *data) | |
381 | { | |
382 | data[1] = inb(dev->iobase + DAS6402_DI_DO_REG); | |
48f16b6a | 383 | |
79e5e6ad | 384 | return insn->n; |
48f16b6a | 385 | } |
48f16b6a | 386 | |
79e5e6ad HS |
387 | static int das6402_do_insn_bits(struct comedi_device *dev, |
388 | struct comedi_subdevice *s, | |
389 | struct comedi_insn *insn, | |
390 | unsigned int *data) | |
48f16b6a | 391 | { |
79e5e6ad HS |
392 | if (comedi_dio_update_state(s, data)) |
393 | outb(s->state, dev->iobase + DAS6402_DI_DO_REG); | |
48f16b6a | 394 | |
79e5e6ad | 395 | data[1] = s->state; |
48f16b6a | 396 | |
79e5e6ad HS |
397 | return insn->n; |
398 | } | |
48f16b6a | 399 | |
79e5e6ad HS |
400 | static void das6402_reset(struct comedi_device *dev) |
401 | { | |
402 | struct das6402_private *devpriv = dev->private; | |
48f16b6a | 403 | |
79e5e6ad HS |
404 | /* enable "Enhanced" mode */ |
405 | outb(DAS6402_MODE_ENHANCED, dev->iobase + DAS6402_MODE_REG); | |
48f16b6a | 406 | |
79e5e6ad HS |
407 | /* enable 10MHz pacer clock */ |
408 | das6402_set_extended(dev, DAS6402_STATUS_W_10MHZ); | |
48f16b6a | 409 | |
79e5e6ad HS |
410 | /* enable software conversion trigger */ |
411 | outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG); | |
48f16b6a | 412 | |
79e5e6ad HS |
413 | /* default ADC to single-ended unipolar 10V inputs */ |
414 | das6402_set_mode(dev, DAS6402_MODE_RANGE(0) | | |
415 | DAS6402_MODE_POLLED | | |
416 | DAS6402_MODE_SE | | |
417 | DAS6402_MODE_UNI); | |
48f16b6a | 418 | |
79e5e6ad HS |
419 | /* default mux for single channel conversion (channel 0) */ |
420 | outw(DAS6402_AI_MUX_HI(0) | DAS6402_AI_MUX_LO(0), | |
421 | dev->iobase + DAS6402_AI_MUX_REG); | |
48f16b6a | 422 | |
79e5e6ad HS |
423 | /* set both DAC's for unipolar 5V output range */ |
424 | devpriv->ao_range = DAS6402_AO_RANGE(0, 2) | DAS6402_AO_RANGE(1, 2); | |
425 | outb(devpriv->ao_range, dev->iobase + DAS6402_TRIG_REG); | |
48f16b6a | 426 | |
79e5e6ad HS |
427 | /* set both DAC's to 0V */ |
428 | outw(0, dev->iobase + DAS6402_AO_DATA_REG(0)); | |
429 | outw(0, dev->iobase + DAS6402_AO_DATA_REG(0)); | |
430 | inw(dev->iobase + DAS6402_AO_LSB_REG(0)); | |
48f16b6a | 431 | |
79e5e6ad HS |
432 | das6402_enable_counter(dev, false); |
433 | ||
434 | /* set all digital outputs low */ | |
435 | outb(0, dev->iobase + DAS6402_DI_DO_REG); | |
436 | ||
437 | das6402_clear_all_interrupts(dev); | |
48f16b6a OS |
438 | } |
439 | ||
0a85b6f0 MT |
440 | static int das6402_attach(struct comedi_device *dev, |
441 | struct comedi_devconfig *it) | |
48f16b6a | 442 | { |
79e5e6ad | 443 | const struct das6402_boardinfo *board = comedi_board(dev); |
9a1a6cf8 | 444 | struct das6402_private *devpriv; |
34c43922 | 445 | struct comedi_subdevice *s; |
79e5e6ad | 446 | int ret; |
9a1a6cf8 | 447 | |
0bdab509 | 448 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
449 | if (!devpriv) |
450 | return -ENOMEM; | |
48f16b6a | 451 | |
79e5e6ad | 452 | ret = comedi_request_region(dev, it->options[0], 0x10); |
8b6c5694 | 453 | if (ret) |
48f16b6a OS |
454 | return ret; |
455 | ||
79e5e6ad HS |
456 | das6402_reset(dev); |
457 | ||
458 | /* IRQs 2,3,5,6,7, 10,11,15 are valid for "enhanced" mode */ | |
459 | if ((1 << it->options[1]) & 0x8cec) { | |
460 | ret = request_irq(it->options[1], das6402_interrupt, 0, | |
461 | dev->board_name, dev); | |
462 | if (ret == 0) { | |
463 | dev->irq = it->options[1]; | |
464 | ||
465 | switch (dev->irq) { | |
466 | case 10: | |
467 | devpriv->irq = 4; | |
468 | break; | |
469 | case 11: | |
470 | devpriv->irq = 1; | |
471 | break; | |
472 | case 15: | |
473 | devpriv->irq = 6; | |
474 | break; | |
475 | default: | |
476 | devpriv->irq = dev->irq; | |
477 | break; | |
478 | } | |
479 | } | |
480 | } | |
481 | ||
482 | ret = comedi_alloc_subdevices(dev, 4); | |
483 | if (ret) | |
484 | return ret; | |
485 | ||
486 | /* Analog Input subdevice */ | |
92cfef5d | 487 | s = &dev->subdevices[0]; |
79e5e6ad HS |
488 | s->type = COMEDI_SUBD_AI; |
489 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; | |
490 | s->n_chan = 64; | |
491 | s->maxdata = board->maxdata; | |
492 | s->range_table = &das6402_ai_ranges; | |
493 | s->insn_read = das6402_ai_insn_read; | |
494 | if (dev->irq) { | |
495 | dev->read_subdev = s; | |
496 | s->subdev_flags |= SDF_CMD_READ; | |
497 | s->len_chanlist = s->n_chan; | |
498 | s->do_cmdtest = das6402_ai_cmdtest; | |
499 | s->do_cmd = das6402_ai_cmd; | |
500 | s->cancel = das6402_ai_cancel; | |
501 | } | |
48f16b6a | 502 | |
79e5e6ad HS |
503 | /* Analog Output subdevice */ |
504 | s = &dev->subdevices[1]; | |
505 | s->type = COMEDI_SUBD_AO; | |
506 | s->subdev_flags = SDF_WRITEABLE; | |
507 | s->n_chan = 2; | |
508 | s->maxdata = board->maxdata; | |
509 | s->range_table = &das6402_ao_ranges; | |
510 | s->insn_write = das6402_ao_insn_write; | |
511 | s->insn_read = das6402_ao_insn_read; | |
512 | ||
513 | /* Digital Input subdevice */ | |
514 | s = &dev->subdevices[2]; | |
515 | s->type = COMEDI_SUBD_DI; | |
516 | s->subdev_flags = SDF_READABLE; | |
517 | s->n_chan = 8; | |
518 | s->maxdata = 1; | |
519 | s->range_table = &range_digital; | |
520 | s->insn_bits = das6402_di_insn_bits; | |
521 | ||
522 | /* Digital Input subdevice */ | |
523 | s = &dev->subdevices[3]; | |
524 | s->type = COMEDI_SUBD_DO; | |
525 | s->subdev_flags = SDF_WRITEABLE; | |
526 | s->n_chan = 8; | |
527 | s->maxdata = 1; | |
528 | s->range_table = &range_digital; | |
529 | s->insn_bits = das6402_do_insn_bits; | |
48f16b6a OS |
530 | |
531 | return 0; | |
532 | } | |
90f703d3 | 533 | |
71e7271b HS |
534 | static struct comedi_driver das6402_driver = { |
535 | .driver_name = "das6402", | |
536 | .module = THIS_MODULE, | |
537 | .attach = das6402_attach, | |
3d1fe3f7 | 538 | .detach = comedi_legacy_detach, |
79e5e6ad HS |
539 | .board_name = &das6402_boards[0].name, |
540 | .num_names = ARRAY_SIZE(das6402_boards), | |
541 | .offset = sizeof(struct das6402_boardinfo), | |
71e7271b HS |
542 | }; |
543 | module_comedi_driver(das6402_driver) | |
544 | ||
79e5e6ad HS |
545 | MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); |
546 | MODULE_DESCRIPTION("Comedi driver for DAS6402 compatible boards"); | |
90f703d3 | 547 | MODULE_LICENSE("GPL"); |