Commit | Line | Data |
---|---|---|
70a350c3 AW |
1 | /* |
2 | comedi/drivers/amplc_pci230.c | |
3 | Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards. | |
4 | ||
5 | Copyright (C) 2001 Allan Willcox <allanwillcox@ozemail.com.au> | |
6 | ||
7 | COMEDI - Linux Control and Measurement Device Interface | |
8 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
9 | ||
10 | This program is free software; you can redistribute it and/or modify | |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2 of the License, or | |
13 | (at your option) any later version. | |
14 | ||
15 | This program is distributed in the hope that it will be useful, | |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | GNU General Public License for more details. | |
19 | ||
20 | You should have received a copy of the GNU General Public License | |
21 | along with this program; if not, write to the Free Software | |
22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
23 | */ | |
24 | /* | |
25 | Driver: amplc_pci230 | |
26 | Description: Amplicon PCI230, PCI260 Multifunction I/O boards | |
27 | Author: Allan Willcox <allanwillcox@ozemail.com.au>, | |
28 | Steve D Sharples <steve.sharples@nottingham.ac.uk>, | |
29 | Ian Abbott <abbotti@mev.co.uk> | |
30 | Updated: Wed, 22 Oct 2008 12:34:49 +0100 | |
31 | Devices: [Amplicon] PCI230 (pci230 or amplc_pci230), | |
32 | PCI230+ (pci230+ or amplc_pci230), | |
33 | PCI260 (pci260 or amplc_pci230), PCI260+ (pci260+ or amplc_pci230) | |
34 | Status: works | |
35 | ||
36 | Configuration options: | |
37 | [0] - PCI bus of device (optional). | |
38 | [1] - PCI slot of device (optional). | |
39 | If bus/slot is not specified, the first available PCI device | |
40 | will be used. | |
41 | ||
42 | Configuring a "amplc_pci230" will match any supported card and it will | |
43 | choose the best match, picking the "+" models if possible. Configuring | |
44 | a "pci230" will match a PCI230 or PCI230+ card and it will be treated as | |
45 | a PCI230. Configuring a "pci260" will match a PCI260 or PCI260+ card | |
46 | and it will be treated as a PCI260. Configuring a "pci230+" will match | |
47 | a PCI230+ card. Configuring a "pci260+" will match a PCI260+ card. | |
48 | ||
49 | Subdevices: | |
50 | ||
51 | PCI230(+) PCI260(+) | |
52 | --------- --------- | |
53 | Subdevices 3 1 | |
54 | 0 AI AI | |
55 | 1 AO | |
56 | 2 DIO | |
57 | ||
58 | AI Subdevice: | |
59 | ||
60 | The AI subdevice has 16 single-ended channels or 8 differential | |
61 | channels. | |
62 | ||
63 | The PCI230 and PCI260 cards have 12-bit resolution. The PCI230+ and | |
64 | PCI260+ cards have 16-bit resolution. | |
65 | ||
66 | For differential mode, use inputs 2N and 2N+1 for channel N (e.g. use | |
67 | inputs 14 and 15 for channel 7). If the card is physically a PCI230 | |
68 | or PCI260 then it actually uses a "pseudo-differential" mode where the | |
69 | inputs are sampled a few microseconds apart. The PCI230+ and PCI260+ | |
70 | use true differential sampling. Another difference is that if the | |
71 | card is physically a PCI230 or PCI260, the inverting input is 2N, | |
72 | whereas for a PCI230+ or PCI260+ the inverting input is 2N+1. So if a | |
73 | PCI230 is physically replaced by a PCI230+ (or a PCI260 with a | |
74 | PCI260+) and differential mode is used, the differential inputs need | |
75 | to be physically swapped on the connector. | |
76 | ||
77 | The following input ranges are supported: | |
78 | ||
79 | 0 => [-10, +10] V | |
80 | 1 => [-5, +5] V | |
81 | 2 => [-2.5, +2.5] V | |
82 | 3 => [-1.25, +1.25] V | |
83 | 4 => [0, 10] V | |
84 | 5 => [0, 5] V | |
85 | 6 => [0, 2.5] V | |
86 | ||
87 | AI Commands: | |
88 | ||
89 | +=========+==============+===========+============+==========+ | |
90 | |start_src|scan_begin_src|convert_src|scan_end_src| stop_src | | |
91 | +=========+==============+===========+============+==========+ | |
92 | |TRIG_NOW | TRIG_FOLLOW |TRIG_TIMER | TRIG_COUNT |TRIG_NONE | | |
93 | |TRIG_INT | |TRIG_EXT(3)| |TRIG_COUNT| | |
94 | | | |TRIG_INT | | | | |
95 | | |--------------|-----------| | | | |
96 | | | TRIG_TIMER(1)|TRIG_TIMER | | | | |
97 | | | TRIG_EXT(2) | | | | | |
98 | | | TRIG_INT | | | | | |
99 | +---------+--------------+-----------+------------+----------+ | |
100 | ||
101 | Note 1: If AI command and AO command are used simultaneously, only | |
102 | one may have scan_begin_src == TRIG_TIMER. | |
103 | ||
104 | Note 2: For PCI230 and PCI230+, scan_begin_src == TRIG_EXT uses | |
105 | DIO channel 16 (pin 49) which will need to be configured as | |
106 | a digital input. For PCI260+, the EXTTRIG/EXTCONVCLK input | |
107 | (pin 17) is used instead. For PCI230, scan_begin_src == | |
108 | TRIG_EXT is not supported. The trigger is a rising edge | |
109 | on the input. | |
110 | ||
111 | Note 3: For convert_src == TRIG_EXT, the EXTTRIG/EXTCONVCLK input | |
112 | (pin 25 on PCI230(+), pin 17 on PCI260(+)) is used. The | |
113 | convert_arg value is interpreted as follows: | |
114 | ||
115 | convert_arg == (CR_EDGE | 0) => rising edge | |
116 | convert_arg == (CR_EDGE | CR_INVERT | 0) => falling edge | |
117 | convert_arg == 0 => falling edge (backwards compatibility) | |
118 | convert_arg == 1 => rising edge (backwards compatibility) | |
119 | ||
120 | All entries in the channel list must use the same analogue reference. | |
121 | If the analogue reference is not AREF_DIFF (not differential) each | |
122 | pair of channel numbers (0 and 1, 2 and 3, etc.) must use the same | |
123 | input range. The input ranges used in the sequence must be all | |
124 | bipolar (ranges 0 to 3) or all unipolar (ranges 4 to 6). The channel | |
125 | sequence must consist of 1 or more identical subsequences. Within the | |
126 | subsequence, channels must be in ascending order with no repeated | |
127 | channels. For example, the following sequences are valid: 0 1 2 3 | |
128 | (single valid subsequence), 0 2 3 5 0 2 3 5 (repeated valid | |
129 | subsequence), 1 1 1 1 (repeated valid subsequence). The following | |
130 | sequences are invalid: 0 3 2 1 (invalid subsequence), 0 2 3 5 0 2 3 | |
131 | (incompletely repeated subsequence). Some versions of the PCI230+ and | |
132 | PCI260+ have a bug that requires a subsequence longer than one entry | |
133 | long to include channel 0. | |
134 | ||
135 | AO Subdevice: | |
136 | ||
137 | The AO subdevice has 2 channels with 12-bit resolution. | |
138 | ||
139 | The following output ranges are supported: | |
140 | ||
141 | 0 => [0, 10] V | |
142 | 1 => [-10, +10] V | |
143 | ||
144 | AO Commands: | |
145 | ||
146 | +=========+==============+===========+============+==========+ | |
147 | |start_src|scan_begin_src|convert_src|scan_end_src| stop_src | | |
148 | +=========+==============+===========+============+==========+ | |
149 | |TRIG_INT | TRIG_TIMER(1)| TRIG_NOW | TRIG_COUNT |TRIG_NONE | | |
150 | | | TRIG_EXT(2) | | |TRIG_COUNT| | |
151 | | | TRIG_INT | | | | | |
152 | +---------+--------------+-----------+------------+----------+ | |
153 | ||
154 | Note 1: If AI command and AO command are used simultaneously, only | |
155 | one may have scan_begin_src == TRIG_TIMER. | |
156 | ||
157 | Note 2: scan_begin_src == TRIG_EXT is only supported if the card is | |
158 | configured as a PCI230+ and is only supported on later | |
159 | versions of the card. As a card configured as a PCI230+ is | |
160 | not guaranteed to support external triggering, please consider | |
161 | this support to be a bonus. It uses the EXTTRIG/ EXTCONVCLK | |
162 | input (PCI230+ pin 25). Triggering will be on the rising edge | |
163 | unless the CR_INVERT flag is set in scan_begin_arg. | |
164 | ||
165 | The channels in the channel sequence must be in ascending order with | |
166 | no repeats. All entries in the channel sequence must use the same | |
167 | output range. | |
168 | ||
169 | DIO Subdevice: | |
170 | ||
171 | The DIO subdevice is a 8255 chip providing 24 DIO channels. The DIO | |
172 | channels are configurable as inputs or outputs in four groups: | |
173 | ||
174 | Port A - channels 0 to 7 | |
175 | Port B - channels 8 to 15 | |
176 | Port CL - channels 16 to 19 | |
177 | Port CH - channels 20 to 23 | |
178 | ||
179 | Only mode 0 of the 8255 chip is supported. | |
180 | ||
181 | Bit 0 of port C (DIO channel 16) is also used as an external scan | |
182 | trigger input for AI commands on PCI230 and PCI230+, so would need to | |
183 | be configured as an input to use it for that purpose. | |
184 | */ | |
185 | /* | |
186 | Extra triggered scan functionality, interrupt bug-fix added by Steve Sharples. | |
187 | Support for PCI230+/260+, more triggered scan functionality, and workarounds | |
188 | for (or detection of) various hardware problems added by Ian Abbott. | |
189 | */ | |
70265d24 | 190 | |
70a350c3 AW |
191 | #include "../comedidev.h" |
192 | ||
193 | #include <linux/delay.h> | |
70265d24 | 194 | #include <linux/interrupt.h> |
70a350c3 AW |
195 | |
196 | #include "comedi_pci.h" | |
197 | #include "8253.h" | |
198 | #include "8255.h" | |
199 | ||
200 | /* PCI230 PCI configuration register information */ | |
201 | #define PCI_VENDOR_ID_AMPLICON 0x14dc | |
202 | #define PCI_DEVICE_ID_PCI230 0x0000 | |
203 | #define PCI_DEVICE_ID_PCI260 0x0006 | |
204 | #define PCI_DEVICE_ID_INVALID 0xffff | |
205 | ||
206 | #define PCI230_IO1_SIZE 32 /* Size of I/O space 1 */ | |
207 | #define PCI230_IO2_SIZE 16 /* Size of I/O space 2 */ | |
208 | ||
209 | /* PCI230 i/o space 1 registers. */ | |
210 | #define PCI230_PPI_X_BASE 0x00 /* User PPI (82C55) base */ | |
211 | #define PCI230_PPI_X_A 0x00 /* User PPI (82C55) port A */ | |
212 | #define PCI230_PPI_X_B 0x01 /* User PPI (82C55) port B */ | |
213 | #define PCI230_PPI_X_C 0x02 /* User PPI (82C55) port C */ | |
214 | #define PCI230_PPI_X_CMD 0x03 /* User PPI (82C55) control word */ | |
215 | #define PCI230_Z2_CT_BASE 0x14 /* 82C54 counter/timer base */ | |
216 | #define PCI230_Z2_CT0 0x14 /* 82C54 counter/timer 0 */ | |
217 | #define PCI230_Z2_CT1 0x15 /* 82C54 counter/timer 1 */ | |
218 | #define PCI230_Z2_CT2 0x16 /* 82C54 counter/timer 2 */ | |
219 | #define PCI230_Z2_CTC 0x17 /* 82C54 counter/timer control word */ | |
220 | #define PCI230_ZCLK_SCE 0x1A /* Group Z Clock Configuration */ | |
221 | #define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration */ | |
222 | #define PCI230_INT_SCE 0x1E /* Interrupt source mask (w) */ | |
223 | #define PCI230_INT_STAT 0x1E /* Interrupt status (r) */ | |
224 | ||
225 | /* PCI230 i/o space 2 registers. */ | |
226 | #define PCI230_DACCON 0x00 /* DAC control */ | |
227 | #define PCI230_DACOUT1 0x02 /* DAC channel 0 (w) */ | |
228 | #define PCI230_DACOUT2 0x04 /* DAC channel 1 (w) (not FIFO mode) */ | |
229 | #define PCI230_ADCDATA 0x08 /* ADC data (r) */ | |
230 | #define PCI230_ADCSWTRIG 0x08 /* ADC software trigger (w) */ | |
231 | #define PCI230_ADCCON 0x0A /* ADC control */ | |
232 | #define PCI230_ADCEN 0x0C /* ADC channel enable bits */ | |
233 | #define PCI230_ADCG 0x0E /* ADC gain control bits */ | |
234 | /* PCI230+ i/o space 2 additional registers. */ | |
235 | #define PCI230P_ADCTRIG 0x10 /* ADC start acquisition trigger */ | |
236 | #define PCI230P_ADCTH 0x12 /* ADC analog trigger threshold */ | |
237 | #define PCI230P_ADCFFTH 0x14 /* ADC FIFO interrupt threshold */ | |
238 | #define PCI230P_ADCFFLEV 0x16 /* ADC FIFO level (r) */ | |
239 | #define PCI230P_ADCPTSC 0x18 /* ADC pre-trigger sample count (r) */ | |
240 | #define PCI230P_ADCHYST 0x1A /* ADC analog trigger hysteresys */ | |
241 | #define PCI230P_EXTFUNC 0x1C /* Extended functions */ | |
242 | #define PCI230P_HWVER 0x1E /* Hardware version (r) */ | |
243 | /* PCI230+ hardware version 2 onwards. */ | |
244 | #define PCI230P2_DACDATA 0x02 /* DAC data (FIFO mode) (w) */ | |
245 | #define PCI230P2_DACSWTRIG 0x02 /* DAC soft trigger (FIFO mode) (r) */ | |
246 | #define PCI230P2_DACEN 0x06 /* DAC channel enable (FIFO mode) */ | |
247 | ||
248 | /* Convertor related constants. */ | |
249 | #define PCI230_DAC_SETTLE 5 /* Analogue output settling time in µs */ | |
250 | /* (DAC itself is 1µs nominally). */ | |
251 | #define PCI230_ADC_SETTLE 1 /* Analogue input settling time in µs */ | |
252 | /* (ADC itself is 1.6µs nominally but we poll | |
253 | * anyway). */ | |
254 | #define PCI230_MUX_SETTLE 10 /* ADC MUX settling time in µS */ | |
255 | /* - 10µs for se, 20µs de. */ | |
256 | ||
257 | /* DACCON read-write values. */ | |
258 | #define PCI230_DAC_OR_UNI (0<<0) /* Output range unipolar */ | |
259 | #define PCI230_DAC_OR_BIP (1<<0) /* Output range bipolar */ | |
260 | #define PCI230_DAC_OR_MASK (1<<0) | |
261 | /* The following applies only if DAC FIFO support is enabled in the EXTFUNC | |
262 | * register (and only for PCI230+ hardware version 2 onwards). */ | |
263 | #define PCI230P2_DAC_FIFO_EN (1<<8) /* FIFO enable */ | |
264 | /* The following apply only if the DAC FIFO is enabled (and only for PCI230+ | |
265 | * hardware version 2 onwards). */ | |
266 | #define PCI230P2_DAC_TRIG_NONE (0<<2) /* No trigger */ | |
267 | #define PCI230P2_DAC_TRIG_SW (1<<2) /* Software trigger trigger */ | |
268 | #define PCI230P2_DAC_TRIG_EXTP (2<<2) /* EXTTRIG +ve edge trigger */ | |
269 | #define PCI230P2_DAC_TRIG_EXTN (3<<2) /* EXTTRIG -ve edge trigger */ | |
270 | #define PCI230P2_DAC_TRIG_Z2CT0 (4<<2) /* CT0-OUT +ve edge trigger */ | |
271 | #define PCI230P2_DAC_TRIG_Z2CT1 (5<<2) /* CT1-OUT +ve edge trigger */ | |
272 | #define PCI230P2_DAC_TRIG_Z2CT2 (6<<2) /* CT2-OUT +ve edge trigger */ | |
273 | #define PCI230P2_DAC_TRIG_MASK (7<<2) | |
274 | #define PCI230P2_DAC_FIFO_WRAP (1<<7) /* FIFO wraparound mode */ | |
275 | #define PCI230P2_DAC_INT_FIFO_EMPTY (0<<9) /* FIFO interrupt empty */ | |
276 | #define PCI230P2_DAC_INT_FIFO_NEMPTY (1<<9) | |
277 | #define PCI230P2_DAC_INT_FIFO_NHALF (2<<9) /* FIFO intr not half full */ | |
278 | #define PCI230P2_DAC_INT_FIFO_HALF (3<<9) | |
279 | #define PCI230P2_DAC_INT_FIFO_NFULL (4<<9) /* FIFO interrupt not full */ | |
280 | #define PCI230P2_DAC_INT_FIFO_FULL (5<<9) | |
281 | #define PCI230P2_DAC_INT_FIFO_MASK (7<<9) | |
282 | ||
283 | /* DACCON read-only values. */ | |
284 | #define PCI230_DAC_BUSY (1<<1) /* DAC busy. */ | |
285 | /* The following apply only if the DAC FIFO is enabled (and only for PCI230+ | |
286 | * hardware version 2 onwards). */ | |
287 | #define PCI230P2_DAC_FIFO_UNDERRUN_LATCHED (1<<5) /* Underrun error */ | |
288 | #define PCI230P2_DAC_FIFO_EMPTY (1<<13) /* FIFO empty */ | |
289 | #define PCI230P2_DAC_FIFO_FULL (1<<14) /* FIFO full */ | |
290 | #define PCI230P2_DAC_FIFO_HALF (1<<15) /* FIFO half full */ | |
291 | ||
292 | /* DACCON write-only, transient values. */ | |
293 | /* The following apply only if the DAC FIFO is enabled (and only for PCI230+ | |
294 | * hardware version 2 onwards). */ | |
295 | #define PCI230P2_DAC_FIFO_UNDERRUN_CLEAR (1<<5) /* Clear underrun */ | |
296 | #define PCI230P2_DAC_FIFO_RESET (1<<12) /* FIFO reset */ | |
297 | ||
298 | /* PCI230+ hardware version 2 DAC FIFO levels. */ | |
299 | #define PCI230P2_DAC_FIFOLEVEL_HALF 512 | |
300 | #define PCI230P2_DAC_FIFOLEVEL_FULL 1024 | |
301 | /* Free space in DAC FIFO. */ | |
302 | #define PCI230P2_DAC_FIFOROOM_EMPTY PCI230P2_DAC_FIFOLEVEL_FULL | |
303 | #define PCI230P2_DAC_FIFOROOM_ONETOHALF \ | |
304 | (PCI230P2_DAC_FIFOLEVEL_FULL - PCI230P2_DAC_FIFOLEVEL_HALF) | |
305 | #define PCI230P2_DAC_FIFOROOM_HALFTOFULL 1 | |
306 | #define PCI230P2_DAC_FIFOROOM_FULL 0 | |
307 | ||
308 | /* ADCCON read/write values. */ | |
309 | #define PCI230_ADC_TRIG_NONE (0<<0) /* No trigger */ | |
310 | #define PCI230_ADC_TRIG_SW (1<<0) /* Software trigger trigger */ | |
311 | #define PCI230_ADC_TRIG_EXTP (2<<0) /* EXTTRIG +ve edge trigger */ | |
312 | #define PCI230_ADC_TRIG_EXTN (3<<0) /* EXTTRIG -ve edge trigger */ | |
313 | #define PCI230_ADC_TRIG_Z2CT0 (4<<0) /* CT0-OUT +ve edge trigger */ | |
314 | #define PCI230_ADC_TRIG_Z2CT1 (5<<0) /* CT1-OUT +ve edge trigger */ | |
315 | #define PCI230_ADC_TRIG_Z2CT2 (6<<0) /* CT2-OUT +ve edge trigger */ | |
316 | #define PCI230_ADC_TRIG_MASK (7<<0) | |
317 | #define PCI230_ADC_IR_UNI (0<<3) /* Input range unipolar */ | |
318 | #define PCI230_ADC_IR_BIP (1<<3) /* Input range bipolar */ | |
319 | #define PCI230_ADC_IR_MASK (1<<3) | |
320 | #define PCI230_ADC_IM_SE (0<<4) /* Input mode single ended */ | |
321 | #define PCI230_ADC_IM_DIF (1<<4) /* Input mode differential */ | |
322 | #define PCI230_ADC_IM_MASK (1<<4) | |
323 | #define PCI230_ADC_FIFO_EN (1<<8) /* FIFO enable */ | |
324 | #define PCI230_ADC_INT_FIFO_EMPTY (0<<9) | |
325 | #define PCI230_ADC_INT_FIFO_NEMPTY (1<<9) /* FIFO interrupt not empty */ | |
326 | #define PCI230_ADC_INT_FIFO_NHALF (2<<9) | |
327 | #define PCI230_ADC_INT_FIFO_HALF (3<<9) /* FIFO interrupt half full */ | |
328 | #define PCI230_ADC_INT_FIFO_NFULL (4<<9) | |
329 | #define PCI230_ADC_INT_FIFO_FULL (5<<9) /* FIFO interrupt full */ | |
330 | #define PCI230P_ADC_INT_FIFO_THRESH (7<<9) /* FIFO interrupt threshold */ | |
331 | #define PCI230_ADC_INT_FIFO_MASK (7<<9) | |
332 | ||
333 | /* ADCCON write-only, transient values. */ | |
334 | #define PCI230_ADC_FIFO_RESET (1<<12) /* FIFO reset */ | |
335 | #define PCI230_ADC_GLOB_RESET (1<<13) /* Global reset */ | |
336 | ||
337 | /* ADCCON read-only values. */ | |
338 | #define PCI230_ADC_BUSY (1<<15) /* ADC busy */ | |
339 | #define PCI230_ADC_FIFO_EMPTY (1<<12) /* FIFO empty */ | |
340 | #define PCI230_ADC_FIFO_FULL (1<<13) /* FIFO full */ | |
341 | #define PCI230_ADC_FIFO_HALF (1<<14) /* FIFO half full */ | |
342 | #define PCI230_ADC_FIFO_FULL_LATCHED (1<<5) /* Indicates overrun occurred */ | |
343 | ||
344 | /* PCI230 ADC FIFO levels. */ | |
345 | #define PCI230_ADC_FIFOLEVEL_HALFFULL 2049 /* Value for FIFO half full */ | |
346 | #define PCI230_ADC_FIFOLEVEL_FULL 4096 /* FIFO size */ | |
347 | ||
348 | /* Value to write to ADCSWTRIG to trigger ADC conversion in software trigger | |
349 | * mode. Can be anything. */ | |
350 | #define PCI230_ADC_CONV 0xffff | |
351 | ||
352 | /* PCI230+ EXTFUNC values. */ | |
353 | #define PCI230P_EXTFUNC_GAT_EXTTRIG (1<<0) | |
354 | /* Route EXTTRIG pin to external gate inputs. */ | |
355 | /* PCI230+ hardware version 2 values. */ | |
356 | #define PCI230P2_EXTFUNC_DACFIFO (1<<1) | |
357 | /* Allow DAC FIFO to be enabled. */ | |
358 | ||
359 | /* | |
360 | * Counter/timer clock input configuration sources. | |
361 | */ | |
362 | #define CLK_CLK 0 /* reserved (channel-specific clock) */ | |
363 | #define CLK_10MHZ 1 /* internal 10 MHz clock */ | |
364 | #define CLK_1MHZ 2 /* internal 1 MHz clock */ | |
365 | #define CLK_100KHZ 3 /* internal 100 kHz clock */ | |
366 | #define CLK_10KHZ 4 /* internal 10 kHz clock */ | |
367 | #define CLK_1KHZ 5 /* internal 1 kHz clock */ | |
368 | #define CLK_OUTNM1 6 /* output of channel-1 modulo total */ | |
369 | #define CLK_EXT 7 /* external clock */ | |
370 | /* Macro to construct clock input configuration register value. */ | |
371 | #define CLK_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7)) | |
372 | /* Timebases in ns. */ | |
373 | #define TIMEBASE_10MHZ 100 | |
374 | #define TIMEBASE_1MHZ 1000 | |
375 | #define TIMEBASE_100KHZ 10000 | |
376 | #define TIMEBASE_10KHZ 100000 | |
377 | #define TIMEBASE_1KHZ 1000000 | |
378 | ||
379 | /* | |
380 | * Counter/timer gate input configuration sources. | |
381 | */ | |
382 | #define GAT_VCC 0 /* VCC (i.e. enabled) */ | |
383 | #define GAT_GND 1 /* GND (i.e. disabled) */ | |
384 | #define GAT_EXT 2 /* external gate input (PPCn on PCI230) */ | |
385 | #define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */ | |
386 | /* Macro to construct gate input configuration register value. */ | |
387 | #define GAT_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7)) | |
388 | ||
389 | /* | |
390 | * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI230 and PCI260: | |
391 | * | |
392 | * Channel's Channel's | |
393 | * clock input gate input | |
394 | * Channel CLK_OUTNM1 GAT_NOUTNM2 | |
395 | * ------- ---------- ----------- | |
396 | * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT | |
397 | * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT | |
398 | * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT | |
399 | */ | |
400 | ||
401 | /* Interrupt enables/status register values. */ | |
402 | #define PCI230_INT_DISABLE 0 | |
403 | #define PCI230_INT_PPI_C0 (1<<0) | |
404 | #define PCI230_INT_PPI_C3 (1<<1) | |
405 | #define PCI230_INT_ADC (1<<2) | |
406 | #define PCI230_INT_ZCLK_CT1 (1<<5) | |
407 | /* For PCI230+ hardware version 2 when DAC FIFO enabled. */ | |
408 | #define PCI230P2_INT_DAC (1<<4) | |
409 | ||
410 | #define PCI230_TEST_BIT(val, n) ((val>>n)&1) | |
411 | /* Assumes bits numbered with zero offset, ie. 0-15 */ | |
412 | ||
413 | /* (Potentially) shared resources and their owners */ | |
414 | enum { | |
415 | RES_Z2CT0, /* Z2-CT0 */ | |
416 | RES_Z2CT1, /* Z2-CT1 */ | |
417 | RES_Z2CT2, /* Z2-CT2 */ | |
418 | NUM_RESOURCES /* Number of (potentially) shared resources. */ | |
419 | }; | |
420 | ||
421 | enum { | |
422 | OWNER_NONE, /* Not owned */ | |
423 | OWNER_AICMD, /* Owned by AI command */ | |
424 | OWNER_AOCMD /* Owned by AO command */ | |
425 | }; | |
426 | ||
427 | /* | |
428 | * Handy macros. | |
429 | */ | |
430 | ||
431 | /* Combine old and new bits. */ | |
432 | #define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask))) | |
433 | ||
434 | /* A generic null function pointer value. */ | |
435 | #define NULLFUNC 0 | |
436 | ||
437 | /* Current CPU. XXX should this be hard_smp_processor_id()? */ | |
438 | #define THISCPU smp_processor_id() | |
439 | ||
440 | /* State flags for atomic bit operations */ | |
441 | #define AI_CMD_STARTED 0 | |
442 | #define AO_CMD_STARTED 1 | |
443 | ||
444 | /* | |
445 | * Board descriptions for the two boards supported. | |
446 | */ | |
447 | ||
b3e8fa97 | 448 | struct pci230_board { |
70a350c3 AW |
449 | const char *name; |
450 | unsigned short id; | |
451 | int ai_chans; | |
452 | int ai_bits; | |
453 | int ao_chans; | |
454 | int ao_bits; | |
455 | int have_dio; | |
456 | unsigned int min_hwver; /* Minimum hardware version supported. */ | |
b3e8fa97 BP |
457 | }; |
458 | static const struct pci230_board pci230_boards[] = { | |
70a350c3 | 459 | { |
0a85b6f0 MT |
460 | .name = "pci230+", |
461 | .id = PCI_DEVICE_ID_PCI230, | |
462 | .ai_chans = 16, | |
463 | .ai_bits = 16, | |
464 | .ao_chans = 2, | |
465 | .ao_bits = 12, | |
466 | .have_dio = 1, | |
467 | .min_hwver = 1, | |
468 | }, | |
70a350c3 | 469 | { |
0a85b6f0 MT |
470 | .name = "pci260+", |
471 | .id = PCI_DEVICE_ID_PCI260, | |
472 | .ai_chans = 16, | |
473 | .ai_bits = 16, | |
474 | .ao_chans = 0, | |
475 | .ao_bits = 0, | |
476 | .have_dio = 0, | |
477 | .min_hwver = 1, | |
478 | }, | |
70a350c3 | 479 | { |
0a85b6f0 MT |
480 | .name = "pci230", |
481 | .id = PCI_DEVICE_ID_PCI230, | |
482 | .ai_chans = 16, | |
483 | .ai_bits = 12, | |
484 | .ao_chans = 2, | |
485 | .ao_bits = 12, | |
486 | .have_dio = 1, | |
487 | }, | |
70a350c3 | 488 | { |
0a85b6f0 MT |
489 | .name = "pci260", |
490 | .id = PCI_DEVICE_ID_PCI260, | |
491 | .ai_chans = 16, | |
492 | .ai_bits = 12, | |
493 | .ao_chans = 0, | |
494 | .ao_bits = 0, | |
495 | .have_dio = 0, | |
496 | }, | |
70a350c3 | 497 | { |
0a85b6f0 MT |
498 | .name = "amplc_pci230", /* Wildcard matches any above */ |
499 | .id = PCI_DEVICE_ID_INVALID, | |
500 | }, | |
70a350c3 AW |
501 | }; |
502 | ||
503 | static DEFINE_PCI_DEVICE_TABLE(pci230_pci_table) = { | |
0a85b6f0 MT |
504 | { |
505 | PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI230, PCI_ANY_ID, | |
506 | PCI_ANY_ID, 0, 0, 0}, { | |
507 | PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI260, PCI_ANY_ID, | |
508 | PCI_ANY_ID, 0, 0, 0}, { | |
509 | 0} | |
70a350c3 AW |
510 | }; |
511 | ||
512 | MODULE_DEVICE_TABLE(pci, pci230_pci_table); | |
513 | /* | |
514 | * Useful for shorthand access to the particular board structure | |
515 | */ | |
b6ac1613 | 516 | #define n_pci230_boards ARRAY_SIZE(pci230_boards) |
b3e8fa97 | 517 | #define thisboard ((const struct pci230_board *)dev->board_ptr) |
70a350c3 AW |
518 | |
519 | /* this structure is for data unique to this hardware driver. If | |
520 | several hardware drivers keep similar information in this structure, | |
71b5f4f1 | 521 | feel free to suggest moving the variable to the struct comedi_device struct. */ |
70a350c3 AW |
522 | struct pci230_private { |
523 | struct pci_dev *pci_dev; | |
524 | spinlock_t isr_spinlock; /* Interrupt spin lock */ | |
525 | spinlock_t res_spinlock; /* Shared resources spin lock */ | |
526 | spinlock_t ai_stop_spinlock; /* Spin lock for stopping AI command */ | |
527 | spinlock_t ao_stop_spinlock; /* Spin lock for stopping AO command */ | |
528 | unsigned long state; /* State flags */ | |
529 | unsigned long iobase1; /* PCI230's I/O space 1 */ | |
790c5541 | 530 | unsigned int ao_readback[2]; /* Used for AO readback */ |
70a350c3 AW |
531 | unsigned int ai_scan_count; /* Number of analogue input scans |
532 | * remaining. */ | |
533 | unsigned int ai_scan_pos; /* Current position within analogue | |
534 | * input scan */ | |
535 | unsigned int ao_scan_count; /* Number of analogue output scans | |
536 | * remaining. */ | |
537 | int intr_cpuid; /* ID of CPU running interrupt routine. */ | |
538 | unsigned short hwver; /* Hardware version (for '+' models). */ | |
539 | unsigned short adccon; /* ADCCON register value. */ | |
540 | unsigned short daccon; /* DACCON register value. */ | |
541 | unsigned short adcfifothresh; /* ADC FIFO programmable interrupt | |
542 | * level threshold (PCI230+/260+). */ | |
543 | unsigned short adcg; /* ADCG register value. */ | |
544 | unsigned char int_en; /* Interrupt enables bits. */ | |
545 | unsigned char ai_continuous; /* Flag set when cmd->stop_src == | |
546 | * TRIG_NONE - user chooses to stop | |
547 | * continuous conversion by | |
548 | * cancelation. */ | |
549 | unsigned char ao_continuous; /* Flag set when cmd->stop_src == | |
550 | * TRIG_NONE - user chooses to stop | |
551 | * continuous conversion by | |
552 | * cancelation. */ | |
553 | unsigned char ai_bipolar; /* Set if bipolar input range so we | |
554 | * know to mangle it. */ | |
555 | unsigned char ao_bipolar; /* Set if bipolar output range so we | |
556 | * know to mangle it. */ | |
557 | unsigned char ier; /* Copy of interrupt enables/status register. */ | |
558 | unsigned char intr_running; /* Flag set in interrupt routine. */ | |
559 | unsigned char res_owner[NUM_RESOURCES]; /* Shared resource owners. */ | |
560 | }; | |
561 | ||
562 | #define devpriv ((struct pci230_private *)dev->private) | |
563 | ||
564 | /* PCI230 clock source periods in ns */ | |
565 | static const unsigned int pci230_timebase[8] = { | |
566 | [CLK_10MHZ] = TIMEBASE_10MHZ, | |
567 | [CLK_1MHZ] = TIMEBASE_1MHZ, | |
568 | [CLK_100KHZ] = TIMEBASE_100KHZ, | |
569 | [CLK_10KHZ] = TIMEBASE_10KHZ, | |
570 | [CLK_1KHZ] = TIMEBASE_1KHZ, | |
571 | }; | |
572 | ||
573 | /* PCI230 analogue input range table */ | |
9ced1de6 | 574 | static const struct comedi_lrange pci230_ai_range = { 7, { |
0a85b6f0 MT |
575 | BIP_RANGE(10), |
576 | BIP_RANGE(5), | |
577 | BIP_RANGE(2.5), | |
578 | BIP_RANGE(1.25), | |
579 | UNI_RANGE(10), | |
580 | UNI_RANGE(5), | |
581 | UNI_RANGE(2.5) | |
582 | } | |
70a350c3 AW |
583 | }; |
584 | ||
585 | /* PCI230 analogue gain bits for each input range. */ | |
586 | static const unsigned char pci230_ai_gain[7] = { 0, 1, 2, 3, 1, 2, 3 }; | |
587 | ||
588 | /* PCI230 adccon bipolar flag for each analogue input range. */ | |
589 | static const unsigned char pci230_ai_bipolar[7] = { 1, 1, 1, 1, 0, 0, 0 }; | |
590 | ||
591 | /* PCI230 analogue output range table */ | |
9ced1de6 | 592 | static const struct comedi_lrange pci230_ao_range = { 2, { |
0a85b6f0 MT |
593 | UNI_RANGE(10), |
594 | BIP_RANGE(10) | |
595 | } | |
70a350c3 AW |
596 | }; |
597 | ||
598 | /* PCI230 daccon bipolar flag for each analogue output range. */ | |
599 | static const unsigned char pci230_ao_bipolar[2] = { 0, 1 }; | |
600 | ||
601 | /* | |
139dfbdf | 602 | * The struct comedi_driver structure tells the Comedi core module |
70a350c3 AW |
603 | * which functions to call to configure/deconfigure (attach/detach) |
604 | * the board, and also about the kernel module that contains | |
605 | * the device code. | |
606 | */ | |
0a85b6f0 MT |
607 | static int pci230_attach(struct comedi_device *dev, |
608 | struct comedi_devconfig *it); | |
da91b269 | 609 | static int pci230_detach(struct comedi_device *dev); |
139dfbdf | 610 | static struct comedi_driver driver_amplc_pci230 = { |
68c3dbff BP |
611 | .driver_name = "amplc_pci230", |
612 | .module = THIS_MODULE, | |
613 | .attach = pci230_attach, | |
614 | .detach = pci230_detach, | |
615 | .board_name = &pci230_boards[0].name, | |
616 | .offset = sizeof(pci230_boards[0]), | |
8629efa4 | 617 | .num_names = ARRAY_SIZE(pci230_boards), |
70a350c3 AW |
618 | }; |
619 | ||
727b286b AT |
620 | static int __devinit driver_amplc_pci230_pci_probe(struct pci_dev *dev, |
621 | const struct pci_device_id | |
622 | *ent) | |
623 | { | |
624 | return comedi_pci_auto_config(dev, driver_amplc_pci230.driver_name); | |
625 | } | |
626 | ||
627 | static void __devexit driver_amplc_pci230_pci_remove(struct pci_dev *dev) | |
628 | { | |
629 | comedi_pci_auto_unconfig(dev); | |
630 | } | |
631 | ||
632 | static struct pci_driver driver_amplc_pci230_pci_driver = { | |
633 | .id_table = pci230_pci_table, | |
634 | .probe = &driver_amplc_pci230_pci_probe, | |
635 | .remove = __devexit_p(&driver_amplc_pci230_pci_remove) | |
636 | }; | |
637 | ||
638 | static int __init driver_amplc_pci230_init_module(void) | |
639 | { | |
640 | int retval; | |
641 | ||
642 | retval = comedi_driver_register(&driver_amplc_pci230); | |
643 | if (retval < 0) | |
644 | return retval; | |
645 | ||
646 | driver_amplc_pci230_pci_driver.name = | |
647 | (char *)driver_amplc_pci230.driver_name; | |
648 | return pci_register_driver(&driver_amplc_pci230_pci_driver); | |
649 | } | |
650 | ||
651 | static void __exit driver_amplc_pci230_cleanup_module(void) | |
652 | { | |
653 | pci_unregister_driver(&driver_amplc_pci230_pci_driver); | |
654 | comedi_driver_unregister(&driver_amplc_pci230); | |
655 | } | |
656 | ||
657 | module_init(driver_amplc_pci230_init_module); | |
658 | module_exit(driver_amplc_pci230_cleanup_module); | |
70a350c3 | 659 | |
0a85b6f0 MT |
660 | static int pci230_ai_rinsn(struct comedi_device *dev, |
661 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
662 | unsigned int *data); | |
663 | static int pci230_ao_winsn(struct comedi_device *dev, | |
664 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
665 | unsigned int *data); | |
666 | static int pci230_ao_rinsn(struct comedi_device *dev, | |
667 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
668 | unsigned int *data); | |
814900c9 | 669 | static void pci230_ct_setup_ns_mode(struct comedi_device *dev, unsigned int ct, |
0a85b6f0 MT |
670 | unsigned int mode, uint64_t ns, |
671 | unsigned int round); | |
70a350c3 | 672 | static void pci230_ns_to_single_timer(unsigned int *ns, unsigned int round); |
814900c9 | 673 | static void pci230_cancel_ct(struct comedi_device *dev, unsigned int ct); |
70265d24 | 674 | static irqreturn_t pci230_interrupt(int irq, void *d); |
0a85b6f0 MT |
675 | static int pci230_ao_cmdtest(struct comedi_device *dev, |
676 | struct comedi_subdevice *s, | |
677 | struct comedi_cmd *cmd); | |
814900c9 | 678 | static int pci230_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s); |
0a85b6f0 MT |
679 | static int pci230_ao_cancel(struct comedi_device *dev, |
680 | struct comedi_subdevice *s); | |
681 | static void pci230_ao_stop(struct comedi_device *dev, | |
682 | struct comedi_subdevice *s); | |
683 | static void pci230_handle_ao_nofifo(struct comedi_device *dev, | |
684 | struct comedi_subdevice *s); | |
685 | static int pci230_handle_ao_fifo(struct comedi_device *dev, | |
686 | struct comedi_subdevice *s); | |
687 | static int pci230_ai_cmdtest(struct comedi_device *dev, | |
688 | struct comedi_subdevice *s, | |
689 | struct comedi_cmd *cmd); | |
814900c9 | 690 | static int pci230_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s); |
0a85b6f0 MT |
691 | static int pci230_ai_cancel(struct comedi_device *dev, |
692 | struct comedi_subdevice *s); | |
693 | static void pci230_ai_stop(struct comedi_device *dev, | |
694 | struct comedi_subdevice *s); | |
695 | static void pci230_handle_ai(struct comedi_device *dev, | |
696 | struct comedi_subdevice *s); | |
814900c9 BP |
697 | |
698 | static short pci230_ai_read(struct comedi_device *dev) | |
70a350c3 AW |
699 | { |
700 | /* Read sample. */ | |
0a85b6f0 | 701 | short data = (short)inw(dev->iobase + PCI230_ADCDATA); |
70a350c3 AW |
702 | |
703 | /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower | |
704 | * four bits reserved for expansion). */ | |
705 | /* PCI230+ is 16 bit AI. */ | |
706 | data = data >> (16 - thisboard->ai_bits); | |
707 | ||
708 | /* If a bipolar range was specified, mangle it (twos | |
709 | * complement->straight binary). */ | |
25f1a98b | 710 | if (devpriv->ai_bipolar) |
70a350c3 | 711 | data ^= 1 << (thisboard->ai_bits - 1); |
25f1a98b | 712 | |
70a350c3 AW |
713 | return data; |
714 | } | |
715 | ||
da91b269 | 716 | static inline unsigned short pci230_ao_mangle_datum(struct comedi_device *dev, |
0a85b6f0 | 717 | short datum) |
70a350c3 AW |
718 | { |
719 | /* If a bipolar range was specified, mangle it (straight binary->twos | |
720 | * complement). */ | |
25f1a98b | 721 | if (devpriv->ao_bipolar) |
70a350c3 | 722 | datum ^= 1 << (thisboard->ao_bits - 1); |
25f1a98b | 723 | |
70a350c3 AW |
724 | |
725 | /* PCI230 is 12 bit - stored in upper bits of 16 bit register (lower | |
726 | * four bits reserved for expansion). */ | |
727 | /* PCI230+ is also 12 bit AO. */ | |
728 | datum <<= (16 - thisboard->ao_bits); | |
729 | return (unsigned short)datum; | |
730 | } | |
731 | ||
0a85b6f0 MT |
732 | static inline void pci230_ao_write_nofifo(struct comedi_device *dev, |
733 | short datum, unsigned int chan) | |
70a350c3 AW |
734 | { |
735 | /* Store unmangled datum to be read back later. */ | |
736 | devpriv->ao_readback[chan] = datum; | |
737 | ||
738 | /* Write mangled datum to appropriate DACOUT register. */ | |
739 | outw(pci230_ao_mangle_datum(dev, datum), dev->iobase + (((chan) == 0) | |
0a85b6f0 MT |
740 | ? PCI230_DACOUT1 |
741 | : | |
742 | PCI230_DACOUT2)); | |
70a350c3 AW |
743 | } |
744 | ||
da91b269 | 745 | static inline void pci230_ao_write_fifo(struct comedi_device *dev, short datum, |
0a85b6f0 | 746 | unsigned int chan) |
70a350c3 AW |
747 | { |
748 | /* Store unmangled datum to be read back later. */ | |
749 | devpriv->ao_readback[chan] = datum; | |
750 | ||
751 | /* Write mangled datum to appropriate DACDATA register. */ | |
752 | outw(pci230_ao_mangle_datum(dev, datum), | |
0a85b6f0 | 753 | dev->iobase + PCI230P2_DACDATA); |
70a350c3 AW |
754 | } |
755 | ||
756 | /* | |
757 | * Attach is called by the Comedi core to configure the driver | |
758 | * for a particular board. If you specified a board_name array | |
759 | * in the driver structure, dev->board_ptr contains that | |
760 | * address. | |
761 | */ | |
da91b269 | 762 | static int pci230_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
70a350c3 | 763 | { |
34c43922 | 764 | struct comedi_subdevice *s; |
70a350c3 AW |
765 | unsigned long iobase1, iobase2; |
766 | /* PCI230's I/O spaces 1 and 2 respectively. */ | |
767 | struct pci_dev *pci_dev; | |
768 | int i = 0, irq_hdl, rc; | |
769 | ||
770 | printk("comedi%d: amplc_pci230: attach %s %d,%d\n", dev->minor, | |
0a85b6f0 | 771 | thisboard->name, it->options[0], it->options[1]); |
70a350c3 AW |
772 | |
773 | /* Allocate the private structure area using alloc_private(). | |
774 | * Macro defined in comedidev.h - memsets struct fields to 0. */ | |
25f1a98b | 775 | if ((alloc_private(dev, sizeof(struct pci230_private))) < 0) |
70a350c3 | 776 | return -ENOMEM; |
25f1a98b | 777 | |
70a350c3 AW |
778 | spin_lock_init(&devpriv->isr_spinlock); |
779 | spin_lock_init(&devpriv->res_spinlock); | |
780 | spin_lock_init(&devpriv->ai_stop_spinlock); | |
781 | spin_lock_init(&devpriv->ao_stop_spinlock); | |
782 | /* Find card */ | |
783 | for (pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | |
0a85b6f0 MT |
784 | pci_dev != NULL; |
785 | pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) { | |
70a350c3 AW |
786 | if (it->options[0] || it->options[1]) { |
787 | /* Match against bus/slot options. */ | |
788 | if (it->options[0] != pci_dev->bus->number || | |
0a85b6f0 | 789 | it->options[1] != PCI_SLOT(pci_dev->devfn)) |
70a350c3 AW |
790 | continue; |
791 | } | |
792 | if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON) | |
793 | continue; | |
794 | if (thisboard->id == PCI_DEVICE_ID_INVALID) { | |
795 | /* The name was specified as "amplc_pci230" which is | |
796 | * used to match any supported device. Replace the | |
797 | * current dev->board_ptr with one that matches the | |
798 | * PCI device ID. */ | |
799 | for (i = 0; i < n_pci230_boards; i++) { | |
800 | if (pci_dev->device == pci230_boards[i].id) { | |
801 | if (pci230_boards[i].min_hwver > 0) { | |
802 | /* Check for a '+' model. | |
803 | * First check length of | |
804 | * registers. */ | |
805 | if (pci_resource_len(pci_dev, 3) | |
0a85b6f0 | 806 | < 32) { |
70a350c3 AW |
807 | /* Not a '+' model. */ |
808 | continue; | |
809 | } | |
810 | /* TODO: temporarily enable the | |
811 | * PCI device and read the | |
812 | * hardware version register. | |
813 | * For now assume it's okay. */ | |
814 | } | |
815 | /* Change board_ptr to matched board */ | |
816 | dev->board_ptr = &pci230_boards[i]; | |
817 | break; | |
818 | } | |
819 | } | |
820 | if (i < n_pci230_boards) | |
821 | break; | |
822 | } else { | |
823 | /* The name was specified as a specific device name. | |
824 | * The current dev->board_ptr is correct. Check | |
825 | * whether it matches the PCI device ID. */ | |
826 | if (thisboard->id == pci_dev->device) { | |
827 | /* Check minimum hardware version. */ | |
828 | if (thisboard->min_hwver > 0) { | |
829 | /* Looking for a '+' model. First | |
830 | * check length of registers. */ | |
831 | if (pci_resource_len(pci_dev, 3) < 32) { | |
832 | /* Not a '+' model. */ | |
833 | continue; | |
834 | } | |
835 | /* TODO: temporarily enable the PCI | |
836 | * device and read the hardware version | |
837 | * register. For now, assume it's | |
838 | * okay. */ | |
839 | break; | |
840 | } else { | |
841 | break; | |
842 | } | |
843 | } | |
844 | } | |
845 | } | |
846 | if (!pci_dev) { | |
847 | printk("comedi%d: No %s card found\n", dev->minor, | |
0a85b6f0 | 848 | thisboard->name); |
70a350c3 AW |
849 | return -EIO; |
850 | } | |
851 | devpriv->pci_dev = pci_dev; | |
852 | ||
853 | /* | |
854 | * Initialize dev->board_name. | |
855 | */ | |
856 | dev->board_name = thisboard->name; | |
857 | ||
858 | /* Enable PCI device and reserve I/O spaces. */ | |
859 | if (comedi_pci_enable(pci_dev, "amplc_pci230") < 0) { | |
860 | printk("comedi%d: failed to enable PCI device " | |
0a85b6f0 | 861 | "and request regions\n", dev->minor); |
70a350c3 AW |
862 | return -EIO; |
863 | } | |
864 | ||
865 | /* Read base addresses of the PCI230's two I/O regions from PCI | |
866 | * configuration register. */ | |
867 | iobase1 = pci_resource_start(pci_dev, 2); | |
868 | iobase2 = pci_resource_start(pci_dev, 3); | |
869 | ||
870 | printk("comedi%d: %s I/O region 1 0x%04lx I/O region 2 0x%04lx\n", | |
0a85b6f0 | 871 | dev->minor, dev->board_name, iobase1, iobase2); |
70a350c3 AW |
872 | |
873 | devpriv->iobase1 = iobase1; | |
874 | dev->iobase = iobase2; | |
875 | ||
876 | /* Read bits of DACCON register - only the output range. */ | |
877 | devpriv->daccon = inw(dev->iobase + PCI230_DACCON) & PCI230_DAC_OR_MASK; | |
878 | ||
879 | /* Read hardware version register and set extended function register | |
880 | * if they exist. */ | |
881 | if (pci_resource_len(pci_dev, 3) >= 32) { | |
882 | unsigned short extfunc = 0; | |
883 | ||
884 | devpriv->hwver = inw(dev->iobase + PCI230P_HWVER); | |
885 | if (devpriv->hwver < thisboard->min_hwver) { | |
886 | printk("comedi%d: %s - bad hardware version " | |
0a85b6f0 MT |
887 | "- got %u, need %u\n", dev->minor, |
888 | dev->board_name, devpriv->hwver, | |
889 | thisboard->min_hwver); | |
70a350c3 AW |
890 | return -EIO; |
891 | } | |
892 | if (devpriv->hwver > 0) { | |
893 | if (!thisboard->have_dio) { | |
894 | /* No DIO ports. Route counters' external gates | |
895 | * to the EXTTRIG signal (PCI260+ pin 17). | |
896 | * (Otherwise, they would be routed to DIO | |
897 | * inputs PC0, PC1 and PC2 which don't exist | |
898 | * on PCI260[+].) */ | |
899 | extfunc |= PCI230P_EXTFUNC_GAT_EXTTRIG; | |
900 | } | |
901 | if ((thisboard->ao_chans > 0) | |
0a85b6f0 | 902 | && (devpriv->hwver >= 2)) { |
70a350c3 AW |
903 | /* Enable DAC FIFO functionality. */ |
904 | extfunc |= PCI230P2_EXTFUNC_DACFIFO; | |
905 | } | |
906 | } | |
907 | outw(extfunc, dev->iobase + PCI230P_EXTFUNC); | |
908 | if ((extfunc & PCI230P2_EXTFUNC_DACFIFO) != 0) { | |
909 | /* Temporarily enable DAC FIFO, reset it and disable | |
910 | * FIFO wraparound. */ | |
911 | outw(devpriv->daccon | PCI230P2_DAC_FIFO_EN | |
0a85b6f0 MT |
912 | | PCI230P2_DAC_FIFO_RESET, |
913 | dev->iobase + PCI230_DACCON); | |
70a350c3 AW |
914 | /* Clear DAC FIFO channel enable register. */ |
915 | outw(0, dev->iobase + PCI230P2_DACEN); | |
916 | /* Disable DAC FIFO. */ | |
917 | outw(devpriv->daccon, dev->iobase + PCI230_DACCON); | |
918 | } | |
919 | } | |
920 | ||
921 | /* Disable board's interrupts. */ | |
922 | outb(0, devpriv->iobase1 + PCI230_INT_SCE); | |
923 | ||
924 | /* Set ADC to a reasonable state. */ | |
925 | devpriv->adcg = 0; | |
926 | devpriv->adccon = PCI230_ADC_TRIG_NONE | PCI230_ADC_IM_SE | |
0a85b6f0 | 927 | | PCI230_ADC_IR_BIP; |
70a350c3 AW |
928 | outw(1 << 0, dev->iobase + PCI230_ADCEN); |
929 | outw(devpriv->adcg, dev->iobase + PCI230_ADCG); | |
930 | outw(devpriv->adccon | PCI230_ADC_FIFO_RESET, | |
0a85b6f0 | 931 | dev->iobase + PCI230_ADCCON); |
70a350c3 AW |
932 | |
933 | /* Register the interrupt handler. */ | |
5f74ea14 GKH |
934 | irq_hdl = request_irq(devpriv->pci_dev->irq, pci230_interrupt, |
935 | IRQF_SHARED, "amplc_pci230", dev); | |
70a350c3 AW |
936 | if (irq_hdl < 0) { |
937 | printk("comedi%d: unable to register irq, " | |
0a85b6f0 MT |
938 | "commands will not be available %d\n", dev->minor, |
939 | devpriv->pci_dev->irq); | |
70a350c3 AW |
940 | } else { |
941 | dev->irq = devpriv->pci_dev->irq; | |
942 | printk("comedi%d: registered irq %u\n", dev->minor, | |
0a85b6f0 | 943 | devpriv->pci_dev->irq); |
70a350c3 AW |
944 | } |
945 | ||
946 | /* | |
947 | * Allocate the subdevice structures. alloc_subdevice() is a | |
948 | * convenient macro defined in comedidev.h. | |
949 | */ | |
950 | if (alloc_subdevices(dev, 3) < 0) | |
951 | return -ENOMEM; | |
952 | ||
953 | s = dev->subdevices + 0; | |
954 | /* analog input subdevice */ | |
955 | s->type = COMEDI_SUBD_AI; | |
956 | s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND; | |
957 | s->n_chan = thisboard->ai_chans; | |
958 | s->maxdata = (1 << thisboard->ai_bits) - 1; | |
959 | s->range_table = &pci230_ai_range; | |
960 | s->insn_read = &pci230_ai_rinsn; | |
961 | s->len_chanlist = 256; /* but there are restrictions. */ | |
962 | /* Only register commands if the interrupt handler is installed. */ | |
963 | if (irq_hdl == 0) { | |
964 | dev->read_subdev = s; | |
965 | s->subdev_flags |= SDF_CMD_READ; | |
966 | s->do_cmd = &pci230_ai_cmd; | |
967 | s->do_cmdtest = &pci230_ai_cmdtest; | |
968 | s->cancel = pci230_ai_cancel; | |
969 | } | |
970 | ||
971 | s = dev->subdevices + 1; | |
972 | /* analog output subdevice */ | |
973 | if (thisboard->ao_chans > 0) { | |
974 | s->type = COMEDI_SUBD_AO; | |
975 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND; | |
976 | s->n_chan = thisboard->ao_chans;; | |
977 | s->maxdata = (1 << thisboard->ao_bits) - 1; | |
978 | s->range_table = &pci230_ao_range; | |
979 | s->insn_write = &pci230_ao_winsn; | |
980 | s->insn_read = &pci230_ao_rinsn; | |
981 | s->len_chanlist = thisboard->ao_chans; | |
982 | /* Only register commands if the interrupt handler is | |
983 | * installed. */ | |
984 | if (irq_hdl == 0) { | |
985 | dev->write_subdev = s; | |
986 | s->subdev_flags |= SDF_CMD_WRITE; | |
987 | s->do_cmd = &pci230_ao_cmd; | |
988 | s->do_cmdtest = &pci230_ao_cmdtest; | |
989 | s->cancel = pci230_ao_cancel; | |
990 | } | |
991 | } else { | |
992 | s->type = COMEDI_SUBD_UNUSED; | |
993 | } | |
994 | ||
995 | s = dev->subdevices + 2; | |
996 | /* digital i/o subdevice */ | |
997 | if (thisboard->have_dio) { | |
998 | rc = subdev_8255_init(dev, s, NULL, | |
0a85b6f0 | 999 | (devpriv->iobase1 + PCI230_PPI_X_BASE)); |
70a350c3 AW |
1000 | if (rc < 0) |
1001 | return rc; | |
1002 | } else { | |
1003 | s->type = COMEDI_SUBD_UNUSED; | |
1004 | } | |
1005 | ||
1006 | printk("comedi%d: attached\n", dev->minor); | |
1007 | ||
1008 | return 1; | |
1009 | } | |
1010 | ||
1011 | /* | |
1012 | * _detach is called to deconfigure a device. It should deallocate | |
1013 | * resources. | |
1014 | * This function is also called when _attach() fails, so it should be | |
1015 | * careful not to release resources that were not necessarily | |
1016 | * allocated by _attach(). dev->private and dev->subdevices are | |
1017 | * deallocated automatically by the core. | |
1018 | */ | |
da91b269 | 1019 | static int pci230_detach(struct comedi_device *dev) |
70a350c3 AW |
1020 | { |
1021 | printk("comedi%d: amplc_pci230: remove\n", dev->minor); | |
1022 | ||
1023 | if (dev->subdevices && thisboard->have_dio) | |
1024 | /* Clean up dio subdevice. */ | |
1025 | subdev_8255_cleanup(dev, dev->subdevices + 2); | |
1026 | ||
1027 | if (dev->irq) | |
5f74ea14 | 1028 | free_irq(dev->irq, dev); |
70a350c3 AW |
1029 | |
1030 | if (devpriv) { | |
1031 | if (devpriv->pci_dev) { | |
25f1a98b | 1032 | if (dev->iobase) |
70a350c3 | 1033 | comedi_pci_disable(devpriv->pci_dev); |
25f1a98b | 1034 | |
70a350c3 AW |
1035 | pci_dev_put(devpriv->pci_dev); |
1036 | } | |
1037 | } | |
1038 | ||
1039 | return 0; | |
1040 | } | |
1041 | ||
da91b269 | 1042 | static int get_resources(struct comedi_device *dev, unsigned int res_mask, |
0a85b6f0 | 1043 | unsigned char owner) |
70a350c3 AW |
1044 | { |
1045 | int ok; | |
1046 | unsigned int i; | |
1047 | unsigned int b; | |
1048 | unsigned int claimed; | |
1049 | unsigned long irqflags; | |
1050 | ||
1051 | ok = 1; | |
1052 | claimed = 0; | |
5f74ea14 | 1053 | spin_lock_irqsave(&devpriv->res_spinlock, irqflags); |
70a350c3 | 1054 | for (b = 1, i = 0; (i < NUM_RESOURCES) |
0a85b6f0 | 1055 | && (res_mask != 0); b <<= 1, i++) { |
70a350c3 AW |
1056 | if ((res_mask & b) != 0) { |
1057 | res_mask &= ~b; | |
1058 | if (devpriv->res_owner[i] == OWNER_NONE) { | |
1059 | devpriv->res_owner[i] = owner; | |
1060 | claimed |= b; | |
1061 | } else if (devpriv->res_owner[i] != owner) { | |
1062 | for (b = 1, i = 0; claimed != 0; b <<= 1, i++) { | |
1063 | if ((claimed & b) != 0) { | |
1064 | devpriv->res_owner[i] | |
0a85b6f0 | 1065 | = OWNER_NONE; |
70a350c3 AW |
1066 | claimed &= ~b; |
1067 | } | |
1068 | } | |
1069 | ok = 0; | |
1070 | break; | |
1071 | } | |
1072 | } | |
1073 | } | |
5f74ea14 | 1074 | spin_unlock_irqrestore(&devpriv->res_spinlock, irqflags); |
70a350c3 AW |
1075 | return ok; |
1076 | } | |
1077 | ||
0a85b6f0 MT |
1078 | static inline int get_one_resource(struct comedi_device *dev, |
1079 | unsigned int resource, unsigned char owner) | |
70a350c3 AW |
1080 | { |
1081 | return get_resources(dev, (1U << resource), owner); | |
1082 | } | |
1083 | ||
da91b269 | 1084 | static void put_resources(struct comedi_device *dev, unsigned int res_mask, |
0a85b6f0 | 1085 | unsigned char owner) |
70a350c3 AW |
1086 | { |
1087 | unsigned int i; | |
1088 | unsigned int b; | |
1089 | unsigned long irqflags; | |
1090 | ||
5f74ea14 | 1091 | spin_lock_irqsave(&devpriv->res_spinlock, irqflags); |
70a350c3 | 1092 | for (b = 1, i = 0; (i < NUM_RESOURCES) |
0a85b6f0 | 1093 | && (res_mask != 0); b <<= 1, i++) { |
70a350c3 AW |
1094 | if ((res_mask & b) != 0) { |
1095 | res_mask &= ~b; | |
25f1a98b | 1096 | if (devpriv->res_owner[i] == owner) |
70a350c3 | 1097 | devpriv->res_owner[i] = OWNER_NONE; |
25f1a98b | 1098 | |
70a350c3 AW |
1099 | } |
1100 | } | |
5f74ea14 | 1101 | spin_unlock_irqrestore(&devpriv->res_spinlock, irqflags); |
70a350c3 AW |
1102 | } |
1103 | ||
0a85b6f0 MT |
1104 | static inline void put_one_resource(struct comedi_device *dev, |
1105 | unsigned int resource, unsigned char owner) | |
70a350c3 AW |
1106 | { |
1107 | put_resources(dev, (1U << resource), owner); | |
1108 | } | |
1109 | ||
0a85b6f0 MT |
1110 | static inline void put_all_resources(struct comedi_device *dev, |
1111 | unsigned char owner) | |
70a350c3 AW |
1112 | { |
1113 | put_resources(dev, (1U << NUM_RESOURCES) - 1, owner); | |
1114 | } | |
1115 | ||
1116 | /* | |
1117 | * COMEDI_SUBD_AI instruction; | |
1118 | */ | |
0a85b6f0 MT |
1119 | static int pci230_ai_rinsn(struct comedi_device *dev, |
1120 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
1121 | unsigned int *data) | |
70a350c3 AW |
1122 | { |
1123 | unsigned int n, i; | |
1124 | unsigned int chan, range, aref; | |
1125 | unsigned int gainshift; | |
1126 | unsigned int status; | |
1127 | unsigned short adccon, adcen; | |
1128 | ||
1129 | /* Unpack channel and range. */ | |
1130 | chan = CR_CHAN(insn->chanspec); | |
1131 | range = CR_RANGE(insn->chanspec); | |
1132 | aref = CR_AREF(insn->chanspec); | |
1133 | if (aref == AREF_DIFF) { | |
1134 | /* Differential. */ | |
1135 | if (chan >= s->n_chan / 2) { | |
1136 | DPRINTK("comedi%d: amplc_pci230: ai_rinsn: " | |
1137 | "differential channel number out of range " | |
1138 | "0 to %u\n", dev->minor, (s->n_chan / 2) - 1); | |
1139 | return -EINVAL; | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | /* Use Z2-CT2 as a conversion trigger instead of the built-in | |
1144 | * software trigger, as otherwise triggering of differential channels | |
1145 | * doesn't work properly for some versions of PCI230/260. Also set | |
1146 | * FIFO mode because the ADC busy bit only works for software triggers. | |
1147 | */ | |
1148 | adccon = PCI230_ADC_TRIG_Z2CT2 | PCI230_ADC_FIFO_EN; | |
1149 | /* Set Z2-CT2 output low to avoid any false triggers. */ | |
1150 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE0); | |
1151 | devpriv->ai_bipolar = pci230_ai_bipolar[range]; | |
1152 | if (aref == AREF_DIFF) { | |
1153 | /* Differential. */ | |
1154 | gainshift = chan * 2; | |
1155 | if (devpriv->hwver == 0) { | |
1156 | /* Original PCI230/260 expects both inputs of the | |
1157 | * differential channel to be enabled. */ | |
1158 | adcen = 3 << gainshift; | |
1159 | } else { | |
1160 | /* PCI230+/260+ expects only one input of the | |
1161 | * differential channel to be enabled. */ | |
1162 | adcen = 1 << gainshift; | |
1163 | } | |
1164 | adccon |= PCI230_ADC_IM_DIF; | |
1165 | } else { | |
1166 | /* Single ended. */ | |
1167 | adcen = 1 << chan; | |
1168 | gainshift = chan & ~1; | |
1169 | adccon |= PCI230_ADC_IM_SE; | |
1170 | } | |
1171 | devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) | |
0a85b6f0 | 1172 | | (pci230_ai_gain[range] << gainshift); |
25f1a98b | 1173 | if (devpriv->ai_bipolar) |
70a350c3 | 1174 | adccon |= PCI230_ADC_IR_BIP; |
25f1a98b | 1175 | else |
70a350c3 | 1176 | adccon |= PCI230_ADC_IR_UNI; |
25f1a98b | 1177 | |
70a350c3 AW |
1178 | |
1179 | /* Enable only this channel in the scan list - otherwise by default | |
1180 | * we'll get one sample from each channel. */ | |
1181 | outw(adcen, dev->iobase + PCI230_ADCEN); | |
1182 | ||
1183 | /* Set gain for channel. */ | |
1184 | outw(devpriv->adcg, dev->iobase + PCI230_ADCG); | |
1185 | ||
1186 | /* Specify uni/bip, se/diff, conversion source, and reset FIFO. */ | |
1187 | devpriv->adccon = adccon; | |
1188 | outw(adccon | PCI230_ADC_FIFO_RESET, dev->iobase + PCI230_ADCCON); | |
1189 | ||
1190 | /* Convert n samples */ | |
1191 | for (n = 0; n < insn->n; n++) { | |
1192 | /* Trigger conversion by toggling Z2-CT2 output (finish with | |
1193 | * output high). */ | |
1194 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, | |
0a85b6f0 | 1195 | I8254_MODE0); |
70a350c3 | 1196 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, |
0a85b6f0 | 1197 | I8254_MODE1); |
70a350c3 AW |
1198 | |
1199 | #define TIMEOUT 100 | |
1200 | /* wait for conversion to end */ | |
1201 | for (i = 0; i < TIMEOUT; i++) { | |
1202 | status = inw(dev->iobase + PCI230_ADCCON); | |
1203 | if (!(status & PCI230_ADC_FIFO_EMPTY)) | |
1204 | break; | |
5f74ea14 | 1205 | udelay(1); |
70a350c3 AW |
1206 | } |
1207 | if (i == TIMEOUT) { | |
5f74ea14 | 1208 | /* printk() should be used instead of printk() |
70a350c3 | 1209 | * whenever the code can be called from real-time. */ |
5f74ea14 | 1210 | printk("timeout\n"); |
70a350c3 AW |
1211 | return -ETIMEDOUT; |
1212 | } | |
1213 | ||
1214 | /* read data */ | |
1215 | data[n] = pci230_ai_read(dev); | |
1216 | } | |
1217 | ||
1218 | /* return the number of samples read/written */ | |
1219 | return n; | |
1220 | } | |
1221 | ||
1222 | /* | |
1223 | * COMEDI_SUBD_AO instructions; | |
1224 | */ | |
0a85b6f0 MT |
1225 | static int pci230_ao_winsn(struct comedi_device *dev, |
1226 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
1227 | unsigned int *data) | |
70a350c3 AW |
1228 | { |
1229 | int i; | |
1230 | int chan, range; | |
1231 | ||
1232 | /* Unpack channel and range. */ | |
1233 | chan = CR_CHAN(insn->chanspec); | |
1234 | range = CR_RANGE(insn->chanspec); | |
1235 | ||
1236 | /* Set range - see analogue output range table; 0 => unipolar 10V, | |
1237 | * 1 => bipolar +/-10V range scale */ | |
1238 | devpriv->ao_bipolar = pci230_ao_bipolar[range]; | |
1239 | outw(range, dev->iobase + PCI230_DACCON); | |
1240 | ||
1241 | /* Writing a list of values to an AO channel is probably not | |
1242 | * very useful, but that's how the interface is defined. */ | |
1243 | for (i = 0; i < insn->n; i++) { | |
1244 | /* Write value to DAC and store it. */ | |
1245 | pci230_ao_write_nofifo(dev, data[i], chan); | |
1246 | } | |
1247 | ||
1248 | /* return the number of samples read/written */ | |
1249 | return i; | |
1250 | } | |
1251 | ||
1252 | /* AO subdevices should have a read insn as well as a write insn. | |
1253 | * Usually this means copying a value stored in devpriv. */ | |
0a85b6f0 MT |
1254 | static int pci230_ao_rinsn(struct comedi_device *dev, |
1255 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
1256 | unsigned int *data) | |
70a350c3 AW |
1257 | { |
1258 | int i; | |
1259 | int chan = CR_CHAN(insn->chanspec); | |
1260 | ||
1261 | for (i = 0; i < insn->n; i++) | |
1262 | data[i] = devpriv->ao_readback[chan]; | |
1263 | ||
1264 | return i; | |
1265 | } | |
1266 | ||
0a85b6f0 MT |
1267 | static int pci230_ao_cmdtest(struct comedi_device *dev, |
1268 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
70a350c3 AW |
1269 | { |
1270 | int err = 0; | |
1271 | unsigned int tmp; | |
1272 | ||
1273 | /* cmdtest tests a particular command to see if it is valid. | |
1274 | * Using the cmdtest ioctl, a user can create a valid cmd | |
1275 | * and then have it executes by the cmd ioctl. | |
1276 | * | |
1277 | * cmdtest returns 1,2,3,4 or 0, depending on which tests | |
1278 | * the command passes. */ | |
1279 | ||
1280 | /* Step 1: make sure trigger sources are trivially valid. | |
1281 | * "invalid source" returned by comedilib to user mode process | |
1282 | * if this fails. */ | |
1283 | ||
1284 | tmp = cmd->start_src; | |
1285 | cmd->start_src &= TRIG_INT; | |
1286 | if (!cmd->start_src || tmp != cmd->start_src) | |
1287 | err++; | |
1288 | ||
1289 | tmp = cmd->scan_begin_src; | |
1290 | if ((thisboard->min_hwver > 0) && (devpriv->hwver >= 2)) { | |
1291 | /* | |
1292 | * For PCI230+ hardware version 2 onwards, allow external | |
1293 | * trigger from EXTTRIG/EXTCONVCLK input (PCI230+ pin 25). | |
1294 | * | |
1295 | * FIXME: The permitted scan_begin_src values shouldn't depend | |
1296 | * on devpriv->hwver (the detected card's actual hardware | |
1297 | * version). They should only depend on thisboard->min_hwver | |
1298 | * (the static capabilities of the configured card). To fix | |
1299 | * it, a new card model, e.g. "pci230+2" would have to be | |
1300 | * defined with min_hwver set to 2. It doesn't seem worth it | |
1301 | * for this alone. At the moment, please consider | |
1302 | * scan_begin_src==TRIG_EXT support to be a bonus rather than a | |
1303 | * guarantee! | |
1304 | */ | |
1305 | cmd->scan_begin_src &= TRIG_TIMER | TRIG_INT | TRIG_EXT; | |
1306 | } else { | |
1307 | cmd->scan_begin_src &= TRIG_TIMER | TRIG_INT; | |
1308 | } | |
1309 | if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) | |
1310 | err++; | |
1311 | ||
1312 | tmp = cmd->convert_src; | |
1313 | cmd->convert_src &= TRIG_NOW; | |
1314 | if (!cmd->convert_src || tmp != cmd->convert_src) | |
1315 | err++; | |
1316 | ||
1317 | tmp = cmd->scan_end_src; | |
1318 | cmd->scan_end_src &= TRIG_COUNT; | |
1319 | if (!cmd->scan_end_src || tmp != cmd->scan_end_src) | |
1320 | err++; | |
1321 | ||
1322 | tmp = cmd->stop_src; | |
1323 | cmd->stop_src &= TRIG_COUNT | TRIG_NONE; | |
1324 | if (!cmd->stop_src || tmp != cmd->stop_src) | |
1325 | err++; | |
1326 | ||
1327 | if (err) | |
1328 | return 1; | |
1329 | ||
1330 | /* Step 2: make sure trigger sources are unique and mutually compatible | |
1331 | * "source conflict" returned by comedilib to user mode process | |
1332 | * if this fails. */ | |
1333 | ||
1334 | /* these tests are true if more than one _src bit is set */ | |
1335 | if ((cmd->start_src & (cmd->start_src - 1)) != 0) | |
1336 | err++; | |
1337 | if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0) | |
1338 | err++; | |
1339 | if ((cmd->convert_src & (cmd->convert_src - 1)) != 0) | |
1340 | err++; | |
1341 | if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0) | |
1342 | err++; | |
1343 | if ((cmd->stop_src & (cmd->stop_src - 1)) != 0) | |
1344 | err++; | |
1345 | ||
1346 | if (err) | |
1347 | return 2; | |
1348 | ||
1349 | /* Step 3: make sure arguments are trivially compatible. | |
1350 | * "invalid argument" returned by comedilib to user mode process | |
1351 | * if this fails. */ | |
1352 | ||
1353 | if (cmd->start_arg != 0) { | |
1354 | cmd->start_arg = 0; | |
1355 | err++; | |
1356 | } | |
1357 | #define MAX_SPEED_AO 8000 /* 8000 ns => 125 kHz */ | |
1358 | #define MIN_SPEED_AO 4294967295u /* 4294967295ns = 4.29s */ | |
1359 | /*- Comedi limit due to unsigned int cmd. Driver limit | |
1360 | * = 2^16 (16bit * counter) * 1000000ns (1kHz onboard | |
1361 | * clock) = 65.536s */ | |
1362 | ||
1363 | switch (cmd->scan_begin_src) { | |
1364 | case TRIG_TIMER: | |
1365 | if (cmd->scan_begin_arg < MAX_SPEED_AO) { | |
1366 | cmd->scan_begin_arg = MAX_SPEED_AO; | |
1367 | err++; | |
1368 | } | |
1369 | if (cmd->scan_begin_arg > MIN_SPEED_AO) { | |
1370 | cmd->scan_begin_arg = MIN_SPEED_AO; | |
1371 | err++; | |
1372 | } | |
1373 | break; | |
1374 | case TRIG_EXT: | |
1375 | /* External trigger - for PCI230+ hardware version 2 onwards. */ | |
1376 | /* Trigger number must be 0. */ | |
1377 | if ((cmd->scan_begin_arg & ~CR_FLAGS_MASK) != 0) { | |
1378 | cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0, | |
0a85b6f0 | 1379 | ~CR_FLAGS_MASK); |
70a350c3 AW |
1380 | err++; |
1381 | } | |
1382 | /* The only flags allowed are CR_EDGE and CR_INVERT. The | |
1383 | * CR_EDGE flag is ignored. */ | |
1384 | if ((cmd->scan_begin_arg | |
0a85b6f0 | 1385 | & (CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT))) != 0) { |
70a350c3 | 1386 | cmd->scan_begin_arg = |
0a85b6f0 MT |
1387 | COMBINE(cmd->scan_begin_arg, 0, |
1388 | CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT)); | |
70a350c3 AW |
1389 | err++; |
1390 | } | |
1391 | break; | |
1392 | default: | |
1393 | if (cmd->scan_begin_arg != 0) { | |
1394 | cmd->scan_begin_arg = 0; | |
1395 | err++; | |
1396 | } | |
1397 | break; | |
1398 | } | |
1399 | ||
1400 | if (cmd->scan_end_arg != cmd->chanlist_len) { | |
1401 | cmd->scan_end_arg = cmd->chanlist_len; | |
1402 | err++; | |
1403 | } | |
1404 | if (cmd->stop_src == TRIG_NONE) { | |
1405 | /* TRIG_NONE */ | |
1406 | if (cmd->stop_arg != 0) { | |
1407 | cmd->stop_arg = 0; | |
1408 | err++; | |
1409 | } | |
1410 | } | |
1411 | ||
1412 | if (err) | |
1413 | return 3; | |
1414 | ||
1415 | /* Step 4: fix up any arguments. | |
1416 | * "argument conflict" returned by comedilib to user mode process | |
1417 | * if this fails. */ | |
1418 | ||
1419 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1420 | tmp = cmd->scan_begin_arg; | |
1421 | pci230_ns_to_single_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1422 | cmd->flags & TRIG_ROUND_MASK); |
70a350c3 AW |
1423 | if (tmp != cmd->scan_begin_arg) |
1424 | err++; | |
1425 | } | |
1426 | ||
1427 | if (err) | |
1428 | return 4; | |
1429 | ||
1430 | /* Step 5: check channel list if it exists. */ | |
1431 | ||
1432 | if (cmd->chanlist && cmd->chanlist_len > 0) { | |
1433 | enum { | |
1434 | seq_err = (1 << 0), | |
1435 | range_err = (1 << 1) | |
1436 | }; | |
1437 | unsigned int errors; | |
1438 | unsigned int n; | |
1439 | unsigned int chan, prev_chan; | |
1440 | unsigned int range, first_range; | |
1441 | ||
1442 | prev_chan = CR_CHAN(cmd->chanlist[0]); | |
1443 | first_range = CR_RANGE(cmd->chanlist[0]); | |
1444 | errors = 0; | |
1445 | for (n = 1; n < cmd->chanlist_len; n++) { | |
1446 | chan = CR_CHAN(cmd->chanlist[n]); | |
1447 | range = CR_RANGE(cmd->chanlist[n]); | |
1448 | /* Channel numbers must strictly increase. */ | |
25f1a98b | 1449 | if (chan < prev_chan) |
70a350c3 | 1450 | errors |= seq_err; |
25f1a98b | 1451 | |
70a350c3 | 1452 | /* Ranges must be the same. */ |
25f1a98b | 1453 | if (range != first_range) |
70a350c3 | 1454 | errors |= range_err; |
25f1a98b | 1455 | |
70a350c3 AW |
1456 | prev_chan = chan; |
1457 | } | |
1458 | if (errors != 0) { | |
1459 | err++; | |
1460 | if ((errors & seq_err) != 0) { | |
1461 | DPRINTK("comedi%d: amplc_pci230: ao_cmdtest: " | |
1462 | "channel numbers must increase\n", | |
1463 | dev->minor); | |
1464 | } | |
1465 | if ((errors & range_err) != 0) { | |
1466 | DPRINTK("comedi%d: amplc_pci230: ao_cmdtest: " | |
1467 | "channels must have the same range\n", | |
1468 | dev->minor); | |
1469 | } | |
1470 | } | |
1471 | } | |
1472 | ||
1473 | if (err) | |
1474 | return 5; | |
1475 | ||
1476 | return 0; | |
1477 | } | |
1478 | ||
da91b269 | 1479 | static int pci230_ao_inttrig_scan_begin(struct comedi_device *dev, |
0a85b6f0 MT |
1480 | struct comedi_subdevice *s, |
1481 | unsigned int trig_num) | |
70a350c3 AW |
1482 | { |
1483 | unsigned long irqflags; | |
1484 | ||
1485 | if (trig_num != 0) | |
1486 | return -EINVAL; | |
1487 | ||
5f74ea14 | 1488 | spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags); |
70a350c3 AW |
1489 | if (test_bit(AO_CMD_STARTED, &devpriv->state)) { |
1490 | /* Perform scan. */ | |
1491 | if (devpriv->hwver < 2) { | |
1492 | /* Not using DAC FIFO. */ | |
0a85b6f0 MT |
1493 | spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, |
1494 | irqflags); | |
70a350c3 AW |
1495 | pci230_handle_ao_nofifo(dev, s); |
1496 | comedi_event(dev, s); | |
1497 | } else { | |
1498 | /* Using DAC FIFO. */ | |
1499 | /* Read DACSWTRIG register to trigger conversion. */ | |
1500 | inw(dev->iobase + PCI230P2_DACSWTRIG); | |
0a85b6f0 MT |
1501 | spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, |
1502 | irqflags); | |
70a350c3 AW |
1503 | } |
1504 | /* Delay. Should driver be responsible for this? */ | |
1505 | /* XXX TODO: See if DAC busy bit can be used. */ | |
5f74ea14 | 1506 | udelay(8); |
70a350c3 AW |
1507 | } |
1508 | ||
1509 | return 1; | |
1510 | } | |
1511 | ||
0a85b6f0 MT |
1512 | static void pci230_ao_start(struct comedi_device *dev, |
1513 | struct comedi_subdevice *s) | |
70a350c3 | 1514 | { |
d163679c | 1515 | struct comedi_async *async = s->async; |
ea6d0d4c | 1516 | struct comedi_cmd *cmd = &async->cmd; |
70a350c3 AW |
1517 | unsigned long irqflags; |
1518 | ||
1519 | set_bit(AO_CMD_STARTED, &devpriv->state); | |
1520 | if (!devpriv->ao_continuous && (devpriv->ao_scan_count == 0)) { | |
1521 | /* An empty acquisition! */ | |
1522 | async->events |= COMEDI_CB_EOA; | |
1523 | pci230_ao_stop(dev, s); | |
1524 | comedi_event(dev, s); | |
1525 | } else { | |
1526 | if (devpriv->hwver >= 2) { | |
1527 | /* Using DAC FIFO. */ | |
1528 | unsigned short scantrig; | |
1529 | int run; | |
1530 | ||
1531 | /* Preload FIFO data. */ | |
1532 | run = pci230_handle_ao_fifo(dev, s); | |
1533 | comedi_event(dev, s); | |
1534 | if (!run) { | |
1535 | /* Stopped. */ | |
1536 | return; | |
1537 | } | |
1538 | /* Set scan trigger source. */ | |
1539 | switch (cmd->scan_begin_src) { | |
1540 | case TRIG_TIMER: | |
1541 | scantrig = PCI230P2_DAC_TRIG_Z2CT1; | |
1542 | break; | |
1543 | case TRIG_EXT: | |
1544 | /* Trigger on EXTTRIG/EXTCONVCLK pin. */ | |
1545 | if ((cmd->scan_begin_arg & CR_INVERT) == 0) { | |
1546 | /* +ve edge */ | |
1547 | scantrig = PCI230P2_DAC_TRIG_EXTP; | |
1548 | } else { | |
1549 | /* -ve edge */ | |
1550 | scantrig = PCI230P2_DAC_TRIG_EXTN; | |
1551 | } | |
1552 | break; | |
1553 | case TRIG_INT: | |
1554 | scantrig = PCI230P2_DAC_TRIG_SW; | |
1555 | break; | |
1556 | default: | |
1557 | /* Shouldn't get here. */ | |
1558 | scantrig = PCI230P2_DAC_TRIG_NONE; | |
1559 | break; | |
1560 | } | |
1561 | devpriv->daccon = (devpriv->daccon | |
0a85b6f0 MT |
1562 | & ~PCI230P2_DAC_TRIG_MASK) | |
1563 | scantrig; | |
70a350c3 AW |
1564 | outw(devpriv->daccon, dev->iobase + PCI230_DACCON); |
1565 | ||
1566 | } | |
1567 | switch (cmd->scan_begin_src) { | |
1568 | case TRIG_TIMER: | |
1569 | if (devpriv->hwver < 2) { | |
1570 | /* Not using DAC FIFO. */ | |
1571 | /* Enable CT1 timer interrupt. */ | |
5f74ea14 | 1572 | spin_lock_irqsave(&devpriv->isr_spinlock, |
0a85b6f0 | 1573 | irqflags); |
70a350c3 AW |
1574 | devpriv->int_en |= PCI230_INT_ZCLK_CT1; |
1575 | devpriv->ier |= PCI230_INT_ZCLK_CT1; | |
1576 | outb(devpriv->ier, | |
0a85b6f0 MT |
1577 | devpriv->iobase1 + PCI230_INT_SCE); |
1578 | spin_unlock_irqrestore(&devpriv->isr_spinlock, | |
1579 | irqflags); | |
70a350c3 AW |
1580 | } |
1581 | /* Set CT1 gate high to start counting. */ | |
1582 | outb(GAT_CONFIG(1, GAT_VCC), | |
0a85b6f0 | 1583 | devpriv->iobase1 + PCI230_ZGAT_SCE); |
70a350c3 AW |
1584 | break; |
1585 | case TRIG_INT: | |
1586 | async->inttrig = pci230_ao_inttrig_scan_begin; | |
1587 | break; | |
1588 | } | |
1589 | if (devpriv->hwver >= 2) { | |
1590 | /* Using DAC FIFO. Enable DAC FIFO interrupt. */ | |
0a85b6f0 | 1591 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
1592 | devpriv->int_en |= PCI230P2_INT_DAC; |
1593 | devpriv->ier |= PCI230P2_INT_DAC; | |
1594 | outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE); | |
5f74ea14 | 1595 | spin_unlock_irqrestore(&devpriv->isr_spinlock, |
0a85b6f0 | 1596 | irqflags); |
70a350c3 AW |
1597 | } |
1598 | } | |
1599 | } | |
1600 | ||
0a85b6f0 MT |
1601 | static int pci230_ao_inttrig_start(struct comedi_device *dev, |
1602 | struct comedi_subdevice *s, | |
1603 | unsigned int trig_num) | |
70a350c3 AW |
1604 | { |
1605 | if (trig_num != 0) | |
1606 | return -EINVAL; | |
1607 | ||
1608 | s->async->inttrig = NULLFUNC; | |
1609 | pci230_ao_start(dev, s); | |
1610 | ||
1611 | return 1; | |
1612 | } | |
1613 | ||
da91b269 | 1614 | static int pci230_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
70a350c3 AW |
1615 | { |
1616 | unsigned short daccon; | |
1617 | unsigned int range; | |
1618 | ||
1619 | /* Get the command. */ | |
ea6d0d4c | 1620 | struct comedi_cmd *cmd = &s->async->cmd; |
70a350c3 AW |
1621 | |
1622 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1623 | /* Claim Z2-CT1. */ | |
25f1a98b | 1624 | if (!get_one_resource(dev, RES_Z2CT1, OWNER_AOCMD)) |
70a350c3 | 1625 | return -EBUSY; |
25f1a98b | 1626 | |
70a350c3 AW |
1627 | } |
1628 | ||
1629 | /* Get number of scans required. */ | |
1630 | if (cmd->stop_src == TRIG_COUNT) { | |
1631 | devpriv->ao_scan_count = cmd->stop_arg; | |
1632 | devpriv->ao_continuous = 0; | |
1633 | } else { | |
1634 | /* TRIG_NONE, user calls cancel. */ | |
1635 | devpriv->ao_scan_count = 0; | |
1636 | devpriv->ao_continuous = 1; | |
1637 | } | |
1638 | ||
1639 | /* Set range - see analogue output range table; 0 => unipolar 10V, | |
1640 | * 1 => bipolar +/-10V range scale */ | |
1641 | range = CR_RANGE(cmd->chanlist[0]); | |
1642 | devpriv->ao_bipolar = pci230_ao_bipolar[range]; | |
1643 | daccon = devpriv->ao_bipolar ? PCI230_DAC_OR_BIP : PCI230_DAC_OR_UNI; | |
1644 | /* Use DAC FIFO for hardware version 2 onwards. */ | |
1645 | if (devpriv->hwver >= 2) { | |
1646 | unsigned short dacen; | |
1647 | unsigned int i; | |
1648 | ||
1649 | dacen = 0; | |
25f1a98b | 1650 | for (i = 0; i < cmd->chanlist_len; i++) |
70a350c3 | 1651 | dacen |= 1 << CR_CHAN(cmd->chanlist[i]); |
25f1a98b | 1652 | |
70a350c3 AW |
1653 | /* Set channel scan list. */ |
1654 | outw(dacen, dev->iobase + PCI230P2_DACEN); | |
1655 | /* | |
1656 | * Enable DAC FIFO. | |
1657 | * Set DAC scan source to 'none'. | |
1658 | * Set DAC FIFO interrupt trigger level to 'not half full'. | |
1659 | * Reset DAC FIFO and clear underrun. | |
1660 | * | |
1661 | * N.B. DAC FIFO interrupts are currently disabled. | |
1662 | */ | |
1663 | daccon |= PCI230P2_DAC_FIFO_EN | PCI230P2_DAC_FIFO_RESET | |
0a85b6f0 MT |
1664 | | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR |
1665 | | PCI230P2_DAC_TRIG_NONE | PCI230P2_DAC_INT_FIFO_NHALF; | |
70a350c3 AW |
1666 | } |
1667 | ||
1668 | /* Set DACCON. */ | |
1669 | outw(daccon, dev->iobase + PCI230_DACCON); | |
1670 | /* Preserve most of DACCON apart from write-only, transient bits. */ | |
1671 | devpriv->daccon = daccon | |
0a85b6f0 | 1672 | & ~(PCI230P2_DAC_FIFO_RESET | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR); |
70a350c3 AW |
1673 | |
1674 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1675 | /* Set the counter timer 1 to the specified scan frequency. */ | |
1676 | /* cmd->scan_begin_arg is sampling period in ns */ | |
1677 | /* gate it off for now. */ | |
1678 | outb(GAT_CONFIG(1, GAT_GND), | |
0a85b6f0 | 1679 | devpriv->iobase1 + PCI230_ZGAT_SCE); |
70a350c3 | 1680 | pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3, |
0a85b6f0 MT |
1681 | cmd->scan_begin_arg, |
1682 | cmd->flags & TRIG_ROUND_MASK); | |
70a350c3 AW |
1683 | } |
1684 | ||
1685 | /* N.B. cmd->start_src == TRIG_INT */ | |
1686 | s->async->inttrig = pci230_ao_inttrig_start; | |
1687 | ||
1688 | return 0; | |
1689 | } | |
1690 | ||
da91b269 | 1691 | static int pci230_ai_check_scan_period(struct comedi_cmd *cmd) |
70a350c3 AW |
1692 | { |
1693 | unsigned int min_scan_period, chanlist_len; | |
1694 | int err = 0; | |
1695 | ||
1696 | chanlist_len = cmd->chanlist_len; | |
25f1a98b | 1697 | if (cmd->chanlist_len == 0) |
70a350c3 | 1698 | chanlist_len = 1; |
25f1a98b | 1699 | |
70a350c3 AW |
1700 | min_scan_period = chanlist_len * cmd->convert_arg; |
1701 | if ((min_scan_period < chanlist_len) | |
0a85b6f0 | 1702 | || (min_scan_period < cmd->convert_arg)) { |
70a350c3 AW |
1703 | /* Arithmetic overflow. */ |
1704 | min_scan_period = UINT_MAX; | |
1705 | err++; | |
1706 | } | |
1707 | if (cmd->scan_begin_arg < min_scan_period) { | |
1708 | cmd->scan_begin_arg = min_scan_period; | |
1709 | err++; | |
1710 | } | |
1711 | ||
1712 | return !err; | |
1713 | } | |
1714 | ||
0a85b6f0 MT |
1715 | static int pci230_ai_cmdtest(struct comedi_device *dev, |
1716 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
70a350c3 AW |
1717 | { |
1718 | int err = 0; | |
1719 | unsigned int tmp; | |
1720 | ||
1721 | /* cmdtest tests a particular command to see if it is valid. | |
1722 | * Using the cmdtest ioctl, a user can create a valid cmd | |
1723 | * and then have it executes by the cmd ioctl. | |
1724 | * | |
1725 | * cmdtest returns 1,2,3,4,5 or 0, depending on which tests | |
1726 | * the command passes. */ | |
1727 | ||
1728 | /* Step 1: make sure trigger sources are trivially valid. | |
1729 | * "invalid source" returned by comedilib to user mode process | |
1730 | * if this fails. */ | |
1731 | ||
1732 | tmp = cmd->start_src; | |
1733 | cmd->start_src &= TRIG_NOW | TRIG_INT; | |
1734 | if (!cmd->start_src || tmp != cmd->start_src) | |
1735 | err++; | |
1736 | ||
1737 | tmp = cmd->scan_begin_src; | |
1738 | /* Unfortunately, we cannot trigger a scan off an external source | |
1739 | * on the PCI260 board, since it uses the PPIC0 (DIO) input, which | |
1740 | * isn't present on the PCI260. For PCI260+ we can use the | |
1741 | * EXTTRIG/EXTCONVCLK input on pin 17 instead. */ | |
1742 | if ((thisboard->have_dio) || (thisboard->min_hwver > 0)) { | |
1743 | cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_INT | |
0a85b6f0 | 1744 | | TRIG_EXT; |
70a350c3 AW |
1745 | } else { |
1746 | cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_INT; | |
1747 | } | |
1748 | if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) | |
1749 | err++; | |
1750 | ||
1751 | tmp = cmd->convert_src; | |
1752 | cmd->convert_src &= TRIG_TIMER | TRIG_INT | TRIG_EXT; | |
1753 | if (!cmd->convert_src || tmp != cmd->convert_src) | |
1754 | err++; | |
1755 | ||
1756 | tmp = cmd->scan_end_src; | |
1757 | cmd->scan_end_src &= TRIG_COUNT; | |
1758 | if (!cmd->scan_end_src || tmp != cmd->scan_end_src) | |
1759 | err++; | |
1760 | ||
1761 | tmp = cmd->stop_src; | |
1762 | cmd->stop_src &= TRIG_COUNT | TRIG_NONE; | |
1763 | if (!cmd->stop_src || tmp != cmd->stop_src) | |
1764 | err++; | |
1765 | ||
1766 | if (err) | |
1767 | return 1; | |
1768 | ||
1769 | /* Step 2: make sure trigger sources are unique and mutually compatible | |
1770 | * "source conflict" returned by comedilib to user mode process | |
1771 | * if this fails. */ | |
1772 | ||
1773 | /* these tests are true if more than one _src bit is set */ | |
1774 | if ((cmd->start_src & (cmd->start_src - 1)) != 0) | |
1775 | err++; | |
1776 | if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0) | |
1777 | err++; | |
1778 | if ((cmd->convert_src & (cmd->convert_src - 1)) != 0) | |
1779 | err++; | |
1780 | if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0) | |
1781 | err++; | |
1782 | if ((cmd->stop_src & (cmd->stop_src - 1)) != 0) | |
1783 | err++; | |
1784 | ||
1785 | /* If scan_begin_src is not TRIG_FOLLOW, then a monostable will be | |
1786 | * set up to generate a fixed number of timed conversion pulses. */ | |
1787 | if ((cmd->scan_begin_src != TRIG_FOLLOW) | |
0a85b6f0 | 1788 | && (cmd->convert_src != TRIG_TIMER)) |
70a350c3 AW |
1789 | err++; |
1790 | ||
1791 | if (err) | |
1792 | return 2; | |
1793 | ||
1794 | /* Step 3: make sure arguments are trivially compatible. | |
1795 | * "invalid argument" returned by comedilib to user mode process | |
1796 | * if this fails. */ | |
1797 | ||
1798 | if (cmd->start_arg != 0) { | |
1799 | cmd->start_arg = 0; | |
1800 | err++; | |
1801 | } | |
1802 | #define MAX_SPEED_AI_SE 3200 /* PCI230 SE: 3200 ns => 312.5 kHz */ | |
1803 | #define MAX_SPEED_AI_DIFF 8000 /* PCI230 DIFF: 8000 ns => 125 kHz */ | |
1804 | #define MAX_SPEED_AI_PLUS 4000 /* PCI230+: 4000 ns => 250 kHz */ | |
1805 | #define MIN_SPEED_AI 4294967295u /* 4294967295ns = 4.29s */ | |
1806 | /*- Comedi limit due to unsigned int cmd. Driver limit | |
1807 | * = 2^16 (16bit * counter) * 1000000ns (1kHz onboard | |
1808 | * clock) = 65.536s */ | |
1809 | ||
1810 | if (cmd->convert_src == TRIG_TIMER) { | |
1811 | unsigned int max_speed_ai; | |
1812 | ||
1813 | if (devpriv->hwver == 0) { | |
1814 | /* PCI230 or PCI260. Max speed depends whether | |
1815 | * single-ended or pseudo-differential. */ | |
1816 | if (cmd->chanlist && (cmd->chanlist_len > 0)) { | |
1817 | /* Peek analogue reference of first channel. */ | |
25f1a98b | 1818 | if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) |
70a350c3 | 1819 | max_speed_ai = MAX_SPEED_AI_DIFF; |
25f1a98b | 1820 | else |
70a350c3 | 1821 | max_speed_ai = MAX_SPEED_AI_SE; |
25f1a98b | 1822 | |
70a350c3 AW |
1823 | } else { |
1824 | /* No channel list. Assume single-ended. */ | |
1825 | max_speed_ai = MAX_SPEED_AI_SE; | |
1826 | } | |
1827 | } else { | |
1828 | /* PCI230+ or PCI260+. */ | |
1829 | max_speed_ai = MAX_SPEED_AI_PLUS; | |
1830 | } | |
1831 | ||
1832 | if (cmd->convert_arg < max_speed_ai) { | |
1833 | cmd->convert_arg = max_speed_ai; | |
1834 | err++; | |
1835 | } | |
1836 | if (cmd->convert_arg > MIN_SPEED_AI) { | |
1837 | cmd->convert_arg = MIN_SPEED_AI; | |
1838 | err++; | |
1839 | } | |
1840 | } else if (cmd->convert_src == TRIG_EXT) { | |
1841 | /* | |
1842 | * external trigger | |
1843 | * | |
1844 | * convert_arg == (CR_EDGE | 0) | |
1845 | * => trigger on +ve edge. | |
1846 | * convert_arg == (CR_EDGE | CR_INVERT | 0) | |
1847 | * => trigger on -ve edge. | |
1848 | */ | |
1849 | if ((cmd->convert_arg & CR_FLAGS_MASK) != 0) { | |
1850 | /* Trigger number must be 0. */ | |
1851 | if ((cmd->convert_arg & ~CR_FLAGS_MASK) != 0) { | |
1852 | cmd->convert_arg = COMBINE(cmd->convert_arg, 0, | |
0a85b6f0 | 1853 | ~CR_FLAGS_MASK); |
70a350c3 AW |
1854 | err++; |
1855 | } | |
1856 | /* The only flags allowed are CR_INVERT and CR_EDGE. | |
1857 | * CR_EDGE is required. */ | |
1858 | if ((cmd->convert_arg & (CR_FLAGS_MASK & ~CR_INVERT)) | |
0a85b6f0 | 1859 | != CR_EDGE) { |
70a350c3 AW |
1860 | /* Set CR_EDGE, preserve CR_INVERT. */ |
1861 | cmd->convert_arg = | |
0a85b6f0 MT |
1862 | COMBINE(cmd->start_arg, (CR_EDGE | 0), |
1863 | CR_FLAGS_MASK & ~CR_INVERT); | |
70a350c3 AW |
1864 | err++; |
1865 | } | |
1866 | } else { | |
1867 | /* Backwards compatibility with previous versions. */ | |
1868 | /* convert_arg == 0 => trigger on -ve edge. */ | |
1869 | /* convert_arg == 1 => trigger on +ve edge. */ | |
1870 | if (cmd->convert_arg > 1) { | |
1871 | /* Default to trigger on +ve edge. */ | |
1872 | cmd->convert_arg = 1; | |
1873 | err++; | |
1874 | } | |
1875 | } | |
1876 | } else { | |
1877 | if (cmd->convert_arg != 0) { | |
1878 | cmd->convert_arg = 0; | |
1879 | err++; | |
1880 | } | |
1881 | } | |
1882 | ||
1883 | if (cmd->scan_end_arg != cmd->chanlist_len) { | |
1884 | cmd->scan_end_arg = cmd->chanlist_len; | |
1885 | err++; | |
1886 | } | |
1887 | ||
1888 | if (cmd->stop_src == TRIG_NONE) { | |
1889 | if (cmd->stop_arg != 0) { | |
1890 | cmd->stop_arg = 0; | |
1891 | err++; | |
1892 | } | |
1893 | } | |
1894 | ||
1895 | if (cmd->scan_begin_src == TRIG_EXT) { | |
1896 | /* external "trigger" to begin each scan | |
1897 | * scan_begin_arg==0 => use PPC0 input -> gate of CT0 -> gate | |
1898 | * of CT2 (sample convert trigger is CT2) */ | |
1899 | if ((cmd->scan_begin_arg & ~CR_FLAGS_MASK) != 0) { | |
1900 | cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0, | |
0a85b6f0 | 1901 | ~CR_FLAGS_MASK); |
70a350c3 AW |
1902 | err++; |
1903 | } | |
1904 | /* The only flag allowed is CR_EDGE, which is ignored. */ | |
1905 | if ((cmd->scan_begin_arg & CR_FLAGS_MASK & ~CR_EDGE) != 0) { | |
1906 | cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0, | |
0a85b6f0 | 1907 | CR_FLAGS_MASK & ~CR_EDGE); |
70a350c3 AW |
1908 | err++; |
1909 | } | |
1910 | } else if (cmd->scan_begin_src == TRIG_TIMER) { | |
1911 | /* N.B. cmd->convert_arg is also TRIG_TIMER */ | |
25f1a98b | 1912 | if (!pci230_ai_check_scan_period(cmd)) |
70a350c3 | 1913 | err++; |
25f1a98b | 1914 | |
70a350c3 AW |
1915 | } else { |
1916 | if (cmd->scan_begin_arg != 0) { | |
1917 | cmd->scan_begin_arg = 0; | |
1918 | err++; | |
1919 | } | |
1920 | } | |
1921 | ||
1922 | if (err) | |
1923 | return 3; | |
1924 | ||
1925 | /* Step 4: fix up any arguments. | |
1926 | * "argument conflict" returned by comedilib to user mode process | |
1927 | * if this fails. */ | |
1928 | ||
1929 | if (cmd->convert_src == TRIG_TIMER) { | |
1930 | tmp = cmd->convert_arg; | |
1931 | pci230_ns_to_single_timer(&cmd->convert_arg, | |
0a85b6f0 | 1932 | cmd->flags & TRIG_ROUND_MASK); |
70a350c3 AW |
1933 | if (tmp != cmd->convert_arg) |
1934 | err++; | |
1935 | } | |
1936 | ||
1937 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1938 | /* N.B. cmd->convert_arg is also TRIG_TIMER */ | |
1939 | tmp = cmd->scan_begin_arg; | |
1940 | pci230_ns_to_single_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1941 | cmd->flags & TRIG_ROUND_MASK); |
70a350c3 AW |
1942 | if (!pci230_ai_check_scan_period(cmd)) { |
1943 | /* Was below minimum required. Round up. */ | |
1944 | pci230_ns_to_single_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1945 | TRIG_ROUND_UP); |
70a350c3 AW |
1946 | pci230_ai_check_scan_period(cmd); |
1947 | } | |
1948 | if (tmp != cmd->scan_begin_arg) | |
1949 | err++; | |
1950 | } | |
1951 | ||
1952 | if (err) | |
1953 | return 4; | |
1954 | ||
1955 | /* Step 5: check channel list if it exists. */ | |
1956 | ||
1957 | if (cmd->chanlist && cmd->chanlist_len > 0) { | |
1958 | enum { | |
1959 | seq_err = 1 << 0, | |
1960 | rangepair_err = 1 << 1, | |
1961 | polarity_err = 1 << 2, | |
1962 | aref_err = 1 << 3, | |
1963 | diffchan_err = 1 << 4, | |
1964 | buggy_chan0_err = 1 << 5 | |
1965 | }; | |
1966 | unsigned int errors; | |
1967 | unsigned int chan, prev_chan; | |
1968 | unsigned int range, prev_range; | |
1969 | unsigned int polarity, prev_polarity; | |
1970 | unsigned int aref, prev_aref; | |
1971 | unsigned int subseq_len; | |
1972 | unsigned int n; | |
1973 | ||
1974 | subseq_len = 0; | |
1975 | errors = 0; | |
1976 | prev_chan = prev_aref = prev_range = prev_polarity = 0; | |
1977 | for (n = 0; n < cmd->chanlist_len; n++) { | |
1978 | chan = CR_CHAN(cmd->chanlist[n]); | |
1979 | range = CR_RANGE(cmd->chanlist[n]); | |
1980 | aref = CR_AREF(cmd->chanlist[n]); | |
1981 | polarity = pci230_ai_bipolar[range]; | |
1982 | /* Only the first half of the channels are available if | |
1983 | * differential. (These are remapped in software. In | |
1984 | * hardware, only the even channels are available.) */ | |
1985 | if ((aref == AREF_DIFF) | |
0a85b6f0 | 1986 | && (chan >= (s->n_chan / 2))) { |
70a350c3 AW |
1987 | errors |= diffchan_err; |
1988 | } | |
1989 | if (n > 0) { | |
1990 | /* Channel numbers must strictly increase or | |
1991 | * subsequence must repeat exactly. */ | |
1992 | if ((chan <= prev_chan) | |
0a85b6f0 | 1993 | && (subseq_len == 0)) { |
70a350c3 AW |
1994 | subseq_len = n; |
1995 | } | |
1996 | if ((subseq_len > 0) | |
0a85b6f0 MT |
1997 | && (cmd->chanlist[n] != |
1998 | cmd->chanlist[n % subseq_len])) { | |
70a350c3 AW |
1999 | errors |= seq_err; |
2000 | } | |
2001 | /* Channels must have same AREF. */ | |
25f1a98b | 2002 | if (aref != prev_aref) |
70a350c3 | 2003 | errors |= aref_err; |
25f1a98b | 2004 | |
70a350c3 | 2005 | /* Channel ranges must have same polarity. */ |
25f1a98b | 2006 | if (polarity != prev_polarity) |
70a350c3 | 2007 | errors |= polarity_err; |
25f1a98b | 2008 | |
70a350c3 AW |
2009 | /* Single-ended channel pairs must have same |
2010 | * range. */ | |
2011 | if ((aref != AREF_DIFF) | |
0a85b6f0 MT |
2012 | && (((chan ^ prev_chan) & ~1) == 0) |
2013 | && (range != prev_range)) { | |
70a350c3 AW |
2014 | errors |= rangepair_err; |
2015 | } | |
2016 | } | |
2017 | prev_chan = chan; | |
2018 | prev_range = range; | |
2019 | prev_aref = aref; | |
2020 | prev_polarity = polarity; | |
2021 | } | |
2022 | if (subseq_len == 0) { | |
2023 | /* Subsequence is whole sequence. */ | |
2024 | subseq_len = n; | |
2025 | } | |
2026 | /* If channel list is a repeating subsequence, need a whole | |
2027 | * number of repeats. */ | |
25f1a98b | 2028 | if ((n % subseq_len) != 0) |
70a350c3 | 2029 | errors |= seq_err; |
25f1a98b | 2030 | |
70a350c3 AW |
2031 | if ((devpriv->hwver > 0) && (devpriv->hwver < 4)) { |
2032 | /* | |
2033 | * Buggy PCI230+ or PCI260+ requires channel 0 to be | |
2034 | * (first) in the sequence if the sequence contains | |
2035 | * more than one channel. Hardware versions 1 and 2 | |
2036 | * have the bug. There is no hardware version 3. | |
2037 | * | |
2038 | * Actually, there are two firmwares that report | |
2039 | * themselves as hardware version 1 (the boards | |
2040 | * have different ADC chips with slightly different | |
2041 | * timing requirements, which was supposed to be | |
2042 | * invisible to software). The first one doesn't | |
2043 | * seem to have the bug, but the second one | |
2044 | * does, and we can't tell them apart! | |
2045 | */ | |
2046 | if ((subseq_len > 1) | |
0a85b6f0 | 2047 | && (CR_CHAN(cmd->chanlist[0]) != 0)) { |
70a350c3 AW |
2048 | errors |= buggy_chan0_err; |
2049 | } | |
2050 | } | |
2051 | if (errors != 0) { | |
2052 | err++; | |
2053 | if ((errors & seq_err) != 0) { | |
2054 | DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: " | |
2055 | "channel numbers must increase or " | |
2056 | "sequence must repeat exactly\n", | |
2057 | dev->minor); | |
2058 | } | |
2059 | if ((errors & rangepair_err) != 0) { | |
2060 | DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: " | |
2061 | "single-ended channel pairs must " | |
2062 | "have the same range\n", dev->minor); | |
2063 | } | |
2064 | if ((errors & polarity_err) != 0) { | |
2065 | DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: " | |
2066 | "channel sequence ranges must be all " | |
2067 | "bipolar or all unipolar\n", | |
2068 | dev->minor); | |
2069 | } | |
2070 | if ((errors & aref_err) != 0) { | |
2071 | DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: " | |
2072 | "channel sequence analogue references " | |
2073 | "must be all the same (single-ended " | |
2074 | "or differential)\n", dev->minor); | |
2075 | } | |
2076 | if ((errors & diffchan_err) != 0) { | |
2077 | DPRINTK("comedi%d: amplc_pci230: ai_cmdtest: " | |
2078 | "differential channel number out of " | |
2079 | "range 0 to %u\n", dev->minor, | |
2080 | (s->n_chan / 2) - 1); | |
2081 | } | |
2082 | if ((errors & buggy_chan0_err) != 0) { | |
2083 | /* Use printk instead of DPRINTK here. */ | |
2084 | printk("comedi: comedi%d: amplc_pci230: " | |
0a85b6f0 MT |
2085 | "ai_cmdtest: Buggy PCI230+/260+ " |
2086 | "h/w version %u requires first channel " | |
2087 | "of multi-channel sequence to be 0 " | |
2088 | "(corrected in h/w version 4)\n", | |
2089 | dev->minor, devpriv->hwver); | |
70a350c3 AW |
2090 | } |
2091 | } | |
2092 | } | |
2093 | ||
2094 | if (err) | |
2095 | return 5; | |
2096 | ||
2097 | return 0; | |
2098 | } | |
2099 | ||
da91b269 | 2100 | static void pci230_ai_update_fifo_trigger_level(struct comedi_device *dev, |
0a85b6f0 | 2101 | struct comedi_subdevice *s) |
70a350c3 | 2102 | { |
ea6d0d4c | 2103 | struct comedi_cmd *cmd = &s->async->cmd; |
70a350c3 AW |
2104 | unsigned int scanlen = cmd->scan_end_arg; |
2105 | unsigned int wake; | |
2106 | unsigned short triglev; | |
2107 | unsigned short adccon; | |
2108 | ||
2109 | if ((cmd->flags & TRIG_WAKE_EOS) != 0) { | |
2110 | /* Wake at end of scan. */ | |
2111 | wake = scanlen - devpriv->ai_scan_pos; | |
2112 | } else { | |
2113 | if (devpriv->ai_continuous | |
0a85b6f0 MT |
2114 | || (devpriv->ai_scan_count >= PCI230_ADC_FIFOLEVEL_HALFFULL) |
2115 | || (scanlen >= PCI230_ADC_FIFOLEVEL_HALFFULL)) { | |
70a350c3 AW |
2116 | wake = PCI230_ADC_FIFOLEVEL_HALFFULL; |
2117 | } else { | |
2118 | wake = (devpriv->ai_scan_count * scanlen) | |
0a85b6f0 | 2119 | - devpriv->ai_scan_pos; |
70a350c3 AW |
2120 | } |
2121 | } | |
2122 | if (wake >= PCI230_ADC_FIFOLEVEL_HALFFULL) { | |
2123 | triglev = PCI230_ADC_INT_FIFO_HALF; | |
2124 | } else { | |
2125 | if ((wake > 1) && (devpriv->hwver > 0)) { | |
2126 | /* PCI230+/260+ programmable FIFO interrupt level. */ | |
2127 | if (devpriv->adcfifothresh != wake) { | |
2128 | devpriv->adcfifothresh = wake; | |
2129 | outw(wake, dev->iobase + PCI230P_ADCFFTH); | |
2130 | } | |
2131 | triglev = PCI230P_ADC_INT_FIFO_THRESH; | |
2132 | } else { | |
2133 | triglev = PCI230_ADC_INT_FIFO_NEMPTY; | |
2134 | } | |
2135 | } | |
2136 | adccon = (devpriv->adccon & ~PCI230_ADC_INT_FIFO_MASK) | triglev; | |
2137 | if (adccon != devpriv->adccon) { | |
2138 | devpriv->adccon = adccon; | |
2139 | outw(adccon, dev->iobase + PCI230_ADCCON); | |
2140 | } | |
2141 | } | |
2142 | ||
0a85b6f0 MT |
2143 | static int pci230_ai_inttrig_convert(struct comedi_device *dev, |
2144 | struct comedi_subdevice *s, | |
2145 | unsigned int trig_num) | |
70a350c3 AW |
2146 | { |
2147 | unsigned long irqflags; | |
2148 | ||
2149 | if (trig_num != 0) | |
2150 | return -EINVAL; | |
2151 | ||
5f74ea14 | 2152 | spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); |
70a350c3 AW |
2153 | if (test_bit(AI_CMD_STARTED, &devpriv->state)) { |
2154 | unsigned int delayus; | |
2155 | ||
2156 | /* Trigger conversion by toggling Z2-CT2 output. Finish | |
2157 | * with output high. */ | |
2158 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, | |
0a85b6f0 | 2159 | I8254_MODE0); |
70a350c3 | 2160 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, |
0a85b6f0 | 2161 | I8254_MODE1); |
70a350c3 AW |
2162 | /* Delay. Should driver be responsible for this? An |
2163 | * alternative would be to wait until conversion is complete, | |
2164 | * but we can't tell when it's complete because the ADC busy | |
2165 | * bit has a different meaning when FIFO enabled (and when | |
2166 | * FIFO not enabled, it only works for software triggers). */ | |
2167 | if (((devpriv->adccon & PCI230_ADC_IM_MASK) | |
0a85b6f0 MT |
2168 | == PCI230_ADC_IM_DIF) |
2169 | && (devpriv->hwver == 0)) { | |
70a350c3 AW |
2170 | /* PCI230/260 in differential mode */ |
2171 | delayus = 8; | |
2172 | } else { | |
2173 | /* single-ended or PCI230+/260+ */ | |
2174 | delayus = 4; | |
2175 | } | |
0a85b6f0 | 2176 | spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags); |
5f74ea14 | 2177 | udelay(delayus); |
70a350c3 | 2178 | } else { |
0a85b6f0 | 2179 | spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags); |
70a350c3 AW |
2180 | } |
2181 | ||
2182 | return 1; | |
2183 | } | |
2184 | ||
da91b269 | 2185 | static int pci230_ai_inttrig_scan_begin(struct comedi_device *dev, |
0a85b6f0 MT |
2186 | struct comedi_subdevice *s, |
2187 | unsigned int trig_num) | |
70a350c3 AW |
2188 | { |
2189 | unsigned long irqflags; | |
2190 | unsigned char zgat; | |
2191 | ||
2192 | if (trig_num != 0) | |
2193 | return -EINVAL; | |
2194 | ||
5f74ea14 | 2195 | spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); |
70a350c3 AW |
2196 | if (test_bit(AI_CMD_STARTED, &devpriv->state)) { |
2197 | /* Trigger scan by waggling CT0 gate source. */ | |
2198 | zgat = GAT_CONFIG(0, GAT_GND); | |
2199 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2200 | zgat = GAT_CONFIG(0, GAT_VCC); | |
2201 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2202 | } | |
5f74ea14 | 2203 | spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags); |
70a350c3 AW |
2204 | |
2205 | return 1; | |
2206 | } | |
2207 | ||
0a85b6f0 MT |
2208 | static void pci230_ai_start(struct comedi_device *dev, |
2209 | struct comedi_subdevice *s) | |
70a350c3 AW |
2210 | { |
2211 | unsigned long irqflags; | |
2212 | unsigned short conv; | |
d163679c | 2213 | struct comedi_async *async = s->async; |
ea6d0d4c | 2214 | struct comedi_cmd *cmd = &async->cmd; |
70a350c3 AW |
2215 | |
2216 | set_bit(AI_CMD_STARTED, &devpriv->state); | |
2217 | if (!devpriv->ai_continuous && (devpriv->ai_scan_count == 0)) { | |
2218 | /* An empty acquisition! */ | |
2219 | async->events |= COMEDI_CB_EOA; | |
2220 | pci230_ai_stop(dev, s); | |
2221 | comedi_event(dev, s); | |
2222 | } else { | |
2223 | /* Enable ADC FIFO trigger level interrupt. */ | |
5f74ea14 | 2224 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2225 | devpriv->int_en |= PCI230_INT_ADC; |
2226 | devpriv->ier |= PCI230_INT_ADC; | |
2227 | outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE); | |
5f74ea14 | 2228 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2229 | |
2230 | /* Update conversion trigger source which is currently set | |
2231 | * to CT2 output, which is currently stuck high. */ | |
2232 | switch (cmd->convert_src) { | |
2233 | default: | |
2234 | conv = PCI230_ADC_TRIG_NONE; | |
2235 | break; | |
2236 | case TRIG_TIMER: | |
2237 | /* Using CT2 output. */ | |
2238 | conv = PCI230_ADC_TRIG_Z2CT2; | |
2239 | break; | |
2240 | case TRIG_EXT: | |
2241 | if ((cmd->convert_arg & CR_EDGE) != 0) { | |
2242 | if ((cmd->convert_arg & CR_INVERT) == 0) { | |
2243 | /* Trigger on +ve edge. */ | |
2244 | conv = PCI230_ADC_TRIG_EXTP; | |
2245 | } else { | |
2246 | /* Trigger on -ve edge. */ | |
2247 | conv = PCI230_ADC_TRIG_EXTN; | |
2248 | } | |
2249 | } else { | |
2250 | /* Backwards compatibility. */ | |
2251 | if (cmd->convert_arg != 0) { | |
2252 | /* Trigger on +ve edge. */ | |
2253 | conv = PCI230_ADC_TRIG_EXTP; | |
2254 | } else { | |
2255 | /* Trigger on -ve edge. */ | |
2256 | conv = PCI230_ADC_TRIG_EXTN; | |
2257 | } | |
2258 | } | |
2259 | break; | |
2260 | case TRIG_INT: | |
2261 | /* Use CT2 output for software trigger due to problems | |
2262 | * in differential mode on PCI230/260. */ | |
2263 | conv = PCI230_ADC_TRIG_Z2CT2; | |
2264 | break; | |
2265 | } | |
2266 | devpriv->adccon = (devpriv->adccon & ~PCI230_ADC_TRIG_MASK) | |
0a85b6f0 | 2267 | | conv; |
70a350c3 | 2268 | outw(devpriv->adccon, dev->iobase + PCI230_ADCCON); |
25f1a98b | 2269 | if (cmd->convert_src == TRIG_INT) |
70a350c3 | 2270 | async->inttrig = pci230_ai_inttrig_convert; |
25f1a98b | 2271 | |
70a350c3 AW |
2272 | /* Update FIFO interrupt trigger level, which is currently |
2273 | * set to "full". */ | |
2274 | pci230_ai_update_fifo_trigger_level(dev, s); | |
2275 | if (cmd->convert_src == TRIG_TIMER) { | |
2276 | /* Update timer gates. */ | |
2277 | unsigned char zgat; | |
2278 | ||
2279 | if (cmd->scan_begin_src != TRIG_FOLLOW) { | |
2280 | /* Conversion timer CT2 needs to be gated by | |
2281 | * inverted output of monostable CT2. */ | |
2282 | zgat = GAT_CONFIG(2, GAT_NOUTNM2); | |
2283 | } else { | |
2284 | /* Conversion timer CT2 needs to be gated on | |
2285 | * continuously. */ | |
2286 | zgat = GAT_CONFIG(2, GAT_VCC); | |
2287 | } | |
2288 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2289 | if (cmd->scan_begin_src != TRIG_FOLLOW) { | |
2290 | /* Set monostable CT0 trigger source. */ | |
2291 | switch (cmd->scan_begin_src) { | |
2292 | default: | |
2293 | zgat = GAT_CONFIG(0, GAT_VCC); | |
2294 | break; | |
2295 | case TRIG_EXT: | |
2296 | /* | |
2297 | * For CT0 on PCI230, the external | |
2298 | * trigger (gate) signal comes from | |
2299 | * PPC0, which is channel 16 of the DIO | |
2300 | * subdevice. The application needs to | |
2301 | * configure this as an input in order | |
2302 | * to use it as an external scan | |
2303 | * trigger. | |
2304 | */ | |
2305 | zgat = GAT_CONFIG(0, GAT_EXT); | |
2306 | break; | |
2307 | case TRIG_TIMER: | |
2308 | /* | |
2309 | * Monostable CT0 triggered by rising | |
2310 | * edge on inverted output of CT1 | |
2311 | * (falling edge on CT1). | |
2312 | */ | |
2313 | zgat = GAT_CONFIG(0, GAT_NOUTNM2); | |
2314 | break; | |
2315 | case TRIG_INT: | |
2316 | /* | |
2317 | * Monostable CT0 is triggered by | |
2318 | * inttrig function waggling the CT0 | |
2319 | * gate source. | |
2320 | */ | |
2321 | zgat = GAT_CONFIG(0, GAT_VCC); | |
2322 | break; | |
2323 | } | |
2324 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2325 | switch (cmd->scan_begin_src) { | |
2326 | case TRIG_TIMER: | |
2327 | /* Scan period timer CT1 needs to be | |
2328 | * gated on to start counting. */ | |
2329 | zgat = GAT_CONFIG(1, GAT_VCC); | |
2330 | outb(zgat, devpriv->iobase1 | |
0a85b6f0 | 2331 | + PCI230_ZGAT_SCE); |
70a350c3 AW |
2332 | break; |
2333 | case TRIG_INT: | |
2334 | async->inttrig = | |
0a85b6f0 | 2335 | pci230_ai_inttrig_scan_begin; |
70a350c3 AW |
2336 | break; |
2337 | } | |
2338 | } | |
2339 | } else if (cmd->convert_src != TRIG_INT) { | |
2340 | /* No longer need Z2-CT2. */ | |
2341 | put_one_resource(dev, RES_Z2CT2, OWNER_AICMD); | |
2342 | } | |
2343 | } | |
2344 | } | |
2345 | ||
0a85b6f0 MT |
2346 | static int pci230_ai_inttrig_start(struct comedi_device *dev, |
2347 | struct comedi_subdevice *s, | |
2348 | unsigned int trig_num) | |
70a350c3 AW |
2349 | { |
2350 | if (trig_num != 0) | |
2351 | return -EINVAL; | |
2352 | ||
2353 | s->async->inttrig = NULLFUNC; | |
2354 | pci230_ai_start(dev, s); | |
2355 | ||
2356 | return 1; | |
2357 | } | |
2358 | ||
da91b269 | 2359 | static int pci230_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
70a350c3 AW |
2360 | { |
2361 | unsigned int i, chan, range, diff; | |
2362 | unsigned int res_mask; | |
2363 | unsigned short adccon, adcen; | |
2364 | unsigned char zgat; | |
2365 | ||
2366 | /* Get the command. */ | |
d163679c | 2367 | struct comedi_async *async = s->async; |
ea6d0d4c | 2368 | struct comedi_cmd *cmd = &async->cmd; |
70a350c3 AW |
2369 | |
2370 | /* | |
2371 | * Determine which shared resources are needed. | |
2372 | */ | |
2373 | res_mask = 0; | |
2374 | /* Need Z2-CT2 to supply a conversion trigger source at a high | |
2375 | * logic level, even if not doing timed conversions. */ | |
2376 | res_mask |= (1U << RES_Z2CT2); | |
2377 | if (cmd->scan_begin_src != TRIG_FOLLOW) { | |
2378 | /* Using Z2-CT0 monostable to gate Z2-CT2 conversion timer */ | |
2379 | res_mask |= (1U << RES_Z2CT0); | |
2380 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
2381 | /* Using Z2-CT1 for scan frequency */ | |
2382 | res_mask |= (1U << RES_Z2CT1); | |
2383 | } | |
2384 | } | |
2385 | /* Claim resources. */ | |
25f1a98b | 2386 | if (!get_resources(dev, res_mask, OWNER_AICMD)) |
70a350c3 | 2387 | return -EBUSY; |
25f1a98b | 2388 | |
70a350c3 AW |
2389 | |
2390 | /* Get number of scans required. */ | |
2391 | if (cmd->stop_src == TRIG_COUNT) { | |
2392 | devpriv->ai_scan_count = cmd->stop_arg; | |
2393 | devpriv->ai_continuous = 0; | |
2394 | } else { | |
2395 | /* TRIG_NONE, user calls cancel. */ | |
2396 | devpriv->ai_scan_count = 0; | |
2397 | devpriv->ai_continuous = 1; | |
2398 | } | |
2399 | devpriv->ai_scan_pos = 0; /* Position within scan. */ | |
2400 | ||
2401 | /* Steps; | |
2402 | * - Set channel scan list. | |
2403 | * - Set channel gains. | |
2404 | * - Enable and reset FIFO, specify uni/bip, se/diff, and set | |
2405 | * start conversion source to point to something at a high logic | |
2406 | * level (we use the output of counter/timer 2 for this purpose. | |
2407 | * - PAUSE to allow things to settle down. | |
2408 | * - Reset the FIFO again because it needs resetting twice and there | |
2409 | * may have been a false conversion trigger on some versions of | |
2410 | * PCI230/260 due to the start conversion source being set to a | |
2411 | * high logic level. | |
2412 | * - Enable ADC FIFO level interrupt. | |
2413 | * - Set actual conversion trigger source and FIFO interrupt trigger | |
2414 | * level. | |
2415 | * - If convert_src is TRIG_TIMER, set up the timers. | |
2416 | */ | |
2417 | ||
2418 | adccon = PCI230_ADC_FIFO_EN; | |
2419 | adcen = 0; | |
2420 | ||
2421 | if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) { | |
2422 | /* Differential - all channels must be differential. */ | |
2423 | diff = 1; | |
2424 | adccon |= PCI230_ADC_IM_DIF; | |
2425 | } else { | |
2426 | /* Single ended - all channels must be single-ended. */ | |
2427 | diff = 0; | |
2428 | adccon |= PCI230_ADC_IM_SE; | |
2429 | } | |
2430 | ||
2431 | range = CR_RANGE(cmd->chanlist[0]); | |
2432 | devpriv->ai_bipolar = pci230_ai_bipolar[range]; | |
25f1a98b | 2433 | if (devpriv->ai_bipolar) |
70a350c3 | 2434 | adccon |= PCI230_ADC_IR_BIP; |
25f1a98b | 2435 | else |
70a350c3 | 2436 | adccon |= PCI230_ADC_IR_UNI; |
25f1a98b | 2437 | |
70a350c3 AW |
2438 | for (i = 0; i < cmd->chanlist_len; i++) { |
2439 | unsigned int gainshift; | |
2440 | ||
2441 | chan = CR_CHAN(cmd->chanlist[i]); | |
2442 | range = CR_RANGE(cmd->chanlist[i]); | |
2443 | if (diff) { | |
2444 | gainshift = 2 * chan; | |
2445 | if (devpriv->hwver == 0) { | |
2446 | /* Original PCI230/260 expects both inputs of | |
2447 | * the differential channel to be enabled. */ | |
2448 | adcen |= 3 << gainshift; | |
2449 | } else { | |
2450 | /* PCI230+/260+ expects only one input of the | |
2451 | * differential channel to be enabled. */ | |
2452 | adcen |= 1 << gainshift; | |
2453 | } | |
2454 | } else { | |
2455 | gainshift = (chan & ~1); | |
2456 | adcen |= 1 << chan; | |
2457 | } | |
2458 | devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) | |
0a85b6f0 | 2459 | | (pci230_ai_gain[range] << gainshift); |
70a350c3 AW |
2460 | } |
2461 | ||
2462 | /* Set channel scan list. */ | |
2463 | outw(adcen, dev->iobase + PCI230_ADCEN); | |
2464 | ||
2465 | /* Set channel gains. */ | |
2466 | outw(devpriv->adcg, dev->iobase + PCI230_ADCG); | |
2467 | ||
2468 | /* Set counter/timer 2 output high for use as the initial start | |
2469 | * conversion source. */ | |
2470 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, 2, I8254_MODE1); | |
2471 | ||
2472 | /* Temporarily use CT2 output as conversion trigger source and | |
2473 | * temporarily set FIFO interrupt trigger level to 'full'. */ | |
2474 | adccon |= PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_Z2CT2; | |
2475 | ||
2476 | /* Enable and reset FIFO, specify FIFO trigger level full, specify | |
2477 | * uni/bip, se/diff, and temporarily set the start conversion source | |
2478 | * to CT2 output. Note that CT2 output is currently high, and this | |
2479 | * will produce a false conversion trigger on some versions of the | |
2480 | * PCI230/260, but that will be dealt with later. */ | |
2481 | devpriv->adccon = adccon; | |
2482 | outw(adccon | PCI230_ADC_FIFO_RESET, dev->iobase + PCI230_ADCCON); | |
2483 | ||
2484 | /* Delay */ | |
2485 | /* Failure to include this will result in the first few channels'-worth | |
2486 | * of data being corrupt, normally manifesting itself by large negative | |
2487 | * voltages. It seems the board needs time to settle between the first | |
2488 | * FIFO reset (above) and the second FIFO reset (below). Setting the | |
2489 | * channel gains and scan list _before_ the first FIFO reset also | |
2490 | * helps, though only slightly. */ | |
5f74ea14 | 2491 | udelay(25); |
70a350c3 AW |
2492 | |
2493 | /* Reset FIFO again. */ | |
2494 | outw(adccon | PCI230_ADC_FIFO_RESET, dev->iobase + PCI230_ADCCON); | |
2495 | ||
2496 | if (cmd->convert_src == TRIG_TIMER) { | |
2497 | /* Set up CT2 as conversion timer, but gate it off for now. | |
2498 | * Note, counter/timer output 2 can be monitored on the | |
2499 | * connector: PCI230 pin 21, PCI260 pin 18. */ | |
2500 | zgat = GAT_CONFIG(2, GAT_GND); | |
2501 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2502 | /* Set counter/timer 2 to the specified conversion period. */ | |
2503 | pci230_ct_setup_ns_mode(dev, 2, I8254_MODE3, cmd->convert_arg, | |
0a85b6f0 | 2504 | cmd->flags & TRIG_ROUND_MASK); |
70a350c3 AW |
2505 | if (cmd->scan_begin_src != TRIG_FOLLOW) { |
2506 | /* | |
2507 | * Set up monostable on CT0 output for scan timing. A | |
2508 | * rising edge on the trigger (gate) input of CT0 will | |
2509 | * trigger the monostable, causing its output to go low | |
2510 | * for the configured period. The period depends on | |
2511 | * the conversion period and the number of conversions | |
2512 | * in the scan. | |
2513 | * | |
2514 | * Set the trigger high before setting up the | |
2515 | * monostable to stop it triggering. The trigger | |
2516 | * source will be changed later. | |
2517 | */ | |
2518 | zgat = GAT_CONFIG(0, GAT_VCC); | |
2519 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2520 | pci230_ct_setup_ns_mode(dev, 0, I8254_MODE1, | |
0a85b6f0 MT |
2521 | ((uint64_t) cmd->convert_arg |
2522 | * cmd->scan_end_arg), | |
2523 | TRIG_ROUND_UP); | |
70a350c3 AW |
2524 | if (cmd->scan_begin_src == TRIG_TIMER) { |
2525 | /* | |
2526 | * Monostable on CT0 will be triggered by | |
2527 | * output of CT1 at configured scan frequency. | |
2528 | * | |
2529 | * Set up CT1 but gate it off for now. | |
2530 | */ | |
2531 | zgat = GAT_CONFIG(1, GAT_GND); | |
2532 | outb(zgat, devpriv->iobase1 + PCI230_ZGAT_SCE); | |
2533 | pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3, | |
0a85b6f0 MT |
2534 | cmd->scan_begin_arg, |
2535 | cmd-> | |
2536 | flags & | |
2537 | TRIG_ROUND_MASK); | |
70a350c3 AW |
2538 | } |
2539 | } | |
2540 | } | |
2541 | ||
2542 | if (cmd->start_src == TRIG_INT) { | |
2543 | s->async->inttrig = pci230_ai_inttrig_start; | |
2544 | } else { | |
2545 | /* TRIG_NOW */ | |
2546 | pci230_ai_start(dev, s); | |
2547 | } | |
2548 | ||
2549 | return 0; | |
2550 | } | |
2551 | ||
2552 | static unsigned int divide_ns(uint64_t ns, unsigned int timebase, | |
0a85b6f0 | 2553 | unsigned int round_mode) |
70a350c3 AW |
2554 | { |
2555 | uint64_t div; | |
2556 | unsigned int rem; | |
2557 | ||
2558 | div = ns; | |
2559 | rem = do_div(div, timebase); | |
2560 | round_mode &= TRIG_ROUND_MASK; | |
2561 | switch (round_mode) { | |
2562 | default: | |
2563 | case TRIG_ROUND_NEAREST: | |
2564 | div += (rem + (timebase / 2)) / timebase; | |
2565 | break; | |
2566 | case TRIG_ROUND_DOWN: | |
2567 | break; | |
2568 | case TRIG_ROUND_UP: | |
2569 | div += (rem + timebase - 1) / timebase; | |
2570 | break; | |
2571 | } | |
2572 | return div > UINT_MAX ? UINT_MAX : (unsigned int)div; | |
2573 | } | |
2574 | ||
2575 | /* Given desired period in ns, returns the required internal clock source | |
2576 | * and gets the initial count. */ | |
2577 | static unsigned int pci230_choose_clk_count(uint64_t ns, unsigned int *count, | |
0a85b6f0 | 2578 | unsigned int round_mode) |
70a350c3 AW |
2579 | { |
2580 | unsigned int clk_src, cnt; | |
2581 | ||
2582 | for (clk_src = CLK_10MHZ;; clk_src++) { | |
2583 | cnt = divide_ns(ns, pci230_timebase[clk_src], round_mode); | |
25f1a98b | 2584 | if ((cnt <= 65536) || (clk_src == CLK_1KHZ)) |
70a350c3 | 2585 | break; |
25f1a98b | 2586 | |
70a350c3 AW |
2587 | } |
2588 | *count = cnt; | |
2589 | return clk_src; | |
2590 | } | |
2591 | ||
2592 | static void pci230_ns_to_single_timer(unsigned int *ns, unsigned int round) | |
2593 | { | |
2594 | unsigned int count; | |
2595 | unsigned int clk_src; | |
2596 | ||
2597 | clk_src = pci230_choose_clk_count(*ns, &count, round); | |
2598 | *ns = count * pci230_timebase[clk_src]; | |
2599 | return; | |
2600 | } | |
2601 | ||
da91b269 | 2602 | static void pci230_ct_setup_ns_mode(struct comedi_device *dev, unsigned int ct, |
0a85b6f0 MT |
2603 | unsigned int mode, uint64_t ns, |
2604 | unsigned int round) | |
70a350c3 AW |
2605 | { |
2606 | unsigned int clk_src; | |
2607 | unsigned int count; | |
2608 | ||
2609 | /* Set mode. */ | |
2610 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, ct, mode); | |
2611 | /* Determine clock source and count. */ | |
2612 | clk_src = pci230_choose_clk_count(ns, &count, round); | |
2613 | /* Program clock source. */ | |
2614 | outb(CLK_CONFIG(ct, clk_src), devpriv->iobase1 + PCI230_ZCLK_SCE); | |
2615 | /* Set initial count. */ | |
25f1a98b | 2616 | if (count >= 65536) |
70a350c3 | 2617 | count = 0; |
25f1a98b | 2618 | |
70a350c3 AW |
2619 | i8254_write(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, ct, count); |
2620 | } | |
2621 | ||
da91b269 | 2622 | static void pci230_cancel_ct(struct comedi_device *dev, unsigned int ct) |
70a350c3 AW |
2623 | { |
2624 | i8254_set_mode(devpriv->iobase1 + PCI230_Z2_CT_BASE, 0, ct, | |
0a85b6f0 | 2625 | I8254_MODE1); |
70a350c3 AW |
2626 | /* Counter ct, 8254 mode 1, initial count not written. */ |
2627 | } | |
2628 | ||
2629 | /* Interrupt handler */ | |
70265d24 | 2630 | static irqreturn_t pci230_interrupt(int irq, void *d) |
70a350c3 AW |
2631 | { |
2632 | unsigned char status_int, valid_status_int; | |
0a85b6f0 | 2633 | struct comedi_device *dev = (struct comedi_device *)d; |
34c43922 | 2634 | struct comedi_subdevice *s; |
70a350c3 AW |
2635 | unsigned long irqflags; |
2636 | ||
2637 | /* Read interrupt status/enable register. */ | |
2638 | status_int = inb(devpriv->iobase1 + PCI230_INT_STAT); | |
2639 | ||
25f1a98b | 2640 | if (status_int == PCI230_INT_DISABLE) |
70a350c3 | 2641 | return IRQ_NONE; |
25f1a98b | 2642 | |
70a350c3 | 2643 | |
5f74ea14 | 2644 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2645 | valid_status_int = devpriv->int_en & status_int; |
2646 | /* Disable triggered interrupts. | |
2647 | * (Only those interrupts that need re-enabling, are, later in the | |
2648 | * handler). */ | |
2649 | devpriv->ier = devpriv->int_en & ~status_int; | |
2650 | outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE); | |
2651 | devpriv->intr_running = 1; | |
2652 | devpriv->intr_cpuid = THISCPU; | |
5f74ea14 | 2653 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2654 | |
2655 | /* | |
2656 | * Check the source of interrupt and handle it. | |
2657 | * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3 | |
2658 | * interrupts. However, at present (Comedi-0.7.60) does not allow | |
2659 | * concurrent execution of commands, instructions or a mixture of the | |
2660 | * two. | |
2661 | */ | |
2662 | ||
2663 | if ((valid_status_int & PCI230_INT_ZCLK_CT1) != 0) { | |
2664 | s = dev->write_subdev; | |
2665 | pci230_handle_ao_nofifo(dev, s); | |
2666 | comedi_event(dev, s); | |
2667 | } | |
2668 | ||
2669 | if ((valid_status_int & PCI230P2_INT_DAC) != 0) { | |
2670 | s = dev->write_subdev; | |
2671 | pci230_handle_ao_fifo(dev, s); | |
2672 | comedi_event(dev, s); | |
2673 | } | |
2674 | ||
2675 | if ((valid_status_int & PCI230_INT_ADC) != 0) { | |
2676 | s = dev->read_subdev; | |
2677 | pci230_handle_ai(dev, s); | |
2678 | comedi_event(dev, s); | |
2679 | } | |
2680 | ||
2681 | /* Reenable interrupts. */ | |
5f74ea14 | 2682 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2683 | if (devpriv->ier != devpriv->int_en) { |
2684 | devpriv->ier = devpriv->int_en; | |
2685 | outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE); | |
2686 | } | |
2687 | devpriv->intr_running = 0; | |
5f74ea14 | 2688 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2689 | |
2690 | return IRQ_HANDLED; | |
2691 | } | |
2692 | ||
0a85b6f0 MT |
2693 | static void pci230_handle_ao_nofifo(struct comedi_device *dev, |
2694 | struct comedi_subdevice *s) | |
70a350c3 | 2695 | { |
790c5541 | 2696 | short data; |
70a350c3 | 2697 | int i, ret; |
d163679c | 2698 | struct comedi_async *async = s->async; |
ea6d0d4c | 2699 | struct comedi_cmd *cmd = &async->cmd; |
70a350c3 | 2700 | |
25f1a98b | 2701 | if (!devpriv->ao_continuous && (devpriv->ao_scan_count == 0)) |
70a350c3 | 2702 | return; |
25f1a98b | 2703 | |
70a350c3 AW |
2704 | |
2705 | for (i = 0; i < cmd->chanlist_len; i++) { | |
2706 | /* Read sample from Comedi's circular buffer. */ | |
2707 | ret = comedi_buf_get(s->async, &data); | |
2708 | if (ret == 0) { | |
2709 | s->async->events |= COMEDI_CB_OVERFLOW; | |
2710 | pci230_ao_stop(dev, s); | |
2711 | comedi_error(dev, "AO buffer underrun"); | |
2712 | return; | |
2713 | } | |
2714 | /* Write value to DAC. */ | |
2715 | pci230_ao_write_nofifo(dev, data, CR_CHAN(cmd->chanlist[i])); | |
2716 | } | |
2717 | ||
2718 | async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; | |
2719 | if (!devpriv->ao_continuous) { | |
2720 | devpriv->ao_scan_count--; | |
2721 | if (devpriv->ao_scan_count == 0) { | |
2722 | /* End of acquisition. */ | |
2723 | async->events |= COMEDI_CB_EOA; | |
2724 | pci230_ao_stop(dev, s); | |
2725 | } | |
2726 | } | |
2727 | } | |
2728 | ||
2729 | /* Loads DAC FIFO (if using it) from buffer. */ | |
2730 | /* Returns 0 if AO finished due to completion or error, 1 if still going. */ | |
0a85b6f0 MT |
2731 | static int pci230_handle_ao_fifo(struct comedi_device *dev, |
2732 | struct comedi_subdevice *s) | |
70a350c3 | 2733 | { |
d163679c | 2734 | struct comedi_async *async = s->async; |
ea6d0d4c | 2735 | struct comedi_cmd *cmd = &async->cmd; |
70a350c3 AW |
2736 | unsigned int num_scans; |
2737 | unsigned int room; | |
2738 | unsigned short dacstat; | |
2739 | unsigned int i, n; | |
2740 | unsigned int bytes_per_scan; | |
2741 | unsigned int events = 0; | |
2742 | int running; | |
2743 | ||
2744 | /* Get DAC FIFO status. */ | |
2745 | dacstat = inw(dev->iobase + PCI230_DACCON); | |
2746 | ||
2747 | /* Determine number of scans available in buffer. */ | |
790c5541 | 2748 | bytes_per_scan = cmd->chanlist_len * sizeof(short); |
70a350c3 AW |
2749 | num_scans = comedi_buf_read_n_available(async) / bytes_per_scan; |
2750 | if (!devpriv->ao_continuous) { | |
2751 | /* Fixed number of scans. */ | |
25f1a98b | 2752 | if (num_scans > devpriv->ao_scan_count) |
70a350c3 | 2753 | num_scans = devpriv->ao_scan_count; |
25f1a98b | 2754 | |
70a350c3 AW |
2755 | if (devpriv->ao_scan_count == 0) { |
2756 | /* End of acquisition. */ | |
2757 | events |= COMEDI_CB_EOA; | |
2758 | } | |
2759 | } | |
2760 | if (events == 0) { | |
2761 | /* Check for FIFO underrun. */ | |
2762 | if ((dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) != 0) { | |
2763 | comedi_error(dev, "AO FIFO underrun"); | |
2764 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; | |
2765 | } | |
2766 | /* Check for buffer underrun if FIFO less than half full | |
2767 | * (otherwise there will be loads of "DAC FIFO not half full" | |
2768 | * interrupts). */ | |
2769 | if ((num_scans == 0) | |
0a85b6f0 | 2770 | && ((dacstat & PCI230P2_DAC_FIFO_HALF) == 0)) { |
70a350c3 AW |
2771 | comedi_error(dev, "AO buffer underrun"); |
2772 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; | |
2773 | } | |
2774 | } | |
2775 | if (events == 0) { | |
2776 | /* Determine how much room is in the FIFO (in samples). */ | |
25f1a98b | 2777 | if ((dacstat & PCI230P2_DAC_FIFO_FULL) != 0) |
70a350c3 | 2778 | room = PCI230P2_DAC_FIFOROOM_FULL; |
25f1a98b | 2779 | else if ((dacstat & PCI230P2_DAC_FIFO_HALF) != 0) |
70a350c3 | 2780 | room = PCI230P2_DAC_FIFOROOM_HALFTOFULL; |
25f1a98b | 2781 | else if ((dacstat & PCI230P2_DAC_FIFO_EMPTY) != 0) |
70a350c3 | 2782 | room = PCI230P2_DAC_FIFOROOM_EMPTY; |
25f1a98b | 2783 | else |
70a350c3 | 2784 | room = PCI230P2_DAC_FIFOROOM_ONETOHALF; |
25f1a98b | 2785 | |
70a350c3 AW |
2786 | /* Convert room to number of scans that can be added. */ |
2787 | room /= cmd->chanlist_len; | |
2788 | /* Determine number of scans to process. */ | |
25f1a98b | 2789 | if (num_scans > room) |
70a350c3 | 2790 | num_scans = room; |
25f1a98b | 2791 | |
70a350c3 AW |
2792 | /* Process scans. */ |
2793 | for (n = 0; n < num_scans; n++) { | |
2794 | for (i = 0; i < cmd->chanlist_len; i++) { | |
790c5541 | 2795 | short datum; |
70a350c3 AW |
2796 | |
2797 | comedi_buf_get(async, &datum); | |
2798 | pci230_ao_write_fifo(dev, datum, | |
0a85b6f0 | 2799 | CR_CHAN(cmd->chanlist[i])); |
70a350c3 AW |
2800 | } |
2801 | } | |
2802 | events |= COMEDI_CB_EOS | COMEDI_CB_BLOCK; | |
2803 | if (!devpriv->ao_continuous) { | |
2804 | devpriv->ao_scan_count -= num_scans; | |
2805 | if (devpriv->ao_scan_count == 0) { | |
2806 | /* All data for the command has been written | |
2807 | * to FIFO. Set FIFO interrupt trigger level | |
2808 | * to 'empty'. */ | |
2809 | devpriv->daccon = (devpriv->daccon | |
0a85b6f0 MT |
2810 | & |
2811 | ~PCI230P2_DAC_INT_FIFO_MASK) | |
2812 | | PCI230P2_DAC_INT_FIFO_EMPTY; | |
70a350c3 | 2813 | outw(devpriv->daccon, |
0a85b6f0 | 2814 | dev->iobase + PCI230_DACCON); |
70a350c3 AW |
2815 | } |
2816 | } | |
2817 | /* Check if FIFO underrun occurred while writing to FIFO. */ | |
2818 | dacstat = inw(dev->iobase + PCI230_DACCON); | |
2819 | if ((dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) != 0) { | |
2820 | comedi_error(dev, "AO FIFO underrun"); | |
2821 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; | |
2822 | } | |
2823 | } | |
2824 | if ((events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) | |
0a85b6f0 | 2825 | != 0) { |
70a350c3 AW |
2826 | /* Stopping AO due to completion or error. */ |
2827 | pci230_ao_stop(dev, s); | |
2828 | running = 0; | |
2829 | } else { | |
2830 | running = 1; | |
2831 | } | |
2832 | async->events |= events; | |
2833 | return running; | |
2834 | } | |
2835 | ||
0a85b6f0 MT |
2836 | static void pci230_handle_ai(struct comedi_device *dev, |
2837 | struct comedi_subdevice *s) | |
70a350c3 AW |
2838 | { |
2839 | unsigned int events = 0; | |
2840 | unsigned int status_fifo; | |
2841 | unsigned int i; | |
2842 | unsigned int todo; | |
2843 | unsigned int fifoamount; | |
d163679c | 2844 | struct comedi_async *async = s->async; |
70a350c3 AW |
2845 | unsigned int scanlen = async->cmd.scan_end_arg; |
2846 | ||
2847 | /* Determine number of samples to read. */ | |
2848 | if (devpriv->ai_continuous) { | |
2849 | todo = PCI230_ADC_FIFOLEVEL_HALFFULL; | |
2850 | } else if (devpriv->ai_scan_count == 0) { | |
2851 | todo = 0; | |
2852 | } else if ((devpriv->ai_scan_count > PCI230_ADC_FIFOLEVEL_HALFFULL) | |
0a85b6f0 | 2853 | || (scanlen > PCI230_ADC_FIFOLEVEL_HALFFULL)) { |
70a350c3 AW |
2854 | todo = PCI230_ADC_FIFOLEVEL_HALFFULL; |
2855 | } else { | |
2856 | todo = (devpriv->ai_scan_count * scanlen) | |
0a85b6f0 | 2857 | - devpriv->ai_scan_pos; |
25f1a98b | 2858 | if (todo > PCI230_ADC_FIFOLEVEL_HALFFULL) |
70a350c3 | 2859 | todo = PCI230_ADC_FIFOLEVEL_HALFFULL; |
25f1a98b | 2860 | |
70a350c3 AW |
2861 | } |
2862 | ||
25f1a98b | 2863 | if (todo == 0) |
70a350c3 | 2864 | return; |
25f1a98b | 2865 | |
70a350c3 AW |
2866 | |
2867 | fifoamount = 0; | |
2868 | for (i = 0; i < todo; i++) { | |
2869 | if (fifoamount == 0) { | |
2870 | /* Read FIFO state. */ | |
2871 | status_fifo = inw(dev->iobase + PCI230_ADCCON); | |
2872 | ||
2873 | if ((status_fifo & PCI230_ADC_FIFO_FULL_LATCHED) != 0) { | |
2874 | /* Report error otherwise FIFO overruns will go | |
2875 | * unnoticed by the caller. */ | |
2876 | comedi_error(dev, "AI FIFO overrun"); | |
2877 | events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR; | |
2878 | break; | |
2879 | } else if ((status_fifo & PCI230_ADC_FIFO_EMPTY) != 0) { | |
2880 | /* FIFO empty. */ | |
2881 | break; | |
2882 | } else if ((status_fifo & PCI230_ADC_FIFO_HALF) != 0) { | |
2883 | /* FIFO half full. */ | |
2884 | fifoamount = PCI230_ADC_FIFOLEVEL_HALFFULL; | |
2885 | } else { | |
2886 | /* FIFO not empty. */ | |
2887 | if (devpriv->hwver > 0) { | |
2888 | /* Read PCI230+/260+ ADC FIFO level. */ | |
2889 | fifoamount = inw(dev->iobase | |
0a85b6f0 | 2890 | + PCI230P_ADCFFLEV); |
70a350c3 AW |
2891 | if (fifoamount == 0) { |
2892 | /* Shouldn't happen. */ | |
2893 | break; | |
2894 | } | |
2895 | } else { | |
2896 | fifoamount = 1; | |
2897 | } | |
2898 | } | |
2899 | } | |
2900 | ||
2901 | /* Read sample and store in Comedi's circular buffer. */ | |
2902 | if (comedi_buf_put(async, pci230_ai_read(dev)) == 0) { | |
2903 | events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; | |
2904 | comedi_error(dev, "AI buffer overflow"); | |
2905 | break; | |
2906 | } | |
2907 | fifoamount--; | |
2908 | devpriv->ai_scan_pos++; | |
2909 | if (devpriv->ai_scan_pos == scanlen) { | |
2910 | /* End of scan. */ | |
2911 | devpriv->ai_scan_pos = 0; | |
2912 | devpriv->ai_scan_count--; | |
2913 | async->events |= COMEDI_CB_EOS; | |
2914 | } | |
2915 | } | |
2916 | ||
2917 | if (!devpriv->ai_continuous && (devpriv->ai_scan_count == 0)) { | |
2918 | /* End of acquisition. */ | |
2919 | events |= COMEDI_CB_EOA; | |
2920 | } else { | |
2921 | /* More samples required, tell Comedi to block. */ | |
2922 | events |= COMEDI_CB_BLOCK; | |
2923 | } | |
2924 | async->events |= events; | |
2925 | ||
2926 | if ((async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR | | |
0a85b6f0 | 2927 | COMEDI_CB_OVERFLOW)) != 0) { |
70a350c3 AW |
2928 | /* disable hardware conversions */ |
2929 | pci230_ai_stop(dev, s); | |
2930 | } else { | |
2931 | /* update FIFO interrupt trigger level */ | |
2932 | pci230_ai_update_fifo_trigger_level(dev, s); | |
2933 | } | |
2934 | } | |
2935 | ||
0a85b6f0 MT |
2936 | static void pci230_ao_stop(struct comedi_device *dev, |
2937 | struct comedi_subdevice *s) | |
70a350c3 AW |
2938 | { |
2939 | unsigned long irqflags; | |
2940 | unsigned char intsrc; | |
2941 | int started; | |
ea6d0d4c | 2942 | struct comedi_cmd *cmd; |
70a350c3 | 2943 | |
5f74ea14 | 2944 | spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags); |
70a350c3 | 2945 | started = test_and_clear_bit(AO_CMD_STARTED, &devpriv->state); |
5f74ea14 | 2946 | spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, irqflags); |
25f1a98b | 2947 | if (!started) |
70a350c3 | 2948 | return; |
25f1a98b | 2949 | |
70a350c3 AW |
2950 | |
2951 | cmd = &s->async->cmd; | |
2952 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
2953 | /* Stop scan rate generator. */ | |
2954 | pci230_cancel_ct(dev, 1); | |
2955 | } | |
2956 | ||
2957 | /* Determine interrupt source. */ | |
2958 | if (devpriv->hwver < 2) { | |
2959 | /* Not using DAC FIFO. Using CT1 interrupt. */ | |
2960 | intsrc = PCI230_INT_ZCLK_CT1; | |
2961 | } else { | |
2962 | /* Using DAC FIFO interrupt. */ | |
2963 | intsrc = PCI230P2_INT_DAC; | |
2964 | } | |
2965 | /* Disable interrupt and wait for interrupt routine to finish running | |
2966 | * unless we are called from the interrupt routine. */ | |
5f74ea14 | 2967 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2968 | devpriv->int_en &= ~intsrc; |
2969 | while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) { | |
5f74ea14 GKH |
2970 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
2971 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); | |
70a350c3 AW |
2972 | } |
2973 | if (devpriv->ier != devpriv->int_en) { | |
2974 | devpriv->ier = devpriv->int_en; | |
2975 | outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE); | |
2976 | } | |
5f74ea14 | 2977 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
2978 | |
2979 | if (devpriv->hwver >= 2) { | |
2980 | /* Using DAC FIFO. Reset FIFO, clear underrun error, | |
2981 | * disable FIFO. */ | |
2982 | devpriv->daccon &= PCI230_DAC_OR_MASK; | |
2983 | outw(devpriv->daccon | PCI230P2_DAC_FIFO_RESET | |
0a85b6f0 MT |
2984 | | PCI230P2_DAC_FIFO_UNDERRUN_CLEAR, |
2985 | dev->iobase + PCI230_DACCON); | |
70a350c3 AW |
2986 | } |
2987 | ||
2988 | /* Release resources. */ | |
2989 | put_all_resources(dev, OWNER_AOCMD); | |
2990 | } | |
2991 | ||
0a85b6f0 MT |
2992 | static int pci230_ao_cancel(struct comedi_device *dev, |
2993 | struct comedi_subdevice *s) | |
70a350c3 AW |
2994 | { |
2995 | pci230_ao_stop(dev, s); | |
2996 | return 0; | |
2997 | } | |
2998 | ||
0a85b6f0 MT |
2999 | static void pci230_ai_stop(struct comedi_device *dev, |
3000 | struct comedi_subdevice *s) | |
70a350c3 AW |
3001 | { |
3002 | unsigned long irqflags; | |
ea6d0d4c | 3003 | struct comedi_cmd *cmd; |
70a350c3 AW |
3004 | int started; |
3005 | ||
5f74ea14 | 3006 | spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags); |
70a350c3 | 3007 | started = test_and_clear_bit(AI_CMD_STARTED, &devpriv->state); |
5f74ea14 | 3008 | spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags); |
25f1a98b | 3009 | if (!started) |
70a350c3 | 3010 | return; |
25f1a98b | 3011 | |
70a350c3 AW |
3012 | |
3013 | cmd = &s->async->cmd; | |
3014 | if (cmd->convert_src == TRIG_TIMER) { | |
3015 | /* Stop conversion rate generator. */ | |
3016 | pci230_cancel_ct(dev, 2); | |
3017 | } | |
3018 | if (cmd->scan_begin_src != TRIG_FOLLOW) { | |
3019 | /* Stop scan period monostable. */ | |
3020 | pci230_cancel_ct(dev, 0); | |
3021 | } | |
3022 | ||
5f74ea14 | 3023 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
3024 | /* Disable ADC interrupt and wait for interrupt routine to finish |
3025 | * running unless we are called from the interrupt routine. */ | |
3026 | devpriv->int_en &= ~PCI230_INT_ADC; | |
3027 | while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) { | |
5f74ea14 GKH |
3028 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
3029 | spin_lock_irqsave(&devpriv->isr_spinlock, irqflags); | |
70a350c3 AW |
3030 | } |
3031 | if (devpriv->ier != devpriv->int_en) { | |
3032 | devpriv->ier = devpriv->int_en; | |
3033 | outb(devpriv->ier, devpriv->iobase1 + PCI230_INT_SCE); | |
3034 | } | |
5f74ea14 | 3035 | spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags); |
70a350c3 AW |
3036 | |
3037 | /* Reset FIFO, disable FIFO and set start conversion source to none. | |
3038 | * Keep se/diff and bip/uni settings */ | |
3039 | devpriv->adccon = (devpriv->adccon & (PCI230_ADC_IR_MASK | |
0a85b6f0 MT |
3040 | | PCI230_ADC_IM_MASK)) | |
3041 | PCI230_ADC_TRIG_NONE; | |
70a350c3 | 3042 | outw(devpriv->adccon | PCI230_ADC_FIFO_RESET, |
0a85b6f0 | 3043 | dev->iobase + PCI230_ADCCON); |
70a350c3 AW |
3044 | |
3045 | /* Release resources. */ | |
3046 | put_all_resources(dev, OWNER_AICMD); | |
3047 | } | |
3048 | ||
0a85b6f0 MT |
3049 | static int pci230_ai_cancel(struct comedi_device *dev, |
3050 | struct comedi_subdevice *s) | |
70a350c3 AW |
3051 | { |
3052 | pci230_ai_stop(dev, s); | |
3053 | return 0; | |
3054 | } | |
90f703d3 AT |
3055 | |
3056 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
3057 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
3058 | MODULE_LICENSE("GPL"); |