]>
Commit | Line | Data |
---|---|---|
8cb9b9fb EP |
1 | /* |
2 | ||
3 | comedi/drivers/adl_pci9111.c | |
4 | ||
5 | Hardware driver for PCI9111 ADLink cards: | |
6 | ||
7 | PCI-9111HR | |
8 | ||
9 | Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr> | |
10 | ||
11 | This program is free software; you can redistribute it and/or modify | |
12 | it under the terms of the GNU General Public License as published by | |
13 | the Free Software Foundation; either version 2 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | This program is distributed in the hope that it will be useful, | |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | GNU General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU General Public License | |
22 | along with this program; if not, write to the Free Software | |
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | */ | |
25 | ||
26 | /* | |
27 | Driver: adl_pci9111 | |
28 | Description: Adlink PCI-9111HR | |
29 | Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr> | |
30 | Devices: [ADLink] PCI-9111HR (adl_pci9111) | |
31 | Status: experimental | |
32 | ||
33 | Supports: | |
34 | ||
35 | - ai_insn read | |
36 | - ao_insn read/write | |
37 | - di_insn read | |
38 | - do_insn read/write | |
39 | - ai_do_cmd mode with the following sources: | |
40 | ||
41 | - start_src TRIG_NOW | |
42 | - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT | |
43 | - convert_src TRIG_TIMER TRIG_EXT | |
44 | - scan_end_src TRIG_COUNT | |
45 | - stop_src TRIG_COUNT TRIG_NONE | |
46 | ||
47 | The scanned channels must be consecutive and start from 0. They must | |
48 | all have the same range and aref. | |
49 | ||
50 | Configuration options: | |
51 | ||
52 | [0] - PCI bus number (optional) | |
53 | [1] - PCI slot number (optional) | |
54 | ||
55 | If bus/slot is not specified, the first available PCI | |
56 | device will be used. | |
57 | ||
58 | */ | |
59 | ||
60 | /* | |
61 | CHANGELOG: | |
62 | ||
63 | 2005/02/17 Extend AI streaming capabilities. Now, scan_begin_arg can be | |
64 | a multiple of chanlist_len*convert_arg. | |
65 | 2002/02/19 Fixed the two's complement conversion in pci9111_(hr_)ai_get_data. | |
66 | 2002/02/18 Added external trigger support for analog input. | |
67 | ||
68 | TODO: | |
69 | ||
70 | - Really test implemented functionality. | |
71 | - Add support for the PCI-9111DG with a probe routine to identify the card type | |
72 | (perhaps with the help of the channel number readback of the A/D Data register). | |
73 | - Add external multiplexer support. | |
74 | ||
75 | */ | |
76 | ||
77 | #include "../comedidev.h" | |
78 | ||
79 | #include <linux/delay.h> | |
80 | ||
81 | #include "8253.h" | |
82 | #include "comedi_pci.h" | |
83 | #include "comedi_fc.h" | |
84 | ||
85 | #define PCI9111_DRIVER_NAME "adl_pci9111" | |
86 | #define PCI9111_HR_DEVICE_ID 0x9111 | |
87 | ||
88 | // TODO: Add other pci9111 board id | |
89 | ||
90 | #define PCI9111_IO_RANGE 0x0100 | |
91 | ||
92 | #define PCI9111_FIFO_HALF_SIZE 512 | |
93 | ||
94 | #define PCI9111_AI_CHANNEL_NBR 16 | |
95 | ||
96 | #define PCI9111_AI_RESOLUTION 12 | |
97 | #define PCI9111_AI_RESOLUTION_MASK 0x0FFF | |
98 | #define PCI9111_AI_RESOLUTION_2_CMP_BIT 0x0800 | |
99 | ||
100 | #define PCI9111_HR_AI_RESOLUTION 16 | |
101 | #define PCI9111_HR_AI_RESOLUTION_MASK 0xFFFF | |
102 | #define PCI9111_HR_AI_RESOLUTION_2_CMP_BIT 0x8000 | |
103 | ||
104 | #define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000 | |
105 | #define PCI9111_AO_CHANNEL_NBR 1 | |
106 | #define PCI9111_AO_RESOLUTION 12 | |
107 | #define PCI9111_AO_RESOLUTION_MASK 0x0FFF | |
108 | #define PCI9111_DI_CHANNEL_NBR 16 | |
109 | #define PCI9111_DO_CHANNEL_NBR 16 | |
110 | #define PCI9111_DO_MASK 0xFFFF | |
111 | ||
112 | #define PCI9111_RANGE_SETTING_DELAY 10 | |
113 | #define PCI9111_AI_INSTANT_READ_UDELAY_US 2 | |
114 | #define PCI9111_AI_INSTANT_READ_TIMEOUT 100 | |
115 | ||
116 | #define PCI9111_8254_CLOCK_PERIOD_NS 500 | |
117 | ||
118 | #define PCI9111_8254_COUNTER_0 0x00 | |
119 | #define PCI9111_8254_COUNTER_1 0x40 | |
120 | #define PCI9111_8254_COUNTER_2 0x80 | |
121 | #define PCI9111_8254_COUNTER_LATCH 0x00 | |
122 | #define PCI9111_8254_READ_LOAD_LSB_ONLY 0x10 | |
123 | #define PCI9111_8254_READ_LOAD_MSB_ONLY 0x20 | |
124 | #define PCI9111_8254_READ_LOAD_LSB_MSB 0x30 | |
125 | #define PCI9111_8254_MODE_0 0x00 | |
126 | #define PCI9111_8254_MODE_1 0x02 | |
127 | #define PCI9111_8254_MODE_2 0x04 | |
128 | #define PCI9111_8254_MODE_3 0x06 | |
129 | #define PCI9111_8254_MODE_4 0x08 | |
130 | #define PCI9111_8254_MODE_5 0x0A | |
131 | #define PCI9111_8254_BINARY_COUNTER 0x00 | |
132 | #define PCI9111_8254_BCD_COUNTER 0x01 | |
133 | ||
134 | /* IO address map */ | |
135 | ||
136 | #define PCI9111_REGISTER_AD_FIFO_VALUE 0x00 // AD Data stored in FIFO | |
137 | #define PCI9111_REGISTER_DA_OUTPUT 0x00 | |
138 | #define PCI9111_REGISTER_DIGITAL_IO 0x02 | |
139 | #define PCI9111_REGISTER_EXTENDED_IO_PORTS 0x04 | |
140 | #define PCI9111_REGISTER_AD_CHANNEL_CONTROL 0x06 // Channel selection | |
141 | #define PCI9111_REGISTER_AD_CHANNEL_READBACK 0x06 | |
142 | #define PCI9111_REGISTER_INPUT_SIGNAL_RANGE 0x08 | |
143 | #define PCI9111_REGISTER_RANGE_STATUS_READBACK 0x08 | |
144 | #define PCI9111_REGISTER_TRIGGER_MODE_CONTROL 0x0A | |
145 | #define PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK 0x0A | |
146 | #define PCI9111_REGISTER_SOFTWARE_TRIGGER 0x0E | |
147 | #define PCI9111_REGISTER_INTERRUPT_CONTROL 0x0C | |
148 | #define PCI9111_REGISTER_8254_COUNTER_0 0x40 | |
149 | #define PCI9111_REGISTER_8254_COUNTER_1 0x42 | |
150 | #define PCI9111_REGISTER_8254_COUNTER_2 0X44 | |
151 | #define PCI9111_REGISTER_8254_CONTROL 0x46 | |
152 | #define PCI9111_REGISTER_INTERRUPT_CLEAR 0x48 | |
153 | ||
154 | #define PCI9111_TRIGGER_MASK 0x0F | |
155 | #define PCI9111_PTRG_OFF (0 << 3) | |
156 | #define PCI9111_PTRG_ON (1 << 3) | |
157 | #define PCI9111_EITS_EXTERNAL (1 << 2) | |
158 | #define PCI9111_EITS_INTERNAL (0 << 2) | |
159 | #define PCI9111_TPST_SOFTWARE_TRIGGER (0 << 1) | |
160 | #define PCI9111_TPST_TIMER_PACER (1 << 1) | |
161 | #define PCI9111_ASCAN_ON (1 << 0) | |
162 | #define PCI9111_ASCAN_OFF (0 << 0) | |
163 | ||
164 | #define PCI9111_ISC0_SET_IRQ_ON_ENDING_OF_AD_CONVERSION (0 << 0) | |
165 | #define PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL (1 << 0) | |
166 | #define PCI9111_ISC1_SET_IRQ_ON_TIMER_TICK (0 << 1) | |
167 | #define PCI9111_ISC1_SET_IRQ_ON_EXT_TRG (1 << 1) | |
168 | #define PCI9111_FFEN_SET_FIFO_ENABLE (0 << 2) | |
169 | #define PCI9111_FFEN_SET_FIFO_DISABLE (1 << 2) | |
170 | ||
171 | #define PCI9111_CHANNEL_MASK 0x0F | |
172 | ||
173 | #define PCI9111_RANGE_MASK 0x07 | |
174 | #define PCI9111_FIFO_EMPTY_MASK 0x10 | |
175 | #define PCI9111_FIFO_HALF_FULL_MASK 0x20 | |
176 | #define PCI9111_FIFO_FULL_MASK 0x40 | |
177 | #define PCI9111_AD_BUSY_MASK 0x80 | |
178 | ||
179 | #define PCI9111_IO_BASE dev->iobase | |
180 | ||
181 | /* | |
182 | * Define inlined function | |
183 | */ | |
184 | ||
185 | #define pci9111_trigger_and_autoscan_get() \ | |
186 | (inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK)&0x0F) | |
187 | ||
188 | #define pci9111_trigger_and_autoscan_set(flags) \ | |
189 | outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_TRIGGER_MODE_CONTROL) | |
190 | ||
191 | #define pci9111_interrupt_and_fifo_get() \ | |
192 | ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK) >> 4) &0x03) | |
193 | ||
194 | #define pci9111_interrupt_and_fifo_set(flags) \ | |
195 | outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL) | |
196 | ||
197 | #define pci9111_interrupt_clear() \ | |
198 | outb(0,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CLEAR) | |
199 | ||
200 | #define pci9111_software_trigger() \ | |
201 | outb(0,PCI9111_IO_BASE+PCI9111_REGISTER_SOFTWARE_TRIGGER) | |
202 | ||
203 | #define pci9111_fifo_reset() \ | |
204 | outb(PCI9111_FFEN_SET_FIFO_ENABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \ | |
205 | outb(PCI9111_FFEN_SET_FIFO_DISABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \ | |
206 | outb(PCI9111_FFEN_SET_FIFO_ENABLE,PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL) | |
207 | ||
208 | #define pci9111_is_fifo_full() \ | |
209 | ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \ | |
210 | PCI9111_FIFO_FULL_MASK)==0) | |
211 | ||
212 | #define pci9111_is_fifo_half_full() \ | |
213 | ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \ | |
214 | PCI9111_FIFO_HALF_FULL_MASK)==0) | |
215 | ||
216 | #define pci9111_is_fifo_empty() \ | |
217 | ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \ | |
218 | PCI9111_FIFO_EMPTY_MASK)==0) | |
219 | ||
220 | #define pci9111_ai_channel_set(channel) \ | |
221 | outb((channel)&PCI9111_CHANNEL_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_CONTROL) | |
222 | ||
223 | #define pci9111_ai_channel_get() \ | |
224 | inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_READBACK)&PCI9111_CHANNEL_MASK | |
225 | ||
226 | #define pci9111_ai_range_set(range) \ | |
227 | outb((range)&PCI9111_RANGE_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_INPUT_SIGNAL_RANGE) | |
228 | ||
229 | #define pci9111_ai_range_get() \ | |
230 | inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)&PCI9111_RANGE_MASK | |
231 | ||
232 | #define pci9111_ai_get_data() \ | |
233 | ((inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE)>>4)&PCI9111_AI_RESOLUTION_MASK) \ | |
234 | ^ PCI9111_AI_RESOLUTION_2_CMP_BIT | |
235 | ||
236 | #define pci9111_hr_ai_get_data() \ | |
237 | (inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE) & PCI9111_HR_AI_RESOLUTION_MASK) \ | |
238 | ^ PCI9111_HR_AI_RESOLUTION_2_CMP_BIT | |
239 | ||
240 | #define pci9111_ao_set_data(data) \ | |
241 | outw(data&PCI9111_AO_RESOLUTION_MASK,PCI9111_IO_BASE+PCI9111_REGISTER_DA_OUTPUT) | |
242 | ||
243 | #define pci9111_di_get_bits() \ | |
244 | inw(PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO) | |
245 | ||
246 | #define pci9111_do_set_bits(bits) \ | |
247 | outw(bits,PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO) | |
248 | ||
249 | #define pci9111_8254_control_set(flags) \ | |
250 | outb(flags,PCI9111_IO_BASE+PCI9111_REGISTER_8254_CONTROL) | |
251 | ||
252 | #define pci9111_8254_counter_0_set(data) \ | |
253 | outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0); \ | |
254 | outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0) | |
255 | ||
256 | #define pci9111_8254_counter_1_set(data) \ | |
257 | outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1); \ | |
258 | outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1) | |
259 | ||
260 | #define pci9111_8254_counter_2_set(data) \ | |
261 | outb(data & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2); \ | |
262 | outb( (data >> 8) & 0xFF, PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2) | |
263 | ||
264 | // | |
265 | // Function prototypes | |
266 | // | |
267 | ||
0707bb04 | 268 | static int pci9111_attach(struct comedi_device * dev, struct comedi_devconfig * it); |
71b5f4f1 | 269 | static int pci9111_detach(struct comedi_device * dev); |
34c43922 | 270 | static void pci9111_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s, |
8cb9b9fb EP |
271 | void *data, unsigned int num_bytes, unsigned int start_chan_index); |
272 | ||
9ced1de6 | 273 | static const struct comedi_lrange pci9111_hr_ai_range = { |
8cb9b9fb EP |
274 | 5, |
275 | { | |
276 | BIP_RANGE(10), | |
277 | BIP_RANGE(5), | |
278 | BIP_RANGE(2.5), | |
279 | BIP_RANGE(1.25), | |
280 | BIP_RANGE(0.625) | |
281 | } | |
282 | }; | |
283 | ||
284 | static DEFINE_PCI_DEVICE_TABLE(pci9111_pci_table) = { | |
285 | {PCI_VENDOR_ID_ADLINK, PCI9111_HR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, | |
286 | 0, 0}, | |
287 | //{ PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, | |
288 | {0} | |
289 | }; | |
290 | ||
291 | MODULE_DEVICE_TABLE(pci, pci9111_pci_table); | |
292 | ||
293 | // | |
294 | // Board specification structure | |
295 | // | |
296 | ||
940579fb | 297 | struct pci9111_board { |
8cb9b9fb EP |
298 | const char *name; // driver name |
299 | int device_id; | |
300 | int ai_channel_nbr; // num of A/D chans | |
301 | int ao_channel_nbr; // num of D/A chans | |
302 | int ai_resolution; // resolution of A/D | |
303 | int ai_resolution_mask; | |
304 | int ao_resolution; // resolution of D/A | |
305 | int ao_resolution_mask; | |
9ced1de6 BP |
306 | const struct comedi_lrange *ai_range_list; // rangelist for A/D |
307 | const struct comedi_lrange *ao_range_list; // rangelist for D/A | |
8cb9b9fb | 308 | unsigned int ai_acquisition_period_min_ns; |
940579fb | 309 | }; |
8cb9b9fb | 310 | |
940579fb | 311 | static const struct pci9111_board pci9111_boards[] = { |
8cb9b9fb EP |
312 | { |
313 | name: "pci9111_hr", | |
314 | device_id:PCI9111_HR_DEVICE_ID, | |
315 | ai_channel_nbr:PCI9111_AI_CHANNEL_NBR, | |
316 | ao_channel_nbr:PCI9111_AO_CHANNEL_NBR, | |
317 | ai_resolution:PCI9111_HR_AI_RESOLUTION, | |
318 | ai_resolution_mask:PCI9111_HR_AI_RESOLUTION_MASK, | |
319 | ao_resolution:PCI9111_AO_RESOLUTION, | |
320 | ao_resolution_mask:PCI9111_AO_RESOLUTION_MASK, | |
321 | ai_range_list:&pci9111_hr_ai_range, | |
322 | ao_range_list:&range_bipolar10, | |
323 | ai_acquisition_period_min_ns:PCI9111_AI_ACQUISITION_PERIOD_MIN_NS} | |
324 | }; | |
325 | ||
326 | #define pci9111_board_nbr \ | |
940579fb | 327 | (sizeof(pci9111_boards)/sizeof(struct pci9111_board)) |
8cb9b9fb | 328 | |
139dfbdf | 329 | static struct comedi_driver pci9111_driver = { |
8cb9b9fb EP |
330 | driver_name:PCI9111_DRIVER_NAME, |
331 | module:THIS_MODULE, | |
332 | attach:pci9111_attach, | |
333 | detach:pci9111_detach, | |
334 | }; | |
335 | ||
336 | COMEDI_PCI_INITCLEANUP(pci9111_driver, pci9111_pci_table); | |
337 | ||
338 | // | |
339 | // Private data structure | |
340 | // | |
341 | ||
c350fa19 | 342 | struct pci9111_private_data { |
8cb9b9fb EP |
343 | struct pci_dev *pci_device; |
344 | unsigned long io_range; // PCI6503 io range | |
345 | ||
346 | unsigned long lcr_io_base; // Local configuration register base address | |
347 | unsigned long lcr_io_range; | |
348 | ||
349 | int stop_counter; | |
350 | int stop_is_none; | |
351 | ||
352 | unsigned int scan_delay; | |
353 | unsigned int chanlist_len; | |
354 | unsigned int chunk_counter; | |
355 | unsigned int chunk_num_samples; | |
356 | ||
357 | int ao_readback; // Last written analog output data | |
358 | ||
359 | int timer_divisor_1; // Divisor values for the 8254 timer pacer | |
360 | int timer_divisor_2; | |
361 | ||
362 | int is_valid; // Is device valid | |
363 | ||
790c5541 | 364 | short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE]; |
c350fa19 | 365 | }; |
8cb9b9fb | 366 | |
c350fa19 | 367 | #define dev_private ((struct pci9111_private_data *)dev->private) |
8cb9b9fb EP |
368 | |
369 | // ------------------------------------------------------------------ | |
370 | // | |
371 | // PLX9050 SECTION | |
372 | // | |
373 | // ------------------------------------------------------------------ | |
374 | ||
375 | #define PLX9050_REGISTER_INTERRUPT_CONTROL 0x4c | |
376 | ||
377 | #define PLX9050_LINTI1_ENABLE (1 << 0) | |
378 | #define PLX9050_LINTI1_ACTIVE_HIGH (1 << 1) | |
379 | #define PLX9050_LINTI1_STATUS (1 << 2) | |
380 | #define PLX9050_LINTI2_ENABLE (1 << 3) | |
381 | #define PLX9050_LINTI2_ACTIVE_HIGH (1 << 4) | |
382 | #define PLX9050_LINTI2_STATUS (1 << 5) | |
383 | #define PLX9050_PCI_INTERRUPT_ENABLE (1 << 6) | |
384 | #define PLX9050_SOFTWARE_INTERRUPT (1 << 7) | |
385 | ||
386 | static void plx9050_interrupt_control(unsigned long io_base, | |
387 | bool LINTi1_enable, | |
388 | bool LINTi1_active_high, | |
389 | bool LINTi2_enable, bool LINTi2_active_high, bool interrupt_enable) | |
390 | { | |
391 | int flags = 0; | |
392 | ||
393 | if (LINTi1_enable) | |
394 | flags |= PLX9050_LINTI1_ENABLE; | |
395 | if (LINTi1_active_high) | |
396 | flags |= PLX9050_LINTI1_ACTIVE_HIGH; | |
397 | if (LINTi2_enable) | |
398 | flags |= PLX9050_LINTI2_ENABLE; | |
399 | if (LINTi2_active_high) | |
400 | flags |= PLX9050_LINTI2_ACTIVE_HIGH; | |
401 | ||
402 | if (interrupt_enable) | |
403 | flags |= PLX9050_PCI_INTERRUPT_ENABLE; | |
404 | ||
405 | outb(flags, io_base + PLX9050_REGISTER_INTERRUPT_CONTROL); | |
406 | } | |
407 | ||
408 | // ------------------------------------------------------------------ | |
409 | // | |
410 | // MISCELLANEOUS SECTION | |
411 | // | |
412 | // ------------------------------------------------------------------ | |
413 | ||
414 | // | |
415 | // 8254 timer | |
416 | // | |
417 | ||
71b5f4f1 | 418 | static void pci9111_timer_set(struct comedi_device * dev) |
8cb9b9fb EP |
419 | { |
420 | pci9111_8254_control_set(PCI9111_8254_COUNTER_0 | | |
421 | PCI9111_8254_READ_LOAD_LSB_MSB | | |
422 | PCI9111_8254_MODE_0 | PCI9111_8254_BINARY_COUNTER); | |
423 | ||
424 | pci9111_8254_control_set(PCI9111_8254_COUNTER_1 | | |
425 | PCI9111_8254_READ_LOAD_LSB_MSB | | |
426 | PCI9111_8254_MODE_2 | PCI9111_8254_BINARY_COUNTER); | |
427 | ||
428 | pci9111_8254_control_set(PCI9111_8254_COUNTER_2 | | |
429 | PCI9111_8254_READ_LOAD_LSB_MSB | | |
430 | PCI9111_8254_MODE_2 | PCI9111_8254_BINARY_COUNTER); | |
431 | ||
432 | comedi_udelay(1); | |
433 | ||
434 | pci9111_8254_counter_2_set(dev_private->timer_divisor_2); | |
435 | pci9111_8254_counter_1_set(dev_private->timer_divisor_1); | |
436 | } | |
437 | ||
438 | typedef enum { | |
439 | software, | |
440 | timer_pacer, | |
441 | external | |
442 | } pci9111_trigger_sources; | |
443 | ||
71b5f4f1 | 444 | static void pci9111_trigger_source_set(struct comedi_device * dev, |
8cb9b9fb EP |
445 | pci9111_trigger_sources source) |
446 | { | |
447 | int flags; | |
448 | ||
449 | flags = pci9111_trigger_and_autoscan_get() & 0x09; | |
450 | ||
451 | switch (source) { | |
452 | case software: | |
453 | flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_SOFTWARE_TRIGGER; | |
454 | break; | |
455 | ||
456 | case timer_pacer: | |
457 | flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_TIMER_PACER; | |
458 | break; | |
459 | ||
460 | case external: | |
461 | flags |= PCI9111_EITS_EXTERNAL; | |
462 | break; | |
463 | } | |
464 | ||
465 | pci9111_trigger_and_autoscan_set(flags); | |
466 | } | |
467 | ||
71b5f4f1 | 468 | static void pci9111_pretrigger_set(struct comedi_device * dev, bool pretrigger) |
8cb9b9fb EP |
469 | { |
470 | int flags; | |
471 | ||
472 | flags = pci9111_trigger_and_autoscan_get() & 0x07; | |
473 | ||
474 | if (pretrigger) | |
475 | flags |= PCI9111_PTRG_ON; | |
476 | ||
477 | pci9111_trigger_and_autoscan_set(flags); | |
478 | } | |
479 | ||
71b5f4f1 | 480 | static void pci9111_autoscan_set(struct comedi_device * dev, bool autoscan) |
8cb9b9fb EP |
481 | { |
482 | int flags; | |
483 | ||
484 | flags = pci9111_trigger_and_autoscan_get() & 0x0e; | |
485 | ||
486 | if (autoscan) | |
487 | flags |= PCI9111_ASCAN_ON; | |
488 | ||
489 | pci9111_trigger_and_autoscan_set(flags); | |
490 | } | |
491 | ||
492 | typedef enum { | |
493 | irq_on_eoc, | |
494 | irq_on_fifo_half_full | |
495 | } pci9111_ISC0_sources; | |
496 | ||
497 | typedef enum { | |
498 | irq_on_timer_tick, | |
499 | irq_on_external_trigger | |
500 | } pci9111_ISC1_sources; | |
501 | ||
71b5f4f1 | 502 | static void pci9111_interrupt_source_set(struct comedi_device * dev, |
8cb9b9fb EP |
503 | pci9111_ISC0_sources irq_0_source, pci9111_ISC1_sources irq_1_source) |
504 | { | |
505 | int flags; | |
506 | ||
507 | flags = pci9111_interrupt_and_fifo_get() & 0x04; | |
508 | ||
509 | if (irq_0_source == irq_on_fifo_half_full) | |
510 | flags |= PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL; | |
511 | ||
512 | if (irq_1_source == irq_on_external_trigger) | |
513 | flags |= PCI9111_ISC1_SET_IRQ_ON_EXT_TRG; | |
514 | ||
515 | pci9111_interrupt_and_fifo_set(flags); | |
516 | } | |
517 | ||
518 | // ------------------------------------------------------------------ | |
519 | // | |
520 | // HARDWARE TRIGGERED ANALOG INPUT SECTION | |
521 | // | |
522 | // ------------------------------------------------------------------ | |
523 | ||
524 | // | |
525 | // Cancel analog input autoscan | |
526 | // | |
527 | ||
528 | #undef AI_DO_CMD_DEBUG | |
529 | ||
34c43922 | 530 | static int pci9111_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s) |
8cb9b9fb EP |
531 | { |
532 | // Disable interrupts | |
533 | ||
534 | plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true, | |
535 | true, false); | |
536 | ||
537 | pci9111_trigger_source_set(dev, software); | |
538 | ||
539 | pci9111_autoscan_set(dev, false); | |
540 | ||
541 | pci9111_fifo_reset(); | |
542 | ||
543 | #ifdef AI_DO_CMD_DEBUG | |
544 | printk(PCI9111_DRIVER_NAME ": ai_cancel\n"); | |
545 | #endif | |
546 | ||
547 | return 0; | |
548 | } | |
549 | ||
550 | // | |
551 | // Test analog input command | |
552 | // | |
553 | ||
554 | #define pci9111_check_trigger_src(src,flags) \ | |
555 | tmp = src; \ | |
556 | src &= flags; \ | |
557 | if (!src || tmp != src) error++ | |
558 | ||
559 | static int | |
71b5f4f1 | 560 | pci9111_ai_do_cmd_test(struct comedi_device * dev, |
ea6d0d4c | 561 | struct comedi_subdevice * s, struct comedi_cmd * cmd) |
8cb9b9fb EP |
562 | { |
563 | int tmp; | |
564 | int error = 0; | |
565 | int range, reference; | |
566 | int i; | |
940579fb | 567 | struct pci9111_board *board = (struct pci9111_board *) dev->board_ptr; |
8cb9b9fb EP |
568 | |
569 | // Step 1 : check if trigger are trivialy valid | |
570 | ||
571 | pci9111_check_trigger_src(cmd->start_src, TRIG_NOW); | |
572 | pci9111_check_trigger_src(cmd->scan_begin_src, | |
573 | TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT); | |
574 | pci9111_check_trigger_src(cmd->convert_src, TRIG_TIMER | TRIG_EXT); | |
575 | pci9111_check_trigger_src(cmd->scan_end_src, TRIG_COUNT); | |
576 | pci9111_check_trigger_src(cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
577 | ||
578 | if (error) | |
579 | return 1; | |
580 | ||
581 | // step 2 : make sure trigger sources are unique and mutually compatible | |
582 | ||
583 | if (cmd->start_src != TRIG_NOW) | |
584 | error++; | |
585 | ||
586 | if ((cmd->scan_begin_src != TRIG_TIMER) && | |
587 | (cmd->scan_begin_src != TRIG_FOLLOW) && | |
588 | (cmd->scan_begin_src != TRIG_EXT)) | |
589 | error++; | |
590 | ||
591 | if ((cmd->convert_src != TRIG_TIMER) && (cmd->convert_src != TRIG_EXT)) { | |
592 | error++; | |
593 | } | |
594 | if ((cmd->convert_src == TRIG_TIMER) && | |
595 | !((cmd->scan_begin_src == TRIG_TIMER) || | |
596 | (cmd->scan_begin_src == TRIG_FOLLOW))) { | |
597 | error++; | |
598 | } | |
599 | if ((cmd->convert_src == TRIG_EXT) && | |
600 | !((cmd->scan_begin_src == TRIG_EXT) || | |
601 | (cmd->scan_begin_src == TRIG_FOLLOW))) { | |
602 | error++; | |
603 | } | |
604 | ||
605 | if (cmd->scan_end_src != TRIG_COUNT) | |
606 | error++; | |
607 | if ((cmd->stop_src != TRIG_COUNT) && (cmd->stop_src != TRIG_NONE)) | |
608 | error++; | |
609 | ||
610 | if (error) | |
611 | return 2; | |
612 | ||
613 | // Step 3 : make sure arguments are trivialy compatible | |
614 | ||
615 | if (cmd->chanlist_len < 1) { | |
616 | cmd->chanlist_len = 1; | |
617 | error++; | |
618 | } | |
619 | ||
620 | if (cmd->chanlist_len > board->ai_channel_nbr) { | |
621 | cmd->chanlist_len = board->ai_channel_nbr; | |
622 | error++; | |
623 | } | |
624 | ||
625 | if ((cmd->start_src == TRIG_NOW) && (cmd->start_arg != 0)) { | |
626 | cmd->start_arg = 0; | |
627 | error++; | |
628 | } | |
629 | ||
630 | if ((cmd->convert_src == TRIG_TIMER) && | |
631 | (cmd->convert_arg < board->ai_acquisition_period_min_ns)) { | |
632 | cmd->convert_arg = board->ai_acquisition_period_min_ns; | |
633 | error++; | |
634 | } | |
635 | if ((cmd->convert_src == TRIG_EXT) && (cmd->convert_arg != 0)) { | |
636 | cmd->convert_arg = 0; | |
637 | error++; | |
638 | } | |
639 | ||
640 | if ((cmd->scan_begin_src == TRIG_TIMER) && | |
641 | (cmd->scan_begin_arg < board->ai_acquisition_period_min_ns)) { | |
642 | cmd->scan_begin_arg = board->ai_acquisition_period_min_ns; | |
643 | error++; | |
644 | } | |
645 | if ((cmd->scan_begin_src == TRIG_FOLLOW) && (cmd->scan_begin_arg != 0)) { | |
646 | cmd->scan_begin_arg = 0; | |
647 | error++; | |
648 | } | |
649 | if ((cmd->scan_begin_src == TRIG_EXT) && (cmd->scan_begin_arg != 0)) { | |
650 | cmd->scan_begin_arg = 0; | |
651 | error++; | |
652 | } | |
653 | ||
654 | if ((cmd->scan_end_src == TRIG_COUNT) && | |
655 | (cmd->scan_end_arg != cmd->chanlist_len)) { | |
656 | cmd->scan_end_arg = cmd->chanlist_len; | |
657 | error++; | |
658 | } | |
659 | ||
660 | if ((cmd->stop_src == TRIG_COUNT) && (cmd->stop_arg < 1)) { | |
661 | cmd->stop_arg = 1; | |
662 | error++; | |
663 | } | |
664 | if ((cmd->stop_src == TRIG_NONE) && (cmd->stop_arg != 0)) { | |
665 | cmd->stop_arg = 0; | |
666 | error++; | |
667 | } | |
668 | ||
669 | if (error) | |
670 | return 3; | |
671 | ||
672 | // Step 4 : fix up any arguments | |
673 | ||
674 | if (cmd->convert_src == TRIG_TIMER) { | |
675 | tmp = cmd->convert_arg; | |
676 | i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS, | |
677 | &(dev_private->timer_divisor_1), | |
678 | &(dev_private->timer_divisor_2), | |
679 | &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); | |
680 | if (tmp != cmd->convert_arg) | |
681 | error++; | |
682 | } | |
683 | // There's only one timer on this card, so the scan_begin timer must | |
684 | // be a multiple of chanlist_len*convert_arg | |
685 | ||
686 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
687 | ||
688 | unsigned int scan_begin_min; | |
689 | unsigned int scan_begin_arg; | |
690 | unsigned int scan_factor; | |
691 | ||
692 | scan_begin_min = cmd->chanlist_len * cmd->convert_arg; | |
693 | ||
694 | if (cmd->scan_begin_arg != scan_begin_min) { | |
695 | if (scan_begin_min < cmd->scan_begin_arg) { | |
696 | scan_factor = | |
697 | cmd->scan_begin_arg / scan_begin_min; | |
698 | scan_begin_arg = scan_factor * scan_begin_min; | |
699 | if (cmd->scan_begin_arg != scan_begin_arg) { | |
700 | cmd->scan_begin_arg = scan_begin_arg; | |
701 | error++; | |
702 | } | |
703 | } else { | |
704 | cmd->scan_begin_arg = scan_begin_min; | |
705 | error++; | |
706 | } | |
707 | } | |
708 | } | |
709 | ||
710 | if (error) | |
711 | return 4; | |
712 | ||
713 | // Step 5 : check channel list | |
714 | ||
715 | if (cmd->chanlist) { | |
716 | ||
717 | range = CR_RANGE(cmd->chanlist[0]); | |
718 | reference = CR_AREF(cmd->chanlist[0]); | |
719 | ||
720 | if (cmd->chanlist_len > 1) { | |
721 | for (i = 0; i < cmd->chanlist_len; i++) { | |
722 | if (CR_CHAN(cmd->chanlist[i]) != i) { | |
723 | comedi_error(dev, | |
724 | "entries in chanlist must be consecutive " | |
725 | "channels,counting upwards from 0\n"); | |
726 | error++; | |
727 | } | |
728 | if (CR_RANGE(cmd->chanlist[i]) != range) { | |
729 | comedi_error(dev, | |
730 | "entries in chanlist must all have the same gain\n"); | |
731 | error++; | |
732 | } | |
733 | if (CR_AREF(cmd->chanlist[i]) != reference) { | |
734 | comedi_error(dev, | |
735 | "entries in chanlist must all have the same reference\n"); | |
736 | error++; | |
737 | } | |
738 | } | |
739 | } else { | |
740 | if ((CR_CHAN(cmd->chanlist[0]) > | |
741 | (board->ai_channel_nbr - 1)) | |
742 | || (CR_CHAN(cmd->chanlist[0]) < 0)) { | |
743 | comedi_error(dev, | |
744 | "channel number is out of limits\n"); | |
745 | error++; | |
746 | } | |
747 | } | |
748 | } | |
749 | ||
750 | if (error) | |
751 | return 5; | |
752 | ||
753 | return 0; | |
754 | ||
755 | } | |
756 | ||
757 | // | |
758 | // Analog input command | |
759 | // | |
760 | ||
34c43922 | 761 | static int pci9111_ai_do_cmd(struct comedi_device * dev, struct comedi_subdevice * subdevice) |
8cb9b9fb | 762 | { |
ea6d0d4c | 763 | struct comedi_cmd *async_cmd = &subdevice->async->cmd; |
8cb9b9fb EP |
764 | |
765 | if (!dev->irq) { | |
766 | comedi_error(dev, | |
767 | "no irq assigned for PCI9111, cannot do hardware conversion"); | |
768 | return -1; | |
769 | } | |
770 | // Set channel scan limit | |
771 | // | |
772 | // PCI9111 allows only scanning from channel 0 to channel n | |
773 | // | |
774 | // TODO: handle the case of an external multiplexer | |
775 | // | |
776 | ||
777 | if (async_cmd->chanlist_len > 1) { | |
778 | pci9111_ai_channel_set((async_cmd->chanlist_len) - 1); | |
779 | pci9111_autoscan_set(dev, true); | |
780 | } else { | |
781 | pci9111_ai_channel_set(CR_CHAN(async_cmd->chanlist[0])); | |
782 | pci9111_autoscan_set(dev, false); | |
783 | } | |
784 | ||
785 | // Set gain | |
786 | // | |
787 | // This is the same gain on every channel | |
788 | // | |
789 | ||
790 | pci9111_ai_range_set(CR_RANGE(async_cmd->chanlist[0])); | |
791 | ||
792 | /* Set counter */ | |
793 | ||
794 | switch (async_cmd->stop_src) { | |
795 | case TRIG_COUNT: | |
796 | dev_private->stop_counter = | |
797 | async_cmd->stop_arg * async_cmd->chanlist_len; | |
798 | dev_private->stop_is_none = 0; | |
799 | break; | |
800 | ||
801 | case TRIG_NONE: | |
802 | dev_private->stop_counter = 0; | |
803 | dev_private->stop_is_none = 1; | |
804 | break; | |
805 | ||
806 | default: | |
807 | comedi_error(dev, "Invalid stop trigger"); | |
808 | return -1; | |
809 | } | |
810 | ||
811 | // Set timer pacer | |
812 | ||
813 | dev_private->scan_delay = 0; | |
814 | switch (async_cmd->convert_src) { | |
815 | case TRIG_TIMER: | |
816 | i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS, | |
817 | &(dev_private->timer_divisor_1), | |
818 | &(dev_private->timer_divisor_2), | |
819 | &(async_cmd->convert_arg), | |
820 | async_cmd->flags & TRIG_ROUND_MASK); | |
821 | #ifdef AI_DO_CMD_DEBUG | |
822 | printk(PCI9111_DRIVER_NAME ": divisors = %d, %d\n", | |
823 | dev_private->timer_divisor_1, | |
824 | dev_private->timer_divisor_2); | |
825 | #endif | |
826 | ||
827 | pci9111_trigger_source_set(dev, software); | |
828 | pci9111_timer_set(dev); | |
829 | pci9111_fifo_reset(); | |
830 | pci9111_interrupt_source_set(dev, irq_on_fifo_half_full, | |
831 | irq_on_timer_tick); | |
832 | pci9111_trigger_source_set(dev, timer_pacer); | |
833 | plx9050_interrupt_control(dev_private->lcr_io_base, true, true, | |
834 | false, true, true); | |
835 | ||
836 | dev_private->scan_delay = | |
837 | (async_cmd->scan_begin_arg / (async_cmd->convert_arg * | |
838 | async_cmd->chanlist_len)) - 1; | |
839 | ||
840 | break; | |
841 | ||
842 | case TRIG_EXT: | |
843 | ||
844 | pci9111_trigger_source_set(dev, external); | |
845 | pci9111_fifo_reset(); | |
846 | pci9111_interrupt_source_set(dev, irq_on_fifo_half_full, | |
847 | irq_on_timer_tick); | |
848 | plx9050_interrupt_control(dev_private->lcr_io_base, true, true, | |
849 | false, true, true); | |
850 | ||
851 | break; | |
852 | ||
853 | default: | |
854 | comedi_error(dev, "Invalid convert trigger"); | |
855 | return -1; | |
856 | } | |
857 | ||
858 | dev_private->stop_counter *= (1 + dev_private->scan_delay); | |
859 | dev_private->chanlist_len = async_cmd->chanlist_len; | |
860 | dev_private->chunk_counter = 0; | |
861 | dev_private->chunk_num_samples = | |
862 | dev_private->chanlist_len * (1 + dev_private->scan_delay); | |
863 | ||
864 | #ifdef AI_DO_CMD_DEBUG | |
865 | printk(PCI9111_DRIVER_NAME ": start interruptions!\n"); | |
866 | printk(PCI9111_DRIVER_NAME ": trigger source = %2x\n", | |
867 | pci9111_trigger_and_autoscan_get()); | |
868 | printk(PCI9111_DRIVER_NAME ": irq source = %2x\n", | |
869 | pci9111_interrupt_and_fifo_get()); | |
870 | printk(PCI9111_DRIVER_NAME ": ai_do_cmd\n"); | |
871 | printk(PCI9111_DRIVER_NAME ": stop counter = %d\n", | |
872 | dev_private->stop_counter); | |
873 | printk(PCI9111_DRIVER_NAME ": scan delay = %d\n", | |
874 | dev_private->scan_delay); | |
875 | printk(PCI9111_DRIVER_NAME ": chanlist_len = %d\n", | |
876 | dev_private->chanlist_len); | |
877 | printk(PCI9111_DRIVER_NAME ": chunk num samples = %d\n", | |
878 | dev_private->chunk_num_samples); | |
879 | #endif | |
880 | ||
881 | return 0; | |
882 | } | |
883 | ||
34c43922 | 884 | static void pci9111_ai_munge(struct comedi_device * dev, struct comedi_subdevice * s, |
8cb9b9fb EP |
885 | void *data, unsigned int num_bytes, unsigned int start_chan_index) |
886 | { | |
790c5541 BP |
887 | unsigned int i, num_samples = num_bytes / sizeof(short); |
888 | short *array = data; | |
8cb9b9fb | 889 | int resolution = |
940579fb | 890 | ((struct pci9111_board *) dev->board_ptr)->ai_resolution; |
8cb9b9fb EP |
891 | |
892 | for (i = 0; i < num_samples; i++) { | |
893 | if (resolution == PCI9111_HR_AI_RESOLUTION) | |
894 | array[i] = | |
895 | (array[i] & PCI9111_HR_AI_RESOLUTION_MASK) ^ | |
896 | PCI9111_HR_AI_RESOLUTION_2_CMP_BIT; | |
897 | else | |
898 | array[i] = | |
899 | ((array[i] >> 4) & PCI9111_AI_RESOLUTION_MASK) ^ | |
900 | PCI9111_AI_RESOLUTION_2_CMP_BIT; | |
901 | } | |
902 | } | |
903 | ||
904 | // ------------------------------------------------------------------ | |
905 | // | |
906 | // INTERRUPT SECTION | |
907 | // | |
908 | // ------------------------------------------------------------------ | |
909 | ||
910 | #undef INTERRUPT_DEBUG | |
911 | ||
912 | static irqreturn_t pci9111_interrupt(int irq, void *p_device PT_REGS_ARG) | |
913 | { | |
71b5f4f1 | 914 | struct comedi_device *dev = p_device; |
34c43922 | 915 | struct comedi_subdevice *subdevice = dev->read_subdev; |
d163679c | 916 | struct comedi_async *async; |
8cb9b9fb EP |
917 | unsigned long irq_flags; |
918 | unsigned char intcsr; | |
919 | ||
920 | if (!dev->attached) { | |
921 | // Ignore interrupt before device fully attached. | |
922 | // Might not even have allocated subdevices yet! | |
923 | return IRQ_NONE; | |
924 | } | |
925 | ||
926 | async = subdevice->async; | |
927 | ||
928 | comedi_spin_lock_irqsave(&dev->spinlock, irq_flags); | |
929 | ||
930 | // Check if we are source of interrupt | |
931 | intcsr = inb(dev_private->lcr_io_base + | |
932 | PLX9050_REGISTER_INTERRUPT_CONTROL); | |
933 | if (!(((intcsr & PLX9050_PCI_INTERRUPT_ENABLE) != 0) | |
934 | && (((intcsr & (PLX9050_LINTI1_ENABLE | | |
935 | PLX9050_LINTI1_STATUS)) | |
936 | == | |
937 | (PLX9050_LINTI1_ENABLE | | |
938 | PLX9050_LINTI1_STATUS)) | |
939 | || ((intcsr & (PLX9050_LINTI2_ENABLE | | |
940 | PLX9050_LINTI2_STATUS)) | |
941 | == | |
942 | (PLX9050_LINTI2_ENABLE | | |
943 | PLX9050_LINTI2_STATUS))))) { | |
944 | // Not the source of the interrupt. | |
945 | // (N.B. not using PLX9050_SOFTWARE_INTERRUPT) | |
946 | comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); | |
947 | return IRQ_NONE; | |
948 | } | |
949 | ||
950 | if ((intcsr & (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) == | |
951 | (PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) { | |
952 | // Interrupt comes from fifo_half-full signal | |
953 | ||
954 | if (pci9111_is_fifo_full()) { | |
955 | comedi_spin_unlock_irqrestore(&dev->spinlock, | |
956 | irq_flags); | |
957 | comedi_error(dev, PCI9111_DRIVER_NAME " fifo overflow"); | |
958 | pci9111_interrupt_clear(); | |
959 | pci9111_ai_cancel(dev, subdevice); | |
960 | async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; | |
961 | comedi_event(dev, subdevice); | |
962 | ||
963 | return IRQ_HANDLED; | |
964 | } | |
965 | ||
966 | if (pci9111_is_fifo_half_full()) { | |
967 | unsigned int num_samples; | |
968 | unsigned int bytes_written = 0; | |
969 | ||
970 | #ifdef INTERRUPT_DEBUG | |
971 | printk(PCI9111_DRIVER_NAME ": fifo is half full\n"); | |
972 | #endif | |
973 | ||
974 | num_samples = | |
975 | PCI9111_FIFO_HALF_SIZE > | |
976 | dev_private->stop_counter | |
977 | && !dev_private->stop_is_none ? dev_private-> | |
978 | stop_counter : PCI9111_FIFO_HALF_SIZE; | |
979 | insw(PCI9111_IO_BASE + PCI9111_REGISTER_AD_FIFO_VALUE, | |
980 | dev_private->ai_bounce_buffer, num_samples); | |
981 | ||
982 | if (dev_private->scan_delay < 1) { | |
983 | bytes_written = | |
984 | cfc_write_array_to_buffer(subdevice, | |
985 | dev_private->ai_bounce_buffer, | |
790c5541 | 986 | num_samples * sizeof(short)); |
8cb9b9fb EP |
987 | } else { |
988 | int position = 0; | |
989 | int to_read; | |
990 | ||
991 | while (position < num_samples) { | |
992 | if (dev_private->chunk_counter < | |
993 | dev_private->chanlist_len) { | |
994 | to_read = | |
995 | dev_private-> | |
996 | chanlist_len - | |
997 | dev_private-> | |
998 | chunk_counter; | |
999 | ||
1000 | if (to_read > | |
1001 | num_samples - position) | |
1002 | to_read = | |
1003 | num_samples - | |
1004 | position; | |
1005 | ||
1006 | bytes_written += | |
1007 | cfc_write_array_to_buffer | |
1008 | (subdevice, | |
1009 | dev_private-> | |
1010 | ai_bounce_buffer + | |
1011 | position, | |
1012 | to_read * | |
790c5541 | 1013 | sizeof(short)); |
8cb9b9fb EP |
1014 | } else { |
1015 | to_read = | |
1016 | dev_private-> | |
1017 | chunk_num_samples - | |
1018 | dev_private-> | |
1019 | chunk_counter; | |
1020 | if (to_read > | |
1021 | num_samples - position) | |
1022 | to_read = | |
1023 | num_samples - | |
1024 | position; | |
1025 | ||
1026 | bytes_written += | |
790c5541 | 1027 | sizeof(short) * |
8cb9b9fb EP |
1028 | to_read; |
1029 | } | |
1030 | ||
1031 | position += to_read; | |
1032 | dev_private->chunk_counter += to_read; | |
1033 | ||
1034 | if (dev_private->chunk_counter >= | |
1035 | dev_private->chunk_num_samples) | |
1036 | dev_private->chunk_counter = 0; | |
1037 | } | |
1038 | } | |
1039 | ||
1040 | dev_private->stop_counter -= | |
790c5541 | 1041 | bytes_written / sizeof(short); |
8cb9b9fb EP |
1042 | } |
1043 | } | |
1044 | ||
1045 | if ((dev_private->stop_counter == 0) && (!dev_private->stop_is_none)) { | |
1046 | async->events |= COMEDI_CB_EOA; | |
1047 | pci9111_ai_cancel(dev, subdevice); | |
1048 | } | |
1049 | ||
1050 | /* Very important, otherwise another interrupt request will be inserted | |
1051 | * and will cause driver hangs on processing interrupt event. */ | |
1052 | ||
1053 | pci9111_interrupt_clear(); | |
1054 | ||
1055 | comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags); | |
1056 | ||
1057 | comedi_event(dev, subdevice); | |
1058 | ||
1059 | return IRQ_HANDLED; | |
1060 | } | |
1061 | ||
1062 | // ------------------------------------------------------------------ | |
1063 | // | |
1064 | // INSTANT ANALOG INPUT OUTPUT SECTION | |
1065 | // | |
1066 | // ------------------------------------------------------------------ | |
1067 | ||
1068 | // | |
1069 | // analog instant input | |
1070 | // | |
1071 | ||
1072 | #undef AI_INSN_DEBUG | |
1073 | ||
71b5f4f1 | 1074 | static int pci9111_ai_insn_read(struct comedi_device * dev, |
90035c08 | 1075 | struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data) |
8cb9b9fb EP |
1076 | { |
1077 | int resolution = | |
940579fb | 1078 | ((struct pci9111_board *) dev->board_ptr)->ai_resolution; |
8cb9b9fb EP |
1079 | |
1080 | int timeout, i; | |
1081 | ||
1082 | #ifdef AI_INSN_DEBUG | |
1083 | printk(PCI9111_DRIVER_NAME ": ai_insn set c/r/n = %2x/%2x/%2x\n", | |
1084 | CR_CHAN((&insn->chanspec)[0]), | |
1085 | CR_RANGE((&insn->chanspec)[0]), insn->n); | |
1086 | #endif | |
1087 | ||
1088 | pci9111_ai_channel_set(CR_CHAN((&insn->chanspec)[0])); | |
1089 | ||
1090 | if ((pci9111_ai_range_get()) != CR_RANGE((&insn->chanspec)[0])) { | |
1091 | pci9111_ai_range_set(CR_RANGE((&insn->chanspec)[0])); | |
1092 | } | |
1093 | ||
1094 | pci9111_fifo_reset(); | |
1095 | ||
1096 | for (i = 0; i < insn->n; i++) { | |
1097 | pci9111_software_trigger(); | |
1098 | ||
1099 | timeout = PCI9111_AI_INSTANT_READ_TIMEOUT; | |
1100 | ||
1101 | while (timeout--) { | |
1102 | if (!pci9111_is_fifo_empty()) | |
1103 | goto conversion_done; | |
1104 | } | |
1105 | ||
1106 | comedi_error(dev, "A/D read timeout"); | |
1107 | data[i] = 0; | |
1108 | pci9111_fifo_reset(); | |
1109 | return -ETIME; | |
1110 | ||
1111 | conversion_done: | |
1112 | ||
1113 | if (resolution == PCI9111_HR_AI_RESOLUTION) { | |
1114 | data[i] = pci9111_hr_ai_get_data(); | |
1115 | } else { | |
1116 | data[i] = pci9111_ai_get_data(); | |
1117 | } | |
1118 | } | |
1119 | ||
1120 | #ifdef AI_INSN_DEBUG | |
1121 | printk(PCI9111_DRIVER_NAME ": ai_insn get c/r/t = %2x/%2x/%2x\n", | |
1122 | pci9111_ai_channel_get(), | |
1123 | pci9111_ai_range_get(), pci9111_trigger_and_autoscan_get()); | |
1124 | #endif | |
1125 | ||
1126 | return i; | |
1127 | } | |
1128 | ||
1129 | // | |
1130 | // Analog instant output | |
1131 | // | |
1132 | ||
1133 | static int | |
71b5f4f1 | 1134 | pci9111_ao_insn_write(struct comedi_device * dev, |
90035c08 | 1135 | struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data) |
8cb9b9fb EP |
1136 | { |
1137 | int i; | |
1138 | ||
1139 | for (i = 0; i < insn->n; i++) { | |
1140 | pci9111_ao_set_data(data[i]); | |
1141 | dev_private->ao_readback = data[i]; | |
1142 | } | |
1143 | ||
1144 | return i; | |
1145 | } | |
1146 | ||
1147 | // | |
1148 | // Analog output readback | |
1149 | // | |
1150 | ||
71b5f4f1 | 1151 | static int pci9111_ao_insn_read(struct comedi_device * dev, |
90035c08 | 1152 | struct comedi_subdevice * s, struct comedi_insn * insn, unsigned int * data) |
8cb9b9fb EP |
1153 | { |
1154 | int i; | |
1155 | ||
1156 | for (i = 0; i < insn->n; i++) { | |
1157 | data[i] = dev_private->ao_readback & PCI9111_AO_RESOLUTION_MASK; | |
1158 | } | |
1159 | ||
1160 | return i; | |
1161 | } | |
1162 | ||
1163 | // ------------------------------------------------------------------ | |
1164 | // | |
1165 | // DIGITAL INPUT OUTPUT SECTION | |
1166 | // | |
1167 | // ------------------------------------------------------------------ | |
1168 | ||
1169 | // | |
1170 | // Digital inputs | |
1171 | // | |
1172 | ||
71b5f4f1 | 1173 | static int pci9111_di_insn_bits(struct comedi_device * dev, |
90035c08 | 1174 | struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data) |
8cb9b9fb | 1175 | { |
790c5541 | 1176 | unsigned int bits; |
8cb9b9fb EP |
1177 | |
1178 | bits = pci9111_di_get_bits(); | |
1179 | data[1] = bits; | |
1180 | ||
1181 | return 2; | |
1182 | } | |
1183 | ||
1184 | // | |
1185 | // Digital outputs | |
1186 | // | |
1187 | ||
71b5f4f1 | 1188 | static int pci9111_do_insn_bits(struct comedi_device * dev, |
90035c08 | 1189 | struct comedi_subdevice * subdevice, struct comedi_insn * insn, unsigned int * data) |
8cb9b9fb | 1190 | { |
790c5541 | 1191 | unsigned int bits; |
8cb9b9fb EP |
1192 | |
1193 | // Only set bits that have been masked | |
1194 | // data[0] = mask | |
1195 | // data[1] = bit state | |
1196 | ||
1197 | data[0] &= PCI9111_DO_MASK; | |
1198 | ||
1199 | bits = subdevice->state; | |
1200 | bits &= ~data[0]; | |
1201 | bits |= data[0] & data[1]; | |
1202 | subdevice->state = bits; | |
1203 | ||
1204 | pci9111_do_set_bits(bits); | |
1205 | ||
1206 | data[1] = bits; | |
1207 | ||
1208 | return 2; | |
1209 | } | |
1210 | ||
1211 | // ------------------------------------------------------------------ | |
1212 | // | |
1213 | // INITIALISATION SECTION | |
1214 | // | |
1215 | // ------------------------------------------------------------------ | |
1216 | ||
1217 | // | |
1218 | // Reset device | |
1219 | // | |
1220 | ||
71b5f4f1 | 1221 | static int pci9111_reset(struct comedi_device * dev) |
8cb9b9fb EP |
1222 | { |
1223 | // Set trigger source to software | |
1224 | ||
1225 | plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true, | |
1226 | true, false); | |
1227 | ||
1228 | pci9111_trigger_source_set(dev, software); | |
1229 | pci9111_pretrigger_set(dev, false); | |
1230 | pci9111_autoscan_set(dev, false); | |
1231 | ||
1232 | // Reset 8254 chip | |
1233 | ||
1234 | dev_private->timer_divisor_1 = 0; | |
1235 | dev_private->timer_divisor_2 = 0; | |
1236 | ||
1237 | pci9111_timer_set(dev); | |
1238 | ||
1239 | return 0; | |
1240 | } | |
1241 | ||
1242 | // | |
1243 | // Attach | |
1244 | // | |
1245 | // - Register PCI device | |
1246 | // - Declare device driver capability | |
1247 | // | |
1248 | ||
0707bb04 | 1249 | static int pci9111_attach(struct comedi_device * dev, struct comedi_devconfig * it) |
8cb9b9fb | 1250 | { |
34c43922 | 1251 | struct comedi_subdevice *subdevice; |
8cb9b9fb EP |
1252 | unsigned long io_base, io_range, lcr_io_base, lcr_io_range; |
1253 | struct pci_dev *pci_device; | |
1254 | int error, i; | |
940579fb | 1255 | const struct pci9111_board *board; |
8cb9b9fb | 1256 | |
c350fa19 | 1257 | if (alloc_private(dev, sizeof(struct pci9111_private_data)) < 0) { |
8cb9b9fb EP |
1258 | return -ENOMEM; |
1259 | } | |
1260 | // | |
1261 | // Probe the device to determine what device in the series it is. | |
1262 | // | |
1263 | ||
1264 | printk("comedi%d: " PCI9111_DRIVER_NAME " driver\n", dev->minor); | |
1265 | ||
1266 | for (pci_device = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); | |
1267 | pci_device != NULL; | |
1268 | pci_device = | |
1269 | pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_device)) { | |
1270 | if (pci_device->vendor == PCI_VENDOR_ID_ADLINK) { | |
1271 | for (i = 0; i < pci9111_board_nbr; i++) { | |
1272 | if (pci9111_boards[i].device_id == | |
1273 | pci_device->device) { | |
1274 | // was a particular bus/slot requested? | |
1275 | if ((it->options[0] != 0) | |
1276 | || (it->options[1] != 0)) { | |
1277 | // are we on the wrong bus/slot? | |
1278 | if (pci_device->bus->number != | |
1279 | it->options[0] | |
1280 | || PCI_SLOT(pci_device-> | |
1281 | devfn) != | |
1282 | it->options[1]) { | |
1283 | continue; | |
1284 | } | |
1285 | } | |
1286 | ||
1287 | dev->board_ptr = pci9111_boards + i; | |
940579fb | 1288 | board = (struct pci9111_board *) dev-> |
8cb9b9fb EP |
1289 | board_ptr; |
1290 | dev_private->pci_device = pci_device; | |
1291 | goto found; | |
1292 | } | |
1293 | } | |
1294 | } | |
1295 | } | |
1296 | ||
1297 | printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n", | |
1298 | dev->minor, it->options[0], it->options[1]); | |
1299 | return -EIO; | |
1300 | ||
1301 | found: | |
1302 | ||
1303 | printk("comedi%d: found %s (b:s:f=%d:%d:%d) , irq=%d\n", | |
1304 | dev->minor, | |
1305 | pci9111_boards[i].name, | |
1306 | pci_device->bus->number, | |
1307 | PCI_SLOT(pci_device->devfn), | |
1308 | PCI_FUNC(pci_device->devfn), pci_device->irq); | |
1309 | ||
1310 | // TODO: Warn about non-tested boards. | |
1311 | ||
1312 | switch (board->device_id) { | |
1313 | }; | |
1314 | ||
1315 | // Read local configuration register base address [PCI_BASE_ADDRESS #1]. | |
1316 | ||
1317 | lcr_io_base = pci_resource_start(pci_device, 1); | |
1318 | lcr_io_range = pci_resource_len(pci_device, 1); | |
1319 | ||
1320 | printk("comedi%d: local configuration registers at address 0x%4lx [0x%4lx]\n", dev->minor, lcr_io_base, lcr_io_range); | |
1321 | ||
1322 | // Enable PCI device and request regions | |
1323 | if (comedi_pci_enable(pci_device, PCI9111_DRIVER_NAME) < 0) { | |
1324 | printk("comedi%d: Failed to enable PCI device and request regions\n", dev->minor); | |
1325 | return -EIO; | |
1326 | } | |
1327 | // Read PCI6308 register base address [PCI_BASE_ADDRESS #2]. | |
1328 | ||
1329 | io_base = pci_resource_start(pci_device, 2); | |
1330 | io_range = pci_resource_len(pci_device, 2); | |
1331 | ||
1332 | printk("comedi%d: 6503 registers at address 0x%4lx [0x%4lx]\n", | |
1333 | dev->minor, io_base, io_range); | |
1334 | ||
1335 | dev->iobase = io_base; | |
1336 | dev->board_name = board->name; | |
1337 | dev_private->io_range = io_range; | |
1338 | dev_private->is_valid = 0; | |
1339 | dev_private->lcr_io_base = lcr_io_base; | |
1340 | dev_private->lcr_io_range = lcr_io_range; | |
1341 | ||
1342 | pci9111_reset(dev); | |
1343 | ||
1344 | // Irq setup | |
1345 | ||
1346 | dev->irq = 0; | |
1347 | if (pci_device->irq > 0) { | |
1348 | if (comedi_request_irq(pci_device->irq, | |
1349 | pci9111_interrupt, | |
1350 | IRQF_SHARED, PCI9111_DRIVER_NAME, dev) != 0) { | |
1351 | printk("comedi%d: unable to allocate irq %u\n", | |
1352 | dev->minor, pci_device->irq); | |
1353 | return -EINVAL; | |
1354 | } | |
1355 | } | |
1356 | dev->irq = pci_device->irq; | |
1357 | ||
1358 | // | |
1359 | // TODO: Add external multiplexer setup (according to option[2]). | |
1360 | // | |
1361 | ||
1362 | if ((error = alloc_subdevices(dev, 4)) < 0) | |
1363 | return error; | |
1364 | ||
1365 | subdevice = dev->subdevices + 0; | |
1366 | dev->read_subdev = subdevice; | |
1367 | ||
1368 | subdevice->type = COMEDI_SUBD_AI; | |
1369 | subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ; | |
1370 | ||
1371 | // | |
1372 | // TODO: Add external multiplexer data | |
1373 | // | |
1374 | // if (devpriv->usemux) { subdevice->n_chan = devpriv->usemux; } | |
1375 | // else { subdevice->n_chan = this_board->n_aichan; } | |
1376 | // | |
1377 | ||
1378 | subdevice->n_chan = board->ai_channel_nbr; | |
1379 | subdevice->maxdata = board->ai_resolution_mask; | |
1380 | subdevice->len_chanlist = board->ai_channel_nbr; | |
1381 | subdevice->range_table = board->ai_range_list; | |
1382 | subdevice->cancel = pci9111_ai_cancel; | |
1383 | subdevice->insn_read = pci9111_ai_insn_read; | |
1384 | subdevice->do_cmdtest = pci9111_ai_do_cmd_test; | |
1385 | subdevice->do_cmd = pci9111_ai_do_cmd; | |
1386 | subdevice->munge = pci9111_ai_munge; | |
1387 | ||
1388 | subdevice = dev->subdevices + 1; | |
1389 | subdevice->type = COMEDI_SUBD_AO; | |
1390 | subdevice->subdev_flags = SDF_WRITABLE | SDF_COMMON; | |
1391 | subdevice->n_chan = board->ao_channel_nbr; | |
1392 | subdevice->maxdata = board->ao_resolution_mask; | |
1393 | subdevice->len_chanlist = board->ao_channel_nbr; | |
1394 | subdevice->range_table = board->ao_range_list; | |
1395 | subdevice->insn_write = pci9111_ao_insn_write; | |
1396 | subdevice->insn_read = pci9111_ao_insn_read; | |
1397 | ||
1398 | subdevice = dev->subdevices + 2; | |
1399 | subdevice->type = COMEDI_SUBD_DI; | |
1400 | subdevice->subdev_flags = SDF_READABLE; | |
1401 | subdevice->n_chan = PCI9111_DI_CHANNEL_NBR; | |
1402 | subdevice->maxdata = 1; | |
1403 | subdevice->range_table = &range_digital; | |
1404 | subdevice->insn_bits = pci9111_di_insn_bits; | |
1405 | ||
1406 | subdevice = dev->subdevices + 3; | |
1407 | subdevice->type = COMEDI_SUBD_DO; | |
1408 | subdevice->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
1409 | subdevice->n_chan = PCI9111_DO_CHANNEL_NBR; | |
1410 | subdevice->maxdata = 1; | |
1411 | subdevice->range_table = &range_digital; | |
1412 | subdevice->insn_bits = pci9111_do_insn_bits; | |
1413 | ||
1414 | dev_private->is_valid = 1; | |
1415 | ||
1416 | return 0; | |
1417 | } | |
1418 | ||
1419 | // | |
1420 | // Detach | |
1421 | // | |
1422 | ||
71b5f4f1 | 1423 | static int pci9111_detach(struct comedi_device * dev) |
8cb9b9fb EP |
1424 | { |
1425 | // Reset device | |
1426 | ||
1427 | if (dev->private != 0) { | |
1428 | if (dev_private->is_valid) | |
1429 | pci9111_reset(dev); | |
1430 | ||
1431 | } | |
1432 | // Release previously allocated irq | |
1433 | ||
1434 | if (dev->irq != 0) { | |
1435 | comedi_free_irq(dev->irq, dev); | |
1436 | } | |
1437 | ||
1438 | if (dev_private != 0 && dev_private->pci_device != 0) { | |
1439 | if (dev->iobase) { | |
1440 | comedi_pci_disable(dev_private->pci_device); | |
1441 | } | |
1442 | pci_dev_put(dev_private->pci_device); | |
1443 | } | |
1444 | ||
1445 | return 0; | |
1446 | } |