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