1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * comedi/drivers/amplc_pci230.c |
4 | * Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards. |
5 | * |
6 | * Copyright (C) 2001 Allan Willcox <allanwillcox@ozemail.com.au> |
7 | * |
8 | * COMEDI - Linux Control and Measurement Device Interface |
9 | * Copyright (C) 2000 David A. Schleef <ds@schleef.org> |
10 | */ |
11 | |
12 | /* |
13 | * Driver: amplc_pci230 |
14 | * Description: Amplicon PCI230, PCI260 Multifunction I/O boards |
15 | * Author: Allan Willcox <allanwillcox@ozemail.com.au>, |
16 | * Steve D Sharples <steve.sharples@nottingham.ac.uk>, |
17 | * Ian Abbott <abbotti@mev.co.uk> |
18 | * Updated: Mon, 01 Sep 2014 10:09:16 +0000 |
19 | * Devices: [Amplicon] PCI230 (amplc_pci230), PCI230+, PCI260, PCI260+ |
20 | * Status: works |
21 | * |
22 | * Configuration options: |
23 | * none |
24 | * |
25 | * Manual configuration of PCI cards is not supported; they are configured |
26 | * automatically. |
27 | * |
28 | * The PCI230+ and PCI260+ have the same PCI device IDs as the PCI230 and |
29 | * PCI260, but can be distinguished by the size of the PCI regions. A |
30 | * card will be configured as a "+" model if detected as such. |
31 | * |
32 | * Subdevices: |
33 | * |
34 | * PCI230(+) PCI260(+) |
35 | * --------- --------- |
36 | * Subdevices 3 1 |
37 | * 0 AI AI |
38 | * 1 AO |
39 | * 2 DIO |
40 | * |
41 | * AI Subdevice: |
42 | * |
43 | * The AI subdevice has 16 single-ended channels or 8 differential |
44 | * channels. |
45 | * |
46 | * The PCI230 and PCI260 cards have 12-bit resolution. The PCI230+ and |
47 | * PCI260+ cards have 16-bit resolution. |
48 | * |
49 | * For differential mode, use inputs 2N and 2N+1 for channel N (e.g. use |
50 | * inputs 14 and 15 for channel 7). If the card is physically a PCI230 |
51 | * or PCI260 then it actually uses a "pseudo-differential" mode where the |
52 | * inputs are sampled a few microseconds apart. The PCI230+ and PCI260+ |
53 | * use true differential sampling. Another difference is that if the |
54 | * card is physically a PCI230 or PCI260, the inverting input is 2N, |
55 | * whereas for a PCI230+ or PCI260+ the inverting input is 2N+1. So if a |
56 | * PCI230 is physically replaced by a PCI230+ (or a PCI260 with a |
57 | * PCI260+) and differential mode is used, the differential inputs need |
58 | * to be physically swapped on the connector. |
59 | * |
60 | * The following input ranges are supported: |
61 | * |
62 | * 0 => [-10, +10] V |
63 | * 1 => [-5, +5] V |
64 | * 2 => [-2.5, +2.5] V |
65 | * 3 => [-1.25, +1.25] V |
66 | * 4 => [0, 10] V |
67 | * 5 => [0, 5] V |
68 | * 6 => [0, 2.5] V |
69 | * |
70 | * AI Commands: |
71 | * |
72 | * +=========+==============+===========+============+==========+ |
73 | * |start_src|scan_begin_src|convert_src|scan_end_src| stop_src | |
74 | * +=========+==============+===========+============+==========+ |
75 | * |TRIG_NOW | TRIG_FOLLOW |TRIG_TIMER | TRIG_COUNT |TRIG_NONE | |
76 | * |TRIG_INT | |TRIG_EXT(3)| |TRIG_COUNT| |
77 | * | | |TRIG_INT | | | |
78 | * | |--------------|-----------| | | |
79 | * | | TRIG_TIMER(1)|TRIG_TIMER | | | |
80 | * | | TRIG_EXT(2) | | | | |
81 | * | | TRIG_INT | | | | |
82 | * +---------+--------------+-----------+------------+----------+ |
83 | * |
84 | * Note 1: If AI command and AO command are used simultaneously, only |
85 | * one may have scan_begin_src == TRIG_TIMER. |
86 | * |
87 | * Note 2: For PCI230 and PCI230+, scan_begin_src == TRIG_EXT uses |
88 | * DIO channel 16 (pin 49) which will need to be configured as |
89 | * a digital input. For PCI260+, the EXTTRIG/EXTCONVCLK input |
90 | * (pin 17) is used instead. For PCI230, scan_begin_src == |
91 | * TRIG_EXT is not supported. The trigger is a rising edge |
92 | * on the input. |
93 | * |
94 | * Note 3: For convert_src == TRIG_EXT, the EXTTRIG/EXTCONVCLK input |
95 | * (pin 25 on PCI230(+), pin 17 on PCI260(+)) is used. The |
96 | * convert_arg value is interpreted as follows: |
97 | * |
98 | * convert_arg == (CR_EDGE | 0) => rising edge |
99 | * convert_arg == (CR_EDGE | CR_INVERT | 0) => falling edge |
100 | * convert_arg == 0 => falling edge (backwards compatibility) |
101 | * convert_arg == 1 => rising edge (backwards compatibility) |
102 | * |
103 | * All entries in the channel list must use the same analogue reference. |
104 | * If the analogue reference is not AREF_DIFF (not differential) each |
105 | * pair of channel numbers (0 and 1, 2 and 3, etc.) must use the same |
106 | * input range. The input ranges used in the sequence must be all |
107 | * bipolar (ranges 0 to 3) or all unipolar (ranges 4 to 6). The channel |
108 | * sequence must consist of 1 or more identical subsequences. Within the |
109 | * subsequence, channels must be in ascending order with no repeated |
110 | * channels. For example, the following sequences are valid: 0 1 2 3 |
111 | * (single valid subsequence), 0 2 3 5 0 2 3 5 (repeated valid |
112 | * subsequence), 1 1 1 1 (repeated valid subsequence). The following |
113 | * sequences are invalid: 0 3 2 1 (invalid subsequence), 0 2 3 5 0 2 3 |
114 | * (incompletely repeated subsequence). Some versions of the PCI230+ and |
115 | * PCI260+ have a bug that requires a subsequence longer than one entry |
116 | * long to include channel 0. |
117 | * |
118 | * AO Subdevice: |
119 | * |
120 | * The AO subdevice has 2 channels with 12-bit resolution. |
121 | * The following output ranges are supported: |
122 | * 0 => [0, 10] V |
123 | * 1 => [-10, +10] V |
124 | * |
125 | * AO Commands: |
126 | * |
127 | * +=========+==============+===========+============+==========+ |
128 | * |start_src|scan_begin_src|convert_src|scan_end_src| stop_src | |
129 | * +=========+==============+===========+============+==========+ |
130 | * |TRIG_INT | TRIG_TIMER(1)| TRIG_NOW | TRIG_COUNT |TRIG_NONE | |
131 | * | | TRIG_EXT(2) | | |TRIG_COUNT| |
132 | * | | TRIG_INT | | | | |
133 | * +---------+--------------+-----------+------------+----------+ |
134 | * |
135 | * Note 1: If AI command and AO command are used simultaneously, only |
136 | * one may have scan_begin_src == TRIG_TIMER. |
137 | * |
138 | * Note 2: scan_begin_src == TRIG_EXT is only supported if the card is |
139 | * configured as a PCI230+ and is only supported on later |
140 | * versions of the card. As a card configured as a PCI230+ is |
141 | * not guaranteed to support external triggering, please consider |
142 | * this support to be a bonus. It uses the EXTTRIG/ EXTCONVCLK |
143 | * input (PCI230+ pin 25). Triggering will be on the rising edge |
144 | * unless the CR_INVERT flag is set in scan_begin_arg. |
145 | * |
146 | * The channels in the channel sequence must be in ascending order with |
147 | * no repeats. All entries in the channel sequence must use the same |
148 | * output range. |
149 | * |
150 | * DIO Subdevice: |
151 | * |
152 | * The DIO subdevice is a 8255 chip providing 24 DIO channels. The DIO |
153 | * channels are configurable as inputs or outputs in four groups: |
154 | * |
155 | * Port A - channels 0 to 7 |
156 | * Port B - channels 8 to 15 |
157 | * Port CL - channels 16 to 19 |
158 | * Port CH - channels 20 to 23 |
159 | * |
160 | * Only mode 0 of the 8255 chip is supported. |
161 | * |
162 | * Bit 0 of port C (DIO channel 16) is also used as an external scan |
163 | * trigger input for AI commands on PCI230 and PCI230+, so would need to |
164 | * be configured as an input to use it for that purpose. |
165 | */ |
166 | |
167 | /* |
168 | * Extra triggered scan functionality, interrupt bug-fix added by Steve |
169 | * Sharples. Support for PCI230+/260+, more triggered scan functionality, |
170 | * and workarounds for (or detection of) various hardware problems added |
171 | * by Ian Abbott. |
172 | */ |
173 | |
174 | #include <linux/module.h> |
175 | #include <linux/delay.h> |
176 | #include <linux/interrupt.h> |
177 | #include <linux/comedi/comedi_pci.h> |
178 | #include <linux/comedi/comedi_8255.h> |
179 | #include <linux/comedi/comedi_8254.h> |
180 | |
181 | /* |
182 | * PCI230 PCI configuration register information |
183 | */ |
184 | #define PCI_DEVICE_ID_PCI230 0x0000 |
185 | #define PCI_DEVICE_ID_PCI260 0x0006 |
186 | |
187 | /* |
188 | * PCI230 i/o space 1 registers. |
189 | */ |
190 | #define PCI230_PPI_X_BASE 0x00 /* User PPI (82C55) base */ |
191 | #define PCI230_PPI_X_A 0x00 /* User PPI (82C55) port A */ |
192 | #define PCI230_PPI_X_B 0x01 /* User PPI (82C55) port B */ |
193 | #define PCI230_PPI_X_C 0x02 /* User PPI (82C55) port C */ |
194 | #define PCI230_PPI_X_CMD 0x03 /* User PPI (82C55) control word */ |
195 | #define PCI230_Z2_CT_BASE 0x14 /* 82C54 counter/timer base */ |
196 | #define PCI230_ZCLK_SCE 0x1A /* Group Z Clock Configuration */ |
197 | #define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration */ |
198 | #define PCI230_INT_SCE 0x1E /* Interrupt source mask (w) */ |
199 | #define PCI230_INT_STAT 0x1E /* Interrupt status (r) */ |
200 | |
201 | /* |
202 | * PCI230 i/o space 2 registers. |
203 | */ |
204 | #define PCI230_DACCON 0x00 /* DAC control */ |
205 | #define PCI230_DACOUT1 0x02 /* DAC channel 0 (w) */ |
206 | #define PCI230_DACOUT2 0x04 /* DAC channel 1 (w) (not FIFO mode) */ |
207 | #define PCI230_ADCDATA 0x08 /* ADC data (r) */ |
208 | #define PCI230_ADCSWTRIG 0x08 /* ADC software trigger (w) */ |
209 | #define PCI230_ADCCON 0x0A /* ADC control */ |
210 | #define PCI230_ADCEN 0x0C /* ADC channel enable bits */ |
211 | #define PCI230_ADCG 0x0E /* ADC gain control bits */ |
212 | /* PCI230+ i/o space 2 additional registers. */ |
213 | #define PCI230P_ADCTRIG 0x10 /* ADC start acquisition trigger */ |
214 | #define PCI230P_ADCTH 0x12 /* ADC analog trigger threshold */ |
215 | #define PCI230P_ADCFFTH 0x14 /* ADC FIFO interrupt threshold */ |
216 | #define PCI230P_ADCFFLEV 0x16 /* ADC FIFO level (r) */ |
217 | #define PCI230P_ADCPTSC 0x18 /* ADC pre-trigger sample count (r) */ |
218 | #define PCI230P_ADCHYST 0x1A /* ADC analog trigger hysteresys */ |
219 | #define PCI230P_EXTFUNC 0x1C /* Extended functions */ |
220 | #define PCI230P_HWVER 0x1E /* Hardware version (r) */ |
221 | /* PCI230+ hardware version 2 onwards. */ |
222 | #define PCI230P2_DACDATA 0x02 /* DAC data (FIFO mode) (w) */ |
223 | #define PCI230P2_DACSWTRIG 0x02 /* DAC soft trigger (FIFO mode) (r) */ |
224 | #define PCI230P2_DACEN 0x06 /* DAC channel enable (FIFO mode) */ |
225 | |
226 | /* |
227 | * DACCON read-write values. |
228 | */ |
229 | #define PCI230_DAC_OR(x) (((x) & 0x1) << 0) |
230 | #define PCI230_DAC_OR_UNI PCI230_DAC_OR(0) /* Output unipolar */ |
231 | #define PCI230_DAC_OR_BIP PCI230_DAC_OR(1) /* Output bipolar */ |
232 | #define PCI230_DAC_OR_MASK PCI230_DAC_OR(1) |
233 | /* |
234 | * The following applies only if DAC FIFO support is enabled in the EXTFUNC |
235 | * register (and only for PCI230+ hardware version 2 onwards). |
236 | */ |
237 | #define PCI230P2_DAC_FIFO_EN BIT(8) /* FIFO enable */ |
238 | /* |
239 | * The following apply only if the DAC FIFO is enabled (and only for PCI230+ |
240 | * hardware version 2 onwards). |
241 | */ |
242 | #define PCI230P2_DAC_TRIG(x) (((x) & 0x7) << 2) |
243 | #define PCI230P2_DAC_TRIG_NONE PCI230P2_DAC_TRIG(0) /* none */ |
244 | #define PCI230P2_DAC_TRIG_SW PCI230P2_DAC_TRIG(1) /* soft trig */ |
245 | #define PCI230P2_DAC_TRIG_EXTP PCI230P2_DAC_TRIG(2) /* ext + edge */ |
246 | #define PCI230P2_DAC_TRIG_EXTN PCI230P2_DAC_TRIG(3) /* ext - edge */ |
247 | #define PCI230P2_DAC_TRIG_Z2CT0 PCI230P2_DAC_TRIG(4) /* Z2 CT0 out */ |
248 | #define PCI230P2_DAC_TRIG_Z2CT1 PCI230P2_DAC_TRIG(5) /* Z2 CT1 out */ |
249 | #define PCI230P2_DAC_TRIG_Z2CT2 PCI230P2_DAC_TRIG(6) /* Z2 CT2 out */ |
250 | #define PCI230P2_DAC_TRIG_MASK PCI230P2_DAC_TRIG(7) |
251 | #define PCI230P2_DAC_FIFO_WRAP BIT(7) /* FIFO wraparound mode */ |
252 | #define PCI230P2_DAC_INT_FIFO(x) (((x) & 7) << 9) |
253 | #define PCI230P2_DAC_INT_FIFO_EMPTY PCI230P2_DAC_INT_FIFO(0) /* empty */ |
254 | #define PCI230P2_DAC_INT_FIFO_NEMPTY PCI230P2_DAC_INT_FIFO(1) /* !empty */ |
255 | #define PCI230P2_DAC_INT_FIFO_NHALF PCI230P2_DAC_INT_FIFO(2) /* !half */ |
256 | #define PCI230P2_DAC_INT_FIFO_HALF PCI230P2_DAC_INT_FIFO(3) /* half */ |
257 | #define PCI230P2_DAC_INT_FIFO_NFULL PCI230P2_DAC_INT_FIFO(4) /* !full */ |
258 | #define PCI230P2_DAC_INT_FIFO_FULL PCI230P2_DAC_INT_FIFO(5) /* full */ |
259 | #define PCI230P2_DAC_INT_FIFO_MASK PCI230P2_DAC_INT_FIFO(7) |
260 | |
261 | /* |
262 | * DACCON read-only values. |
263 | */ |
264 | #define PCI230_DAC_BUSY BIT(1) /* DAC busy. */ |
265 | /* |
266 | * The following apply only if the DAC FIFO is enabled (and only for PCI230+ |
267 | * hardware version 2 onwards). |
268 | */ |
269 | #define PCI230P2_DAC_FIFO_UNDERRUN_LATCHED BIT(5) /* Underrun error */ |
270 | #define PCI230P2_DAC_FIFO_EMPTY BIT(13) /* FIFO empty */ |
271 | #define PCI230P2_DAC_FIFO_FULL BIT(14) /* FIFO full */ |
272 | #define PCI230P2_DAC_FIFO_HALF BIT(15) /* FIFO half full */ |
273 | |
274 | /* |
275 | * DACCON write-only, transient values. |
276 | */ |
277 | /* |
278 | * The following apply only if the DAC FIFO is enabled (and only for PCI230+ |
279 | * hardware version 2 onwards). |
280 | */ |
281 | #define PCI230P2_DAC_FIFO_UNDERRUN_CLEAR BIT(5) /* Clear underrun */ |
282 | #define PCI230P2_DAC_FIFO_RESET BIT(12) /* FIFO reset */ |
283 | |
284 | /* |
285 | * PCI230+ hardware version 2 DAC FIFO levels. |
286 | */ |
287 | #define PCI230P2_DAC_FIFOLEVEL_HALF 512 |
288 | #define PCI230P2_DAC_FIFOLEVEL_FULL 1024 |
289 | /* Free space in DAC FIFO. */ |
290 | #define PCI230P2_DAC_FIFOROOM_EMPTY PCI230P2_DAC_FIFOLEVEL_FULL |
291 | #define PCI230P2_DAC_FIFOROOM_ONETOHALF \ |
292 | (PCI230P2_DAC_FIFOLEVEL_FULL - PCI230P2_DAC_FIFOLEVEL_HALF) |
293 | #define PCI230P2_DAC_FIFOROOM_HALFTOFULL 1 |
294 | #define PCI230P2_DAC_FIFOROOM_FULL 0 |
295 | |
296 | /* |
297 | * ADCCON read/write values. |
298 | */ |
299 | #define PCI230_ADC_TRIG(x) (((x) & 0x7) << 0) |
300 | #define PCI230_ADC_TRIG_NONE PCI230_ADC_TRIG(0) /* none */ |
301 | #define PCI230_ADC_TRIG_SW PCI230_ADC_TRIG(1) /* soft trig */ |
302 | #define PCI230_ADC_TRIG_EXTP PCI230_ADC_TRIG(2) /* ext + edge */ |
303 | #define PCI230_ADC_TRIG_EXTN PCI230_ADC_TRIG(3) /* ext - edge */ |
304 | #define PCI230_ADC_TRIG_Z2CT0 PCI230_ADC_TRIG(4) /* Z2 CT0 out*/ |
305 | #define PCI230_ADC_TRIG_Z2CT1 PCI230_ADC_TRIG(5) /* Z2 CT1 out */ |
306 | #define PCI230_ADC_TRIG_Z2CT2 PCI230_ADC_TRIG(6) /* Z2 CT2 out */ |
307 | #define PCI230_ADC_TRIG_MASK PCI230_ADC_TRIG(7) |
308 | #define PCI230_ADC_IR(x) (((x) & 0x1) << 3) |
309 | #define PCI230_ADC_IR_UNI PCI230_ADC_IR(0) /* Input unipolar */ |
310 | #define PCI230_ADC_IR_BIP PCI230_ADC_IR(1) /* Input bipolar */ |
311 | #define PCI230_ADC_IR_MASK PCI230_ADC_IR(1) |
312 | #define PCI230_ADC_IM(x) (((x) & 0x1) << 4) |
313 | #define PCI230_ADC_IM_SE PCI230_ADC_IM(0) /* single ended */ |
314 | #define PCI230_ADC_IM_DIF PCI230_ADC_IM(1) /* differential */ |
315 | #define PCI230_ADC_IM_MASK PCI230_ADC_IM(1) |
316 | #define PCI230_ADC_FIFO_EN BIT(8) /* FIFO enable */ |
317 | #define PCI230_ADC_INT_FIFO(x) (((x) & 0x7) << 9) |
318 | #define PCI230_ADC_INT_FIFO_EMPTY PCI230_ADC_INT_FIFO(0) /* empty */ |
319 | #define PCI230_ADC_INT_FIFO_NEMPTY PCI230_ADC_INT_FIFO(1) /* !empty */ |
320 | #define PCI230_ADC_INT_FIFO_NHALF PCI230_ADC_INT_FIFO(2) /* !half */ |
321 | #define PCI230_ADC_INT_FIFO_HALF PCI230_ADC_INT_FIFO(3) /* half */ |
322 | #define PCI230_ADC_INT_FIFO_NFULL PCI230_ADC_INT_FIFO(4) /* !full */ |
323 | #define PCI230_ADC_INT_FIFO_FULL PCI230_ADC_INT_FIFO(5) /* full */ |
324 | #define PCI230P_ADC_INT_FIFO_THRESH PCI230_ADC_INT_FIFO(7) /* threshold */ |
325 | #define PCI230_ADC_INT_FIFO_MASK PCI230_ADC_INT_FIFO(7) |
326 | |
327 | /* |
328 | * ADCCON write-only, transient values. |
329 | */ |
330 | #define PCI230_ADC_FIFO_RESET BIT(12) /* FIFO reset */ |
331 | #define PCI230_ADC_GLOB_RESET BIT(13) /* Global reset */ |
332 | |
333 | /* |
334 | * ADCCON read-only values. |
335 | */ |
336 | #define PCI230_ADC_BUSY BIT(15) /* ADC busy */ |
337 | #define PCI230_ADC_FIFO_EMPTY BIT(12) /* FIFO empty */ |
338 | #define PCI230_ADC_FIFO_FULL BIT(13) /* FIFO full */ |
339 | #define PCI230_ADC_FIFO_HALF BIT(14) /* FIFO half full */ |
340 | #define PCI230_ADC_FIFO_FULL_LATCHED BIT(5) /* FIFO overrun occurred */ |
341 | |
342 | /* |
343 | * PCI230 ADC FIFO levels. |
344 | */ |
345 | #define PCI230_ADC_FIFOLEVEL_HALFFULL 2049 /* Value for FIFO half full */ |
346 | #define PCI230_ADC_FIFOLEVEL_FULL 4096 /* FIFO size */ |
347 | |
348 | /* |
349 | * PCI230+ EXTFUNC values. |
350 | */ |
351 | /* Route EXTTRIG pin to external gate inputs. */ |
352 | #define PCI230P_EXTFUNC_GAT_EXTTRIG BIT(0) |
353 | /* PCI230+ hardware version 2 values. */ |
354 | /* Allow DAC FIFO to be enabled. */ |
355 | #define PCI230P2_EXTFUNC_DACFIFO BIT(1) |
356 | |
357 | /* |
358 | * Counter/timer clock input configuration sources. |
359 | */ |
360 | #define CLK_CLK 0 /* reserved (channel-specific clock) */ |
361 | #define CLK_10MHZ 1 /* internal 10 MHz clock */ |
362 | #define CLK_1MHZ 2 /* internal 1 MHz clock */ |
363 | #define CLK_100KHZ 3 /* internal 100 kHz clock */ |
364 | #define CLK_10KHZ 4 /* internal 10 kHz clock */ |
365 | #define CLK_1KHZ 5 /* internal 1 kHz clock */ |
366 | #define CLK_OUTNM1 6 /* output of channel-1 modulo total */ |
367 | #define CLK_EXT 7 /* external clock */ |
368 | |
369 | static unsigned int pci230_clk_config(unsigned int chan, unsigned int src) |
370 | { |
371 | return ((chan & 3) << 3) | (src & 7); |
372 | } |
373 | |
374 | /* |
375 | * Counter/timer gate input configuration sources. |
376 | */ |
377 | #define GAT_VCC 0 /* VCC (i.e. enabled) */ |
378 | #define GAT_GND 1 /* GND (i.e. disabled) */ |
379 | #define GAT_EXT 2 /* external gate input (PPCn on PCI230) */ |
380 | #define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */ |
381 | |
382 | static unsigned int pci230_gat_config(unsigned int chan, unsigned int src) |
383 | { |
384 | return ((chan & 3) << 3) | (src & 7); |
385 | } |
386 | |
387 | /* |
388 | * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI230 and PCI260: |
389 | * |
390 | * Channel's Channel's |
391 | * clock input gate input |
392 | * Channel CLK_OUTNM1 GAT_NOUTNM2 |
393 | * ------- ---------- ----------- |
394 | * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT |
395 | * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT |
396 | * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT |
397 | */ |
398 | |
399 | /* |
400 | * Interrupt enables/status register values. |
401 | */ |
402 | #define PCI230_INT_DISABLE 0 |
403 | #define PCI230_INT_PPI_C0 BIT(0) |
404 | #define PCI230_INT_PPI_C3 BIT(1) |
405 | #define PCI230_INT_ADC BIT(2) |
406 | #define PCI230_INT_ZCLK_CT1 BIT(5) |
407 | /* For PCI230+ hardware version 2 when DAC FIFO enabled. */ |
408 | #define PCI230P2_INT_DAC BIT(4) |
409 | |
410 | /* |
411 | * (Potentially) shared resources and their owners |
412 | */ |
413 | enum { |
414 | RES_Z2CT0 = BIT(0), /* Z2-CT0 */ |
415 | RES_Z2CT1 = BIT(1), /* Z2-CT1 */ |
416 | RES_Z2CT2 = BIT(2) /* Z2-CT2 */ |
417 | }; |
418 | |
419 | enum { |
420 | OWNER_AICMD, /* Owned by AI command */ |
421 | OWNER_AOCMD, /* Owned by AO command */ |
422 | NUM_OWNERS /* Number of owners */ |
423 | }; |
424 | |
425 | /* |
426 | * Handy macros. |
427 | */ |
428 | |
429 | /* Combine old and new bits. */ |
430 | #define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask))) |
431 | |
432 | /* Current CPU. XXX should this be hard_smp_processor_id()? */ |
433 | #define THISCPU smp_processor_id() |
434 | |
435 | /* |
436 | * Board descriptions for the two boards supported. |
437 | */ |
438 | |
439 | struct pci230_board { |
440 | const char *name; |
441 | unsigned short id; |
442 | unsigned char ai_bits; |
443 | unsigned char ao_bits; |
444 | unsigned char min_hwver; /* Minimum hardware version supported. */ |
445 | unsigned int have_dio:1; |
446 | }; |
447 | |
448 | static const struct pci230_board pci230_boards[] = { |
449 | { |
450 | .name = "pci230+" , |
451 | .id = PCI_DEVICE_ID_PCI230, |
452 | .ai_bits = 16, |
453 | .ao_bits = 12, |
454 | .have_dio = true, |
455 | .min_hwver = 1, |
456 | }, |
457 | { |
458 | .name = "pci260+" , |
459 | .id = PCI_DEVICE_ID_PCI260, |
460 | .ai_bits = 16, |
461 | .min_hwver = 1, |
462 | }, |
463 | { |
464 | .name = "pci230" , |
465 | .id = PCI_DEVICE_ID_PCI230, |
466 | .ai_bits = 12, |
467 | .ao_bits = 12, |
468 | .have_dio = true, |
469 | }, |
470 | { |
471 | .name = "pci260" , |
472 | .id = PCI_DEVICE_ID_PCI260, |
473 | .ai_bits = 12, |
474 | }, |
475 | }; |
476 | |
477 | struct pci230_private { |
478 | spinlock_t isr_spinlock; /* Interrupt spin lock */ |
479 | spinlock_t res_spinlock; /* Shared resources spin lock */ |
480 | spinlock_t ai_stop_spinlock; /* Spin lock for stopping AI command */ |
481 | spinlock_t ao_stop_spinlock; /* Spin lock for stopping AO command */ |
482 | unsigned long daqio; /* PCI230's DAQ I/O space */ |
483 | int intr_cpuid; /* ID of CPU running ISR */ |
484 | unsigned short hwver; /* Hardware version (for '+' models) */ |
485 | unsigned short adccon; /* ADCCON register value */ |
486 | unsigned short daccon; /* DACCON register value */ |
487 | unsigned short adcfifothresh; /* ADC FIFO threshold (PCI230+/260+) */ |
488 | unsigned short adcg; /* ADCG register value */ |
489 | unsigned char ier; /* Interrupt enable bits */ |
490 | unsigned char res_owned[NUM_OWNERS]; /* Owned resources */ |
491 | unsigned int intr_running:1; /* Flag set in interrupt routine */ |
492 | unsigned int ai_bipolar:1; /* Flag AI range is bipolar */ |
493 | unsigned int ao_bipolar:1; /* Flag AO range is bipolar */ |
494 | unsigned int ai_cmd_started:1; /* Flag AI command started */ |
495 | unsigned int ao_cmd_started:1; /* Flag AO command started */ |
496 | }; |
497 | |
498 | /* PCI230 clock source periods in ns */ |
499 | static const unsigned int pci230_timebase[8] = { |
500 | [CLK_10MHZ] = I8254_OSC_BASE_10MHZ, |
501 | [CLK_1MHZ] = I8254_OSC_BASE_1MHZ, |
502 | [CLK_100KHZ] = I8254_OSC_BASE_100KHZ, |
503 | [CLK_10KHZ] = I8254_OSC_BASE_10KHZ, |
504 | [CLK_1KHZ] = I8254_OSC_BASE_1KHZ, |
505 | }; |
506 | |
507 | /* PCI230 analogue input range table */ |
508 | static const struct comedi_lrange pci230_ai_range = { |
509 | 7, { |
510 | BIP_RANGE(10), |
511 | BIP_RANGE(5), |
512 | BIP_RANGE(2.5), |
513 | BIP_RANGE(1.25), |
514 | UNI_RANGE(10), |
515 | UNI_RANGE(5), |
516 | UNI_RANGE(2.5) |
517 | } |
518 | }; |
519 | |
520 | /* PCI230 analogue gain bits for each input range. */ |
521 | static const unsigned char pci230_ai_gain[7] = { 0, 1, 2, 3, 1, 2, 3 }; |
522 | |
523 | /* PCI230 analogue output range table */ |
524 | static const struct comedi_lrange pci230_ao_range = { |
525 | 2, { |
526 | UNI_RANGE(10), |
527 | BIP_RANGE(10) |
528 | } |
529 | }; |
530 | |
531 | static unsigned short pci230_ai_read(struct comedi_device *dev) |
532 | { |
533 | const struct pci230_board *board = dev->board_ptr; |
534 | struct pci230_private *devpriv = dev->private; |
535 | unsigned short data; |
536 | |
537 | /* Read sample. */ |
538 | data = inw(port: devpriv->daqio + PCI230_ADCDATA); |
539 | /* |
540 | * PCI230 is 12 bit - stored in upper bits of 16 bit register |
541 | * (lower four bits reserved for expansion). PCI230+ is 16 bit AI. |
542 | * |
543 | * If a bipolar range was specified, mangle it |
544 | * (twos complement->straight binary). |
545 | */ |
546 | if (devpriv->ai_bipolar) |
547 | data ^= 0x8000; |
548 | data >>= (16 - board->ai_bits); |
549 | return data; |
550 | } |
551 | |
552 | static unsigned short pci230_ao_mangle_datum(struct comedi_device *dev, |
553 | unsigned short datum) |
554 | { |
555 | const struct pci230_board *board = dev->board_ptr; |
556 | struct pci230_private *devpriv = dev->private; |
557 | |
558 | /* |
559 | * PCI230 is 12 bit - stored in upper bits of 16 bit register (lower |
560 | * four bits reserved for expansion). PCI230+ is also 12 bit AO. |
561 | */ |
562 | datum <<= (16 - board->ao_bits); |
563 | /* |
564 | * If a bipolar range was specified, mangle it |
565 | * (straight binary->twos complement). |
566 | */ |
567 | if (devpriv->ao_bipolar) |
568 | datum ^= 0x8000; |
569 | return datum; |
570 | } |
571 | |
572 | static void pci230_ao_write_nofifo(struct comedi_device *dev, |
573 | unsigned short datum, unsigned int chan) |
574 | { |
575 | struct pci230_private *devpriv = dev->private; |
576 | |
577 | /* Write mangled datum to appropriate DACOUT register. */ |
578 | outw(value: pci230_ao_mangle_datum(dev, datum), |
579 | port: devpriv->daqio + ((chan == 0) ? PCI230_DACOUT1 : PCI230_DACOUT2)); |
580 | } |
581 | |
582 | static void pci230_ao_write_fifo(struct comedi_device *dev, |
583 | unsigned short datum, unsigned int chan) |
584 | { |
585 | struct pci230_private *devpriv = dev->private; |
586 | |
587 | /* Write mangled datum to appropriate DACDATA register. */ |
588 | outw(value: pci230_ao_mangle_datum(dev, datum), |
589 | port: devpriv->daqio + PCI230P2_DACDATA); |
590 | } |
591 | |
592 | static bool pci230_claim_shared(struct comedi_device *dev, |
593 | unsigned char res_mask, unsigned int owner) |
594 | { |
595 | struct pci230_private *devpriv = dev->private; |
596 | unsigned int o; |
597 | unsigned long irqflags; |
598 | |
599 | spin_lock_irqsave(&devpriv->res_spinlock, irqflags); |
600 | for (o = 0; o < NUM_OWNERS; o++) { |
601 | if (o == owner) |
602 | continue; |
603 | if (devpriv->res_owned[o] & res_mask) { |
604 | spin_unlock_irqrestore(lock: &devpriv->res_spinlock, |
605 | flags: irqflags); |
606 | return false; |
607 | } |
608 | } |
609 | devpriv->res_owned[owner] |= res_mask; |
610 | spin_unlock_irqrestore(lock: &devpriv->res_spinlock, flags: irqflags); |
611 | return true; |
612 | } |
613 | |
614 | static void pci230_release_shared(struct comedi_device *dev, |
615 | unsigned char res_mask, unsigned int owner) |
616 | { |
617 | struct pci230_private *devpriv = dev->private; |
618 | unsigned long irqflags; |
619 | |
620 | spin_lock_irqsave(&devpriv->res_spinlock, irqflags); |
621 | devpriv->res_owned[owner] &= ~res_mask; |
622 | spin_unlock_irqrestore(lock: &devpriv->res_spinlock, flags: irqflags); |
623 | } |
624 | |
625 | static void pci230_release_all_resources(struct comedi_device *dev, |
626 | unsigned int owner) |
627 | { |
628 | pci230_release_shared(dev, res_mask: (unsigned char)~0, owner); |
629 | } |
630 | |
631 | static unsigned int pci230_divide_ns(u64 ns, unsigned int timebase, |
632 | unsigned int flags) |
633 | { |
634 | u64 div; |
635 | unsigned int rem; |
636 | |
637 | div = ns; |
638 | rem = do_div(div, timebase); |
639 | switch (flags & CMDF_ROUND_MASK) { |
640 | default: |
641 | case CMDF_ROUND_NEAREST: |
642 | div += DIV_ROUND_CLOSEST(rem, timebase); |
643 | break; |
644 | case CMDF_ROUND_DOWN: |
645 | break; |
646 | case CMDF_ROUND_UP: |
647 | div += DIV_ROUND_UP(rem, timebase); |
648 | break; |
649 | } |
650 | return div > UINT_MAX ? UINT_MAX : (unsigned int)div; |
651 | } |
652 | |
653 | /* |
654 | * Given desired period in ns, returns the required internal clock source |
655 | * and gets the initial count. |
656 | */ |
657 | static unsigned int pci230_choose_clk_count(u64 ns, unsigned int *count, |
658 | unsigned int flags) |
659 | { |
660 | unsigned int clk_src, cnt; |
661 | |
662 | for (clk_src = CLK_10MHZ;; clk_src++) { |
663 | cnt = pci230_divide_ns(ns, timebase: pci230_timebase[clk_src], flags); |
664 | if (cnt <= 65536 || clk_src == CLK_1KHZ) |
665 | break; |
666 | } |
667 | *count = cnt; |
668 | return clk_src; |
669 | } |
670 | |
671 | static void pci230_ns_to_single_timer(unsigned int *ns, unsigned int flags) |
672 | { |
673 | unsigned int count; |
674 | unsigned int clk_src; |
675 | |
676 | clk_src = pci230_choose_clk_count(ns: *ns, count: &count, flags); |
677 | *ns = count * pci230_timebase[clk_src]; |
678 | } |
679 | |
680 | static void pci230_ct_setup_ns_mode(struct comedi_device *dev, unsigned int ct, |
681 | unsigned int mode, u64 ns, |
682 | unsigned int flags) |
683 | { |
684 | unsigned int clk_src; |
685 | unsigned int count; |
686 | |
687 | /* Set mode. */ |
688 | comedi_8254_set_mode(i8254: dev->pacer, counter: ct, mode); |
689 | /* Determine clock source and count. */ |
690 | clk_src = pci230_choose_clk_count(ns, count: &count, flags); |
691 | /* Program clock source. */ |
692 | outb(value: pci230_clk_config(chan: ct, src: clk_src), port: dev->iobase + PCI230_ZCLK_SCE); |
693 | /* Set initial count. */ |
694 | if (count >= 65536) |
695 | count = 0; |
696 | |
697 | comedi_8254_write(i8254: dev->pacer, counter: ct, val: count); |
698 | } |
699 | |
700 | static void pci230_cancel_ct(struct comedi_device *dev, unsigned int ct) |
701 | { |
702 | /* Counter ct, 8254 mode 1, initial count not written. */ |
703 | comedi_8254_set_mode(i8254: dev->pacer, counter: ct, mode: I8254_MODE1); |
704 | } |
705 | |
706 | static int pci230_ai_eoc(struct comedi_device *dev, |
707 | struct comedi_subdevice *s, |
708 | struct comedi_insn *insn, |
709 | unsigned long context) |
710 | { |
711 | struct pci230_private *devpriv = dev->private; |
712 | unsigned int status; |
713 | |
714 | status = inw(port: devpriv->daqio + PCI230_ADCCON); |
715 | if ((status & PCI230_ADC_FIFO_EMPTY) == 0) |
716 | return 0; |
717 | return -EBUSY; |
718 | } |
719 | |
720 | static int pci230_ai_insn_read(struct comedi_device *dev, |
721 | struct comedi_subdevice *s, |
722 | struct comedi_insn *insn, unsigned int *data) |
723 | { |
724 | struct pci230_private *devpriv = dev->private; |
725 | unsigned int n; |
726 | unsigned int chan, range, aref; |
727 | unsigned int gainshift; |
728 | unsigned short adccon, adcen; |
729 | int ret; |
730 | |
731 | /* Unpack channel and range. */ |
732 | chan = CR_CHAN(insn->chanspec); |
733 | range = CR_RANGE(insn->chanspec); |
734 | aref = CR_AREF(insn->chanspec); |
735 | if (aref == AREF_DIFF) { |
736 | /* Differential. */ |
737 | if (chan >= s->n_chan / 2) { |
738 | dev_dbg(dev->class_dev, |
739 | "%s: differential channel number out of range 0 to %u\n" , |
740 | __func__, (s->n_chan / 2) - 1); |
741 | return -EINVAL; |
742 | } |
743 | } |
744 | |
745 | /* |
746 | * Use Z2-CT2 as a conversion trigger instead of the built-in |
747 | * software trigger, as otherwise triggering of differential channels |
748 | * doesn't work properly for some versions of PCI230/260. Also set |
749 | * FIFO mode because the ADC busy bit only works for software triggers. |
750 | */ |
751 | adccon = PCI230_ADC_TRIG_Z2CT2 | PCI230_ADC_FIFO_EN; |
752 | /* Set Z2-CT2 output low to avoid any false triggers. */ |
753 | comedi_8254_set_mode(i8254: dev->pacer, counter: 2, mode: I8254_MODE0); |
754 | devpriv->ai_bipolar = comedi_range_is_bipolar(s, range); |
755 | if (aref == AREF_DIFF) { |
756 | /* Differential. */ |
757 | gainshift = chan * 2; |
758 | if (devpriv->hwver == 0) { |
759 | /* |
760 | * Original PCI230/260 expects both inputs of the |
761 | * differential channel to be enabled. |
762 | */ |
763 | adcen = 3 << gainshift; |
764 | } else { |
765 | /* |
766 | * PCI230+/260+ expects only one input of the |
767 | * differential channel to be enabled. |
768 | */ |
769 | adcen = 1 << gainshift; |
770 | } |
771 | adccon |= PCI230_ADC_IM_DIF; |
772 | } else { |
773 | /* Single ended. */ |
774 | adcen = 1 << chan; |
775 | gainshift = chan & ~1; |
776 | adccon |= PCI230_ADC_IM_SE; |
777 | } |
778 | devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) | |
779 | (pci230_ai_gain[range] << gainshift); |
780 | if (devpriv->ai_bipolar) |
781 | adccon |= PCI230_ADC_IR_BIP; |
782 | else |
783 | adccon |= PCI230_ADC_IR_UNI; |
784 | |
785 | /* |
786 | * Enable only this channel in the scan list - otherwise by default |
787 | * we'll get one sample from each channel. |
788 | */ |
789 | outw(value: adcen, port: devpriv->daqio + PCI230_ADCEN); |
790 | |
791 | /* Set gain for channel. */ |
792 | outw(value: devpriv->adcg, port: devpriv->daqio + PCI230_ADCG); |
793 | |
794 | /* Specify uni/bip, se/diff, conversion source, and reset FIFO. */ |
795 | devpriv->adccon = adccon; |
796 | outw(value: adccon | PCI230_ADC_FIFO_RESET, port: devpriv->daqio + PCI230_ADCCON); |
797 | |
798 | /* Convert n samples */ |
799 | for (n = 0; n < insn->n; n++) { |
800 | /* |
801 | * Trigger conversion by toggling Z2-CT2 output |
802 | * (finish with output high). |
803 | */ |
804 | comedi_8254_set_mode(i8254: dev->pacer, counter: 2, mode: I8254_MODE0); |
805 | comedi_8254_set_mode(i8254: dev->pacer, counter: 2, mode: I8254_MODE1); |
806 | |
807 | /* wait for conversion to end */ |
808 | ret = comedi_timeout(dev, s, insn, cb: pci230_ai_eoc, context: 0); |
809 | if (ret) |
810 | return ret; |
811 | |
812 | /* read data */ |
813 | data[n] = pci230_ai_read(dev); |
814 | } |
815 | |
816 | /* return the number of samples read/written */ |
817 | return n; |
818 | } |
819 | |
820 | static int pci230_ao_insn_write(struct comedi_device *dev, |
821 | struct comedi_subdevice *s, |
822 | struct comedi_insn *insn, |
823 | unsigned int *data) |
824 | { |
825 | struct pci230_private *devpriv = dev->private; |
826 | unsigned int chan = CR_CHAN(insn->chanspec); |
827 | unsigned int range = CR_RANGE(insn->chanspec); |
828 | unsigned int val = s->readback[chan]; |
829 | int i; |
830 | |
831 | /* |
832 | * Set range - see analogue output range table; 0 => unipolar 10V, |
833 | * 1 => bipolar +/-10V range scale |
834 | */ |
835 | devpriv->ao_bipolar = comedi_range_is_bipolar(s, range); |
836 | outw(value: range, port: devpriv->daqio + PCI230_DACCON); |
837 | |
838 | for (i = 0; i < insn->n; i++) { |
839 | val = data[i]; |
840 | pci230_ao_write_nofifo(dev, datum: val, chan); |
841 | } |
842 | s->readback[chan] = val; |
843 | |
844 | return insn->n; |
845 | } |
846 | |
847 | static int pci230_ao_check_chanlist(struct comedi_device *dev, |
848 | struct comedi_subdevice *s, |
849 | struct comedi_cmd *cmd) |
850 | { |
851 | unsigned int prev_chan = CR_CHAN(cmd->chanlist[0]); |
852 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); |
853 | int i; |
854 | |
855 | for (i = 1; i < cmd->chanlist_len; i++) { |
856 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
857 | unsigned int range = CR_RANGE(cmd->chanlist[i]); |
858 | |
859 | if (chan < prev_chan) { |
860 | dev_dbg(dev->class_dev, |
861 | "%s: channel numbers must increase\n" , |
862 | __func__); |
863 | return -EINVAL; |
864 | } |
865 | |
866 | if (range != range0) { |
867 | dev_dbg(dev->class_dev, |
868 | "%s: channels must have the same range\n" , |
869 | __func__); |
870 | return -EINVAL; |
871 | } |
872 | |
873 | prev_chan = chan; |
874 | } |
875 | |
876 | return 0; |
877 | } |
878 | |
879 | static int pci230_ao_cmdtest(struct comedi_device *dev, |
880 | struct comedi_subdevice *s, struct comedi_cmd *cmd) |
881 | { |
882 | const struct pci230_board *board = dev->board_ptr; |
883 | struct pci230_private *devpriv = dev->private; |
884 | int err = 0; |
885 | unsigned int tmp; |
886 | |
887 | /* Step 1 : check if triggers are trivially valid */ |
888 | |
889 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_INT); |
890 | |
891 | tmp = TRIG_TIMER | TRIG_INT; |
892 | if (board->min_hwver > 0 && devpriv->hwver >= 2) { |
893 | /* |
894 | * For PCI230+ hardware version 2 onwards, allow external |
895 | * trigger from EXTTRIG/EXTCONVCLK input (PCI230+ pin 25). |
896 | * |
897 | * FIXME: The permitted scan_begin_src values shouldn't depend |
898 | * on devpriv->hwver (the detected card's actual hardware |
899 | * version). They should only depend on board->min_hwver |
900 | * (the static capabilities of the configured card). To fix |
901 | * it, a new card model, e.g. "pci230+2" would have to be |
902 | * defined with min_hwver set to 2. It doesn't seem worth it |
903 | * for this alone. At the moment, please consider |
904 | * scan_begin_src==TRIG_EXT support to be a bonus rather than a |
905 | * guarantee! |
906 | */ |
907 | tmp |= TRIG_EXT; |
908 | } |
909 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, flags: tmp); |
910 | |
911 | err |= comedi_check_trigger_src(src: &cmd->convert_src, TRIG_NOW); |
912 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
913 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_COUNT | TRIG_NONE); |
914 | |
915 | if (err) |
916 | return 1; |
917 | |
918 | /* Step 2a : make sure trigger sources are unique */ |
919 | |
920 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
921 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
922 | |
923 | /* Step 2b : and mutually compatible */ |
924 | |
925 | if (err) |
926 | return 2; |
927 | |
928 | /* Step 3: check if arguments are trivially valid */ |
929 | |
930 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
931 | |
932 | #define MAX_SPEED_AO 8000 /* 8000 ns => 125 kHz */ |
933 | /* |
934 | * Comedi limit due to unsigned int cmd. Driver limit = |
935 | * 2^16 (16bit * counter) * 1000000ns (1kHz onboard clock) = 65.536s |
936 | */ |
937 | #define MIN_SPEED_AO 4294967295u /* 4294967295ns = 4.29s */ |
938 | |
939 | switch (cmd->scan_begin_src) { |
940 | case TRIG_TIMER: |
941 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, |
942 | MAX_SPEED_AO); |
943 | err |= comedi_check_trigger_arg_max(arg: &cmd->scan_begin_arg, |
944 | MIN_SPEED_AO); |
945 | break; |
946 | case TRIG_EXT: |
947 | /* |
948 | * External trigger - for PCI230+ hardware version 2 onwards. |
949 | */ |
950 | /* Trigger number must be 0. */ |
951 | if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) { |
952 | cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0, |
953 | ~CR_FLAGS_MASK); |
954 | err |= -EINVAL; |
955 | } |
956 | /* |
957 | * The only flags allowed are CR_EDGE and CR_INVERT. |
958 | * The CR_EDGE flag is ignored. |
959 | */ |
960 | if (cmd->scan_begin_arg & CR_FLAGS_MASK & |
961 | ~(CR_EDGE | CR_INVERT)) { |
962 | cmd->scan_begin_arg = |
963 | COMBINE(cmd->scan_begin_arg, 0, |
964 | CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT)); |
965 | err |= -EINVAL; |
966 | } |
967 | break; |
968 | default: |
969 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: 0); |
970 | break; |
971 | } |
972 | |
973 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
974 | val: cmd->chanlist_len); |
975 | |
976 | if (cmd->stop_src == TRIG_COUNT) |
977 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
978 | else /* TRIG_NONE */ |
979 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
980 | |
981 | if (err) |
982 | return 3; |
983 | |
984 | /* Step 4: fix up any arguments */ |
985 | |
986 | if (cmd->scan_begin_src == TRIG_TIMER) { |
987 | tmp = cmd->scan_begin_arg; |
988 | pci230_ns_to_single_timer(ns: &cmd->scan_begin_arg, flags: cmd->flags); |
989 | if (tmp != cmd->scan_begin_arg) |
990 | err++; |
991 | } |
992 | |
993 | if (err) |
994 | return 4; |
995 | |
996 | /* Step 5: check channel list if it exists */ |
997 | if (cmd->chanlist && cmd->chanlist_len > 0) |
998 | err |= pci230_ao_check_chanlist(dev, s, cmd); |
999 | |
1000 | if (err) |
1001 | return 5; |
1002 | |
1003 | return 0; |
1004 | } |
1005 | |
1006 | static void pci230_ao_stop(struct comedi_device *dev, |
1007 | struct comedi_subdevice *s) |
1008 | { |
1009 | struct pci230_private *devpriv = dev->private; |
1010 | unsigned long irqflags; |
1011 | unsigned char intsrc; |
1012 | bool started; |
1013 | struct comedi_cmd *cmd; |
1014 | |
1015 | spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags); |
1016 | started = devpriv->ao_cmd_started; |
1017 | devpriv->ao_cmd_started = false; |
1018 | spin_unlock_irqrestore(lock: &devpriv->ao_stop_spinlock, flags: irqflags); |
1019 | if (!started) |
1020 | return; |
1021 | cmd = &s->async->cmd; |
1022 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1023 | /* Stop scan rate generator. */ |
1024 | pci230_cancel_ct(dev, ct: 1); |
1025 | } |
1026 | /* Determine interrupt source. */ |
1027 | if (devpriv->hwver < 2) { |
1028 | /* Not using DAC FIFO. Using CT1 interrupt. */ |
1029 | intsrc = PCI230_INT_ZCLK_CT1; |
1030 | } else { |
1031 | /* Using DAC FIFO interrupt. */ |
1032 | intsrc = PCI230P2_INT_DAC; |
1033 | } |
1034 | /* |
1035 | * Disable interrupt and wait for interrupt routine to finish running |
1036 | * unless we are called from the interrupt routine. |
1037 | */ |
1038 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1039 | devpriv->ier &= ~intsrc; |
1040 | while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) { |
1041 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
1042 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1043 | } |
1044 | outb(value: devpriv->ier, port: dev->iobase + PCI230_INT_SCE); |
1045 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
1046 | if (devpriv->hwver >= 2) { |
1047 | /* |
1048 | * Using DAC FIFO. Reset FIFO, clear underrun error, |
1049 | * disable FIFO. |
1050 | */ |
1051 | devpriv->daccon &= PCI230_DAC_OR_MASK; |
1052 | outw(value: devpriv->daccon | PCI230P2_DAC_FIFO_RESET | |
1053 | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR, |
1054 | port: devpriv->daqio + PCI230_DACCON); |
1055 | } |
1056 | /* Release resources. */ |
1057 | pci230_release_all_resources(dev, owner: OWNER_AOCMD); |
1058 | } |
1059 | |
1060 | static void pci230_handle_ao_nofifo(struct comedi_device *dev, |
1061 | struct comedi_subdevice *s) |
1062 | { |
1063 | struct comedi_async *async = s->async; |
1064 | struct comedi_cmd *cmd = &async->cmd; |
1065 | unsigned short data; |
1066 | int i; |
1067 | |
1068 | if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) |
1069 | return; |
1070 | |
1071 | for (i = 0; i < cmd->chanlist_len; i++) { |
1072 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
1073 | |
1074 | if (!comedi_buf_read_samples(s, data: &data, nsamples: 1)) { |
1075 | async->events |= COMEDI_CB_OVERFLOW; |
1076 | return; |
1077 | } |
1078 | pci230_ao_write_nofifo(dev, datum: data, chan); |
1079 | s->readback[chan] = data; |
1080 | } |
1081 | |
1082 | if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) |
1083 | async->events |= COMEDI_CB_EOA; |
1084 | } |
1085 | |
1086 | /* |
1087 | * Loads DAC FIFO (if using it) from buffer. |
1088 | * Returns false if AO finished due to completion or error, true if still going. |
1089 | */ |
1090 | static bool pci230_handle_ao_fifo(struct comedi_device *dev, |
1091 | struct comedi_subdevice *s) |
1092 | { |
1093 | struct pci230_private *devpriv = dev->private; |
1094 | struct comedi_async *async = s->async; |
1095 | struct comedi_cmd *cmd = &async->cmd; |
1096 | unsigned int num_scans = comedi_nscans_left(s, nscans: 0); |
1097 | unsigned int room; |
1098 | unsigned short dacstat; |
1099 | unsigned int i, n; |
1100 | unsigned int events = 0; |
1101 | |
1102 | /* Get DAC FIFO status. */ |
1103 | dacstat = inw(port: devpriv->daqio + PCI230_DACCON); |
1104 | |
1105 | if (cmd->stop_src == TRIG_COUNT && num_scans == 0) |
1106 | events |= COMEDI_CB_EOA; |
1107 | |
1108 | if (events == 0) { |
1109 | /* Check for FIFO underrun. */ |
1110 | if (dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) { |
1111 | dev_err(dev->class_dev, "AO FIFO underrun\n" ); |
1112 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; |
1113 | } |
1114 | /* |
1115 | * Check for buffer underrun if FIFO less than half full |
1116 | * (otherwise there will be loads of "DAC FIFO not half full" |
1117 | * interrupts). |
1118 | */ |
1119 | if (num_scans == 0 && |
1120 | (dacstat & PCI230P2_DAC_FIFO_HALF) == 0) { |
1121 | dev_err(dev->class_dev, "AO buffer underrun\n" ); |
1122 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; |
1123 | } |
1124 | } |
1125 | if (events == 0) { |
1126 | /* Determine how much room is in the FIFO (in samples). */ |
1127 | if (dacstat & PCI230P2_DAC_FIFO_FULL) |
1128 | room = PCI230P2_DAC_FIFOROOM_FULL; |
1129 | else if (dacstat & PCI230P2_DAC_FIFO_HALF) |
1130 | room = PCI230P2_DAC_FIFOROOM_HALFTOFULL; |
1131 | else if (dacstat & PCI230P2_DAC_FIFO_EMPTY) |
1132 | room = PCI230P2_DAC_FIFOROOM_EMPTY; |
1133 | else |
1134 | room = PCI230P2_DAC_FIFOROOM_ONETOHALF; |
1135 | /* Convert room to number of scans that can be added. */ |
1136 | room /= cmd->chanlist_len; |
1137 | /* Determine number of scans to process. */ |
1138 | if (num_scans > room) |
1139 | num_scans = room; |
1140 | /* Process scans. */ |
1141 | for (n = 0; n < num_scans; n++) { |
1142 | for (i = 0; i < cmd->chanlist_len; i++) { |
1143 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
1144 | unsigned short datum; |
1145 | |
1146 | comedi_buf_read_samples(s, data: &datum, nsamples: 1); |
1147 | pci230_ao_write_fifo(dev, datum, chan); |
1148 | s->readback[chan] = datum; |
1149 | } |
1150 | } |
1151 | |
1152 | if (cmd->stop_src == TRIG_COUNT && |
1153 | async->scans_done >= cmd->stop_arg) { |
1154 | /* |
1155 | * All data for the command has been written |
1156 | * to FIFO. Set FIFO interrupt trigger level |
1157 | * to 'empty'. |
1158 | */ |
1159 | devpriv->daccon &= ~PCI230P2_DAC_INT_FIFO_MASK; |
1160 | devpriv->daccon |= PCI230P2_DAC_INT_FIFO_EMPTY; |
1161 | outw(value: devpriv->daccon, port: devpriv->daqio + PCI230_DACCON); |
1162 | } |
1163 | /* Check if FIFO underrun occurred while writing to FIFO. */ |
1164 | dacstat = inw(port: devpriv->daqio + PCI230_DACCON); |
1165 | if (dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) { |
1166 | dev_err(dev->class_dev, "AO FIFO underrun\n" ); |
1167 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; |
1168 | } |
1169 | } |
1170 | async->events |= events; |
1171 | return !(async->events & COMEDI_CB_CANCEL_MASK); |
1172 | } |
1173 | |
1174 | static int pci230_ao_inttrig_scan_begin(struct comedi_device *dev, |
1175 | struct comedi_subdevice *s, |
1176 | unsigned int trig_num) |
1177 | { |
1178 | struct pci230_private *devpriv = dev->private; |
1179 | unsigned long irqflags; |
1180 | |
1181 | if (trig_num) |
1182 | return -EINVAL; |
1183 | |
1184 | spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags); |
1185 | if (!devpriv->ao_cmd_started) { |
1186 | spin_unlock_irqrestore(lock: &devpriv->ao_stop_spinlock, flags: irqflags); |
1187 | return 1; |
1188 | } |
1189 | /* Perform scan. */ |
1190 | if (devpriv->hwver < 2) { |
1191 | /* Not using DAC FIFO. */ |
1192 | spin_unlock_irqrestore(lock: &devpriv->ao_stop_spinlock, flags: irqflags); |
1193 | pci230_handle_ao_nofifo(dev, s); |
1194 | comedi_handle_events(dev, s); |
1195 | } else { |
1196 | /* Using DAC FIFO. */ |
1197 | /* Read DACSWTRIG register to trigger conversion. */ |
1198 | inw(port: devpriv->daqio + PCI230P2_DACSWTRIG); |
1199 | spin_unlock_irqrestore(lock: &devpriv->ao_stop_spinlock, flags: irqflags); |
1200 | } |
1201 | /* Delay. Should driver be responsible for this? */ |
1202 | /* XXX TODO: See if DAC busy bit can be used. */ |
1203 | udelay(8); |
1204 | return 1; |
1205 | } |
1206 | |
1207 | static void pci230_ao_start(struct comedi_device *dev, |
1208 | struct comedi_subdevice *s) |
1209 | { |
1210 | struct pci230_private *devpriv = dev->private; |
1211 | struct comedi_async *async = s->async; |
1212 | struct comedi_cmd *cmd = &async->cmd; |
1213 | unsigned long irqflags; |
1214 | |
1215 | devpriv->ao_cmd_started = true; |
1216 | |
1217 | if (devpriv->hwver >= 2) { |
1218 | /* Using DAC FIFO. */ |
1219 | unsigned short scantrig; |
1220 | bool run; |
1221 | |
1222 | /* Preload FIFO data. */ |
1223 | run = pci230_handle_ao_fifo(dev, s); |
1224 | comedi_handle_events(dev, s); |
1225 | if (!run) { |
1226 | /* Stopped. */ |
1227 | return; |
1228 | } |
1229 | /* Set scan trigger source. */ |
1230 | switch (cmd->scan_begin_src) { |
1231 | case TRIG_TIMER: |
1232 | scantrig = PCI230P2_DAC_TRIG_Z2CT1; |
1233 | break; |
1234 | case TRIG_EXT: |
1235 | /* Trigger on EXTTRIG/EXTCONVCLK pin. */ |
1236 | if ((cmd->scan_begin_arg & CR_INVERT) == 0) { |
1237 | /* +ve edge */ |
1238 | scantrig = PCI230P2_DAC_TRIG_EXTP; |
1239 | } else { |
1240 | /* -ve edge */ |
1241 | scantrig = PCI230P2_DAC_TRIG_EXTN; |
1242 | } |
1243 | break; |
1244 | case TRIG_INT: |
1245 | scantrig = PCI230P2_DAC_TRIG_SW; |
1246 | break; |
1247 | default: |
1248 | /* Shouldn't get here. */ |
1249 | scantrig = PCI230P2_DAC_TRIG_NONE; |
1250 | break; |
1251 | } |
1252 | devpriv->daccon = |
1253 | (devpriv->daccon & ~PCI230P2_DAC_TRIG_MASK) | scantrig; |
1254 | outw(value: devpriv->daccon, port: devpriv->daqio + PCI230_DACCON); |
1255 | } |
1256 | switch (cmd->scan_begin_src) { |
1257 | case TRIG_TIMER: |
1258 | if (devpriv->hwver < 2) { |
1259 | /* Not using DAC FIFO. */ |
1260 | /* Enable CT1 timer interrupt. */ |
1261 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1262 | devpriv->ier |= PCI230_INT_ZCLK_CT1; |
1263 | outb(value: devpriv->ier, port: dev->iobase + PCI230_INT_SCE); |
1264 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, |
1265 | flags: irqflags); |
1266 | } |
1267 | /* Set CT1 gate high to start counting. */ |
1268 | outb(value: pci230_gat_config(chan: 1, GAT_VCC), |
1269 | port: dev->iobase + PCI230_ZGAT_SCE); |
1270 | break; |
1271 | case TRIG_INT: |
1272 | async->inttrig = pci230_ao_inttrig_scan_begin; |
1273 | break; |
1274 | } |
1275 | if (devpriv->hwver >= 2) { |
1276 | /* Using DAC FIFO. Enable DAC FIFO interrupt. */ |
1277 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1278 | devpriv->ier |= PCI230P2_INT_DAC; |
1279 | outb(value: devpriv->ier, port: dev->iobase + PCI230_INT_SCE); |
1280 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
1281 | } |
1282 | } |
1283 | |
1284 | static int pci230_ao_inttrig_start(struct comedi_device *dev, |
1285 | struct comedi_subdevice *s, |
1286 | unsigned int trig_num) |
1287 | { |
1288 | struct comedi_cmd *cmd = &s->async->cmd; |
1289 | |
1290 | if (trig_num != cmd->start_src) |
1291 | return -EINVAL; |
1292 | |
1293 | s->async->inttrig = NULL; |
1294 | pci230_ao_start(dev, s); |
1295 | |
1296 | return 1; |
1297 | } |
1298 | |
1299 | static int pci230_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
1300 | { |
1301 | struct pci230_private *devpriv = dev->private; |
1302 | unsigned short daccon; |
1303 | unsigned int range; |
1304 | |
1305 | /* Get the command. */ |
1306 | struct comedi_cmd *cmd = &s->async->cmd; |
1307 | |
1308 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1309 | /* Claim Z2-CT1. */ |
1310 | if (!pci230_claim_shared(dev, res_mask: RES_Z2CT1, owner: OWNER_AOCMD)) |
1311 | return -EBUSY; |
1312 | } |
1313 | |
1314 | /* |
1315 | * Set range - see analogue output range table; 0 => unipolar 10V, |
1316 | * 1 => bipolar +/-10V range scale |
1317 | */ |
1318 | range = CR_RANGE(cmd->chanlist[0]); |
1319 | devpriv->ao_bipolar = comedi_range_is_bipolar(s, range); |
1320 | daccon = devpriv->ao_bipolar ? PCI230_DAC_OR_BIP : PCI230_DAC_OR_UNI; |
1321 | /* Use DAC FIFO for hardware version 2 onwards. */ |
1322 | if (devpriv->hwver >= 2) { |
1323 | unsigned short dacen; |
1324 | unsigned int i; |
1325 | |
1326 | dacen = 0; |
1327 | for (i = 0; i < cmd->chanlist_len; i++) |
1328 | dacen |= 1 << CR_CHAN(cmd->chanlist[i]); |
1329 | |
1330 | /* Set channel scan list. */ |
1331 | outw(value: dacen, port: devpriv->daqio + PCI230P2_DACEN); |
1332 | /* |
1333 | * Enable DAC FIFO. |
1334 | * Set DAC scan source to 'none'. |
1335 | * Set DAC FIFO interrupt trigger level to 'not half full'. |
1336 | * Reset DAC FIFO and clear underrun. |
1337 | * |
1338 | * N.B. DAC FIFO interrupts are currently disabled. |
1339 | */ |
1340 | daccon |= PCI230P2_DAC_FIFO_EN | PCI230P2_DAC_FIFO_RESET | |
1341 | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR | |
1342 | PCI230P2_DAC_TRIG_NONE | PCI230P2_DAC_INT_FIFO_NHALF; |
1343 | } |
1344 | |
1345 | /* Set DACCON. */ |
1346 | outw(value: daccon, port: devpriv->daqio + PCI230_DACCON); |
1347 | /* Preserve most of DACCON apart from write-only, transient bits. */ |
1348 | devpriv->daccon = daccon & ~(PCI230P2_DAC_FIFO_RESET | |
1349 | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR); |
1350 | |
1351 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1352 | /* |
1353 | * Set the counter timer 1 to the specified scan frequency. |
1354 | * cmd->scan_begin_arg is sampling period in ns. |
1355 | * Gate it off for now. |
1356 | */ |
1357 | outb(value: pci230_gat_config(chan: 1, GAT_GND), |
1358 | port: dev->iobase + PCI230_ZGAT_SCE); |
1359 | pci230_ct_setup_ns_mode(dev, ct: 1, mode: I8254_MODE3, |
1360 | ns: cmd->scan_begin_arg, |
1361 | flags: cmd->flags); |
1362 | } |
1363 | |
1364 | /* N.B. cmd->start_src == TRIG_INT */ |
1365 | s->async->inttrig = pci230_ao_inttrig_start; |
1366 | |
1367 | return 0; |
1368 | } |
1369 | |
1370 | static int pci230_ao_cancel(struct comedi_device *dev, |
1371 | struct comedi_subdevice *s) |
1372 | { |
1373 | pci230_ao_stop(dev, s); |
1374 | return 0; |
1375 | } |
1376 | |
1377 | static int pci230_ai_check_scan_period(struct comedi_cmd *cmd) |
1378 | { |
1379 | unsigned int min_scan_period, chanlist_len; |
1380 | int err = 0; |
1381 | |
1382 | chanlist_len = cmd->chanlist_len; |
1383 | if (cmd->chanlist_len == 0) |
1384 | chanlist_len = 1; |
1385 | |
1386 | min_scan_period = chanlist_len * cmd->convert_arg; |
1387 | if (min_scan_period < chanlist_len || |
1388 | min_scan_period < cmd->convert_arg) { |
1389 | /* Arithmetic overflow. */ |
1390 | min_scan_period = UINT_MAX; |
1391 | err++; |
1392 | } |
1393 | if (cmd->scan_begin_arg < min_scan_period) { |
1394 | cmd->scan_begin_arg = min_scan_period; |
1395 | err++; |
1396 | } |
1397 | |
1398 | return !err; |
1399 | } |
1400 | |
1401 | static int pci230_ai_check_chanlist(struct comedi_device *dev, |
1402 | struct comedi_subdevice *s, |
1403 | struct comedi_cmd *cmd) |
1404 | { |
1405 | struct pci230_private *devpriv = dev->private; |
1406 | unsigned int max_diff_chan = (s->n_chan / 2) - 1; |
1407 | unsigned int prev_chan = 0; |
1408 | unsigned int prev_range = 0; |
1409 | unsigned int prev_aref = 0; |
1410 | bool prev_bipolar = false; |
1411 | unsigned int subseq_len = 0; |
1412 | int i; |
1413 | |
1414 | for (i = 0; i < cmd->chanlist_len; i++) { |
1415 | unsigned int chanspec = cmd->chanlist[i]; |
1416 | unsigned int chan = CR_CHAN(chanspec); |
1417 | unsigned int range = CR_RANGE(chanspec); |
1418 | unsigned int aref = CR_AREF(chanspec); |
1419 | bool bipolar = comedi_range_is_bipolar(s, range); |
1420 | |
1421 | if (aref == AREF_DIFF && chan >= max_diff_chan) { |
1422 | dev_dbg(dev->class_dev, |
1423 | "%s: differential channel number out of range 0 to %u\n" , |
1424 | __func__, max_diff_chan); |
1425 | return -EINVAL; |
1426 | } |
1427 | |
1428 | if (i > 0) { |
1429 | /* |
1430 | * Channel numbers must strictly increase or |
1431 | * subsequence must repeat exactly. |
1432 | */ |
1433 | if (chan <= prev_chan && subseq_len == 0) |
1434 | subseq_len = i; |
1435 | |
1436 | if (subseq_len > 0 && |
1437 | cmd->chanlist[i % subseq_len] != chanspec) { |
1438 | dev_dbg(dev->class_dev, |
1439 | "%s: channel numbers must increase or sequence must repeat exactly\n" , |
1440 | __func__); |
1441 | return -EINVAL; |
1442 | } |
1443 | |
1444 | if (aref != prev_aref) { |
1445 | dev_dbg(dev->class_dev, |
1446 | "%s: channel sequence analogue references must be all the same (single-ended or differential)\n" , |
1447 | __func__); |
1448 | return -EINVAL; |
1449 | } |
1450 | |
1451 | if (bipolar != prev_bipolar) { |
1452 | dev_dbg(dev->class_dev, |
1453 | "%s: channel sequence ranges must be all bipolar or all unipolar\n" , |
1454 | __func__); |
1455 | return -EINVAL; |
1456 | } |
1457 | |
1458 | if (aref != AREF_DIFF && range != prev_range && |
1459 | ((chan ^ prev_chan) & ~1) == 0) { |
1460 | dev_dbg(dev->class_dev, |
1461 | "%s: single-ended channel pairs must have the same range\n" , |
1462 | __func__); |
1463 | return -EINVAL; |
1464 | } |
1465 | } |
1466 | prev_chan = chan; |
1467 | prev_range = range; |
1468 | prev_aref = aref; |
1469 | prev_bipolar = bipolar; |
1470 | } |
1471 | |
1472 | if (subseq_len == 0) |
1473 | subseq_len = cmd->chanlist_len; |
1474 | |
1475 | if (cmd->chanlist_len % subseq_len) { |
1476 | dev_dbg(dev->class_dev, |
1477 | "%s: sequence must repeat exactly\n" , __func__); |
1478 | return -EINVAL; |
1479 | } |
1480 | |
1481 | /* |
1482 | * Buggy PCI230+ or PCI260+ requires channel 0 to be (first) in the |
1483 | * sequence if the sequence contains more than one channel. Hardware |
1484 | * versions 1 and 2 have the bug. There is no hardware version 3. |
1485 | * |
1486 | * Actually, there are two firmwares that report themselves as |
1487 | * hardware version 1 (the boards have different ADC chips with |
1488 | * slightly different timing requirements, which was supposed to |
1489 | * be invisible to software). The first one doesn't seem to have |
1490 | * the bug, but the second one does, and we can't tell them apart! |
1491 | */ |
1492 | if (devpriv->hwver > 0 && devpriv->hwver < 4) { |
1493 | if (subseq_len > 1 && CR_CHAN(cmd->chanlist[0])) { |
1494 | dev_info(dev->class_dev, |
1495 | "amplc_pci230: ai_cmdtest: Buggy PCI230+/260+ h/w version %u requires first channel of multi-channel sequence to be 0 (corrected in h/w version 4)\n" , |
1496 | devpriv->hwver); |
1497 | return -EINVAL; |
1498 | } |
1499 | } |
1500 | |
1501 | return 0; |
1502 | } |
1503 | |
1504 | static int pci230_ai_cmdtest(struct comedi_device *dev, |
1505 | struct comedi_subdevice *s, struct comedi_cmd *cmd) |
1506 | { |
1507 | const struct pci230_board *board = dev->board_ptr; |
1508 | struct pci230_private *devpriv = dev->private; |
1509 | int err = 0; |
1510 | unsigned int tmp; |
1511 | |
1512 | /* Step 1 : check if triggers are trivially valid */ |
1513 | |
1514 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_NOW | TRIG_INT); |
1515 | |
1516 | tmp = TRIG_FOLLOW | TRIG_TIMER | TRIG_INT; |
1517 | if (board->have_dio || board->min_hwver > 0) { |
1518 | /* |
1519 | * Unfortunately, we cannot trigger a scan off an external |
1520 | * source on the PCI260 board, since it uses the PPIC0 (DIO) |
1521 | * input, which isn't present on the PCI260. For PCI260+ |
1522 | * we can use the EXTTRIG/EXTCONVCLK input on pin 17 instead. |
1523 | */ |
1524 | tmp |= TRIG_EXT; |
1525 | } |
1526 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, flags: tmp); |
1527 | err |= comedi_check_trigger_src(src: &cmd->convert_src, |
1528 | TRIG_TIMER | TRIG_INT | TRIG_EXT); |
1529 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
1530 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_COUNT | TRIG_NONE); |
1531 | |
1532 | if (err) |
1533 | return 1; |
1534 | |
1535 | /* Step 2a : make sure trigger sources are unique */ |
1536 | |
1537 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
1538 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
1539 | err |= comedi_check_trigger_is_unique(src: cmd->convert_src); |
1540 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
1541 | |
1542 | /* Step 2b : and mutually compatible */ |
1543 | |
1544 | /* |
1545 | * If scan_begin_src is not TRIG_FOLLOW, then a monostable will be |
1546 | * set up to generate a fixed number of timed conversion pulses. |
1547 | */ |
1548 | if (cmd->scan_begin_src != TRIG_FOLLOW && |
1549 | cmd->convert_src != TRIG_TIMER) |
1550 | err |= -EINVAL; |
1551 | |
1552 | if (err) |
1553 | return 2; |
1554 | |
1555 | /* Step 3: check if arguments are trivially valid */ |
1556 | |
1557 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
1558 | |
1559 | #define MAX_SPEED_AI_SE 3200 /* PCI230 SE: 3200 ns => 312.5 kHz */ |
1560 | #define MAX_SPEED_AI_DIFF 8000 /* PCI230 DIFF: 8000 ns => 125 kHz */ |
1561 | #define MAX_SPEED_AI_PLUS 4000 /* PCI230+: 4000 ns => 250 kHz */ |
1562 | /* |
1563 | * Comedi limit due to unsigned int cmd. Driver limit = |
1564 | * 2^16 (16bit * counter) * 1000000ns (1kHz onboard clock) = 65.536s |
1565 | */ |
1566 | #define MIN_SPEED_AI 4294967295u /* 4294967295ns = 4.29s */ |
1567 | |
1568 | if (cmd->convert_src == TRIG_TIMER) { |
1569 | unsigned int max_speed_ai; |
1570 | |
1571 | if (devpriv->hwver == 0) { |
1572 | /* |
1573 | * PCI230 or PCI260. Max speed depends whether |
1574 | * single-ended or pseudo-differential. |
1575 | */ |
1576 | if (cmd->chanlist && cmd->chanlist_len > 0) { |
1577 | /* Peek analogue reference of first channel. */ |
1578 | if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) |
1579 | max_speed_ai = MAX_SPEED_AI_DIFF; |
1580 | else |
1581 | max_speed_ai = MAX_SPEED_AI_SE; |
1582 | |
1583 | } else { |
1584 | /* No channel list. Assume single-ended. */ |
1585 | max_speed_ai = MAX_SPEED_AI_SE; |
1586 | } |
1587 | } else { |
1588 | /* PCI230+ or PCI260+. */ |
1589 | max_speed_ai = MAX_SPEED_AI_PLUS; |
1590 | } |
1591 | |
1592 | err |= comedi_check_trigger_arg_min(arg: &cmd->convert_arg, |
1593 | val: max_speed_ai); |
1594 | err |= comedi_check_trigger_arg_max(arg: &cmd->convert_arg, |
1595 | MIN_SPEED_AI); |
1596 | } else if (cmd->convert_src == TRIG_EXT) { |
1597 | /* |
1598 | * external trigger |
1599 | * |
1600 | * convert_arg == (CR_EDGE | 0) |
1601 | * => trigger on +ve edge. |
1602 | * convert_arg == (CR_EDGE | CR_INVERT | 0) |
1603 | * => trigger on -ve edge. |
1604 | */ |
1605 | if (cmd->convert_arg & CR_FLAGS_MASK) { |
1606 | /* Trigger number must be 0. */ |
1607 | if (cmd->convert_arg & ~CR_FLAGS_MASK) { |
1608 | cmd->convert_arg = COMBINE(cmd->convert_arg, 0, |
1609 | ~CR_FLAGS_MASK); |
1610 | err |= -EINVAL; |
1611 | } |
1612 | /* |
1613 | * The only flags allowed are CR_INVERT and CR_EDGE. |
1614 | * CR_EDGE is required. |
1615 | */ |
1616 | if ((cmd->convert_arg & CR_FLAGS_MASK & ~CR_INVERT) != |
1617 | CR_EDGE) { |
1618 | /* Set CR_EDGE, preserve CR_INVERT. */ |
1619 | cmd->convert_arg = |
1620 | COMBINE(cmd->start_arg, CR_EDGE | 0, |
1621 | CR_FLAGS_MASK & ~CR_INVERT); |
1622 | err |= -EINVAL; |
1623 | } |
1624 | } else { |
1625 | /* |
1626 | * Backwards compatibility with previous versions: |
1627 | * convert_arg == 0 => trigger on -ve edge. |
1628 | * convert_arg == 1 => trigger on +ve edge. |
1629 | */ |
1630 | err |= comedi_check_trigger_arg_max(arg: &cmd->convert_arg, |
1631 | val: 1); |
1632 | } |
1633 | } else { |
1634 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: 0); |
1635 | } |
1636 | |
1637 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
1638 | val: cmd->chanlist_len); |
1639 | |
1640 | if (cmd->stop_src == TRIG_COUNT) |
1641 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
1642 | else /* TRIG_NONE */ |
1643 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
1644 | |
1645 | if (cmd->scan_begin_src == TRIG_EXT) { |
1646 | /* |
1647 | * external "trigger" to begin each scan: |
1648 | * scan_begin_arg==0 => use PPC0 input -> gate of CT0 -> gate |
1649 | * of CT2 (sample convert trigger is CT2) |
1650 | */ |
1651 | if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) { |
1652 | cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0, |
1653 | ~CR_FLAGS_MASK); |
1654 | err |= -EINVAL; |
1655 | } |
1656 | /* The only flag allowed is CR_EDGE, which is ignored. */ |
1657 | if (cmd->scan_begin_arg & CR_FLAGS_MASK & ~CR_EDGE) { |
1658 | cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0, |
1659 | CR_FLAGS_MASK & ~CR_EDGE); |
1660 | err |= -EINVAL; |
1661 | } |
1662 | } else if (cmd->scan_begin_src == TRIG_TIMER) { |
1663 | /* N.B. cmd->convert_arg is also TRIG_TIMER */ |
1664 | if (!pci230_ai_check_scan_period(cmd)) |
1665 | err |= -EINVAL; |
1666 | |
1667 | } else { |
1668 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: 0); |
1669 | } |
1670 | |
1671 | if (err) |
1672 | return 3; |
1673 | |
1674 | /* Step 4: fix up any arguments */ |
1675 | |
1676 | if (cmd->convert_src == TRIG_TIMER) { |
1677 | tmp = cmd->convert_arg; |
1678 | pci230_ns_to_single_timer(ns: &cmd->convert_arg, flags: cmd->flags); |
1679 | if (tmp != cmd->convert_arg) |
1680 | err++; |
1681 | } |
1682 | |
1683 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1684 | /* N.B. cmd->convert_arg is also TRIG_TIMER */ |
1685 | tmp = cmd->scan_begin_arg; |
1686 | pci230_ns_to_single_timer(ns: &cmd->scan_begin_arg, flags: cmd->flags); |
1687 | if (!pci230_ai_check_scan_period(cmd)) { |
1688 | /* Was below minimum required. Round up. */ |
1689 | pci230_ns_to_single_timer(ns: &cmd->scan_begin_arg, |
1690 | CMDF_ROUND_UP); |
1691 | pci230_ai_check_scan_period(cmd); |
1692 | } |
1693 | if (tmp != cmd->scan_begin_arg) |
1694 | err++; |
1695 | } |
1696 | |
1697 | if (err) |
1698 | return 4; |
1699 | |
1700 | /* Step 5: check channel list if it exists */ |
1701 | if (cmd->chanlist && cmd->chanlist_len > 0) |
1702 | err |= pci230_ai_check_chanlist(dev, s, cmd); |
1703 | |
1704 | if (err) |
1705 | return 5; |
1706 | |
1707 | return 0; |
1708 | } |
1709 | |
1710 | static void pci230_ai_update_fifo_trigger_level(struct comedi_device *dev, |
1711 | struct comedi_subdevice *s) |
1712 | { |
1713 | struct pci230_private *devpriv = dev->private; |
1714 | struct comedi_cmd *cmd = &s->async->cmd; |
1715 | unsigned int wake; |
1716 | unsigned short triglev; |
1717 | unsigned short adccon; |
1718 | |
1719 | if (cmd->flags & CMDF_WAKE_EOS) |
1720 | wake = cmd->scan_end_arg - s->async->cur_chan; |
1721 | else |
1722 | wake = comedi_nsamples_left(s, PCI230_ADC_FIFOLEVEL_HALFFULL); |
1723 | |
1724 | if (wake >= PCI230_ADC_FIFOLEVEL_HALFFULL) { |
1725 | triglev = PCI230_ADC_INT_FIFO_HALF; |
1726 | } else if (wake > 1 && devpriv->hwver > 0) { |
1727 | /* PCI230+/260+ programmable FIFO interrupt level. */ |
1728 | if (devpriv->adcfifothresh != wake) { |
1729 | devpriv->adcfifothresh = wake; |
1730 | outw(value: wake, port: devpriv->daqio + PCI230P_ADCFFTH); |
1731 | } |
1732 | triglev = PCI230P_ADC_INT_FIFO_THRESH; |
1733 | } else { |
1734 | triglev = PCI230_ADC_INT_FIFO_NEMPTY; |
1735 | } |
1736 | adccon = (devpriv->adccon & ~PCI230_ADC_INT_FIFO_MASK) | triglev; |
1737 | if (adccon != devpriv->adccon) { |
1738 | devpriv->adccon = adccon; |
1739 | outw(value: adccon, port: devpriv->daqio + PCI230_ADCCON); |
1740 | } |
1741 | } |
1742 | |
1743 | static int pci230_ai_inttrig_convert(struct comedi_device *dev, |
1744 | struct comedi_subdevice *s, |
1745 | unsigned int trig_num) |
1746 | { |
1747 | struct pci230_private *devpriv = dev->private; |
1748 | unsigned long irqflags; |
1749 | unsigned int delayus; |
1750 | |
1751 | if (trig_num) |
1752 | return -EINVAL; |
1753 | |
1754 | spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); |
1755 | if (!devpriv->ai_cmd_started) { |
1756 | spin_unlock_irqrestore(lock: &devpriv->ai_stop_spinlock, flags: irqflags); |
1757 | return 1; |
1758 | } |
1759 | /* |
1760 | * Trigger conversion by toggling Z2-CT2 output. |
1761 | * Finish with output high. |
1762 | */ |
1763 | comedi_8254_set_mode(i8254: dev->pacer, counter: 2, mode: I8254_MODE0); |
1764 | comedi_8254_set_mode(i8254: dev->pacer, counter: 2, mode: I8254_MODE1); |
1765 | /* |
1766 | * Delay. Should driver be responsible for this? An |
1767 | * alternative would be to wait until conversion is complete, |
1768 | * but we can't tell when it's complete because the ADC busy |
1769 | * bit has a different meaning when FIFO enabled (and when |
1770 | * FIFO not enabled, it only works for software triggers). |
1771 | */ |
1772 | if ((devpriv->adccon & PCI230_ADC_IM_MASK) == PCI230_ADC_IM_DIF && |
1773 | devpriv->hwver == 0) { |
1774 | /* PCI230/260 in differential mode */ |
1775 | delayus = 8; |
1776 | } else { |
1777 | /* single-ended or PCI230+/260+ */ |
1778 | delayus = 4; |
1779 | } |
1780 | spin_unlock_irqrestore(lock: &devpriv->ai_stop_spinlock, flags: irqflags); |
1781 | udelay(delayus); |
1782 | return 1; |
1783 | } |
1784 | |
1785 | static int pci230_ai_inttrig_scan_begin(struct comedi_device *dev, |
1786 | struct comedi_subdevice *s, |
1787 | unsigned int trig_num) |
1788 | { |
1789 | struct pci230_private *devpriv = dev->private; |
1790 | unsigned long irqflags; |
1791 | unsigned char zgat; |
1792 | |
1793 | if (trig_num) |
1794 | return -EINVAL; |
1795 | |
1796 | spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); |
1797 | if (devpriv->ai_cmd_started) { |
1798 | /* Trigger scan by waggling CT0 gate source. */ |
1799 | zgat = pci230_gat_config(chan: 0, GAT_GND); |
1800 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
1801 | zgat = pci230_gat_config(chan: 0, GAT_VCC); |
1802 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
1803 | } |
1804 | spin_unlock_irqrestore(lock: &devpriv->ai_stop_spinlock, flags: irqflags); |
1805 | |
1806 | return 1; |
1807 | } |
1808 | |
1809 | static void pci230_ai_stop(struct comedi_device *dev, |
1810 | struct comedi_subdevice *s) |
1811 | { |
1812 | struct pci230_private *devpriv = dev->private; |
1813 | unsigned long irqflags; |
1814 | struct comedi_cmd *cmd; |
1815 | bool started; |
1816 | |
1817 | spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); |
1818 | started = devpriv->ai_cmd_started; |
1819 | devpriv->ai_cmd_started = false; |
1820 | spin_unlock_irqrestore(lock: &devpriv->ai_stop_spinlock, flags: irqflags); |
1821 | if (!started) |
1822 | return; |
1823 | cmd = &s->async->cmd; |
1824 | if (cmd->convert_src == TRIG_TIMER) { |
1825 | /* Stop conversion rate generator. */ |
1826 | pci230_cancel_ct(dev, ct: 2); |
1827 | } |
1828 | if (cmd->scan_begin_src != TRIG_FOLLOW) { |
1829 | /* Stop scan period monostable. */ |
1830 | pci230_cancel_ct(dev, ct: 0); |
1831 | } |
1832 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1833 | /* |
1834 | * Disable ADC interrupt and wait for interrupt routine to finish |
1835 | * running unless we are called from the interrupt routine. |
1836 | */ |
1837 | devpriv->ier &= ~PCI230_INT_ADC; |
1838 | while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) { |
1839 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
1840 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1841 | } |
1842 | outb(value: devpriv->ier, port: dev->iobase + PCI230_INT_SCE); |
1843 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
1844 | /* |
1845 | * Reset FIFO, disable FIFO and set start conversion source to none. |
1846 | * Keep se/diff and bip/uni settings. |
1847 | */ |
1848 | devpriv->adccon = |
1849 | (devpriv->adccon & (PCI230_ADC_IR_MASK | PCI230_ADC_IM_MASK)) | |
1850 | PCI230_ADC_TRIG_NONE; |
1851 | outw(value: devpriv->adccon | PCI230_ADC_FIFO_RESET, |
1852 | port: devpriv->daqio + PCI230_ADCCON); |
1853 | /* Release resources. */ |
1854 | pci230_release_all_resources(dev, owner: OWNER_AICMD); |
1855 | } |
1856 | |
1857 | static void pci230_ai_start(struct comedi_device *dev, |
1858 | struct comedi_subdevice *s) |
1859 | { |
1860 | struct pci230_private *devpriv = dev->private; |
1861 | unsigned long irqflags; |
1862 | unsigned short conv; |
1863 | struct comedi_async *async = s->async; |
1864 | struct comedi_cmd *cmd = &async->cmd; |
1865 | |
1866 | devpriv->ai_cmd_started = true; |
1867 | |
1868 | /* Enable ADC FIFO trigger level interrupt. */ |
1869 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
1870 | devpriv->ier |= PCI230_INT_ADC; |
1871 | outb(value: devpriv->ier, port: dev->iobase + PCI230_INT_SCE); |
1872 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
1873 | |
1874 | /* |
1875 | * Update conversion trigger source which is currently set |
1876 | * to CT2 output, which is currently stuck high. |
1877 | */ |
1878 | switch (cmd->convert_src) { |
1879 | default: |
1880 | conv = PCI230_ADC_TRIG_NONE; |
1881 | break; |
1882 | case TRIG_TIMER: |
1883 | /* Using CT2 output. */ |
1884 | conv = PCI230_ADC_TRIG_Z2CT2; |
1885 | break; |
1886 | case TRIG_EXT: |
1887 | if (cmd->convert_arg & CR_EDGE) { |
1888 | if ((cmd->convert_arg & CR_INVERT) == 0) { |
1889 | /* Trigger on +ve edge. */ |
1890 | conv = PCI230_ADC_TRIG_EXTP; |
1891 | } else { |
1892 | /* Trigger on -ve edge. */ |
1893 | conv = PCI230_ADC_TRIG_EXTN; |
1894 | } |
1895 | } else { |
1896 | /* Backwards compatibility. */ |
1897 | if (cmd->convert_arg) { |
1898 | /* Trigger on +ve edge. */ |
1899 | conv = PCI230_ADC_TRIG_EXTP; |
1900 | } else { |
1901 | /* Trigger on -ve edge. */ |
1902 | conv = PCI230_ADC_TRIG_EXTN; |
1903 | } |
1904 | } |
1905 | break; |
1906 | case TRIG_INT: |
1907 | /* |
1908 | * Use CT2 output for software trigger due to problems |
1909 | * in differential mode on PCI230/260. |
1910 | */ |
1911 | conv = PCI230_ADC_TRIG_Z2CT2; |
1912 | break; |
1913 | } |
1914 | devpriv->adccon = (devpriv->adccon & ~PCI230_ADC_TRIG_MASK) | conv; |
1915 | outw(value: devpriv->adccon, port: devpriv->daqio + PCI230_ADCCON); |
1916 | if (cmd->convert_src == TRIG_INT) |
1917 | async->inttrig = pci230_ai_inttrig_convert; |
1918 | |
1919 | /* |
1920 | * Update FIFO interrupt trigger level, which is currently |
1921 | * set to "full". |
1922 | */ |
1923 | pci230_ai_update_fifo_trigger_level(dev, s); |
1924 | if (cmd->convert_src == TRIG_TIMER) { |
1925 | /* Update timer gates. */ |
1926 | unsigned char zgat; |
1927 | |
1928 | if (cmd->scan_begin_src != TRIG_FOLLOW) { |
1929 | /* |
1930 | * Conversion timer CT2 needs to be gated by |
1931 | * inverted output of monostable CT2. |
1932 | */ |
1933 | zgat = pci230_gat_config(chan: 2, GAT_NOUTNM2); |
1934 | } else { |
1935 | /* |
1936 | * Conversion timer CT2 needs to be gated on |
1937 | * continuously. |
1938 | */ |
1939 | zgat = pci230_gat_config(chan: 2, GAT_VCC); |
1940 | } |
1941 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
1942 | if (cmd->scan_begin_src != TRIG_FOLLOW) { |
1943 | /* Set monostable CT0 trigger source. */ |
1944 | switch (cmd->scan_begin_src) { |
1945 | default: |
1946 | zgat = pci230_gat_config(chan: 0, GAT_VCC); |
1947 | break; |
1948 | case TRIG_EXT: |
1949 | /* |
1950 | * For CT0 on PCI230, the external trigger |
1951 | * (gate) signal comes from PPC0, which is |
1952 | * channel 16 of the DIO subdevice. The |
1953 | * application needs to configure this as an |
1954 | * input in order to use it as an external scan |
1955 | * trigger. |
1956 | */ |
1957 | zgat = pci230_gat_config(chan: 0, GAT_EXT); |
1958 | break; |
1959 | case TRIG_TIMER: |
1960 | /* |
1961 | * Monostable CT0 triggered by rising edge on |
1962 | * inverted output of CT1 (falling edge on CT1). |
1963 | */ |
1964 | zgat = pci230_gat_config(chan: 0, GAT_NOUTNM2); |
1965 | break; |
1966 | case TRIG_INT: |
1967 | /* |
1968 | * Monostable CT0 is triggered by inttrig |
1969 | * function waggling the CT0 gate source. |
1970 | */ |
1971 | zgat = pci230_gat_config(chan: 0, GAT_VCC); |
1972 | break; |
1973 | } |
1974 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
1975 | switch (cmd->scan_begin_src) { |
1976 | case TRIG_TIMER: |
1977 | /* |
1978 | * Scan period timer CT1 needs to be |
1979 | * gated on to start counting. |
1980 | */ |
1981 | zgat = pci230_gat_config(chan: 1, GAT_VCC); |
1982 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
1983 | break; |
1984 | case TRIG_INT: |
1985 | async->inttrig = pci230_ai_inttrig_scan_begin; |
1986 | break; |
1987 | } |
1988 | } |
1989 | } else if (cmd->convert_src != TRIG_INT) { |
1990 | /* No longer need Z2-CT2. */ |
1991 | pci230_release_shared(dev, res_mask: RES_Z2CT2, owner: OWNER_AICMD); |
1992 | } |
1993 | } |
1994 | |
1995 | static int pci230_ai_inttrig_start(struct comedi_device *dev, |
1996 | struct comedi_subdevice *s, |
1997 | unsigned int trig_num) |
1998 | { |
1999 | struct comedi_cmd *cmd = &s->async->cmd; |
2000 | |
2001 | if (trig_num != cmd->start_arg) |
2002 | return -EINVAL; |
2003 | |
2004 | s->async->inttrig = NULL; |
2005 | pci230_ai_start(dev, s); |
2006 | |
2007 | return 1; |
2008 | } |
2009 | |
2010 | static void pci230_handle_ai(struct comedi_device *dev, |
2011 | struct comedi_subdevice *s) |
2012 | { |
2013 | struct pci230_private *devpriv = dev->private; |
2014 | struct comedi_async *async = s->async; |
2015 | struct comedi_cmd *cmd = &async->cmd; |
2016 | unsigned int status_fifo; |
2017 | unsigned int i; |
2018 | unsigned int nsamples; |
2019 | unsigned int fifoamount; |
2020 | unsigned short val; |
2021 | |
2022 | /* Determine number of samples to read. */ |
2023 | nsamples = comedi_nsamples_left(s, PCI230_ADC_FIFOLEVEL_HALFFULL); |
2024 | if (nsamples == 0) |
2025 | return; |
2026 | |
2027 | fifoamount = 0; |
2028 | for (i = 0; i < nsamples; i++) { |
2029 | if (fifoamount == 0) { |
2030 | /* Read FIFO state. */ |
2031 | status_fifo = inw(port: devpriv->daqio + PCI230_ADCCON); |
2032 | if (status_fifo & PCI230_ADC_FIFO_FULL_LATCHED) { |
2033 | /* |
2034 | * Report error otherwise FIFO overruns will go |
2035 | * unnoticed by the caller. |
2036 | */ |
2037 | dev_err(dev->class_dev, "AI FIFO overrun\n" ); |
2038 | async->events |= COMEDI_CB_ERROR; |
2039 | break; |
2040 | } else if (status_fifo & PCI230_ADC_FIFO_EMPTY) { |
2041 | /* FIFO empty. */ |
2042 | break; |
2043 | } else if (status_fifo & PCI230_ADC_FIFO_HALF) { |
2044 | /* FIFO half full. */ |
2045 | fifoamount = PCI230_ADC_FIFOLEVEL_HALFFULL; |
2046 | } else if (devpriv->hwver > 0) { |
2047 | /* Read PCI230+/260+ ADC FIFO level. */ |
2048 | fifoamount = inw(port: devpriv->daqio + |
2049 | PCI230P_ADCFFLEV); |
2050 | if (fifoamount == 0) |
2051 | break; /* Shouldn't happen. */ |
2052 | } else { |
2053 | /* FIFO not empty. */ |
2054 | fifoamount = 1; |
2055 | } |
2056 | } |
2057 | |
2058 | val = pci230_ai_read(dev); |
2059 | if (!comedi_buf_write_samples(s, data: &val, nsamples: 1)) |
2060 | break; |
2061 | |
2062 | fifoamount--; |
2063 | |
2064 | if (cmd->stop_src == TRIG_COUNT && |
2065 | async->scans_done >= cmd->stop_arg) { |
2066 | async->events |= COMEDI_CB_EOA; |
2067 | break; |
2068 | } |
2069 | } |
2070 | |
2071 | /* update FIFO interrupt trigger level if still running */ |
2072 | if (!(async->events & COMEDI_CB_CANCEL_MASK)) |
2073 | pci230_ai_update_fifo_trigger_level(dev, s); |
2074 | } |
2075 | |
2076 | static int pci230_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
2077 | { |
2078 | struct pci230_private *devpriv = dev->private; |
2079 | unsigned int i, chan, range, diff; |
2080 | unsigned int res_mask; |
2081 | unsigned short adccon, adcen; |
2082 | unsigned char zgat; |
2083 | |
2084 | /* Get the command. */ |
2085 | struct comedi_async *async = s->async; |
2086 | struct comedi_cmd *cmd = &async->cmd; |
2087 | |
2088 | /* |
2089 | * Determine which shared resources are needed. |
2090 | */ |
2091 | res_mask = 0; |
2092 | /* |
2093 | * Need Z2-CT2 to supply a conversion trigger source at a high |
2094 | * logic level, even if not doing timed conversions. |
2095 | */ |
2096 | res_mask |= RES_Z2CT2; |
2097 | if (cmd->scan_begin_src != TRIG_FOLLOW) { |
2098 | /* Using Z2-CT0 monostable to gate Z2-CT2 conversion timer */ |
2099 | res_mask |= RES_Z2CT0; |
2100 | if (cmd->scan_begin_src == TRIG_TIMER) { |
2101 | /* Using Z2-CT1 for scan frequency */ |
2102 | res_mask |= RES_Z2CT1; |
2103 | } |
2104 | } |
2105 | /* Claim resources. */ |
2106 | if (!pci230_claim_shared(dev, res_mask, owner: OWNER_AICMD)) |
2107 | return -EBUSY; |
2108 | |
2109 | /* |
2110 | * Steps: |
2111 | * - Set channel scan list. |
2112 | * - Set channel gains. |
2113 | * - Enable and reset FIFO, specify uni/bip, se/diff, and set |
2114 | * start conversion source to point to something at a high logic |
2115 | * level (we use the output of counter/timer 2 for this purpose. |
2116 | * - PAUSE to allow things to settle down. |
2117 | * - Reset the FIFO again because it needs resetting twice and there |
2118 | * may have been a false conversion trigger on some versions of |
2119 | * PCI230/260 due to the start conversion source being set to a |
2120 | * high logic level. |
2121 | * - Enable ADC FIFO level interrupt. |
2122 | * - Set actual conversion trigger source and FIFO interrupt trigger |
2123 | * level. |
2124 | * - If convert_src is TRIG_TIMER, set up the timers. |
2125 | */ |
2126 | |
2127 | adccon = PCI230_ADC_FIFO_EN; |
2128 | adcen = 0; |
2129 | |
2130 | if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) { |
2131 | /* Differential - all channels must be differential. */ |
2132 | diff = 1; |
2133 | adccon |= PCI230_ADC_IM_DIF; |
2134 | } else { |
2135 | /* Single ended - all channels must be single-ended. */ |
2136 | diff = 0; |
2137 | adccon |= PCI230_ADC_IM_SE; |
2138 | } |
2139 | |
2140 | range = CR_RANGE(cmd->chanlist[0]); |
2141 | devpriv->ai_bipolar = comedi_range_is_bipolar(s, range); |
2142 | if (devpriv->ai_bipolar) |
2143 | adccon |= PCI230_ADC_IR_BIP; |
2144 | else |
2145 | adccon |= PCI230_ADC_IR_UNI; |
2146 | |
2147 | for (i = 0; i < cmd->chanlist_len; i++) { |
2148 | unsigned int gainshift; |
2149 | |
2150 | chan = CR_CHAN(cmd->chanlist[i]); |
2151 | range = CR_RANGE(cmd->chanlist[i]); |
2152 | if (diff) { |
2153 | gainshift = 2 * chan; |
2154 | if (devpriv->hwver == 0) { |
2155 | /* |
2156 | * Original PCI230/260 expects both inputs of |
2157 | * the differential channel to be enabled. |
2158 | */ |
2159 | adcen |= 3 << gainshift; |
2160 | } else { |
2161 | /* |
2162 | * PCI230+/260+ expects only one input of the |
2163 | * differential channel to be enabled. |
2164 | */ |
2165 | adcen |= 1 << gainshift; |
2166 | } |
2167 | } else { |
2168 | gainshift = chan & ~1; |
2169 | adcen |= 1 << chan; |
2170 | } |
2171 | devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) | |
2172 | (pci230_ai_gain[range] << gainshift); |
2173 | } |
2174 | |
2175 | /* Set channel scan list. */ |
2176 | outw(value: adcen, port: devpriv->daqio + PCI230_ADCEN); |
2177 | |
2178 | /* Set channel gains. */ |
2179 | outw(value: devpriv->adcg, port: devpriv->daqio + PCI230_ADCG); |
2180 | |
2181 | /* |
2182 | * Set counter/timer 2 output high for use as the initial start |
2183 | * conversion source. |
2184 | */ |
2185 | comedi_8254_set_mode(i8254: dev->pacer, counter: 2, mode: I8254_MODE1); |
2186 | |
2187 | /* |
2188 | * Temporarily use CT2 output as conversion trigger source and |
2189 | * temporarily set FIFO interrupt trigger level to 'full'. |
2190 | */ |
2191 | adccon |= PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_Z2CT2; |
2192 | |
2193 | /* |
2194 | * Enable and reset FIFO, specify FIFO trigger level full, specify |
2195 | * uni/bip, se/diff, and temporarily set the start conversion source |
2196 | * to CT2 output. Note that CT2 output is currently high, and this |
2197 | * will produce a false conversion trigger on some versions of the |
2198 | * PCI230/260, but that will be dealt with later. |
2199 | */ |
2200 | devpriv->adccon = adccon; |
2201 | outw(value: adccon | PCI230_ADC_FIFO_RESET, port: devpriv->daqio + PCI230_ADCCON); |
2202 | |
2203 | /* |
2204 | * Delay - |
2205 | * Failure to include this will result in the first few channels'-worth |
2206 | * of data being corrupt, normally manifesting itself by large negative |
2207 | * voltages. It seems the board needs time to settle between the first |
2208 | * FIFO reset (above) and the second FIFO reset (below). Setting the |
2209 | * channel gains and scan list _before_ the first FIFO reset also |
2210 | * helps, though only slightly. |
2211 | */ |
2212 | usleep_range(min: 25, max: 100); |
2213 | |
2214 | /* Reset FIFO again. */ |
2215 | outw(value: adccon | PCI230_ADC_FIFO_RESET, port: devpriv->daqio + PCI230_ADCCON); |
2216 | |
2217 | if (cmd->convert_src == TRIG_TIMER) { |
2218 | /* |
2219 | * Set up CT2 as conversion timer, but gate it off for now. |
2220 | * Note, counter/timer output 2 can be monitored on the |
2221 | * connector: PCI230 pin 21, PCI260 pin 18. |
2222 | */ |
2223 | zgat = pci230_gat_config(chan: 2, GAT_GND); |
2224 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
2225 | /* Set counter/timer 2 to the specified conversion period. */ |
2226 | pci230_ct_setup_ns_mode(dev, ct: 2, mode: I8254_MODE3, ns: cmd->convert_arg, |
2227 | flags: cmd->flags); |
2228 | if (cmd->scan_begin_src != TRIG_FOLLOW) { |
2229 | /* |
2230 | * Set up monostable on CT0 output for scan timing. A |
2231 | * rising edge on the trigger (gate) input of CT0 will |
2232 | * trigger the monostable, causing its output to go low |
2233 | * for the configured period. The period depends on |
2234 | * the conversion period and the number of conversions |
2235 | * in the scan. |
2236 | * |
2237 | * Set the trigger high before setting up the |
2238 | * monostable to stop it triggering. The trigger |
2239 | * source will be changed later. |
2240 | */ |
2241 | zgat = pci230_gat_config(chan: 0, GAT_VCC); |
2242 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
2243 | pci230_ct_setup_ns_mode(dev, ct: 0, mode: I8254_MODE1, |
2244 | ns: ((u64)cmd->convert_arg * |
2245 | cmd->scan_end_arg), |
2246 | CMDF_ROUND_UP); |
2247 | if (cmd->scan_begin_src == TRIG_TIMER) { |
2248 | /* |
2249 | * Monostable on CT0 will be triggered by |
2250 | * output of CT1 at configured scan frequency. |
2251 | * |
2252 | * Set up CT1 but gate it off for now. |
2253 | */ |
2254 | zgat = pci230_gat_config(chan: 1, GAT_GND); |
2255 | outb(value: zgat, port: dev->iobase + PCI230_ZGAT_SCE); |
2256 | pci230_ct_setup_ns_mode(dev, ct: 1, mode: I8254_MODE3, |
2257 | ns: cmd->scan_begin_arg, |
2258 | flags: cmd->flags); |
2259 | } |
2260 | } |
2261 | } |
2262 | |
2263 | if (cmd->start_src == TRIG_INT) |
2264 | s->async->inttrig = pci230_ai_inttrig_start; |
2265 | else /* TRIG_NOW */ |
2266 | pci230_ai_start(dev, s); |
2267 | |
2268 | return 0; |
2269 | } |
2270 | |
2271 | static int pci230_ai_cancel(struct comedi_device *dev, |
2272 | struct comedi_subdevice *s) |
2273 | { |
2274 | pci230_ai_stop(dev, s); |
2275 | return 0; |
2276 | } |
2277 | |
2278 | /* Interrupt handler */ |
2279 | static irqreturn_t pci230_interrupt(int irq, void *d) |
2280 | { |
2281 | unsigned char status_int, valid_status_int, temp_ier; |
2282 | struct comedi_device *dev = d; |
2283 | struct pci230_private *devpriv = dev->private; |
2284 | struct comedi_subdevice *s_ao = dev->write_subdev; |
2285 | struct comedi_subdevice *s_ai = dev->read_subdev; |
2286 | unsigned long irqflags; |
2287 | |
2288 | /* Read interrupt status/enable register. */ |
2289 | status_int = inb(port: dev->iobase + PCI230_INT_STAT); |
2290 | |
2291 | if (status_int == PCI230_INT_DISABLE) |
2292 | return IRQ_NONE; |
2293 | |
2294 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
2295 | valid_status_int = devpriv->ier & status_int; |
2296 | /* |
2297 | * Disable triggered interrupts. |
2298 | * (Only those interrupts that need re-enabling, are, later in the |
2299 | * handler). |
2300 | */ |
2301 | temp_ier = devpriv->ier & ~status_int; |
2302 | outb(value: temp_ier, port: dev->iobase + PCI230_INT_SCE); |
2303 | devpriv->intr_running = true; |
2304 | devpriv->intr_cpuid = THISCPU; |
2305 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
2306 | |
2307 | /* |
2308 | * Check the source of interrupt and handle it. |
2309 | * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3 |
2310 | * interrupts. However, at present (Comedi-0.7.60) does not allow |
2311 | * concurrent execution of commands, instructions or a mixture of the |
2312 | * two. |
2313 | */ |
2314 | |
2315 | if (valid_status_int & PCI230_INT_ZCLK_CT1) |
2316 | pci230_handle_ao_nofifo(dev, s: s_ao); |
2317 | |
2318 | if (valid_status_int & PCI230P2_INT_DAC) |
2319 | pci230_handle_ao_fifo(dev, s: s_ao); |
2320 | |
2321 | if (valid_status_int & PCI230_INT_ADC) |
2322 | pci230_handle_ai(dev, s: s_ai); |
2323 | |
2324 | /* Reenable interrupts. */ |
2325 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
2326 | if (devpriv->ier != temp_ier) |
2327 | outb(value: devpriv->ier, port: dev->iobase + PCI230_INT_SCE); |
2328 | devpriv->intr_running = false; |
2329 | spin_unlock_irqrestore(lock: &devpriv->isr_spinlock, flags: irqflags); |
2330 | |
2331 | if (s_ao) |
2332 | comedi_handle_events(dev, s: s_ao); |
2333 | comedi_handle_events(dev, s: s_ai); |
2334 | |
2335 | return IRQ_HANDLED; |
2336 | } |
2337 | |
2338 | /* Check if PCI device matches a specific board. */ |
2339 | static bool pci230_match_pci_board(const struct pci230_board *board, |
2340 | struct pci_dev *pci_dev) |
2341 | { |
2342 | /* assume pci_dev->device != PCI_DEVICE_ID_INVALID */ |
2343 | if (board->id != pci_dev->device) |
2344 | return false; |
2345 | if (board->min_hwver == 0) |
2346 | return true; |
2347 | /* Looking for a '+' model. First check length of registers. */ |
2348 | if (pci_resource_len(pci_dev, 3) < 32) |
2349 | return false; /* Not a '+' model. */ |
2350 | /* |
2351 | * TODO: temporarily enable PCI device and read the hardware version |
2352 | * register. For now, assume it's okay. |
2353 | */ |
2354 | return true; |
2355 | } |
2356 | |
2357 | /* Look for board matching PCI device. */ |
2358 | static const struct pci230_board *pci230_find_pci_board(struct pci_dev *pci_dev) |
2359 | { |
2360 | unsigned int i; |
2361 | |
2362 | for (i = 0; i < ARRAY_SIZE(pci230_boards); i++) |
2363 | if (pci230_match_pci_board(board: &pci230_boards[i], pci_dev)) |
2364 | return &pci230_boards[i]; |
2365 | return NULL; |
2366 | } |
2367 | |
2368 | static int pci230_auto_attach(struct comedi_device *dev, |
2369 | unsigned long context_unused) |
2370 | { |
2371 | struct pci_dev *pci_dev = comedi_to_pci_dev(dev); |
2372 | const struct pci230_board *board; |
2373 | struct pci230_private *devpriv; |
2374 | struct comedi_subdevice *s; |
2375 | int rc; |
2376 | |
2377 | dev_info(dev->class_dev, "amplc_pci230: attach pci %s\n" , |
2378 | pci_name(pci_dev)); |
2379 | |
2380 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
2381 | if (!devpriv) |
2382 | return -ENOMEM; |
2383 | |
2384 | spin_lock_init(&devpriv->isr_spinlock); |
2385 | spin_lock_init(&devpriv->res_spinlock); |
2386 | spin_lock_init(&devpriv->ai_stop_spinlock); |
2387 | spin_lock_init(&devpriv->ao_stop_spinlock); |
2388 | |
2389 | board = pci230_find_pci_board(pci_dev); |
2390 | if (!board) { |
2391 | dev_err(dev->class_dev, |
2392 | "amplc_pci230: BUG! cannot determine board type!\n" ); |
2393 | return -EINVAL; |
2394 | } |
2395 | dev->board_ptr = board; |
2396 | dev->board_name = board->name; |
2397 | |
2398 | rc = comedi_pci_enable(dev); |
2399 | if (rc) |
2400 | return rc; |
2401 | |
2402 | /* |
2403 | * Read base addresses of the PCI230's two I/O regions from PCI |
2404 | * configuration register. |
2405 | */ |
2406 | dev->iobase = pci_resource_start(pci_dev, 2); |
2407 | devpriv->daqio = pci_resource_start(pci_dev, 3); |
2408 | dev_dbg(dev->class_dev, |
2409 | "%s I/O region 1 0x%04lx I/O region 2 0x%04lx\n" , |
2410 | dev->board_name, dev->iobase, devpriv->daqio); |
2411 | /* Read bits of DACCON register - only the output range. */ |
2412 | devpriv->daccon = inw(port: devpriv->daqio + PCI230_DACCON) & |
2413 | PCI230_DAC_OR_MASK; |
2414 | /* |
2415 | * Read hardware version register and set extended function register |
2416 | * if they exist. |
2417 | */ |
2418 | if (pci_resource_len(pci_dev, 3) >= 32) { |
2419 | unsigned short extfunc = 0; |
2420 | |
2421 | devpriv->hwver = inw(port: devpriv->daqio + PCI230P_HWVER); |
2422 | if (devpriv->hwver < board->min_hwver) { |
2423 | dev_err(dev->class_dev, |
2424 | "%s - bad hardware version - got %u, need %u\n" , |
2425 | dev->board_name, devpriv->hwver, |
2426 | board->min_hwver); |
2427 | return -EIO; |
2428 | } |
2429 | if (devpriv->hwver > 0) { |
2430 | if (!board->have_dio) { |
2431 | /* |
2432 | * No DIO ports. Route counters' external gates |
2433 | * to the EXTTRIG signal (PCI260+ pin 17). |
2434 | * (Otherwise, they would be routed to DIO |
2435 | * inputs PC0, PC1 and PC2 which don't exist |
2436 | * on PCI260[+].) |
2437 | */ |
2438 | extfunc |= PCI230P_EXTFUNC_GAT_EXTTRIG; |
2439 | } |
2440 | if (board->ao_bits && devpriv->hwver >= 2) { |
2441 | /* Enable DAC FIFO functionality. */ |
2442 | extfunc |= PCI230P2_EXTFUNC_DACFIFO; |
2443 | } |
2444 | } |
2445 | outw(value: extfunc, port: devpriv->daqio + PCI230P_EXTFUNC); |
2446 | if (extfunc & PCI230P2_EXTFUNC_DACFIFO) { |
2447 | /* |
2448 | * Temporarily enable DAC FIFO, reset it and disable |
2449 | * FIFO wraparound. |
2450 | */ |
2451 | outw(value: devpriv->daccon | PCI230P2_DAC_FIFO_EN | |
2452 | PCI230P2_DAC_FIFO_RESET, |
2453 | port: devpriv->daqio + PCI230_DACCON); |
2454 | /* Clear DAC FIFO channel enable register. */ |
2455 | outw(value: 0, port: devpriv->daqio + PCI230P2_DACEN); |
2456 | /* Disable DAC FIFO. */ |
2457 | outw(value: devpriv->daccon, port: devpriv->daqio + PCI230_DACCON); |
2458 | } |
2459 | } |
2460 | /* Disable board's interrupts. */ |
2461 | outb(value: 0, port: dev->iobase + PCI230_INT_SCE); |
2462 | /* Set ADC to a reasonable state. */ |
2463 | devpriv->adcg = 0; |
2464 | devpriv->adccon = PCI230_ADC_TRIG_NONE | PCI230_ADC_IM_SE | |
2465 | PCI230_ADC_IR_BIP; |
2466 | outw(BIT(0), port: devpriv->daqio + PCI230_ADCEN); |
2467 | outw(value: devpriv->adcg, port: devpriv->daqio + PCI230_ADCG); |
2468 | outw(value: devpriv->adccon | PCI230_ADC_FIFO_RESET, |
2469 | port: devpriv->daqio + PCI230_ADCCON); |
2470 | |
2471 | if (pci_dev->irq) { |
2472 | rc = request_irq(irq: pci_dev->irq, handler: pci230_interrupt, IRQF_SHARED, |
2473 | name: dev->board_name, dev); |
2474 | if (rc == 0) |
2475 | dev->irq = pci_dev->irq; |
2476 | } |
2477 | |
2478 | dev->pacer = comedi_8254_io_alloc(iobase: dev->iobase + PCI230_Z2_CT_BASE, |
2479 | osc_base: 0, I8254_IO8, regshift: 0); |
2480 | if (IS_ERR(ptr: dev->pacer)) |
2481 | return PTR_ERR(ptr: dev->pacer); |
2482 | |
2483 | rc = comedi_alloc_subdevices(dev, num_subdevices: 3); |
2484 | if (rc) |
2485 | return rc; |
2486 | |
2487 | s = &dev->subdevices[0]; |
2488 | /* analog input subdevice */ |
2489 | s->type = COMEDI_SUBD_AI; |
2490 | s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND; |
2491 | s->n_chan = 16; |
2492 | s->maxdata = (1 << board->ai_bits) - 1; |
2493 | s->range_table = &pci230_ai_range; |
2494 | s->insn_read = pci230_ai_insn_read; |
2495 | s->len_chanlist = 256; /* but there are restrictions. */ |
2496 | if (dev->irq) { |
2497 | dev->read_subdev = s; |
2498 | s->subdev_flags |= SDF_CMD_READ; |
2499 | s->do_cmd = pci230_ai_cmd; |
2500 | s->do_cmdtest = pci230_ai_cmdtest; |
2501 | s->cancel = pci230_ai_cancel; |
2502 | } |
2503 | |
2504 | s = &dev->subdevices[1]; |
2505 | /* analog output subdevice */ |
2506 | if (board->ao_bits) { |
2507 | s->type = COMEDI_SUBD_AO; |
2508 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND; |
2509 | s->n_chan = 2; |
2510 | s->maxdata = (1 << board->ao_bits) - 1; |
2511 | s->range_table = &pci230_ao_range; |
2512 | s->insn_write = pci230_ao_insn_write; |
2513 | s->len_chanlist = 2; |
2514 | if (dev->irq) { |
2515 | dev->write_subdev = s; |
2516 | s->subdev_flags |= SDF_CMD_WRITE; |
2517 | s->do_cmd = pci230_ao_cmd; |
2518 | s->do_cmdtest = pci230_ao_cmdtest; |
2519 | s->cancel = pci230_ao_cancel; |
2520 | } |
2521 | |
2522 | rc = comedi_alloc_subdev_readback(s); |
2523 | if (rc) |
2524 | return rc; |
2525 | } else { |
2526 | s->type = COMEDI_SUBD_UNUSED; |
2527 | } |
2528 | |
2529 | s = &dev->subdevices[2]; |
2530 | /* digital i/o subdevice */ |
2531 | if (board->have_dio) { |
2532 | rc = subdev_8255_io_init(dev, s, PCI230_PPI_X_BASE); |
2533 | if (rc) |
2534 | return rc; |
2535 | } else { |
2536 | s->type = COMEDI_SUBD_UNUSED; |
2537 | } |
2538 | |
2539 | return 0; |
2540 | } |
2541 | |
2542 | static struct comedi_driver amplc_pci230_driver = { |
2543 | .driver_name = "amplc_pci230" , |
2544 | .module = THIS_MODULE, |
2545 | .auto_attach = pci230_auto_attach, |
2546 | .detach = comedi_pci_detach, |
2547 | }; |
2548 | |
2549 | static int amplc_pci230_pci_probe(struct pci_dev *dev, |
2550 | const struct pci_device_id *id) |
2551 | { |
2552 | return comedi_pci_auto_config(pcidev: dev, driver: &lc_pci230_driver, |
2553 | context: id->driver_data); |
2554 | } |
2555 | |
2556 | static const struct pci_device_id amplc_pci230_pci_table[] = { |
2557 | { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI230) }, |
2558 | { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI260) }, |
2559 | { 0 } |
2560 | }; |
2561 | MODULE_DEVICE_TABLE(pci, amplc_pci230_pci_table); |
2562 | |
2563 | static struct pci_driver amplc_pci230_pci_driver = { |
2564 | .name = "amplc_pci230" , |
2565 | .id_table = amplc_pci230_pci_table, |
2566 | .probe = amplc_pci230_pci_probe, |
2567 | .remove = comedi_pci_auto_unconfig, |
2568 | }; |
2569 | module_comedi_pci_driver(amplc_pci230_driver, amplc_pci230_pci_driver); |
2570 | |
2571 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
2572 | MODULE_DESCRIPTION("Comedi driver for Amplicon PCI230(+) and PCI260(+)" ); |
2573 | MODULE_LICENSE("GPL" ); |
2574 | |