1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * cb_pcidas.c |
4 | * Developed by Ivan Martinez and Frank Mori Hess, with valuable help from |
5 | * David Schleef and the rest of the Comedi developers comunity. |
6 | * |
7 | * Copyright (C) 2001-2003 Ivan Martinez <imr@oersted.dtu.dk> |
8 | * Copyright (C) 2001,2002 Frank Mori Hess <fmhess@users.sourceforge.net> |
9 | * |
10 | * COMEDI - Linux Control and Measurement Device Interface |
11 | * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> |
12 | */ |
13 | |
14 | /* |
15 | * Driver: cb_pcidas |
16 | * Description: MeasurementComputing PCI-DAS series |
17 | * with the AMCC S5933 PCI controller |
18 | * Devices: [Measurement Computing] PCI-DAS1602/16 (cb_pcidas), |
19 | * PCI-DAS1602/16jr, PCI-DAS1602/12, PCI-DAS1200, PCI-DAS1200jr, |
20 | * PCI-DAS1000, PCI-DAS1001, PCI_DAS1002 |
21 | * Author: Ivan Martinez <imr@oersted.dtu.dk>, |
22 | * Frank Mori Hess <fmhess@users.sourceforge.net> |
23 | * Updated: 2003-3-11 |
24 | * |
25 | * Status: |
26 | * There are many reports of the driver being used with most of the |
27 | * supported cards. Despite no detailed log is maintained, it can |
28 | * be said that the driver is quite tested and stable. |
29 | * |
30 | * The boards may be autocalibrated using the comedi_calibrate |
31 | * utility. |
32 | * |
33 | * Configuration options: not applicable, uses PCI auto config |
34 | * |
35 | * For commands, the scanned channels must be consecutive |
36 | * (i.e. 4-5-6-7, 2-3-4,...), and must all have the same |
37 | * range and aref. |
38 | * |
39 | * AI Triggering: |
40 | * For start_src == TRIG_EXT, the A/D EXTERNAL TRIGGER IN (pin 45) is used. |
41 | * For 1602 series, the start_arg is interpreted as follows: |
42 | * start_arg == 0 => gated trigger (level high) |
43 | * start_arg == CR_INVERT => gated trigger (level low) |
44 | * start_arg == CR_EDGE => Rising edge |
45 | * start_arg == CR_EDGE | CR_INVERT => Falling edge |
46 | * For the other boards the trigger will be done on rising edge |
47 | */ |
48 | |
49 | /* |
50 | * TODO: |
51 | * analog triggering on 1602 series |
52 | */ |
53 | |
54 | #include <linux/module.h> |
55 | #include <linux/delay.h> |
56 | #include <linux/interrupt.h> |
57 | #include <linux/comedi/comedi_pci.h> |
58 | #include <linux/comedi/comedi_8255.h> |
59 | #include <linux/comedi/comedi_8254.h> |
60 | |
61 | #include "amcc_s5933.h" |
62 | |
63 | #define AI_BUFFER_SIZE 1024 /* max ai fifo size */ |
64 | #define AO_BUFFER_SIZE 1024 /* max ao fifo size */ |
65 | |
66 | /* |
67 | * PCI BAR1 Register map (devpriv->pcibar1) |
68 | */ |
69 | #define PCIDAS_CTRL_REG 0x00 /* INTERRUPT / ADC FIFO register */ |
70 | #define PCIDAS_CTRL_INT(x) (((x) & 0x3) << 0) |
71 | #define PCIDAS_CTRL_INT_NONE PCIDAS_CTRL_INT(0) /* no int selected */ |
72 | #define PCIDAS_CTRL_INT_EOS PCIDAS_CTRL_INT(1) /* int on end of scan */ |
73 | #define PCIDAS_CTRL_INT_FHF PCIDAS_CTRL_INT(2) /* int on fifo half full */ |
74 | #define PCIDAS_CTRL_INT_FNE PCIDAS_CTRL_INT(3) /* int on fifo not empty */ |
75 | #define PCIDAS_CTRL_INT_MASK PCIDAS_CTRL_INT(3) /* mask of int select bits */ |
76 | #define PCIDAS_CTRL_INTE BIT(2) /* int enable */ |
77 | #define PCIDAS_CTRL_DAHFIE BIT(3) /* dac half full int enable */ |
78 | #define PCIDAS_CTRL_EOAIE BIT(4) /* end of acq. int enable */ |
79 | #define PCIDAS_CTRL_DAHFI BIT(5) /* dac half full status / clear */ |
80 | #define PCIDAS_CTRL_EOAI BIT(6) /* end of acq. int status / clear */ |
81 | #define PCIDAS_CTRL_INT_CLR BIT(7) /* int status / clear */ |
82 | #define PCIDAS_CTRL_EOBI BIT(9) /* end of burst int status */ |
83 | #define PCIDAS_CTRL_ADHFI BIT(10) /* half-full int status */ |
84 | #define PCIDAS_CTRL_ADNEI BIT(11) /* fifo not empty int status (latch) */ |
85 | #define PCIDAS_CTRL_ADNE BIT(12) /* fifo not empty status (realtime) */ |
86 | #define PCIDAS_CTRL_DAEMIE BIT(12) /* dac empty int enable */ |
87 | #define PCIDAS_CTRL_LADFUL BIT(13) /* fifo overflow / clear */ |
88 | #define PCIDAS_CTRL_DAEMI BIT(14) /* dac fifo empty int status / clear */ |
89 | |
90 | #define PCIDAS_CTRL_AI_INT (PCIDAS_CTRL_EOAI | PCIDAS_CTRL_EOBI | \ |
91 | PCIDAS_CTRL_ADHFI | PCIDAS_CTRL_ADNEI | \ |
92 | PCIDAS_CTRL_LADFUL) |
93 | #define PCIDAS_CTRL_AO_INT (PCIDAS_CTRL_DAHFI | PCIDAS_CTRL_DAEMI) |
94 | |
95 | #define PCIDAS_AI_REG 0x02 /* ADC CHANNEL MUX AND CONTROL reg */ |
96 | #define PCIDAS_AI_FIRST(x) ((x) & 0xf) |
97 | #define PCIDAS_AI_LAST(x) (((x) & 0xf) << 4) |
98 | #define PCIDAS_AI_CHAN(x) (PCIDAS_AI_FIRST(x) | PCIDAS_AI_LAST(x)) |
99 | #define PCIDAS_AI_GAIN(x) (((x) & 0x3) << 8) |
100 | #define PCIDAS_AI_SE BIT(10) /* Inputs in single-ended mode */ |
101 | #define PCIDAS_AI_UNIP BIT(11) /* Analog front-end unipolar mode */ |
102 | #define PCIDAS_AI_PACER(x) (((x) & 0x3) << 12) |
103 | #define PCIDAS_AI_PACER_SW PCIDAS_AI_PACER(0) /* software pacer */ |
104 | #define PCIDAS_AI_PACER_INT PCIDAS_AI_PACER(1) /* int. pacer */ |
105 | #define PCIDAS_AI_PACER_EXTN PCIDAS_AI_PACER(2) /* ext. falling edge */ |
106 | #define PCIDAS_AI_PACER_EXTP PCIDAS_AI_PACER(3) /* ext. rising edge */ |
107 | #define PCIDAS_AI_PACER_MASK PCIDAS_AI_PACER(3) /* pacer source bits */ |
108 | #define PCIDAS_AI_EOC BIT(14) /* adc not busy */ |
109 | |
110 | #define PCIDAS_TRIG_REG 0x04 /* TRIGGER CONTROL/STATUS register */ |
111 | #define PCIDAS_TRIG_SEL(x) (((x) & 0x3) << 0) |
112 | #define PCIDAS_TRIG_SEL_NONE PCIDAS_TRIG_SEL(0) /* no start trigger */ |
113 | #define PCIDAS_TRIG_SEL_SW PCIDAS_TRIG_SEL(1) /* software start trigger */ |
114 | #define PCIDAS_TRIG_SEL_EXT PCIDAS_TRIG_SEL(2) /* ext. start trigger */ |
115 | #define PCIDAS_TRIG_SEL_ANALOG PCIDAS_TRIG_SEL(3) /* ext. analog trigger */ |
116 | #define PCIDAS_TRIG_SEL_MASK PCIDAS_TRIG_SEL(3) /* start trigger mask */ |
117 | #define PCIDAS_TRIG_POL BIT(2) /* invert trigger (1602 only) */ |
118 | #define PCIDAS_TRIG_MODE BIT(3) /* edge/level triggered (1602 only) */ |
119 | #define PCIDAS_TRIG_EN BIT(4) /* enable external start trigger */ |
120 | #define PCIDAS_TRIG_BURSTE BIT(5) /* burst mode enable */ |
121 | #define PCIDAS_TRIG_CLR BIT(7) /* clear external trigger */ |
122 | |
123 | #define PCIDAS_CALIB_REG 0x06 /* CALIBRATION register */ |
124 | #define PCIDAS_CALIB_8800_SEL BIT(8) /* select 8800 caldac */ |
125 | #define PCIDAS_CALIB_TRIM_SEL BIT(9) /* select ad7376 trim pot */ |
126 | #define PCIDAS_CALIB_DAC08_SEL BIT(10) /* select dac08 caldac */ |
127 | #define PCIDAS_CALIB_SRC(x) (((x) & 0x7) << 11) |
128 | #define PCIDAS_CALIB_EN BIT(14) /* calibration source enable */ |
129 | #define PCIDAS_CALIB_DATA BIT(15) /* serial data bit going to caldac */ |
130 | |
131 | #define PCIDAS_AO_REG 0x08 /* dac control and status register */ |
132 | #define PCIDAS_AO_EMPTY BIT(0) /* fifo empty, write clear (1602) */ |
133 | #define PCIDAS_AO_DACEN BIT(1) /* dac enable */ |
134 | #define PCIDAS_AO_START BIT(2) /* start/arm fifo (1602) */ |
135 | #define PCIDAS_AO_PACER(x) (((x) & 0x3) << 3) /* (1602) */ |
136 | #define PCIDAS_AO_PACER_SW PCIDAS_AO_PACER(0) /* software pacer */ |
137 | #define PCIDAS_AO_PACER_INT PCIDAS_AO_PACER(1) /* int. pacer */ |
138 | #define PCIDAS_AO_PACER_EXTN PCIDAS_AO_PACER(2) /* ext. falling edge */ |
139 | #define PCIDAS_AO_PACER_EXTP PCIDAS_AO_PACER(3) /* ext. rising edge */ |
140 | #define PCIDAS_AO_PACER_MASK PCIDAS_AO_PACER(3) /* pacer source bits */ |
141 | #define PCIDAS_AO_CHAN_EN(c) BIT(5 + ((c) & 0x1)) |
142 | #define PCIDAS_AO_CHAN_MASK (PCIDAS_AO_CHAN_EN(0) | PCIDAS_AO_CHAN_EN(1)) |
143 | #define PCIDAS_AO_UPDATE_BOTH BIT(7) /* update both dacs */ |
144 | #define PCIDAS_AO_RANGE(c, r) (((r) & 0x3) << (8 + 2 * ((c) & 0x1))) |
145 | #define PCIDAS_AO_RANGE_MASK(c) PCIDAS_AO_RANGE((c), 0x3) |
146 | |
147 | /* |
148 | * PCI BAR2 Register map (devpriv->pcibar2) |
149 | */ |
150 | #define PCIDAS_AI_DATA_REG 0x00 |
151 | #define PCIDAS_AI_FIFO_CLR_REG 0x02 |
152 | |
153 | /* |
154 | * PCI BAR3 Register map (dev->iobase) |
155 | */ |
156 | #define PCIDAS_AI_8254_BASE 0x00 |
157 | #define PCIDAS_8255_BASE 0x04 |
158 | #define PCIDAS_AO_8254_BASE 0x08 |
159 | |
160 | /* |
161 | * PCI BAR4 Register map (devpriv->pcibar4) |
162 | */ |
163 | #define PCIDAS_AO_DATA_REG(x) (0x00 + ((x) * 2)) |
164 | #define PCIDAS_AO_FIFO_REG 0x00 |
165 | #define PCIDAS_AO_FIFO_CLR_REG 0x02 |
166 | |
167 | /* analog input ranges for most boards */ |
168 | static const struct comedi_lrange cb_pcidas_ranges = { |
169 | 8, { |
170 | BIP_RANGE(10), |
171 | BIP_RANGE(5), |
172 | BIP_RANGE(2.5), |
173 | BIP_RANGE(1.25), |
174 | UNI_RANGE(10), |
175 | UNI_RANGE(5), |
176 | UNI_RANGE(2.5), |
177 | UNI_RANGE(1.25) |
178 | } |
179 | }; |
180 | |
181 | /* pci-das1001 input ranges */ |
182 | static const struct comedi_lrange cb_pcidas_alt_ranges = { |
183 | 8, { |
184 | BIP_RANGE(10), |
185 | BIP_RANGE(1), |
186 | BIP_RANGE(0.1), |
187 | BIP_RANGE(0.01), |
188 | UNI_RANGE(10), |
189 | UNI_RANGE(1), |
190 | UNI_RANGE(0.1), |
191 | UNI_RANGE(0.01) |
192 | } |
193 | }; |
194 | |
195 | /* analog output ranges */ |
196 | static const struct comedi_lrange cb_pcidas_ao_ranges = { |
197 | 4, { |
198 | BIP_RANGE(5), |
199 | BIP_RANGE(10), |
200 | UNI_RANGE(5), |
201 | UNI_RANGE(10) |
202 | } |
203 | }; |
204 | |
205 | enum cb_pcidas_boardid { |
206 | BOARD_PCIDAS1602_16, |
207 | BOARD_PCIDAS1200, |
208 | BOARD_PCIDAS1602_12, |
209 | BOARD_PCIDAS1200_JR, |
210 | BOARD_PCIDAS1602_16_JR, |
211 | BOARD_PCIDAS1000, |
212 | BOARD_PCIDAS1001, |
213 | BOARD_PCIDAS1002, |
214 | }; |
215 | |
216 | struct cb_pcidas_board { |
217 | const char *name; |
218 | int ai_speed; /* fastest conversion period in ns */ |
219 | int ao_scan_speed; /* analog output scan speed for 1602 series */ |
220 | int fifo_size; /* number of samples fifo can hold */ |
221 | unsigned int is_16bit; /* ai/ao is 1=16-bit; 0=12-bit */ |
222 | unsigned int use_alt_range:1; /* use alternate ai range table */ |
223 | unsigned int has_ao:1; /* has 2 analog output channels */ |
224 | unsigned int has_ao_fifo:1; /* analog output has fifo */ |
225 | unsigned int has_ad8402:1; /* trimpot type 1=AD8402; 0=AD7376 */ |
226 | unsigned int has_dac08:1; |
227 | unsigned int is_1602:1; |
228 | }; |
229 | |
230 | static const struct cb_pcidas_board cb_pcidas_boards[] = { |
231 | [BOARD_PCIDAS1602_16] = { |
232 | .name = "pci-das1602/16" , |
233 | .ai_speed = 5000, |
234 | .ao_scan_speed = 10000, |
235 | .fifo_size = 512, |
236 | .is_16bit = 1, |
237 | .has_ao = 1, |
238 | .has_ao_fifo = 1, |
239 | .has_ad8402 = 1, |
240 | .has_dac08 = 1, |
241 | .is_1602 = 1, |
242 | }, |
243 | [BOARD_PCIDAS1200] = { |
244 | .name = "pci-das1200" , |
245 | .ai_speed = 3200, |
246 | .fifo_size = 1024, |
247 | .has_ao = 1, |
248 | }, |
249 | [BOARD_PCIDAS1602_12] = { |
250 | .name = "pci-das1602/12" , |
251 | .ai_speed = 3200, |
252 | .ao_scan_speed = 4000, |
253 | .fifo_size = 1024, |
254 | .has_ao = 1, |
255 | .has_ao_fifo = 1, |
256 | .is_1602 = 1, |
257 | }, |
258 | [BOARD_PCIDAS1200_JR] = { |
259 | .name = "pci-das1200/jr" , |
260 | .ai_speed = 3200, |
261 | .fifo_size = 1024, |
262 | }, |
263 | [BOARD_PCIDAS1602_16_JR] = { |
264 | .name = "pci-das1602/16/jr" , |
265 | .ai_speed = 5000, |
266 | .fifo_size = 512, |
267 | .is_16bit = 1, |
268 | .has_ad8402 = 1, |
269 | .has_dac08 = 1, |
270 | .is_1602 = 1, |
271 | }, |
272 | [BOARD_PCIDAS1000] = { |
273 | .name = "pci-das1000" , |
274 | .ai_speed = 4000, |
275 | .fifo_size = 1024, |
276 | }, |
277 | [BOARD_PCIDAS1001] = { |
278 | .name = "pci-das1001" , |
279 | .ai_speed = 6800, |
280 | .fifo_size = 1024, |
281 | .use_alt_range = 1, |
282 | .has_ao = 1, |
283 | }, |
284 | [BOARD_PCIDAS1002] = { |
285 | .name = "pci-das1002" , |
286 | .ai_speed = 6800, |
287 | .fifo_size = 1024, |
288 | .has_ao = 1, |
289 | }, |
290 | }; |
291 | |
292 | struct cb_pcidas_private { |
293 | struct comedi_8254 *ao_pacer; |
294 | /* base addresses */ |
295 | unsigned long amcc; /* pcibar0 */ |
296 | unsigned long pcibar1; |
297 | unsigned long pcibar2; |
298 | unsigned long pcibar4; |
299 | /* bits to write to registers */ |
300 | unsigned int ctrl; |
301 | unsigned int amcc_intcsr; |
302 | unsigned int ao_ctrl; |
303 | /* fifo buffers */ |
304 | unsigned short ai_buffer[AI_BUFFER_SIZE]; |
305 | unsigned short ao_buffer[AO_BUFFER_SIZE]; |
306 | unsigned int calib_src; |
307 | }; |
308 | |
309 | static int cb_pcidas_ai_eoc(struct comedi_device *dev, |
310 | struct comedi_subdevice *s, |
311 | struct comedi_insn *insn, |
312 | unsigned long context) |
313 | { |
314 | struct cb_pcidas_private *devpriv = dev->private; |
315 | unsigned int status; |
316 | |
317 | status = inw(port: devpriv->pcibar1 + PCIDAS_AI_REG); |
318 | if (status & PCIDAS_AI_EOC) |
319 | return 0; |
320 | return -EBUSY; |
321 | } |
322 | |
323 | static int cb_pcidas_ai_insn_read(struct comedi_device *dev, |
324 | struct comedi_subdevice *s, |
325 | struct comedi_insn *insn, |
326 | unsigned int *data) |
327 | { |
328 | struct cb_pcidas_private *devpriv = dev->private; |
329 | unsigned int chan = CR_CHAN(insn->chanspec); |
330 | unsigned int range = CR_RANGE(insn->chanspec); |
331 | unsigned int aref = CR_AREF(insn->chanspec); |
332 | unsigned int bits; |
333 | int ret; |
334 | int n; |
335 | |
336 | /* enable calibration input if appropriate */ |
337 | if (insn->chanspec & CR_ALT_SOURCE) { |
338 | outw(PCIDAS_CALIB_EN | PCIDAS_CALIB_SRC(devpriv->calib_src), |
339 | port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
340 | chan = 0; |
341 | } else { |
342 | outw(value: 0, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
343 | } |
344 | |
345 | /* set mux limits and gain */ |
346 | bits = PCIDAS_AI_CHAN(chan) | PCIDAS_AI_GAIN(range); |
347 | /* set unipolar/bipolar */ |
348 | if (comedi_range_is_unipolar(s, range)) |
349 | bits |= PCIDAS_AI_UNIP; |
350 | /* set single-ended/differential */ |
351 | if (aref != AREF_DIFF) |
352 | bits |= PCIDAS_AI_SE; |
353 | outw(value: bits, port: devpriv->pcibar1 + PCIDAS_AI_REG); |
354 | |
355 | /* clear fifo */ |
356 | outw(value: 0, port: devpriv->pcibar2 + PCIDAS_AI_FIFO_CLR_REG); |
357 | |
358 | /* convert n samples */ |
359 | for (n = 0; n < insn->n; n++) { |
360 | /* trigger conversion */ |
361 | outw(value: 0, port: devpriv->pcibar2 + PCIDAS_AI_DATA_REG); |
362 | |
363 | /* wait for conversion to end */ |
364 | ret = comedi_timeout(dev, s, insn, cb: cb_pcidas_ai_eoc, context: 0); |
365 | if (ret) |
366 | return ret; |
367 | |
368 | /* read data */ |
369 | data[n] = inw(port: devpriv->pcibar2 + PCIDAS_AI_DATA_REG); |
370 | } |
371 | |
372 | /* return the number of samples read/written */ |
373 | return n; |
374 | } |
375 | |
376 | static int cb_pcidas_ai_insn_config(struct comedi_device *dev, |
377 | struct comedi_subdevice *s, |
378 | struct comedi_insn *insn, |
379 | unsigned int *data) |
380 | { |
381 | struct cb_pcidas_private *devpriv = dev->private; |
382 | int id = data[0]; |
383 | unsigned int source = data[1]; |
384 | |
385 | switch (id) { |
386 | case INSN_CONFIG_ALT_SOURCE: |
387 | if (source >= 8) { |
388 | dev_err(dev->class_dev, |
389 | "invalid calibration source: %i\n" , |
390 | source); |
391 | return -EINVAL; |
392 | } |
393 | devpriv->calib_src = source; |
394 | break; |
395 | default: |
396 | return -EINVAL; |
397 | } |
398 | return insn->n; |
399 | } |
400 | |
401 | /* analog output insn for pcidas-1000 and 1200 series */ |
402 | static int cb_pcidas_ao_nofifo_insn_write(struct comedi_device *dev, |
403 | struct comedi_subdevice *s, |
404 | struct comedi_insn *insn, |
405 | unsigned int *data) |
406 | { |
407 | struct cb_pcidas_private *devpriv = dev->private; |
408 | unsigned int chan = CR_CHAN(insn->chanspec); |
409 | unsigned int range = CR_RANGE(insn->chanspec); |
410 | unsigned int val = s->readback[chan]; |
411 | unsigned long flags; |
412 | int i; |
413 | |
414 | /* set channel and range */ |
415 | spin_lock_irqsave(&dev->spinlock, flags); |
416 | devpriv->ao_ctrl &= ~(PCIDAS_AO_UPDATE_BOTH | |
417 | PCIDAS_AO_RANGE_MASK(chan)); |
418 | devpriv->ao_ctrl |= PCIDAS_AO_DACEN | PCIDAS_AO_RANGE(chan, range); |
419 | outw(value: devpriv->ao_ctrl, port: devpriv->pcibar1 + PCIDAS_AO_REG); |
420 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
421 | |
422 | for (i = 0; i < insn->n; i++) { |
423 | val = data[i]; |
424 | outw(value: val, port: devpriv->pcibar4 + PCIDAS_AO_DATA_REG(chan)); |
425 | } |
426 | |
427 | s->readback[chan] = val; |
428 | |
429 | return insn->n; |
430 | } |
431 | |
432 | /* analog output insn for pcidas-1602 series */ |
433 | static int cb_pcidas_ao_fifo_insn_write(struct comedi_device *dev, |
434 | struct comedi_subdevice *s, |
435 | struct comedi_insn *insn, |
436 | unsigned int *data) |
437 | { |
438 | struct cb_pcidas_private *devpriv = dev->private; |
439 | unsigned int chan = CR_CHAN(insn->chanspec); |
440 | unsigned int range = CR_RANGE(insn->chanspec); |
441 | unsigned int val = s->readback[chan]; |
442 | unsigned long flags; |
443 | int i; |
444 | |
445 | /* clear dac fifo */ |
446 | outw(value: 0, port: devpriv->pcibar4 + PCIDAS_AO_FIFO_CLR_REG); |
447 | |
448 | /* set channel and range */ |
449 | spin_lock_irqsave(&dev->spinlock, flags); |
450 | devpriv->ao_ctrl &= ~(PCIDAS_AO_CHAN_MASK | PCIDAS_AO_RANGE_MASK(chan) | |
451 | PCIDAS_AO_PACER_MASK); |
452 | devpriv->ao_ctrl |= PCIDAS_AO_DACEN | PCIDAS_AO_RANGE(chan, range) | |
453 | PCIDAS_AO_CHAN_EN(chan) | PCIDAS_AO_START; |
454 | outw(value: devpriv->ao_ctrl, port: devpriv->pcibar1 + PCIDAS_AO_REG); |
455 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
456 | |
457 | for (i = 0; i < insn->n; i++) { |
458 | val = data[i]; |
459 | outw(value: val, port: devpriv->pcibar4 + PCIDAS_AO_FIFO_REG); |
460 | } |
461 | |
462 | s->readback[chan] = val; |
463 | |
464 | return insn->n; |
465 | } |
466 | |
467 | static int cb_pcidas_eeprom_ready(struct comedi_device *dev, |
468 | struct comedi_subdevice *s, |
469 | struct comedi_insn *insn, |
470 | unsigned long context) |
471 | { |
472 | struct cb_pcidas_private *devpriv = dev->private; |
473 | unsigned int status; |
474 | |
475 | status = inb(port: devpriv->amcc + AMCC_OP_REG_MCSR_NVCMD); |
476 | if ((status & MCSR_NV_BUSY) == 0) |
477 | return 0; |
478 | return -EBUSY; |
479 | } |
480 | |
481 | static int cb_pcidas_eeprom_insn_read(struct comedi_device *dev, |
482 | struct comedi_subdevice *s, |
483 | struct comedi_insn *insn, |
484 | unsigned int *data) |
485 | { |
486 | struct cb_pcidas_private *devpriv = dev->private; |
487 | unsigned int chan = CR_CHAN(insn->chanspec); |
488 | int ret; |
489 | int i; |
490 | |
491 | for (i = 0; i < insn->n; i++) { |
492 | /* make sure eeprom is ready */ |
493 | ret = comedi_timeout(dev, s, insn, cb: cb_pcidas_eeprom_ready, context: 0); |
494 | if (ret) |
495 | return ret; |
496 | |
497 | /* set address (chan) and read operation */ |
498 | outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_LOW_ADDR, |
499 | port: devpriv->amcc + AMCC_OP_REG_MCSR_NVCMD); |
500 | outb(value: chan & 0xff, port: devpriv->amcc + AMCC_OP_REG_MCSR_NVDATA); |
501 | outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_HIGH_ADDR, |
502 | port: devpriv->amcc + AMCC_OP_REG_MCSR_NVCMD); |
503 | outb(value: (chan >> 8) & 0xff, |
504 | port: devpriv->amcc + AMCC_OP_REG_MCSR_NVDATA); |
505 | outb(MCSR_NV_ENABLE | MCSR_NV_READ, |
506 | port: devpriv->amcc + AMCC_OP_REG_MCSR_NVCMD); |
507 | |
508 | /* wait for data to be returned */ |
509 | ret = comedi_timeout(dev, s, insn, cb: cb_pcidas_eeprom_ready, context: 0); |
510 | if (ret) |
511 | return ret; |
512 | |
513 | data[i] = inb(port: devpriv->amcc + AMCC_OP_REG_MCSR_NVDATA); |
514 | } |
515 | |
516 | return insn->n; |
517 | } |
518 | |
519 | static void cb_pcidas_calib_write(struct comedi_device *dev, |
520 | unsigned int val, unsigned int len, |
521 | bool trimpot) |
522 | { |
523 | struct cb_pcidas_private *devpriv = dev->private; |
524 | unsigned int calib_bits; |
525 | unsigned int bit; |
526 | |
527 | calib_bits = PCIDAS_CALIB_EN | PCIDAS_CALIB_SRC(devpriv->calib_src); |
528 | if (trimpot) { |
529 | /* select trimpot */ |
530 | calib_bits |= PCIDAS_CALIB_TRIM_SEL; |
531 | outw(value: calib_bits, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
532 | } |
533 | |
534 | /* write bitstream to calibration device */ |
535 | for (bit = 1 << (len - 1); bit; bit >>= 1) { |
536 | if (val & bit) |
537 | calib_bits |= PCIDAS_CALIB_DATA; |
538 | else |
539 | calib_bits &= ~PCIDAS_CALIB_DATA; |
540 | udelay(1); |
541 | outw(value: calib_bits, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
542 | } |
543 | udelay(1); |
544 | |
545 | calib_bits = PCIDAS_CALIB_EN | PCIDAS_CALIB_SRC(devpriv->calib_src); |
546 | |
547 | if (!trimpot) { |
548 | /* select caldac */ |
549 | outw(value: calib_bits | PCIDAS_CALIB_8800_SEL, |
550 | port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
551 | udelay(1); |
552 | } |
553 | |
554 | /* latch value to trimpot/caldac */ |
555 | outw(value: calib_bits, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
556 | } |
557 | |
558 | static int cb_pcidas_caldac_insn_write(struct comedi_device *dev, |
559 | struct comedi_subdevice *s, |
560 | struct comedi_insn *insn, |
561 | unsigned int *data) |
562 | { |
563 | unsigned int chan = CR_CHAN(insn->chanspec); |
564 | |
565 | if (insn->n) { |
566 | unsigned int val = data[insn->n - 1]; |
567 | |
568 | if (s->readback[chan] != val) { |
569 | /* write 11-bit channel/value to caldac */ |
570 | cb_pcidas_calib_write(dev, val: (chan << 8) | val, len: 11, |
571 | trimpot: false); |
572 | s->readback[chan] = val; |
573 | } |
574 | } |
575 | |
576 | return insn->n; |
577 | } |
578 | |
579 | static void cb_pcidas_dac08_write(struct comedi_device *dev, unsigned int val) |
580 | { |
581 | struct cb_pcidas_private *devpriv = dev->private; |
582 | |
583 | val |= PCIDAS_CALIB_EN | PCIDAS_CALIB_SRC(devpriv->calib_src); |
584 | |
585 | /* latch the new value into the caldac */ |
586 | outw(value: val, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
587 | udelay(1); |
588 | outw(value: val | PCIDAS_CALIB_DAC08_SEL, |
589 | port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
590 | udelay(1); |
591 | outw(value: val, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
592 | udelay(1); |
593 | } |
594 | |
595 | static int cb_pcidas_dac08_insn_write(struct comedi_device *dev, |
596 | struct comedi_subdevice *s, |
597 | struct comedi_insn *insn, |
598 | unsigned int *data) |
599 | { |
600 | unsigned int chan = CR_CHAN(insn->chanspec); |
601 | |
602 | if (insn->n) { |
603 | unsigned int val = data[insn->n - 1]; |
604 | |
605 | if (s->readback[chan] != val) { |
606 | cb_pcidas_dac08_write(dev, val); |
607 | s->readback[chan] = val; |
608 | } |
609 | } |
610 | |
611 | return insn->n; |
612 | } |
613 | |
614 | static void cb_pcidas_trimpot_write(struct comedi_device *dev, |
615 | unsigned int chan, unsigned int val) |
616 | { |
617 | const struct cb_pcidas_board *board = dev->board_ptr; |
618 | |
619 | if (board->has_ad8402) { |
620 | /* write 10-bit channel/value to AD8402 trimpot */ |
621 | cb_pcidas_calib_write(dev, val: (chan << 8) | val, len: 10, trimpot: true); |
622 | } else { |
623 | /* write 7-bit value to AD7376 trimpot */ |
624 | cb_pcidas_calib_write(dev, val, len: 7, trimpot: true); |
625 | } |
626 | } |
627 | |
628 | static int cb_pcidas_trimpot_insn_write(struct comedi_device *dev, |
629 | struct comedi_subdevice *s, |
630 | struct comedi_insn *insn, |
631 | unsigned int *data) |
632 | { |
633 | unsigned int chan = CR_CHAN(insn->chanspec); |
634 | |
635 | if (insn->n) { |
636 | unsigned int val = data[insn->n - 1]; |
637 | |
638 | if (s->readback[chan] != val) { |
639 | cb_pcidas_trimpot_write(dev, chan, val); |
640 | s->readback[chan] = val; |
641 | } |
642 | } |
643 | |
644 | return insn->n; |
645 | } |
646 | |
647 | static int cb_pcidas_ai_check_chanlist(struct comedi_device *dev, |
648 | struct comedi_subdevice *s, |
649 | struct comedi_cmd *cmd) |
650 | { |
651 | unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); |
652 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); |
653 | int i; |
654 | |
655 | for (i = 1; i < cmd->chanlist_len; i++) { |
656 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
657 | unsigned int range = CR_RANGE(cmd->chanlist[i]); |
658 | |
659 | if (chan != (chan0 + i) % s->n_chan) { |
660 | dev_dbg(dev->class_dev, |
661 | "entries in chanlist must be consecutive channels, counting upwards\n" ); |
662 | return -EINVAL; |
663 | } |
664 | |
665 | if (range != range0) { |
666 | dev_dbg(dev->class_dev, |
667 | "entries in chanlist must all have the same gain\n" ); |
668 | return -EINVAL; |
669 | } |
670 | } |
671 | return 0; |
672 | } |
673 | |
674 | static int cb_pcidas_ai_cmdtest(struct comedi_device *dev, |
675 | struct comedi_subdevice *s, |
676 | struct comedi_cmd *cmd) |
677 | { |
678 | const struct cb_pcidas_board *board = dev->board_ptr; |
679 | int err = 0; |
680 | unsigned int arg; |
681 | |
682 | /* Step 1 : check if triggers are trivially valid */ |
683 | |
684 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_NOW | TRIG_EXT); |
685 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
686 | TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT); |
687 | err |= comedi_check_trigger_src(src: &cmd->convert_src, |
688 | TRIG_TIMER | TRIG_NOW | TRIG_EXT); |
689 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
690 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_COUNT | TRIG_NONE); |
691 | |
692 | if (err) |
693 | return 1; |
694 | |
695 | /* Step 2a : make sure trigger sources are unique */ |
696 | |
697 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
698 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
699 | err |= comedi_check_trigger_is_unique(src: cmd->convert_src); |
700 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
701 | |
702 | /* Step 2b : and mutually compatible */ |
703 | |
704 | if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW) |
705 | err |= -EINVAL; |
706 | if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW) |
707 | err |= -EINVAL; |
708 | if (cmd->start_src == TRIG_EXT && |
709 | (cmd->convert_src == TRIG_EXT || cmd->scan_begin_src == TRIG_EXT)) |
710 | err |= -EINVAL; |
711 | |
712 | if (err) |
713 | return 2; |
714 | |
715 | /* Step 3: check if arguments are trivially valid */ |
716 | |
717 | switch (cmd->start_src) { |
718 | case TRIG_NOW: |
719 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
720 | break; |
721 | case TRIG_EXT: |
722 | /* External trigger, only CR_EDGE and CR_INVERT flags allowed */ |
723 | if ((cmd->start_arg |
724 | & (CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT))) != 0) { |
725 | cmd->start_arg &= ~(CR_FLAGS_MASK & |
726 | ~(CR_EDGE | CR_INVERT)); |
727 | err |= -EINVAL; |
728 | } |
729 | if (!board->is_1602 && (cmd->start_arg & CR_INVERT)) { |
730 | cmd->start_arg &= (CR_FLAGS_MASK & ~CR_INVERT); |
731 | err |= -EINVAL; |
732 | } |
733 | break; |
734 | } |
735 | |
736 | if (cmd->scan_begin_src == TRIG_TIMER) { |
737 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, |
738 | val: board->ai_speed * |
739 | cmd->chanlist_len); |
740 | } |
741 | |
742 | if (cmd->convert_src == TRIG_TIMER) { |
743 | err |= comedi_check_trigger_arg_min(arg: &cmd->convert_arg, |
744 | val: board->ai_speed); |
745 | } |
746 | |
747 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
748 | val: cmd->chanlist_len); |
749 | |
750 | if (cmd->stop_src == TRIG_COUNT) |
751 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
752 | else /* TRIG_NONE */ |
753 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
754 | |
755 | if (err) |
756 | return 3; |
757 | |
758 | /* step 4: fix up any arguments */ |
759 | |
760 | if (cmd->scan_begin_src == TRIG_TIMER) { |
761 | arg = cmd->scan_begin_arg; |
762 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
763 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: arg); |
764 | } |
765 | if (cmd->convert_src == TRIG_TIMER) { |
766 | arg = cmd->convert_arg; |
767 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
768 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: arg); |
769 | } |
770 | |
771 | if (err) |
772 | return 4; |
773 | |
774 | /* Step 5: check channel list if it exists */ |
775 | if (cmd->chanlist && cmd->chanlist_len > 0) |
776 | err |= cb_pcidas_ai_check_chanlist(dev, s, cmd); |
777 | |
778 | if (err) |
779 | return 5; |
780 | |
781 | return 0; |
782 | } |
783 | |
784 | static int cb_pcidas_ai_cmd(struct comedi_device *dev, |
785 | struct comedi_subdevice *s) |
786 | { |
787 | const struct cb_pcidas_board *board = dev->board_ptr; |
788 | struct cb_pcidas_private *devpriv = dev->private; |
789 | struct comedi_async *async = s->async; |
790 | struct comedi_cmd *cmd = &async->cmd; |
791 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); |
792 | unsigned int bits; |
793 | unsigned long flags; |
794 | |
795 | /* make sure PCIDAS_CALIB_EN is disabled */ |
796 | outw(value: 0, port: devpriv->pcibar1 + PCIDAS_CALIB_REG); |
797 | /* initialize before settings pacer source and count values */ |
798 | outw(PCIDAS_TRIG_SEL_NONE, port: devpriv->pcibar1 + PCIDAS_TRIG_REG); |
799 | /* clear fifo */ |
800 | outw(value: 0, port: devpriv->pcibar2 + PCIDAS_AI_FIFO_CLR_REG); |
801 | |
802 | /* set mux limits, gain and pacer source */ |
803 | bits = PCIDAS_AI_FIRST(CR_CHAN(cmd->chanlist[0])) | |
804 | PCIDAS_AI_LAST(CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1])) | |
805 | PCIDAS_AI_GAIN(range0); |
806 | /* set unipolar/bipolar */ |
807 | if (comedi_range_is_unipolar(s, range: range0)) |
808 | bits |= PCIDAS_AI_UNIP; |
809 | /* set singleended/differential */ |
810 | if (CR_AREF(cmd->chanlist[0]) != AREF_DIFF) |
811 | bits |= PCIDAS_AI_SE; |
812 | /* set pacer source */ |
813 | if (cmd->convert_src == TRIG_EXT || cmd->scan_begin_src == TRIG_EXT) |
814 | bits |= PCIDAS_AI_PACER_EXTP; |
815 | else |
816 | bits |= PCIDAS_AI_PACER_INT; |
817 | outw(value: bits, port: devpriv->pcibar1 + PCIDAS_AI_REG); |
818 | |
819 | /* load counters */ |
820 | if (cmd->scan_begin_src == TRIG_TIMER || |
821 | cmd->convert_src == TRIG_TIMER) { |
822 | comedi_8254_update_divisors(i8254: dev->pacer); |
823 | comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: true); |
824 | } |
825 | |
826 | /* enable interrupts */ |
827 | spin_lock_irqsave(&dev->spinlock, flags); |
828 | devpriv->ctrl |= PCIDAS_CTRL_INTE; |
829 | devpriv->ctrl &= ~PCIDAS_CTRL_INT_MASK; |
830 | if (cmd->flags & CMDF_WAKE_EOS) { |
831 | if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1) { |
832 | /* interrupt end of burst */ |
833 | devpriv->ctrl |= PCIDAS_CTRL_INT_EOS; |
834 | } else { |
835 | /* interrupt fifo not empty */ |
836 | devpriv->ctrl |= PCIDAS_CTRL_INT_FNE; |
837 | } |
838 | } else { |
839 | /* interrupt fifo half full */ |
840 | devpriv->ctrl |= PCIDAS_CTRL_INT_FHF; |
841 | } |
842 | |
843 | /* enable (and clear) interrupts */ |
844 | outw(value: devpriv->ctrl | |
845 | PCIDAS_CTRL_EOAI | PCIDAS_CTRL_INT_CLR | PCIDAS_CTRL_LADFUL, |
846 | port: devpriv->pcibar1 + PCIDAS_CTRL_REG); |
847 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
848 | |
849 | /* set start trigger and burst mode */ |
850 | bits = 0; |
851 | if (cmd->start_src == TRIG_NOW) { |
852 | bits |= PCIDAS_TRIG_SEL_SW; |
853 | } else { /* TRIG_EXT */ |
854 | bits |= PCIDAS_TRIG_SEL_EXT | PCIDAS_TRIG_EN | PCIDAS_TRIG_CLR; |
855 | if (board->is_1602) { |
856 | if (cmd->start_arg & CR_INVERT) |
857 | bits |= PCIDAS_TRIG_POL; |
858 | if (cmd->start_arg & CR_EDGE) |
859 | bits |= PCIDAS_TRIG_MODE; |
860 | } |
861 | } |
862 | if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1) |
863 | bits |= PCIDAS_TRIG_BURSTE; |
864 | outw(value: bits, port: devpriv->pcibar1 + PCIDAS_TRIG_REG); |
865 | |
866 | return 0; |
867 | } |
868 | |
869 | static int cb_pcidas_ao_check_chanlist(struct comedi_device *dev, |
870 | struct comedi_subdevice *s, |
871 | struct comedi_cmd *cmd) |
872 | { |
873 | unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); |
874 | |
875 | if (cmd->chanlist_len > 1) { |
876 | unsigned int chan1 = CR_CHAN(cmd->chanlist[1]); |
877 | |
878 | if (chan0 != 0 || chan1 != 1) { |
879 | dev_dbg(dev->class_dev, |
880 | "channels must be ordered channel 0, channel 1 in chanlist\n" ); |
881 | return -EINVAL; |
882 | } |
883 | } |
884 | |
885 | return 0; |
886 | } |
887 | |
888 | static int cb_pcidas_ao_cmdtest(struct comedi_device *dev, |
889 | struct comedi_subdevice *s, |
890 | struct comedi_cmd *cmd) |
891 | { |
892 | const struct cb_pcidas_board *board = dev->board_ptr; |
893 | struct cb_pcidas_private *devpriv = dev->private; |
894 | int err = 0; |
895 | |
896 | /* Step 1 : check if triggers are trivially valid */ |
897 | |
898 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_INT); |
899 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
900 | TRIG_TIMER | TRIG_EXT); |
901 | err |= comedi_check_trigger_src(src: &cmd->convert_src, TRIG_NOW); |
902 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
903 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_COUNT | TRIG_NONE); |
904 | |
905 | if (err) |
906 | return 1; |
907 | |
908 | /* Step 2a : make sure trigger sources are unique */ |
909 | |
910 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
911 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
912 | |
913 | /* Step 2b : and mutually compatible */ |
914 | |
915 | if (err) |
916 | return 2; |
917 | |
918 | /* Step 3: check if arguments are trivially valid */ |
919 | |
920 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
921 | |
922 | if (cmd->scan_begin_src == TRIG_TIMER) { |
923 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, |
924 | val: board->ao_scan_speed); |
925 | } |
926 | |
927 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
928 | val: cmd->chanlist_len); |
929 | |
930 | if (cmd->stop_src == TRIG_COUNT) |
931 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
932 | else /* TRIG_NONE */ |
933 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
934 | |
935 | if (err) |
936 | return 3; |
937 | |
938 | /* step 4: fix up any arguments */ |
939 | |
940 | if (cmd->scan_begin_src == TRIG_TIMER) { |
941 | unsigned int arg = cmd->scan_begin_arg; |
942 | |
943 | comedi_8254_cascade_ns_to_timer(i8254: devpriv->ao_pacer, |
944 | nanosec: &arg, flags: cmd->flags); |
945 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: arg); |
946 | } |
947 | |
948 | if (err) |
949 | return 4; |
950 | |
951 | /* Step 5: check channel list if it exists */ |
952 | if (cmd->chanlist && cmd->chanlist_len > 0) |
953 | err |= cb_pcidas_ao_check_chanlist(dev, s, cmd); |
954 | |
955 | if (err) |
956 | return 5; |
957 | |
958 | return 0; |
959 | } |
960 | |
961 | static int cb_pcidas_ai_cancel(struct comedi_device *dev, |
962 | struct comedi_subdevice *s) |
963 | { |
964 | struct cb_pcidas_private *devpriv = dev->private; |
965 | unsigned long flags; |
966 | |
967 | spin_lock_irqsave(&dev->spinlock, flags); |
968 | /* disable interrupts */ |
969 | devpriv->ctrl &= ~(PCIDAS_CTRL_INTE | PCIDAS_CTRL_EOAIE); |
970 | outw(value: devpriv->ctrl, port: devpriv->pcibar1 + PCIDAS_CTRL_REG); |
971 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
972 | |
973 | /* disable start trigger source and burst mode */ |
974 | outw(PCIDAS_TRIG_SEL_NONE, port: devpriv->pcibar1 + PCIDAS_TRIG_REG); |
975 | outw(PCIDAS_AI_PACER_SW, port: devpriv->pcibar1 + PCIDAS_AI_REG); |
976 | |
977 | return 0; |
978 | } |
979 | |
980 | static void cb_pcidas_ao_load_fifo(struct comedi_device *dev, |
981 | struct comedi_subdevice *s, |
982 | unsigned int nsamples) |
983 | { |
984 | struct cb_pcidas_private *devpriv = dev->private; |
985 | unsigned int nbytes; |
986 | |
987 | nsamples = comedi_nsamples_left(s, nsamples); |
988 | nbytes = comedi_buf_read_samples(s, data: devpriv->ao_buffer, nsamples); |
989 | |
990 | nsamples = comedi_bytes_to_samples(s, nbytes); |
991 | outsw(port: devpriv->pcibar4 + PCIDAS_AO_FIFO_REG, |
992 | addr: devpriv->ao_buffer, count: nsamples); |
993 | } |
994 | |
995 | static int cb_pcidas_ao_inttrig(struct comedi_device *dev, |
996 | struct comedi_subdevice *s, |
997 | unsigned int trig_num) |
998 | { |
999 | const struct cb_pcidas_board *board = dev->board_ptr; |
1000 | struct cb_pcidas_private *devpriv = dev->private; |
1001 | struct comedi_async *async = s->async; |
1002 | struct comedi_cmd *cmd = &async->cmd; |
1003 | unsigned long flags; |
1004 | |
1005 | if (trig_num != cmd->start_arg) |
1006 | return -EINVAL; |
1007 | |
1008 | cb_pcidas_ao_load_fifo(dev, s, nsamples: board->fifo_size); |
1009 | |
1010 | /* enable dac half-full and empty interrupts */ |
1011 | spin_lock_irqsave(&dev->spinlock, flags); |
1012 | devpriv->ctrl |= PCIDAS_CTRL_DAEMIE | PCIDAS_CTRL_DAHFIE; |
1013 | |
1014 | /* enable and clear interrupts */ |
1015 | outw(value: devpriv->ctrl | PCIDAS_CTRL_DAEMI | PCIDAS_CTRL_DAHFI, |
1016 | port: devpriv->pcibar1 + PCIDAS_CTRL_REG); |
1017 | |
1018 | /* start dac */ |
1019 | devpriv->ao_ctrl |= PCIDAS_AO_START | PCIDAS_AO_DACEN | PCIDAS_AO_EMPTY; |
1020 | outw(value: devpriv->ao_ctrl, port: devpriv->pcibar1 + PCIDAS_AO_REG); |
1021 | |
1022 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1023 | |
1024 | async->inttrig = NULL; |
1025 | |
1026 | return 0; |
1027 | } |
1028 | |
1029 | static int cb_pcidas_ao_cmd(struct comedi_device *dev, |
1030 | struct comedi_subdevice *s) |
1031 | { |
1032 | struct cb_pcidas_private *devpriv = dev->private; |
1033 | struct comedi_async *async = s->async; |
1034 | struct comedi_cmd *cmd = &async->cmd; |
1035 | unsigned int i; |
1036 | unsigned long flags; |
1037 | |
1038 | /* set channel limits, gain */ |
1039 | spin_lock_irqsave(&dev->spinlock, flags); |
1040 | for (i = 0; i < cmd->chanlist_len; i++) { |
1041 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
1042 | unsigned int range = CR_RANGE(cmd->chanlist[i]); |
1043 | |
1044 | /* enable channel */ |
1045 | devpriv->ao_ctrl |= PCIDAS_AO_CHAN_EN(chan); |
1046 | /* set range */ |
1047 | devpriv->ao_ctrl |= PCIDAS_AO_RANGE(chan, range); |
1048 | } |
1049 | |
1050 | /* disable analog out before settings pacer source and count values */ |
1051 | outw(value: devpriv->ao_ctrl, port: devpriv->pcibar1 + PCIDAS_AO_REG); |
1052 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1053 | |
1054 | /* clear fifo */ |
1055 | outw(value: 0, port: devpriv->pcibar4 + PCIDAS_AO_FIFO_CLR_REG); |
1056 | |
1057 | /* load counters */ |
1058 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1059 | comedi_8254_update_divisors(i8254: devpriv->ao_pacer); |
1060 | comedi_8254_pacer_enable(i8254: devpriv->ao_pacer, counter1: 1, counter2: 2, enable: true); |
1061 | } |
1062 | |
1063 | /* set pacer source */ |
1064 | spin_lock_irqsave(&dev->spinlock, flags); |
1065 | switch (cmd->scan_begin_src) { |
1066 | case TRIG_TIMER: |
1067 | devpriv->ao_ctrl |= PCIDAS_AO_PACER_INT; |
1068 | break; |
1069 | case TRIG_EXT: |
1070 | devpriv->ao_ctrl |= PCIDAS_AO_PACER_EXTP; |
1071 | break; |
1072 | default: |
1073 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1074 | dev_err(dev->class_dev, "error setting dac pacer source\n" ); |
1075 | return -1; |
1076 | } |
1077 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1078 | |
1079 | async->inttrig = cb_pcidas_ao_inttrig; |
1080 | |
1081 | return 0; |
1082 | } |
1083 | |
1084 | static int cb_pcidas_ao_cancel(struct comedi_device *dev, |
1085 | struct comedi_subdevice *s) |
1086 | { |
1087 | struct cb_pcidas_private *devpriv = dev->private; |
1088 | unsigned long flags; |
1089 | |
1090 | spin_lock_irqsave(&dev->spinlock, flags); |
1091 | /* disable interrupts */ |
1092 | devpriv->ctrl &= ~(PCIDAS_CTRL_DAHFIE | PCIDAS_CTRL_DAEMIE); |
1093 | outw(value: devpriv->ctrl, port: devpriv->pcibar1 + PCIDAS_CTRL_REG); |
1094 | |
1095 | /* disable output */ |
1096 | devpriv->ao_ctrl &= ~(PCIDAS_AO_DACEN | PCIDAS_AO_PACER_MASK); |
1097 | outw(value: devpriv->ao_ctrl, port: devpriv->pcibar1 + PCIDAS_AO_REG); |
1098 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1099 | |
1100 | return 0; |
1101 | } |
1102 | |
1103 | static unsigned int cb_pcidas_ao_interrupt(struct comedi_device *dev, |
1104 | unsigned int status) |
1105 | { |
1106 | const struct cb_pcidas_board *board = dev->board_ptr; |
1107 | struct cb_pcidas_private *devpriv = dev->private; |
1108 | struct comedi_subdevice *s = dev->write_subdev; |
1109 | struct comedi_async *async = s->async; |
1110 | struct comedi_cmd *cmd = &async->cmd; |
1111 | unsigned int irq_clr = 0; |
1112 | |
1113 | if (status & PCIDAS_CTRL_DAEMI) { |
1114 | irq_clr |= PCIDAS_CTRL_DAEMI; |
1115 | |
1116 | if (inw(port: devpriv->pcibar4 + PCIDAS_AO_REG) & PCIDAS_AO_EMPTY) { |
1117 | if (cmd->stop_src == TRIG_COUNT && |
1118 | async->scans_done >= cmd->stop_arg) { |
1119 | async->events |= COMEDI_CB_EOA; |
1120 | } else { |
1121 | dev_err(dev->class_dev, "dac fifo underflow\n" ); |
1122 | async->events |= COMEDI_CB_ERROR; |
1123 | } |
1124 | } |
1125 | } else if (status & PCIDAS_CTRL_DAHFI) { |
1126 | irq_clr |= PCIDAS_CTRL_DAHFI; |
1127 | |
1128 | cb_pcidas_ao_load_fifo(dev, s, nsamples: board->fifo_size / 2); |
1129 | } |
1130 | |
1131 | comedi_handle_events(dev, s); |
1132 | |
1133 | return irq_clr; |
1134 | } |
1135 | |
1136 | static unsigned int cb_pcidas_ai_interrupt(struct comedi_device *dev, |
1137 | unsigned int status) |
1138 | { |
1139 | const struct cb_pcidas_board *board = dev->board_ptr; |
1140 | struct cb_pcidas_private *devpriv = dev->private; |
1141 | struct comedi_subdevice *s = dev->read_subdev; |
1142 | struct comedi_async *async = s->async; |
1143 | struct comedi_cmd *cmd = &async->cmd; |
1144 | unsigned int irq_clr = 0; |
1145 | |
1146 | if (status & PCIDAS_CTRL_ADHFI) { |
1147 | unsigned int num_samples; |
1148 | |
1149 | irq_clr |= PCIDAS_CTRL_INT_CLR; |
1150 | |
1151 | /* FIFO is half-full - read data */ |
1152 | num_samples = comedi_nsamples_left(s, nsamples: board->fifo_size / 2); |
1153 | insw(port: devpriv->pcibar2 + PCIDAS_AI_DATA_REG, |
1154 | addr: devpriv->ai_buffer, count: num_samples); |
1155 | comedi_buf_write_samples(s, data: devpriv->ai_buffer, nsamples: num_samples); |
1156 | |
1157 | if (cmd->stop_src == TRIG_COUNT && |
1158 | async->scans_done >= cmd->stop_arg) |
1159 | async->events |= COMEDI_CB_EOA; |
1160 | } else if (status & (PCIDAS_CTRL_ADNEI | PCIDAS_CTRL_EOBI)) { |
1161 | unsigned int i; |
1162 | |
1163 | irq_clr |= PCIDAS_CTRL_INT_CLR; |
1164 | |
1165 | /* FIFO is not empty - read data until empty or timeoout */ |
1166 | for (i = 0; i < 10000; i++) { |
1167 | unsigned short val; |
1168 | |
1169 | /* break if fifo is empty */ |
1170 | if ((inw(port: devpriv->pcibar1 + PCIDAS_CTRL_REG) & |
1171 | PCIDAS_CTRL_ADNE) == 0) |
1172 | break; |
1173 | val = inw(port: devpriv->pcibar2 + PCIDAS_AI_DATA_REG); |
1174 | comedi_buf_write_samples(s, data: &val, nsamples: 1); |
1175 | |
1176 | if (cmd->stop_src == TRIG_COUNT && |
1177 | async->scans_done >= cmd->stop_arg) { |
1178 | async->events |= COMEDI_CB_EOA; |
1179 | break; |
1180 | } |
1181 | } |
1182 | } else if (status & PCIDAS_CTRL_EOAI) { |
1183 | irq_clr |= PCIDAS_CTRL_EOAI; |
1184 | |
1185 | dev_err(dev->class_dev, |
1186 | "bug! encountered end of acquisition interrupt?\n" ); |
1187 | } |
1188 | |
1189 | /* check for fifo overflow */ |
1190 | if (status & PCIDAS_CTRL_LADFUL) { |
1191 | irq_clr |= PCIDAS_CTRL_LADFUL; |
1192 | |
1193 | dev_err(dev->class_dev, "fifo overflow\n" ); |
1194 | async->events |= COMEDI_CB_ERROR; |
1195 | } |
1196 | |
1197 | comedi_handle_events(dev, s); |
1198 | |
1199 | return irq_clr; |
1200 | } |
1201 | |
1202 | static irqreturn_t cb_pcidas_interrupt(int irq, void *d) |
1203 | { |
1204 | struct comedi_device *dev = d; |
1205 | struct cb_pcidas_private *devpriv = dev->private; |
1206 | unsigned int irq_clr = 0; |
1207 | unsigned int amcc_status; |
1208 | unsigned int status; |
1209 | |
1210 | if (!dev->attached) |
1211 | return IRQ_NONE; |
1212 | |
1213 | amcc_status = inl(port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
1214 | |
1215 | if ((INTCSR_INTR_ASSERTED & amcc_status) == 0) |
1216 | return IRQ_NONE; |
1217 | |
1218 | /* make sure mailbox 4 is empty */ |
1219 | inl_p(port: devpriv->amcc + AMCC_OP_REG_IMB4); |
1220 | /* clear interrupt on amcc s5933 */ |
1221 | outl(value: devpriv->amcc_intcsr | INTCSR_INBOX_INTR_STATUS, |
1222 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
1223 | |
1224 | status = inw(port: devpriv->pcibar1 + PCIDAS_CTRL_REG); |
1225 | |
1226 | /* handle analog output interrupts */ |
1227 | if (status & PCIDAS_CTRL_AO_INT) |
1228 | irq_clr |= cb_pcidas_ao_interrupt(dev, status); |
1229 | |
1230 | /* handle analog input interrupts */ |
1231 | if (status & PCIDAS_CTRL_AI_INT) |
1232 | irq_clr |= cb_pcidas_ai_interrupt(dev, status); |
1233 | |
1234 | if (irq_clr) { |
1235 | unsigned long flags; |
1236 | |
1237 | spin_lock_irqsave(&dev->spinlock, flags); |
1238 | outw(value: devpriv->ctrl | irq_clr, |
1239 | port: devpriv->pcibar1 + PCIDAS_CTRL_REG); |
1240 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1241 | } |
1242 | |
1243 | return IRQ_HANDLED; |
1244 | } |
1245 | |
1246 | static int cb_pcidas_auto_attach(struct comedi_device *dev, |
1247 | unsigned long context) |
1248 | { |
1249 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1250 | const struct cb_pcidas_board *board = NULL; |
1251 | struct cb_pcidas_private *devpriv; |
1252 | struct comedi_subdevice *s; |
1253 | int i; |
1254 | int ret; |
1255 | |
1256 | if (context < ARRAY_SIZE(cb_pcidas_boards)) |
1257 | board = &cb_pcidas_boards[context]; |
1258 | if (!board) |
1259 | return -ENODEV; |
1260 | dev->board_ptr = board; |
1261 | dev->board_name = board->name; |
1262 | |
1263 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
1264 | if (!devpriv) |
1265 | return -ENOMEM; |
1266 | |
1267 | ret = comedi_pci_enable(dev); |
1268 | if (ret) |
1269 | return ret; |
1270 | |
1271 | devpriv->amcc = pci_resource_start(pcidev, 0); |
1272 | devpriv->pcibar1 = pci_resource_start(pcidev, 1); |
1273 | devpriv->pcibar2 = pci_resource_start(pcidev, 2); |
1274 | dev->iobase = pci_resource_start(pcidev, 3); |
1275 | if (board->has_ao) |
1276 | devpriv->pcibar4 = pci_resource_start(pcidev, 4); |
1277 | |
1278 | /* disable and clear interrupts on amcc s5933 */ |
1279 | outl(INTCSR_INBOX_INTR_STATUS, |
1280 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
1281 | |
1282 | ret = request_irq(irq: pcidev->irq, handler: cb_pcidas_interrupt, IRQF_SHARED, |
1283 | name: "cb_pcidas" , dev); |
1284 | if (ret) { |
1285 | dev_dbg(dev->class_dev, "unable to allocate irq %d\n" , |
1286 | pcidev->irq); |
1287 | return ret; |
1288 | } |
1289 | dev->irq = pcidev->irq; |
1290 | |
1291 | dev->pacer = comedi_8254_io_alloc(iobase: dev->iobase + PCIDAS_AI_8254_BASE, |
1292 | I8254_OSC_BASE_10MHZ, I8254_IO8, regshift: 0); |
1293 | if (IS_ERR(ptr: dev->pacer)) |
1294 | return PTR_ERR(ptr: dev->pacer); |
1295 | |
1296 | devpriv->ao_pacer = |
1297 | comedi_8254_io_alloc(iobase: dev->iobase + PCIDAS_AO_8254_BASE, |
1298 | I8254_OSC_BASE_10MHZ, I8254_IO8, regshift: 0); |
1299 | if (IS_ERR(ptr: devpriv->ao_pacer)) |
1300 | return PTR_ERR(ptr: devpriv->ao_pacer); |
1301 | |
1302 | ret = comedi_alloc_subdevices(dev, num_subdevices: 7); |
1303 | if (ret) |
1304 | return ret; |
1305 | |
1306 | /* Analog Input subdevice */ |
1307 | s = &dev->subdevices[0]; |
1308 | s->type = COMEDI_SUBD_AI; |
1309 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; |
1310 | s->n_chan = 16; |
1311 | s->maxdata = board->is_16bit ? 0xffff : 0x0fff; |
1312 | s->range_table = board->use_alt_range ? &cb_pcidas_alt_ranges |
1313 | : &cb_pcidas_ranges; |
1314 | s->insn_read = cb_pcidas_ai_insn_read; |
1315 | s->insn_config = cb_pcidas_ai_insn_config; |
1316 | if (dev->irq) { |
1317 | dev->read_subdev = s; |
1318 | s->subdev_flags |= SDF_CMD_READ; |
1319 | s->len_chanlist = s->n_chan; |
1320 | s->do_cmd = cb_pcidas_ai_cmd; |
1321 | s->do_cmdtest = cb_pcidas_ai_cmdtest; |
1322 | s->cancel = cb_pcidas_ai_cancel; |
1323 | } |
1324 | |
1325 | /* Analog Output subdevice */ |
1326 | s = &dev->subdevices[1]; |
1327 | if (board->has_ao) { |
1328 | s->type = COMEDI_SUBD_AO; |
1329 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND; |
1330 | s->n_chan = 2; |
1331 | s->maxdata = board->is_16bit ? 0xffff : 0x0fff; |
1332 | s->range_table = &cb_pcidas_ao_ranges; |
1333 | s->insn_write = (board->has_ao_fifo) |
1334 | ? cb_pcidas_ao_fifo_insn_write |
1335 | : cb_pcidas_ao_nofifo_insn_write; |
1336 | |
1337 | ret = comedi_alloc_subdev_readback(s); |
1338 | if (ret) |
1339 | return ret; |
1340 | |
1341 | if (dev->irq && board->has_ao_fifo) { |
1342 | dev->write_subdev = s; |
1343 | s->subdev_flags |= SDF_CMD_WRITE; |
1344 | s->len_chanlist = s->n_chan; |
1345 | s->do_cmdtest = cb_pcidas_ao_cmdtest; |
1346 | s->do_cmd = cb_pcidas_ao_cmd; |
1347 | s->cancel = cb_pcidas_ao_cancel; |
1348 | } |
1349 | } else { |
1350 | s->type = COMEDI_SUBD_UNUSED; |
1351 | } |
1352 | |
1353 | /* 8255 */ |
1354 | s = &dev->subdevices[2]; |
1355 | ret = subdev_8255_io_init(dev, s, PCIDAS_8255_BASE); |
1356 | if (ret) |
1357 | return ret; |
1358 | |
1359 | /* Memory subdevice - serial EEPROM */ |
1360 | s = &dev->subdevices[3]; |
1361 | s->type = COMEDI_SUBD_MEMORY; |
1362 | s->subdev_flags = SDF_READABLE | SDF_INTERNAL; |
1363 | s->n_chan = 256; |
1364 | s->maxdata = 0xff; |
1365 | s->insn_read = cb_pcidas_eeprom_insn_read; |
1366 | |
1367 | /* Calibration subdevice - 8800 caldac */ |
1368 | s = &dev->subdevices[4]; |
1369 | s->type = COMEDI_SUBD_CALIB; |
1370 | s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL; |
1371 | s->n_chan = 8; |
1372 | s->maxdata = 0xff; |
1373 | s->insn_write = cb_pcidas_caldac_insn_write; |
1374 | |
1375 | ret = comedi_alloc_subdev_readback(s); |
1376 | if (ret) |
1377 | return ret; |
1378 | |
1379 | for (i = 0; i < s->n_chan; i++) { |
1380 | unsigned int val = s->maxdata / 2; |
1381 | |
1382 | /* write 11-bit channel/value to caldac */ |
1383 | cb_pcidas_calib_write(dev, val: (i << 8) | val, len: 11, trimpot: false); |
1384 | s->readback[i] = val; |
1385 | } |
1386 | |
1387 | /* Calibration subdevice - trim potentiometer */ |
1388 | s = &dev->subdevices[5]; |
1389 | s->type = COMEDI_SUBD_CALIB; |
1390 | s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL; |
1391 | if (board->has_ad8402) { |
1392 | /* |
1393 | * pci-das1602/16 have an AD8402 trimpot: |
1394 | * chan 0 : adc gain |
1395 | * chan 1 : adc postgain offset |
1396 | */ |
1397 | s->n_chan = 2; |
1398 | s->maxdata = 0xff; |
1399 | } else { |
1400 | /* all other boards have an AD7376 trimpot */ |
1401 | s->n_chan = 1; |
1402 | s->maxdata = 0x7f; |
1403 | } |
1404 | s->insn_write = cb_pcidas_trimpot_insn_write; |
1405 | |
1406 | ret = comedi_alloc_subdev_readback(s); |
1407 | if (ret) |
1408 | return ret; |
1409 | |
1410 | for (i = 0; i < s->n_chan; i++) { |
1411 | cb_pcidas_trimpot_write(dev, chan: i, val: s->maxdata / 2); |
1412 | s->readback[i] = s->maxdata / 2; |
1413 | } |
1414 | |
1415 | /* Calibration subdevice - pci-das1602/16 pregain offset (dac08) */ |
1416 | s = &dev->subdevices[6]; |
1417 | if (board->has_dac08) { |
1418 | s->type = COMEDI_SUBD_CALIB; |
1419 | s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL; |
1420 | s->n_chan = 1; |
1421 | s->maxdata = 0xff; |
1422 | s->insn_write = cb_pcidas_dac08_insn_write; |
1423 | |
1424 | ret = comedi_alloc_subdev_readback(s); |
1425 | if (ret) |
1426 | return ret; |
1427 | |
1428 | for (i = 0; i < s->n_chan; i++) { |
1429 | cb_pcidas_dac08_write(dev, val: s->maxdata / 2); |
1430 | s->readback[i] = s->maxdata / 2; |
1431 | } |
1432 | } else { |
1433 | s->type = COMEDI_SUBD_UNUSED; |
1434 | } |
1435 | |
1436 | /* make sure mailbox 4 is empty */ |
1437 | inl(port: devpriv->amcc + AMCC_OP_REG_IMB4); |
1438 | /* Set bits to enable incoming mailbox interrupts on amcc s5933. */ |
1439 | devpriv->amcc_intcsr = INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) | |
1440 | INTCSR_INBOX_FULL_INT; |
1441 | /* clear and enable interrupt on amcc s5933 */ |
1442 | outl(value: devpriv->amcc_intcsr | INTCSR_INBOX_INTR_STATUS, |
1443 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
1444 | |
1445 | return 0; |
1446 | } |
1447 | |
1448 | static void cb_pcidas_detach(struct comedi_device *dev) |
1449 | { |
1450 | struct cb_pcidas_private *devpriv = dev->private; |
1451 | |
1452 | if (devpriv) { |
1453 | if (devpriv->amcc) |
1454 | outl(INTCSR_INBOX_INTR_STATUS, |
1455 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
1456 | if (!IS_ERR(ptr: devpriv->ao_pacer)) |
1457 | kfree(objp: devpriv->ao_pacer); |
1458 | } |
1459 | comedi_pci_detach(dev); |
1460 | } |
1461 | |
1462 | static struct comedi_driver cb_pcidas_driver = { |
1463 | .driver_name = "cb_pcidas" , |
1464 | .module = THIS_MODULE, |
1465 | .auto_attach = cb_pcidas_auto_attach, |
1466 | .detach = cb_pcidas_detach, |
1467 | }; |
1468 | |
1469 | static int cb_pcidas_pci_probe(struct pci_dev *dev, |
1470 | const struct pci_device_id *id) |
1471 | { |
1472 | return comedi_pci_auto_config(pcidev: dev, driver: &cb_pcidas_driver, |
1473 | context: id->driver_data); |
1474 | } |
1475 | |
1476 | static const struct pci_device_id cb_pcidas_pci_table[] = { |
1477 | { PCI_VDEVICE(CB, 0x0001), BOARD_PCIDAS1602_16 }, |
1478 | { PCI_VDEVICE(CB, 0x000f), BOARD_PCIDAS1200 }, |
1479 | { PCI_VDEVICE(CB, 0x0010), BOARD_PCIDAS1602_12 }, |
1480 | { PCI_VDEVICE(CB, 0x0019), BOARD_PCIDAS1200_JR }, |
1481 | { PCI_VDEVICE(CB, 0x001c), BOARD_PCIDAS1602_16_JR }, |
1482 | { PCI_VDEVICE(CB, 0x004c), BOARD_PCIDAS1000 }, |
1483 | { PCI_VDEVICE(CB, 0x001a), BOARD_PCIDAS1001 }, |
1484 | { PCI_VDEVICE(CB, 0x001b), BOARD_PCIDAS1002 }, |
1485 | { 0 } |
1486 | }; |
1487 | MODULE_DEVICE_TABLE(pci, cb_pcidas_pci_table); |
1488 | |
1489 | static struct pci_driver cb_pcidas_pci_driver = { |
1490 | .name = "cb_pcidas" , |
1491 | .id_table = cb_pcidas_pci_table, |
1492 | .probe = cb_pcidas_pci_probe, |
1493 | .remove = comedi_pci_auto_unconfig, |
1494 | }; |
1495 | module_comedi_pci_driver(cb_pcidas_driver, cb_pcidas_pci_driver); |
1496 | |
1497 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
1498 | MODULE_DESCRIPTION("Comedi driver for MeasurementComputing PCI-DAS series" ); |
1499 | MODULE_LICENSE("GPL" ); |
1500 | |