Commit | Line | Data |
---|---|---|
3d9f0739 DC |
1 | /* |
2 | comedi/drivers/rtd520.c | |
3 | Comedi driver for Real Time Devices (RTD) PCI4520/DM7520 | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2001 David A. Schleef <ds@schleef.org> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | */ | |
22 | /* | |
23 | Driver: rtd520 | |
24 | Description: Real Time Devices PCI4520/DM7520 | |
25 | Author: Dan Christian | |
26 | Devices: [Real Time Devices] DM7520HR-1 (rtd520), DM7520HR-8, | |
27 | PCI4520, PCI4520-8 | |
28 | Status: Works. Only tested on DM7520-8. Not SMP safe. | |
29 | ||
30 | Configuration options: | |
31 | [0] - PCI bus of device (optional) | |
32 | If bus/slot is not specified, the first available PCI | |
33 | device will be used. | |
34 | [1] - PCI slot of device (optional) | |
35 | */ | |
36 | /* | |
37 | Created by Dan Christian, NASA Ames Research Center. | |
38 | ||
39 | The PCI4520 is a PCI card. The DM7520 is a PC/104-plus card. | |
40 | Both have: | |
41 | 8/16 12 bit ADC with FIFO and channel gain table | |
42 | 8 bits high speed digital out (for external MUX) (or 8 in or 8 out) | |
43 | 8 bits high speed digital in with FIFO and interrupt on change (or 8 IO) | |
44 | 2 12 bit DACs with FIFOs | |
45 | 2 bits output | |
46 | 2 bits input | |
47 | bus mastering DMA | |
48 | timers: ADC sample, pacer, burst, about, delay, DA1, DA2 | |
49 | sample counter | |
50 | 3 user timer/counters (8254) | |
51 | external interrupt | |
52 | ||
53 | The DM7520 has slightly fewer features (fewer gain steps). | |
54 | ||
55 | These boards can support external multiplexors and multi-board | |
56 | synchronization, but this driver doesn't support that. | |
57 | ||
58 | Board docs: http://www.rtdusa.com/PC104/DM/analog%20IO/dm7520.htm | |
59 | Data sheet: http://www.rtdusa.com/pdf/dm7520.pdf | |
60 | Example source: http://www.rtdusa.com/examples/dm/dm7520.zip | |
61 | Call them and ask for the register level manual. | |
bc8bf90a | 62 | PCI chip: http://www.plxtech.com/products/io/pci9080 |
3d9f0739 DC |
63 | |
64 | Notes: | |
65 | This board is memory mapped. There is some IO stuff, but it isn't needed. | |
66 | ||
67 | I use a pretty loose naming style within the driver (rtd_blah). | |
68 | All externally visible names should be rtd520_blah. | |
69 | I use camelCase for structures (and inside them). | |
70 | I may also use upper CamelCase for function names (old habit). | |
71 | ||
72 | This board is somewhat related to the RTD PCI4400 board. | |
73 | ||
74 | I borrowed heavily from the ni_mio_common, ni_atmio16d, mite, and | |
75 | das1800, since they have the best documented code. Driver | |
76 | cb_pcidas64.c uses the same DMA controller. | |
77 | ||
25985edc | 78 | As far as I can tell, the About interrupt doesn't work if Sample is |
3d9f0739 DC |
79 | also enabled. It turns out that About really isn't needed, since |
80 | we always count down samples read. | |
81 | ||
82 | There was some timer/counter code, but it didn't follow the right API. | |
83 | ||
84 | */ | |
85 | ||
86 | /* | |
87 | driver status: | |
88 | ||
89 | Analog-In supports instruction and command mode. | |
90 | ||
91 | With DMA, you can sample at 1.15Mhz with 70% idle on a 400Mhz K6-2 | |
92 | (single channel, 64K read buffer). I get random system lockups when | |
93 | using DMA with ALI-15xx based systems. I haven't been able to test | |
94 | any other chipsets. The lockups happen soon after the start of an | |
95 | acquistion, not in the middle of a long run. | |
96 | ||
97 | Without DMA, you can do 620Khz sampling with 20% idle on a 400Mhz K6-2 | |
98 | (with a 256K read buffer). | |
99 | ||
100 | Digital-IO and Analog-Out only support instruction mode. | |
101 | ||
102 | */ | |
103 | ||
25436dc9 | 104 | #include <linux/interrupt.h> |
3d9f0739 DC |
105 | #include <linux/delay.h> |
106 | ||
107 | #include "../comedidev.h" | |
108 | #include "comedi_pci.h" | |
109 | ||
110 | #define DRV_NAME "rtd520" | |
111 | ||
112 | /*====================================================================== | |
113 | Driver specific stuff (tunable) | |
114 | ======================================================================*/ | |
115 | /* Enable this to test the new DMA support. You may get hard lock ups */ | |
116 | /*#define USE_DMA*/ | |
117 | ||
118 | /* We really only need 2 buffers. More than that means being much | |
119 | smarter about knowing which ones are full. */ | |
120 | #define DMA_CHAIN_COUNT 2 /* max DMA segments/buffers in a ring (min 2) */ | |
121 | ||
122 | /* Target period for periodic transfers. This sets the user read latency. */ | |
123 | /* Note: There are certain rates where we give this up and transfer 1/2 FIFO */ | |
124 | /* If this is too low, efficiency is poor */ | |
125 | #define TRANS_TARGET_PERIOD 10000000 /* 10 ms (in nanoseconds) */ | |
126 | ||
127 | /* Set a practical limit on how long a list to support (affects memory use) */ | |
128 | /* The board support a channel list up to the FIFO length (1K or 8K) */ | |
129 | #define RTD_MAX_CHANLIST 128 /* max channel list that we allow */ | |
130 | ||
131 | /* tuning for ai/ao instruction done polling */ | |
132 | #ifdef FAST_SPIN | |
133 | #define WAIT_QUIETLY /* as nothing, spin on done bit */ | |
134 | #define RTD_ADC_TIMEOUT 66000 /* 2 msec at 33mhz bus rate */ | |
135 | #define RTD_DAC_TIMEOUT 66000 | |
136 | #define RTD_DMA_TIMEOUT 33000 /* 1 msec */ | |
137 | #else | |
138 | /* by delaying, power and electrical noise are reduced somewhat */ | |
bc8bf90a | 139 | #define WAIT_QUIETLY udelay(1) |
3d9f0739 DC |
140 | #define RTD_ADC_TIMEOUT 2000 /* in usec */ |
141 | #define RTD_DAC_TIMEOUT 2000 /* in usec */ | |
142 | #define RTD_DMA_TIMEOUT 1000 /* in usec */ | |
143 | #endif | |
144 | ||
145 | /*====================================================================== | |
146 | Board specific stuff | |
147 | ======================================================================*/ | |
148 | ||
149 | /* registers */ | |
150 | #define PCI_VENDOR_ID_RTD 0x1435 | |
151 | /* | |
152 | The board has three memory windows: las0, las1, and lcfg (the PCI chip) | |
153 | Las1 has the data and can be burst DMAed 32bits at a time. | |
154 | */ | |
155 | #define LCFG_PCIINDEX 0 | |
156 | /* PCI region 1 is a 256 byte IO space mapping. Use??? */ | |
157 | #define LAS0_PCIINDEX 2 /* PCI memory resources */ | |
158 | #define LAS1_PCIINDEX 3 | |
159 | #define LCFG_PCISIZE 0x100 | |
160 | #define LAS0_PCISIZE 0x200 | |
161 | #define LAS1_PCISIZE 0x10 | |
162 | ||
163 | #define RTD_CLOCK_RATE 8000000 /* 8Mhz onboard clock */ | |
164 | #define RTD_CLOCK_BASE 125 /* clock period in ns */ | |
165 | ||
166 | /* Note: these speed are slower than the spec, but fit the counter resolution*/ | |
167 | #define RTD_MAX_SPEED 1625 /* when sampling, in nanoseconds */ | |
168 | /* max speed if we don't have to wait for settling */ | |
169 | #define RTD_MAX_SPEED_1 875 /* if single channel, in nanoseconds */ | |
170 | ||
171 | #define RTD_MIN_SPEED 2097151875 /* (24bit counter) in nanoseconds */ | |
172 | /* min speed when only 1 channel (no burst counter) */ | |
173 | #define RTD_MIN_SPEED_1 5000000 /* 200Hz, in nanoseconds */ | |
174 | ||
175 | #include "rtd520.h" | |
176 | #include "plx9080.h" | |
177 | ||
178 | /* Setup continuous ring of 1/2 FIFO transfers. See RTD manual p91 */ | |
179 | #define DMA_MODE_BITS (\ | |
180 | PLX_LOCAL_BUS_16_WIDE_BITS \ | |
181 | | PLX_DMA_EN_READYIN_BIT \ | |
182 | | PLX_DMA_LOCAL_BURST_EN_BIT \ | |
183 | | PLX_EN_CHAIN_BIT \ | |
184 | | PLX_DMA_INTR_PCI_BIT \ | |
185 | | PLX_LOCAL_ADDR_CONST_BIT \ | |
186 | | PLX_DEMAND_MODE_BIT) | |
187 | ||
188 | #define DMA_TRANSFER_BITS (\ | |
189 | /* descriptors in PCI memory*/ PLX_DESC_IN_PCI_BIT \ | |
190 | /* interrupt at end of block */ | PLX_INTR_TERM_COUNT \ | |
191 | /* from board to PCI */ | PLX_XFER_LOCAL_TO_PCI) | |
192 | ||
193 | /*====================================================================== | |
194 | Comedi specific stuff | |
195 | ======================================================================*/ | |
196 | ||
197 | /* | |
198 | The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128) | |
199 | */ | |
9ced1de6 | 200 | static const struct comedi_lrange rtd_ai_7520_range = { 18, { |
0a85b6f0 MT |
201 | /* +-5V input range gain steps */ |
202 | BIP_RANGE(5.0), | |
203 | BIP_RANGE(5.0 / 2), | |
204 | BIP_RANGE(5.0 / 4), | |
205 | BIP_RANGE(5.0 / 8), | |
206 | BIP_RANGE(5.0 / | |
207 | 16), | |
208 | BIP_RANGE(5.0 / | |
209 | 32), | |
210 | /* +-10V input range gain steps */ | |
211 | BIP_RANGE(10.0), | |
212 | BIP_RANGE(10.0 / | |
213 | 2), | |
214 | BIP_RANGE(10.0 / | |
215 | 4), | |
216 | BIP_RANGE(10.0 / | |
217 | 8), | |
218 | BIP_RANGE(10.0 / | |
219 | 16), | |
220 | BIP_RANGE(10.0 / | |
221 | 32), | |
222 | /* +10V input range gain steps */ | |
223 | UNI_RANGE(10.0), | |
224 | UNI_RANGE(10.0 / | |
225 | 2), | |
226 | UNI_RANGE(10.0 / | |
227 | 4), | |
228 | UNI_RANGE(10.0 / | |
229 | 8), | |
230 | UNI_RANGE(10.0 / | |
231 | 16), | |
232 | UNI_RANGE(10.0 / | |
233 | 32), | |
234 | ||
235 | } | |
3d9f0739 DC |
236 | }; |
237 | ||
238 | /* PCI4520 has two more gains (6 more entries) */ | |
9ced1de6 | 239 | static const struct comedi_lrange rtd_ai_4520_range = { 24, { |
0a85b6f0 MT |
240 | /* +-5V input range gain steps */ |
241 | BIP_RANGE(5.0), | |
242 | BIP_RANGE(5.0 / 2), | |
243 | BIP_RANGE(5.0 / 4), | |
244 | BIP_RANGE(5.0 / 8), | |
245 | BIP_RANGE(5.0 / | |
246 | 16), | |
247 | BIP_RANGE(5.0 / | |
248 | 32), | |
249 | BIP_RANGE(5.0 / | |
250 | 64), | |
251 | BIP_RANGE(5.0 / | |
252 | 128), | |
253 | /* +-10V input range gain steps */ | |
254 | BIP_RANGE(10.0), | |
255 | BIP_RANGE(10.0 / | |
256 | 2), | |
257 | BIP_RANGE(10.0 / | |
258 | 4), | |
259 | BIP_RANGE(10.0 / | |
260 | 8), | |
261 | BIP_RANGE(10.0 / | |
262 | 16), | |
263 | BIP_RANGE(10.0 / | |
264 | 32), | |
265 | BIP_RANGE(10.0 / | |
266 | 64), | |
267 | BIP_RANGE(10.0 / | |
268 | 128), | |
269 | /* +10V input range gain steps */ | |
270 | UNI_RANGE(10.0), | |
271 | UNI_RANGE(10.0 / | |
272 | 2), | |
273 | UNI_RANGE(10.0 / | |
274 | 4), | |
275 | UNI_RANGE(10.0 / | |
276 | 8), | |
277 | UNI_RANGE(10.0 / | |
278 | 16), | |
279 | UNI_RANGE(10.0 / | |
280 | 32), | |
281 | UNI_RANGE(10.0 / | |
282 | 64), | |
283 | UNI_RANGE(10.0 / | |
284 | 128), | |
285 | } | |
3d9f0739 DC |
286 | }; |
287 | ||
288 | /* Table order matches range values */ | |
9ced1de6 | 289 | static const struct comedi_lrange rtd_ao_range = { 4, { |
0a85b6f0 MT |
290 | RANGE(0, 5), |
291 | RANGE(0, 10), | |
292 | RANGE(-5, 5), | |
293 | RANGE(-10, 10), | |
294 | } | |
3d9f0739 DC |
295 | }; |
296 | ||
297 | /* | |
298 | Board descriptions | |
299 | */ | |
d80235ce | 300 | struct rtdBoard { |
3d9f0739 DC |
301 | const char *name; /* must be first */ |
302 | int device_id; | |
303 | int aiChans; | |
304 | int aiBits; | |
305 | int aiMaxGain; | |
306 | int range10Start; /* start of +-10V range */ | |
307 | int rangeUniStart; /* start of +10V range */ | |
d80235ce | 308 | }; |
3d9f0739 | 309 | |
d80235ce | 310 | static const struct rtdBoard rtd520Boards[] = { |
3d9f0739 | 311 | { |
0a85b6f0 MT |
312 | .name = "DM7520", |
313 | .device_id = 0x7520, | |
314 | .aiChans = 16, | |
315 | .aiBits = 12, | |
316 | .aiMaxGain = 32, | |
317 | .range10Start = 6, | |
318 | .rangeUniStart = 12, | |
319 | }, | |
3d9f0739 | 320 | { |
0a85b6f0 MT |
321 | .name = "PCI4520", |
322 | .device_id = 0x4520, | |
323 | .aiChans = 16, | |
324 | .aiBits = 12, | |
325 | .aiMaxGain = 128, | |
326 | .range10Start = 8, | |
327 | .rangeUniStart = 16, | |
328 | }, | |
3d9f0739 DC |
329 | }; |
330 | ||
331 | static DEFINE_PCI_DEVICE_TABLE(rtd520_pci_table) = { | |
e43d94e5 JMC |
332 | { PCI_DEVICE(PCI_VENDOR_ID_RTD, 0x7520) }, |
333 | { PCI_DEVICE(PCI_VENDOR_ID_RTD, 0x4520) }, | |
334 | { 0 } | |
3d9f0739 DC |
335 | }; |
336 | ||
337 | MODULE_DEVICE_TABLE(pci, rtd520_pci_table); | |
338 | ||
339 | /* | |
340 | * Useful for shorthand access to the particular board structure | |
341 | */ | |
d80235ce | 342 | #define thisboard ((const struct rtdBoard *)dev->board_ptr) |
3d9f0739 DC |
343 | |
344 | /* | |
345 | This structure is for data unique to this hardware driver. | |
346 | This is also unique for each board in the system. | |
347 | */ | |
d80235ce | 348 | struct rtdPrivate { |
3d9f0739 DC |
349 | /* memory mapped board structures */ |
350 | void *las0; | |
351 | void *las1; | |
352 | void *lcfg; | |
353 | ||
354 | unsigned long intCount; /* interrupt count */ | |
355 | long aiCount; /* total transfer size (samples) */ | |
356 | int transCount; /* # to tranfer data. 0->1/2FIFO */ | |
357 | int flags; /* flag event modes */ | |
358 | ||
359 | /* PCI device info */ | |
360 | struct pci_dev *pci_dev; | |
361 | int got_regions; /* non-zero if PCI regions owned */ | |
362 | ||
363 | /* channel list info */ | |
364 | /* chanBipolar tracks whether a channel is bipolar (and needs +2048) */ | |
365 | unsigned char chanBipolar[RTD_MAX_CHANLIST / 8]; /* bit array */ | |
366 | ||
367 | /* read back data */ | |
790c5541 | 368 | unsigned int aoValue[2]; /* Used for AO read back */ |
3d9f0739 DC |
369 | |
370 | /* timer gate (when enabled) */ | |
371 | u8 utcGate[4]; /* 1 extra allows simple range check */ | |
372 | ||
25985edc | 373 | /* shadow registers affect other registers, but can't be read back */ |
3d9f0739 DC |
374 | /* The macros below update these on writes */ |
375 | u16 intMask; /* interrupt mask */ | |
376 | u16 intClearMask; /* interrupt clear mask */ | |
377 | u8 utcCtrl[4]; /* crtl mode for 3 utc + read back */ | |
378 | u8 dioStatus; /* could be read back (dio0Ctrl) */ | |
379 | #ifdef USE_DMA | |
380 | /* Always DMA 1/2 FIFO. Buffer (dmaBuff?) is (at least) twice that size. | |
381 | After transferring, interrupt processes 1/2 FIFO and passes to comedi */ | |
382 | s16 dma0Offset; /* current processing offset (0, 1/2) */ | |
383 | uint16_t *dma0Buff[DMA_CHAIN_COUNT]; /* DMA buffers (for ADC) */ | |
384 | dma_addr_t dma0BuffPhysAddr[DMA_CHAIN_COUNT]; /* physical addresses */ | |
385 | struct plx_dma_desc *dma0Chain; /* DMA descriptor ring for dmaBuff */ | |
386 | dma_addr_t dma0ChainPhysAddr; /* physical addresses */ | |
387 | /* shadow registers */ | |
388 | u8 dma0Control; | |
389 | u8 dma1Control; | |
390 | #endif /* USE_DMA */ | |
391 | unsigned fifoLen; | |
d80235ce | 392 | }; |
3d9f0739 DC |
393 | |
394 | /* bit defines for "flags" */ | |
395 | #define SEND_EOS 0x01 /* send End Of Scan events */ | |
396 | #define DMA0_ACTIVE 0x02 /* DMA0 is active */ | |
397 | #define DMA1_ACTIVE 0x04 /* DMA1 is active */ | |
398 | ||
399 | /* Macros for accessing channel list bit array */ | |
8086fff8 | 400 | #define CHAN_ARRAY_TEST(array, index) \ |
3d9f0739 | 401 | (((array)[(index)/8] >> ((index) & 0x7)) & 0x1) |
8086fff8 | 402 | #define CHAN_ARRAY_SET(array, index) \ |
3d9f0739 | 403 | (((array)[(index)/8] |= 1 << ((index) & 0x7))) |
8086fff8 | 404 | #define CHAN_ARRAY_CLEAR(array, index) \ |
3d9f0739 DC |
405 | (((array)[(index)/8] &= ~(1 << ((index) & 0x7)))) |
406 | ||
407 | /* | |
408 | * most drivers define the following macro to make it easy to | |
409 | * access the private structure. | |
410 | */ | |
d80235ce | 411 | #define devpriv ((struct rtdPrivate *)dev->private) |
3d9f0739 DC |
412 | |
413 | /* Macros to access registers */ | |
414 | ||
415 | /* Reset board */ | |
416 | #define RtdResetBoard(dev) \ | |
bc8bf90a | 417 | writel(0, devpriv->las0+LAS0_BOARD_RESET) |
3d9f0739 DC |
418 | |
419 | /* Reset channel gain table read pointer */ | |
420 | #define RtdResetCGT(dev) \ | |
bc8bf90a | 421 | writel(0, devpriv->las0+LAS0_CGT_RESET) |
3d9f0739 DC |
422 | |
423 | /* Reset channel gain table read and write pointers */ | |
424 | #define RtdClearCGT(dev) \ | |
bc8bf90a | 425 | writel(0, devpriv->las0+LAS0_CGT_CLEAR) |
3d9f0739 DC |
426 | |
427 | /* Reset channel gain table read and write pointers */ | |
8086fff8 | 428 | #define RtdEnableCGT(dev, v) \ |
bc8bf90a | 429 | writel((v > 0) ? 1 : 0, devpriv->las0+LAS0_CGT_ENABLE) |
3d9f0739 DC |
430 | |
431 | /* Write channel gain table entry */ | |
8086fff8 | 432 | #define RtdWriteCGTable(dev, v) \ |
bc8bf90a | 433 | writel(v, devpriv->las0+LAS0_CGT_WRITE) |
3d9f0739 DC |
434 | |
435 | /* Write Channel Gain Latch */ | |
8086fff8 | 436 | #define RtdWriteCGLatch(dev, v) \ |
bc8bf90a | 437 | writel(v, devpriv->las0+LAS0_CGL_WRITE) |
3d9f0739 DC |
438 | |
439 | /* Reset ADC FIFO */ | |
440 | #define RtdAdcClearFifo(dev) \ | |
bc8bf90a | 441 | writel(0, devpriv->las0+LAS0_ADC_FIFO_CLEAR) |
3d9f0739 DC |
442 | |
443 | /* Set ADC start conversion source select (write only) */ | |
8086fff8 | 444 | #define RtdAdcConversionSource(dev, v) \ |
bc8bf90a | 445 | writel(v, devpriv->las0+LAS0_ADC_CONVERSION) |
3d9f0739 DC |
446 | |
447 | /* Set burst start source select (write only) */ | |
8086fff8 | 448 | #define RtdBurstStartSource(dev, v) \ |
bc8bf90a | 449 | writel(v, devpriv->las0+LAS0_BURST_START) |
3d9f0739 DC |
450 | |
451 | /* Set Pacer start source select (write only) */ | |
8086fff8 | 452 | #define RtdPacerStartSource(dev, v) \ |
bc8bf90a | 453 | writel(v, devpriv->las0+LAS0_PACER_START) |
3d9f0739 DC |
454 | |
455 | /* Set Pacer stop source select (write only) */ | |
8086fff8 | 456 | #define RtdPacerStopSource(dev, v) \ |
bc8bf90a | 457 | writel(v, devpriv->las0+LAS0_PACER_STOP) |
3d9f0739 DC |
458 | |
459 | /* Set Pacer clock source select (write only) 0=external 1=internal */ | |
8086fff8 | 460 | #define RtdPacerClockSource(dev, v) \ |
bc8bf90a | 461 | writel((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_SELECT) |
3d9f0739 DC |
462 | |
463 | /* Set sample counter source select (write only) */ | |
8086fff8 | 464 | #define RtdAdcSampleCounterSource(dev, v) \ |
bc8bf90a | 465 | writel(v, devpriv->las0+LAS0_ADC_SCNT_SRC) |
3d9f0739 DC |
466 | |
467 | /* Set Pacer trigger mode select (write only) 0=single cycle, 1=repeat */ | |
8086fff8 | 468 | #define RtdPacerTriggerMode(dev, v) \ |
bc8bf90a | 469 | writel((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_REPEAT) |
3d9f0739 DC |
470 | |
471 | /* Set About counter stop enable (write only) */ | |
8086fff8 | 472 | #define RtdAboutStopEnable(dev, v) \ |
bc8bf90a | 473 | writel((v > 0) ? 1 : 0, devpriv->las0+LAS0_ACNT_STOP_ENABLE) |
3d9f0739 DC |
474 | |
475 | /* Set external trigger polarity (write only) 0=positive edge, 1=negative */ | |
8086fff8 | 476 | #define RtdTriggerPolarity(dev, v) \ |
bc8bf90a | 477 | writel((v > 0) ? 1 : 0, devpriv->las0+LAS0_ETRG_POLARITY) |
3d9f0739 DC |
478 | |
479 | /* Start single ADC conversion */ | |
480 | #define RtdAdcStart(dev) \ | |
bc8bf90a | 481 | writew(0, devpriv->las0+LAS0_ADC) |
3d9f0739 DC |
482 | |
483 | /* Read one ADC data value (12bit (with sign extend) as 16bit) */ | |
484 | /* Note: matches what DMA would get. Actual value >> 3 */ | |
485 | #define RtdAdcFifoGet(dev) \ | |
bc8bf90a | 486 | readw(devpriv->las1+LAS1_ADC_FIFO) |
3d9f0739 | 487 | |
25985edc | 488 | /* Read two ADC data values (DOESN'T WORK) */ |
3d9f0739 | 489 | #define RtdAdcFifoGet2(dev) \ |
bc8bf90a | 490 | readl(devpriv->las1+LAS1_ADC_FIFO) |
3d9f0739 DC |
491 | |
492 | /* FIFO status */ | |
493 | #define RtdFifoStatus(dev) \ | |
bc8bf90a | 494 | readl(devpriv->las0+LAS0_ADC) |
3d9f0739 DC |
495 | |
496 | /* pacer start/stop read=start, write=stop*/ | |
497 | #define RtdPacerStart(dev) \ | |
bc8bf90a | 498 | readl(devpriv->las0+LAS0_PACER) |
3d9f0739 | 499 | #define RtdPacerStop(dev) \ |
bc8bf90a | 500 | writel(0, devpriv->las0+LAS0_PACER) |
3d9f0739 DC |
501 | |
502 | /* Interrupt status */ | |
503 | #define RtdInterruptStatus(dev) \ | |
bc8bf90a | 504 | readw(devpriv->las0+LAS0_IT) |
3d9f0739 DC |
505 | |
506 | /* Interrupt mask */ | |
8086fff8 | 507 | #define RtdInterruptMask(dev, v) \ |
bc8bf90a | 508 | writew((devpriv->intMask = (v)), devpriv->las0+LAS0_IT) |
3d9f0739 DC |
509 | |
510 | /* Interrupt status clear (only bits set in mask) */ | |
511 | #define RtdInterruptClear(dev) \ | |
bc8bf90a | 512 | readw(devpriv->las0+LAS0_CLEAR) |
3d9f0739 DC |
513 | |
514 | /* Interrupt clear mask */ | |
8086fff8 | 515 | #define RtdInterruptClearMask(dev, v) \ |
bc8bf90a | 516 | writew((devpriv->intClearMask = (v)), devpriv->las0+LAS0_CLEAR) |
3d9f0739 DC |
517 | |
518 | /* Interrupt overrun status */ | |
519 | #define RtdInterruptOverrunStatus(dev) \ | |
bc8bf90a | 520 | readl(devpriv->las0+LAS0_OVERRUN) |
3d9f0739 DC |
521 | |
522 | /* Interrupt overrun clear */ | |
523 | #define RtdInterruptOverrunClear(dev) \ | |
bc8bf90a | 524 | writel(0, devpriv->las0+LAS0_OVERRUN) |
3d9f0739 DC |
525 | |
526 | /* Pacer counter, 24bit */ | |
527 | #define RtdPacerCount(dev) \ | |
bc8bf90a | 528 | readl(devpriv->las0+LAS0_PCLK) |
8086fff8 | 529 | #define RtdPacerCounter(dev, v) \ |
bc8bf90a | 530 | writel((v) & 0xffffff, devpriv->las0+LAS0_PCLK) |
3d9f0739 DC |
531 | |
532 | /* Burst counter, 10bit */ | |
533 | #define RtdBurstCount(dev) \ | |
bc8bf90a | 534 | readl(devpriv->las0+LAS0_BCLK) |
8086fff8 | 535 | #define RtdBurstCounter(dev, v) \ |
bc8bf90a | 536 | writel((v) & 0x3ff, devpriv->las0+LAS0_BCLK) |
3d9f0739 DC |
537 | |
538 | /* Delay counter, 16bit */ | |
539 | #define RtdDelayCount(dev) \ | |
bc8bf90a | 540 | readl(devpriv->las0+LAS0_DCLK) |
8086fff8 | 541 | #define RtdDelayCounter(dev, v) \ |
bc8bf90a | 542 | writel((v) & 0xffff, devpriv->las0+LAS0_DCLK) |
3d9f0739 DC |
543 | |
544 | /* About counter, 16bit */ | |
545 | #define RtdAboutCount(dev) \ | |
bc8bf90a | 546 | readl(devpriv->las0+LAS0_ACNT) |
8086fff8 | 547 | #define RtdAboutCounter(dev, v) \ |
bc8bf90a | 548 | writel((v) & 0xffff, devpriv->las0+LAS0_ACNT) |
3d9f0739 DC |
549 | |
550 | /* ADC sample counter, 10bit */ | |
551 | #define RtdAdcSampleCount(dev) \ | |
bc8bf90a | 552 | readl(devpriv->las0+LAS0_ADC_SCNT) |
8086fff8 | 553 | #define RtdAdcSampleCounter(dev, v) \ |
bc8bf90a | 554 | writel((v) & 0x3ff, devpriv->las0+LAS0_ADC_SCNT) |
3d9f0739 DC |
555 | |
556 | /* User Timer/Counter (8254) */ | |
8086fff8 | 557 | #define RtdUtcCounterGet(dev, n) \ |
bc8bf90a NR |
558 | readb(devpriv->las0 \ |
559 | + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) | |
3d9f0739 | 560 | |
8086fff8 | 561 | #define RtdUtcCounterPut(dev, n, v) \ |
bc8bf90a NR |
562 | writeb((v) & 0xff, devpriv->las0 \ |
563 | + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) | |
3d9f0739 DC |
564 | |
565 | /* Set UTC (8254) control byte */ | |
8086fff8 | 566 | #define RtdUtcCtrlPut(dev, n, v) \ |
bc8bf90a NR |
567 | writeb(devpriv->utcCtrl[(n) & 3] = (((n) & 3) << 6) | ((v) & 0x3f), \ |
568 | devpriv->las0 + LAS0_UTC_CTRL) | |
3d9f0739 DC |
569 | |
570 | /* Set UTCn clock source (write only) */ | |
8086fff8 | 571 | #define RtdUtcClockSource(dev, n, v) \ |
bc8bf90a NR |
572 | writew(v, devpriv->las0 \ |
573 | + ((n <= 0) ? LAS0_UTC0_CLOCK : \ | |
574 | ((1 == n) ? LAS0_UTC1_CLOCK : LAS0_UTC2_CLOCK))) | |
3d9f0739 DC |
575 | |
576 | /* Set UTCn gate source (write only) */ | |
8086fff8 | 577 | #define RtdUtcGateSource(dev, n, v) \ |
bc8bf90a NR |
578 | writew(v, devpriv->las0 \ |
579 | + ((n <= 0) ? LAS0_UTC0_GATE : \ | |
580 | ((1 == n) ? LAS0_UTC1_GATE : LAS0_UTC2_GATE))) | |
3d9f0739 DC |
581 | |
582 | /* User output N source select (write only) */ | |
8086fff8 | 583 | #define RtdUsrOutSource(dev, n, v) \ |
bc8bf90a | 584 | writel(v, devpriv->las0+((n <= 0) ? LAS0_UOUT0_SELECT : LAS0_UOUT1_SELECT)) |
3d9f0739 DC |
585 | |
586 | /* Digital IO */ | |
587 | #define RtdDio0Read(dev) \ | |
bc8bf90a | 588 | (readw(devpriv->las0+LAS0_DIO0) & 0xff) |
8086fff8 | 589 | #define RtdDio0Write(dev, v) \ |
bc8bf90a | 590 | writew((v) & 0xff, devpriv->las0+LAS0_DIO0) |
3d9f0739 DC |
591 | |
592 | #define RtdDio1Read(dev) \ | |
bc8bf90a | 593 | (readw(devpriv->las0+LAS0_DIO1) & 0xff) |
8086fff8 | 594 | #define RtdDio1Write(dev, v) \ |
bc8bf90a | 595 | writew((v) & 0xff, devpriv->las0+LAS0_DIO1) |
3d9f0739 DC |
596 | |
597 | #define RtdDioStatusRead(dev) \ | |
bc8bf90a | 598 | (readw(devpriv->las0+LAS0_DIO_STATUS) & 0xff) |
8086fff8 | 599 | #define RtdDioStatusWrite(dev, v) \ |
bc8bf90a | 600 | writew((devpriv->dioStatus = (v)), devpriv->las0+LAS0_DIO_STATUS) |
3d9f0739 DC |
601 | |
602 | #define RtdDio0CtrlRead(dev) \ | |
bc8bf90a | 603 | (readw(devpriv->las0+LAS0_DIO0_CTRL) & 0xff) |
8086fff8 | 604 | #define RtdDio0CtrlWrite(dev, v) \ |
bc8bf90a | 605 | writew((v) & 0xff, devpriv->las0+LAS0_DIO0_CTRL) |
3d9f0739 DC |
606 | |
607 | /* Digital to Analog converter */ | |
608 | /* Write one data value (sign + 12bit + marker bits) */ | |
609 | /* Note: matches what DMA would put. Actual value << 3 */ | |
8086fff8 | 610 | #define RtdDacFifoPut(dev, n, v) \ |
bc8bf90a | 611 | writew((v), devpriv->las1 + (((n) == 0) ? LAS1_DAC1_FIFO : LAS1_DAC2_FIFO)) |
3d9f0739 DC |
612 | |
613 | /* Start single DAC conversion */ | |
8086fff8 | 614 | #define RtdDacUpdate(dev, n) \ |
bc8bf90a | 615 | writew(0, devpriv->las0 + (((n) == 0) ? LAS0_DAC1 : LAS0_DAC2)) |
3d9f0739 DC |
616 | |
617 | /* Start single DAC conversion on both DACs */ | |
618 | #define RtdDacBothUpdate(dev) \ | |
bc8bf90a | 619 | writew(0, devpriv->las0+LAS0_DAC) |
3d9f0739 DC |
620 | |
621 | /* Set DAC output type and range */ | |
8086fff8 | 622 | #define RtdDacRange(dev, n, v) \ |
bc8bf90a NR |
623 | writew((v) & 7, devpriv->las0 \ |
624 | +(((n) == 0) ? LAS0_DAC1_CTRL : LAS0_DAC2_CTRL)) | |
3d9f0739 DC |
625 | |
626 | /* Reset DAC FIFO */ | |
8086fff8 | 627 | #define RtdDacClearFifo(dev, n) \ |
bc8bf90a | 628 | writel(0, devpriv->las0+(((n) == 0) ? LAS0_DAC1_RESET : LAS0_DAC2_RESET)) |
3d9f0739 DC |
629 | |
630 | /* Set source for DMA 0 (write only, shadow?) */ | |
8086fff8 | 631 | #define RtdDma0Source(dev, n) \ |
bc8bf90a | 632 | writel((n) & 0xf, devpriv->las0+LAS0_DMA0_SRC) |
3d9f0739 DC |
633 | |
634 | /* Set source for DMA 1 (write only, shadow?) */ | |
8086fff8 | 635 | #define RtdDma1Source(dev, n) \ |
bc8bf90a | 636 | writel((n) & 0xf, devpriv->las0+LAS0_DMA1_SRC) |
3d9f0739 DC |
637 | |
638 | /* Reset board state for DMA 0 */ | |
639 | #define RtdDma0Reset(dev) \ | |
bc8bf90a | 640 | writel(0, devpriv->las0+LAS0_DMA0_RESET) |
3d9f0739 DC |
641 | |
642 | /* Reset board state for DMA 1 */ | |
643 | #define RtdDma1Reset(dev) \ | |
bc8bf90a | 644 | writel(0, devpriv->las0+LAS0_DMA1_SRC) |
3d9f0739 DC |
645 | |
646 | /* PLX9080 interrupt mask and status */ | |
647 | #define RtdPlxInterruptRead(dev) \ | |
bc8bf90a | 648 | readl(devpriv->lcfg+LCFG_ITCSR) |
8086fff8 | 649 | #define RtdPlxInterruptWrite(dev, v) \ |
bc8bf90a | 650 | writel(v, devpriv->lcfg+LCFG_ITCSR) |
3d9f0739 DC |
651 | |
652 | /* Set mode for DMA 0 */ | |
8086fff8 | 653 | #define RtdDma0Mode(dev, m) \ |
bc8bf90a | 654 | writel((m), devpriv->lcfg+LCFG_DMAMODE0) |
3d9f0739 DC |
655 | |
656 | /* Set PCI address for DMA 0 */ | |
8086fff8 | 657 | #define RtdDma0PciAddr(dev, a) \ |
bc8bf90a | 658 | writel((a), devpriv->lcfg+LCFG_DMAPADR0) |
3d9f0739 DC |
659 | |
660 | /* Set local address for DMA 0 */ | |
8086fff8 | 661 | #define RtdDma0LocalAddr(dev, a) \ |
bc8bf90a | 662 | writel((a), devpriv->lcfg+LCFG_DMALADR0) |
3d9f0739 DC |
663 | |
664 | /* Set byte count for DMA 0 */ | |
8086fff8 | 665 | #define RtdDma0Count(dev, c) \ |
bc8bf90a | 666 | writel((c), devpriv->lcfg+LCFG_DMASIZ0) |
3d9f0739 DC |
667 | |
668 | /* Set next descriptor for DMA 0 */ | |
8086fff8 | 669 | #define RtdDma0Next(dev, a) \ |
bc8bf90a | 670 | writel((a), devpriv->lcfg+LCFG_DMADPR0) |
3d9f0739 DC |
671 | |
672 | /* Set mode for DMA 1 */ | |
8086fff8 | 673 | #define RtdDma1Mode(dev, m) \ |
bc8bf90a | 674 | writel((m), devpriv->lcfg+LCFG_DMAMODE1) |
3d9f0739 DC |
675 | |
676 | /* Set PCI address for DMA 1 */ | |
8086fff8 | 677 | #define RtdDma1PciAddr(dev, a) \ |
bc8bf90a | 678 | writel((a), devpriv->lcfg+LCFG_DMAADR1) |
3d9f0739 DC |
679 | |
680 | /* Set local address for DMA 1 */ | |
8086fff8 | 681 | #define RtdDma1LocalAddr(dev, a) \ |
bc8bf90a | 682 | writel((a), devpriv->lcfg+LCFG_DMALADR1) |
3d9f0739 DC |
683 | |
684 | /* Set byte count for DMA 1 */ | |
8086fff8 | 685 | #define RtdDma1Count(dev, c) \ |
bc8bf90a | 686 | writel((c), devpriv->lcfg+LCFG_DMASIZ1) |
3d9f0739 DC |
687 | |
688 | /* Set next descriptor for DMA 1 */ | |
8086fff8 | 689 | #define RtdDma1Next(dev, a) \ |
bc8bf90a | 690 | writel((a), devpriv->lcfg+LCFG_DMADPR1) |
3d9f0739 DC |
691 | |
692 | /* Set control for DMA 0 (write only, shadow?) */ | |
8086fff8 | 693 | #define RtdDma0Control(dev, n) \ |
bc8bf90a | 694 | writeb(devpriv->dma0Control = (n), devpriv->lcfg+LCFG_DMACSR0) |
3d9f0739 DC |
695 | |
696 | /* Get status for DMA 0 */ | |
697 | #define RtdDma0Status(dev) \ | |
bc8bf90a | 698 | readb(devpriv->lcfg+LCFG_DMACSR0) |
3d9f0739 DC |
699 | |
700 | /* Set control for DMA 1 (write only, shadow?) */ | |
8086fff8 | 701 | #define RtdDma1Control(dev, n) \ |
bc8bf90a | 702 | writeb(devpriv->dma1Control = (n), devpriv->lcfg+LCFG_DMACSR1) |
3d9f0739 DC |
703 | |
704 | /* Get status for DMA 1 */ | |
705 | #define RtdDma1Status(dev) \ | |
bc8bf90a | 706 | readb(devpriv->lcfg+LCFG_DMACSR1) |
3d9f0739 DC |
707 | |
708 | /* | |
139dfbdf | 709 | * The struct comedi_driver structure tells the Comedi core module |
3d9f0739 DC |
710 | * which functions to call to configure/deconfigure (attac/detach) |
711 | * the board, and also about the kernel module that contains | |
712 | * the device code. | |
713 | */ | |
0707bb04 | 714 | static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
71b5f4f1 | 715 | static int rtd_detach(struct comedi_device *dev); |
3d9f0739 | 716 | |
139dfbdf | 717 | static struct comedi_driver rtd520Driver = { |
b4918808 BP |
718 | .driver_name = DRV_NAME, |
719 | .module = THIS_MODULE, | |
720 | .attach = rtd_attach, | |
721 | .detach = rtd_detach, | |
3d9f0739 DC |
722 | }; |
723 | ||
34c43922 | 724 | static int rtd_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 725 | struct comedi_insn *insn, unsigned int *data); |
34c43922 | 726 | static int rtd_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 727 | struct comedi_insn *insn, unsigned int *data); |
34c43922 | 728 | static int rtd_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 MT |
729 | struct comedi_insn *insn, unsigned int *data); |
730 | static int rtd_dio_insn_bits(struct comedi_device *dev, | |
731 | struct comedi_subdevice *s, | |
732 | struct comedi_insn *insn, unsigned int *data); | |
733 | static int rtd_dio_insn_config(struct comedi_device *dev, | |
734 | struct comedi_subdevice *s, | |
735 | struct comedi_insn *insn, unsigned int *data); | |
34c43922 | 736 | static int rtd_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 737 | struct comedi_cmd *cmd); |
34c43922 BP |
738 | static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s); |
739 | static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s); | |
740 | /* static int rtd_ai_poll (struct comedi_device *dev,struct comedi_subdevice *s); */ | |
3d9f0739 | 741 | static int rtd_ns_to_timer(unsigned int *ns, int roundMode); |
70265d24 | 742 | static irqreturn_t rtd_interrupt(int irq, void *d); |
71b5f4f1 | 743 | static int rtd520_probe_fifo_depth(struct comedi_device *dev); |
3d9f0739 DC |
744 | |
745 | /* | |
746 | * Attach is called by the Comedi core to configure the driver | |
747 | * for a particular board. If you specified a board_name array | |
748 | * in the driver structure, dev->board_ptr contains that | |
749 | * address. | |
750 | */ | |
0707bb04 | 751 | static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
3d9f0739 | 752 | { /* board name and options flags */ |
34c43922 | 753 | struct comedi_subdevice *s; |
3d9f0739 DC |
754 | struct pci_dev *pcidev; |
755 | int ret; | |
c4fb2bab | 756 | resource_size_t physLas0; /* configuration */ |
3d9f0739 DC |
757 | resource_size_t physLas1; /* data area */ |
758 | resource_size_t physLcfg; /* PLX9080 */ | |
759 | #ifdef USE_DMA | |
760 | int index; | |
761 | #endif | |
762 | ||
bc8bf90a | 763 | printk(KERN_INFO "comedi%d: rtd520 attaching.\n", dev->minor); |
3d9f0739 | 764 | |
bc8bf90a | 765 | #if defined(CONFIG_COMEDI_DEBUG) && defined(USE_DMA) |
3d9f0739 DC |
766 | /* You can set this a load time: modprobe comedi comedi_debug=1 */ |
767 | if (0 == comedi_debug) /* force DMA debug printks */ | |
768 | comedi_debug = 1; | |
769 | #endif | |
770 | ||
771 | /* | |
772 | * Allocate the private structure area. alloc_private() is a | |
773 | * convenient macro defined in comedidev.h. | |
774 | */ | |
d80235ce | 775 | if (alloc_private(dev, sizeof(struct rtdPrivate)) < 0) |
3d9f0739 DC |
776 | return -ENOMEM; |
777 | ||
778 | /* | |
779 | * Probe the device to determine what device in the series it is. | |
780 | */ | |
781 | for (pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, NULL); | |
0a85b6f0 MT |
782 | pcidev != NULL; |
783 | pcidev = pci_get_device(PCI_VENDOR_ID_RTD, PCI_ANY_ID, pcidev)) { | |
3d9f0739 DC |
784 | int i; |
785 | ||
786 | if (it->options[0] || it->options[1]) { | |
787 | if (pcidev->bus->number != it->options[0] | |
0a85b6f0 | 788 | || PCI_SLOT(pcidev->devfn) != it->options[1]) { |
3d9f0739 DC |
789 | continue; |
790 | } | |
791 | } | |
0a85b6f0 MT |
792 | for (i = 0; i < ARRAY_SIZE(rtd520Boards); ++i) { |
793 | if (pcidev->device == rtd520Boards[i].device_id) { | |
3d9f0739 DC |
794 | dev->board_ptr = &rtd520Boards[i]; |
795 | break; | |
796 | } | |
797 | } | |
0a85b6f0 MT |
798 | if (dev->board_ptr) |
799 | break; /* found one */ | |
3d9f0739 DC |
800 | } |
801 | if (!pcidev) { | |
802 | if (it->options[0] && it->options[1]) { | |
bc8bf90a | 803 | printk(KERN_INFO "No RTD card at bus=%d slot=%d.\n", |
0a85b6f0 | 804 | it->options[0], it->options[1]); |
3d9f0739 | 805 | } else { |
bc8bf90a | 806 | printk(KERN_INFO "No RTD card found.\n"); |
3d9f0739 DC |
807 | } |
808 | return -EIO; | |
809 | } | |
810 | devpriv->pci_dev = pcidev; | |
811 | dev->board_name = thisboard->name; | |
812 | ||
197c82bf BP |
813 | ret = comedi_pci_enable(pcidev, DRV_NAME); |
814 | if (ret < 0) { | |
bc8bf90a | 815 | printk(KERN_INFO "Failed to enable PCI device and request regions.\n"); |
3d9f0739 DC |
816 | return ret; |
817 | } | |
818 | devpriv->got_regions = 1; | |
819 | ||
820 | /* | |
821 | * Initialize base addresses | |
822 | */ | |
823 | /* Get the physical address from PCI config */ | |
824 | physLas0 = pci_resource_start(devpriv->pci_dev, LAS0_PCIINDEX); | |
825 | physLas1 = pci_resource_start(devpriv->pci_dev, LAS1_PCIINDEX); | |
826 | physLcfg = pci_resource_start(devpriv->pci_dev, LCFG_PCIINDEX); | |
827 | /* Now have the kernel map this into memory */ | |
828 | /* ASSUME page aligned */ | |
829 | devpriv->las0 = ioremap_nocache(physLas0, LAS0_PCISIZE); | |
830 | devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE); | |
831 | devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE); | |
832 | ||
bc8bf90a | 833 | if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg) |
3d9f0739 | 834 | return -ENOMEM; |
bc8bf90a | 835 | |
3d9f0739 DC |
836 | |
837 | DPRINTK("%s: LAS0=%llx, LAS1=%llx, CFG=%llx.\n", dev->board_name, | |
838 | (unsigned long long)physLas0, (unsigned long long)physLas1, | |
839 | (unsigned long long)physLcfg); | |
840 | { /* The RTD driver does this */ | |
841 | unsigned char pci_latency; | |
842 | u16 revision; | |
843 | /*uint32_t epld_version; */ | |
844 | ||
845 | pci_read_config_word(devpriv->pci_dev, PCI_REVISION_ID, | |
0a85b6f0 | 846 | &revision); |
3d9f0739 DC |
847 | DPRINTK("%s: PCI revision %d.\n", dev->board_name, revision); |
848 | ||
849 | pci_read_config_byte(devpriv->pci_dev, | |
0a85b6f0 | 850 | PCI_LATENCY_TIMER, &pci_latency); |
3d9f0739 | 851 | if (pci_latency < 32) { |
bc8bf90a | 852 | printk(KERN_INFO "%s: PCI latency changed from %d to %d\n", |
0a85b6f0 | 853 | dev->board_name, pci_latency, 32); |
3d9f0739 | 854 | pci_write_config_byte(devpriv->pci_dev, |
0a85b6f0 | 855 | PCI_LATENCY_TIMER, 32); |
3d9f0739 DC |
856 | } else { |
857 | DPRINTK("rtd520: PCI latency = %d\n", pci_latency); | |
858 | } | |
859 | ||
25985edc | 860 | /* Undocumented EPLD version (doesn't match RTD driver results) */ |
3d9f0739 DC |
861 | /*DPRINTK ("rtd520: Reading epld from %p\n", |
862 | devpriv->las0+0); | |
863 | epld_version = readl (devpriv->las0+0); | |
864 | if ((epld_version & 0xF0) >> 4 == 0x0F) { | |
865 | DPRINTK("rtd520: pre-v8 EPLD. (%x)\n", epld_version); | |
866 | } else { | |
867 | DPRINTK("rtd520: EPLD version %x.\n", epld_version >> 4); | |
868 | } */ | |
869 | } | |
870 | ||
871 | /* Show board configuration */ | |
872 | printk("%s:", dev->board_name); | |
873 | ||
874 | /* | |
875 | * Allocate the subdevice structures. alloc_subdevice() is a | |
876 | * convenient macro defined in comedidev.h. | |
877 | */ | |
bc8bf90a | 878 | if (alloc_subdevices(dev, 4) < 0) |
3d9f0739 | 879 | return -ENOMEM; |
bc8bf90a | 880 | |
3d9f0739 DC |
881 | |
882 | s = dev->subdevices + 0; | |
883 | dev->read_subdev = s; | |
884 | /* analog input subdevice */ | |
885 | s->type = COMEDI_SUBD_AI; | |
886 | s->subdev_flags = | |
0a85b6f0 | 887 | SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF | SDF_CMD_READ; |
3d9f0739 DC |
888 | s->n_chan = thisboard->aiChans; |
889 | s->maxdata = (1 << thisboard->aiBits) - 1; | |
bc8bf90a | 890 | if (thisboard->aiMaxGain <= 32) |
3d9f0739 | 891 | s->range_table = &rtd_ai_7520_range; |
bc8bf90a | 892 | else |
3d9f0739 | 893 | s->range_table = &rtd_ai_4520_range; |
bc8bf90a | 894 | |
3d9f0739 DC |
895 | s->len_chanlist = RTD_MAX_CHANLIST; /* devpriv->fifoLen */ |
896 | s->insn_read = rtd_ai_rinsn; | |
897 | s->do_cmd = rtd_ai_cmd; | |
898 | s->do_cmdtest = rtd_ai_cmdtest; | |
899 | s->cancel = rtd_ai_cancel; | |
0a85b6f0 | 900 | /* s->poll = rtd_ai_poll; *//* not ready yet */ |
3d9f0739 DC |
901 | |
902 | s = dev->subdevices + 1; | |
903 | /* analog output subdevice */ | |
904 | s->type = COMEDI_SUBD_AO; | |
905 | s->subdev_flags = SDF_WRITABLE; | |
906 | s->n_chan = 2; | |
907 | s->maxdata = (1 << thisboard->aiBits) - 1; | |
908 | s->range_table = &rtd_ao_range; | |
909 | s->insn_write = rtd_ao_winsn; | |
910 | s->insn_read = rtd_ao_rinsn; | |
911 | ||
912 | s = dev->subdevices + 2; | |
913 | /* digital i/o subdevice */ | |
914 | s->type = COMEDI_SUBD_DIO; | |
915 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
916 | /* we only support port 0 right now. Ignoring port 1 and user IO */ | |
917 | s->n_chan = 8; | |
918 | s->maxdata = 1; | |
919 | s->range_table = &range_digital; | |
920 | s->insn_bits = rtd_dio_insn_bits; | |
921 | s->insn_config = rtd_dio_insn_config; | |
922 | ||
923 | /* timer/counter subdevices (not currently supported) */ | |
924 | s = dev->subdevices + 3; | |
925 | s->type = COMEDI_SUBD_COUNTER; | |
926 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
927 | s->n_chan = 3; | |
928 | s->maxdata = 0xffff; | |
929 | ||
930 | /* initialize board, per RTD spec */ | |
931 | /* also, initialize shadow registers */ | |
932 | RtdResetBoard(dev); | |
0a85b6f0 | 933 | udelay(100); /* needed? */ |
3d9f0739 DC |
934 | RtdPlxInterruptWrite(dev, 0); |
935 | RtdInterruptMask(dev, 0); /* and sets shadow */ | |
936 | RtdInterruptClearMask(dev, ~0); /* and sets shadow */ | |
937 | RtdInterruptClear(dev); /* clears bits set by mask */ | |
938 | RtdInterruptOverrunClear(dev); | |
939 | RtdClearCGT(dev); | |
940 | RtdAdcClearFifo(dev); | |
941 | RtdDacClearFifo(dev, 0); | |
942 | RtdDacClearFifo(dev, 1); | |
943 | /* clear digital IO fifo */ | |
944 | RtdDioStatusWrite(dev, 0); /* safe state, set shadow */ | |
945 | RtdUtcCtrlPut(dev, 0, 0x30); /* safe state, set shadow */ | |
946 | RtdUtcCtrlPut(dev, 1, 0x30); /* safe state, set shadow */ | |
947 | RtdUtcCtrlPut(dev, 2, 0x30); /* safe state, set shadow */ | |
948 | RtdUtcCtrlPut(dev, 3, 0); /* safe state, set shadow */ | |
949 | /* TODO: set user out source ??? */ | |
950 | ||
951 | /* check if our interrupt is available and get it */ | |
5f74ea14 | 952 | ret = request_irq(devpriv->pci_dev->irq, rtd_interrupt, |
0a85b6f0 | 953 | IRQF_SHARED, DRV_NAME, dev); |
197c82bf BP |
954 | |
955 | if (ret < 0) { | |
3d9f0739 | 956 | printk("Could not get interrupt! (%u)\n", |
0a85b6f0 | 957 | devpriv->pci_dev->irq); |
3d9f0739 DC |
958 | return ret; |
959 | } | |
960 | dev->irq = devpriv->pci_dev->irq; | |
961 | printk("( irq=%u )", dev->irq); | |
962 | ||
963 | ret = rtd520_probe_fifo_depth(dev); | |
bc8bf90a | 964 | if (ret < 0) |
3d9f0739 | 965 | return ret; |
bc8bf90a | 966 | |
3d9f0739 DC |
967 | devpriv->fifoLen = ret; |
968 | printk("( fifoLen=%d )", devpriv->fifoLen); | |
969 | ||
970 | #ifdef USE_DMA | |
971 | if (dev->irq > 0) { | |
972 | printk("( DMA buff=%d )\n", DMA_CHAIN_COUNT); | |
973 | /* The PLX9080 has 2 DMA controllers, but there could be 4 sources: | |
974 | ADC, digital, DAC1, and DAC2. Since only the ADC supports cmd mode | |
975 | right now, this isn't an issue (yet) */ | |
976 | devpriv->dma0Offset = 0; | |
977 | ||
978 | for (index = 0; index < DMA_CHAIN_COUNT; index++) { | |
979 | devpriv->dma0Buff[index] = | |
0a85b6f0 MT |
980 | pci_alloc_consistent(devpriv->pci_dev, |
981 | sizeof(u16) * | |
982 | devpriv->fifoLen / 2, | |
983 | &devpriv-> | |
984 | dma0BuffPhysAddr[index]); | |
3d9f0739 DC |
985 | if (devpriv->dma0Buff[index] == NULL) { |
986 | ret = -ENOMEM; | |
987 | goto rtd_attach_die_error; | |
988 | } | |
989 | /*DPRINTK ("buff[%d] @ %p virtual, %x PCI\n", | |
990 | index, | |
991 | devpriv->dma0Buff[index], devpriv->dma0BuffPhysAddr[index]); */ | |
992 | } | |
993 | ||
994 | /* setup DMA descriptor ring (use cpu_to_le32 for byte ordering?) */ | |
995 | devpriv->dma0Chain = | |
0a85b6f0 MT |
996 | pci_alloc_consistent(devpriv->pci_dev, |
997 | sizeof(struct plx_dma_desc) * | |
998 | DMA_CHAIN_COUNT, | |
999 | &devpriv->dma0ChainPhysAddr); | |
3d9f0739 DC |
1000 | for (index = 0; index < DMA_CHAIN_COUNT; index++) { |
1001 | devpriv->dma0Chain[index].pci_start_addr = | |
0a85b6f0 | 1002 | devpriv->dma0BuffPhysAddr[index]; |
3d9f0739 | 1003 | devpriv->dma0Chain[index].local_start_addr = |
0a85b6f0 | 1004 | DMALADDR_ADC; |
3d9f0739 | 1005 | devpriv->dma0Chain[index].transfer_size = |
0a85b6f0 | 1006 | sizeof(u16) * devpriv->fifoLen / 2; |
3d9f0739 | 1007 | devpriv->dma0Chain[index].next = |
0a85b6f0 MT |
1008 | (devpriv->dma0ChainPhysAddr + ((index + |
1009 | 1) % | |
1010 | (DMA_CHAIN_COUNT)) | |
1011 | * sizeof(devpriv->dma0Chain[0])) | |
1012 | | DMA_TRANSFER_BITS; | |
3d9f0739 DC |
1013 | /*DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n", |
1014 | index, | |
1015 | ((long)devpriv->dma0ChainPhysAddr | |
1016 | + (index * sizeof(devpriv->dma0Chain[0]))), | |
1017 | devpriv->dma0Chain[index].pci_start_addr, | |
1018 | devpriv->dma0Chain[index].local_start_addr, | |
1019 | devpriv->dma0Chain[index].transfer_size, | |
1020 | devpriv->dma0Chain[index].next); */ | |
1021 | } | |
1022 | ||
1023 | if (devpriv->dma0Chain == NULL) { | |
1024 | ret = -ENOMEM; | |
1025 | goto rtd_attach_die_error; | |
1026 | } | |
1027 | ||
1028 | RtdDma0Mode(dev, DMA_MODE_BITS); | |
1029 | RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */ | |
1030 | } else { | |
bc8bf90a | 1031 | printk(KERN_INFO "( no IRQ->no DMA )"); |
3d9f0739 DC |
1032 | } |
1033 | #endif /* USE_DMA */ | |
1034 | ||
1035 | if (dev->irq) { /* enable plx9080 interrupts */ | |
1036 | RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE); | |
1037 | } | |
1038 | ||
1039 | printk("\ncomedi%d: rtd520 driver attached.\n", dev->minor); | |
1040 | ||
1041 | return 1; | |
1042 | ||
1043 | #if 0 | |
1044 | /* hit an error, clean up memory and return ret */ | |
b6c77757 | 1045 | /* rtd_attach_die_error: */ |
3d9f0739 DC |
1046 | #ifdef USE_DMA |
1047 | for (index = 0; index < DMA_CHAIN_COUNT; index++) { | |
1048 | if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory */ | |
1049 | pci_free_consistent(devpriv->pci_dev, | |
0a85b6f0 MT |
1050 | sizeof(u16) * devpriv->fifoLen / 2, |
1051 | devpriv->dma0Buff[index], | |
1052 | devpriv->dma0BuffPhysAddr[index]); | |
3d9f0739 DC |
1053 | devpriv->dma0Buff[index] = NULL; |
1054 | } | |
1055 | } | |
1056 | if (NULL != devpriv->dma0Chain) { | |
1057 | pci_free_consistent(devpriv->pci_dev, | |
0a85b6f0 MT |
1058 | sizeof(struct plx_dma_desc) |
1059 | * DMA_CHAIN_COUNT, | |
1060 | devpriv->dma0Chain, | |
1061 | devpriv->dma0ChainPhysAddr); | |
3d9f0739 DC |
1062 | devpriv->dma0Chain = NULL; |
1063 | } | |
1064 | #endif /* USE_DMA */ | |
1065 | /* subdevices and priv are freed by the core */ | |
1066 | if (dev->irq) { | |
1067 | /* disable interrupt controller */ | |
1068 | RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev) | |
0a85b6f0 | 1069 | & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E)); |
5f74ea14 | 1070 | free_irq(dev->irq, dev); |
3d9f0739 DC |
1071 | } |
1072 | ||
1073 | /* release all regions that were allocated */ | |
bc8bf90a | 1074 | if (devpriv->las0) |
3d9f0739 | 1075 | iounmap(devpriv->las0); |
bc8bf90a NR |
1076 | |
1077 | if (devpriv->las1) | |
3d9f0739 | 1078 | iounmap(devpriv->las1); |
bc8bf90a NR |
1079 | |
1080 | if (devpriv->lcfg) | |
3d9f0739 | 1081 | iounmap(devpriv->lcfg); |
bc8bf90a NR |
1082 | |
1083 | if (devpriv->pci_dev) | |
3d9f0739 | 1084 | pci_dev_put(devpriv->pci_dev); |
bc8bf90a | 1085 | |
3d9f0739 DC |
1086 | return ret; |
1087 | #endif | |
1088 | } | |
1089 | ||
1090 | /* | |
1091 | * _detach is called to deconfigure a device. It should deallocate | |
1092 | * resources. | |
1093 | * This function is also called when _attach() fails, so it should be | |
1094 | * careful not to release resources that were not necessarily | |
1095 | * allocated by _attach(). dev->private and dev->subdevices are | |
1096 | * deallocated automatically by the core. | |
1097 | */ | |
71b5f4f1 | 1098 | static int rtd_detach(struct comedi_device *dev) |
3d9f0739 DC |
1099 | { |
1100 | #ifdef USE_DMA | |
1101 | int index; | |
1102 | #endif | |
1103 | ||
1104 | DPRINTK("comedi%d: rtd520: removing (%ld ints)\n", | |
1105 | dev->minor, (devpriv ? devpriv->intCount : 0L)); | |
1106 | if (devpriv && devpriv->lcfg) { | |
0a85b6f0 MT |
1107 | DPRINTK |
1108 | ("(int status 0x%x, overrun status 0x%x, fifo status 0x%x)...\n", | |
1109 | 0xffff & RtdInterruptStatus(dev), | |
1110 | 0xffff & RtdInterruptOverrunStatus(dev), | |
1111 | (0xffff & RtdFifoStatus(dev)) ^ 0x6666); | |
3d9f0739 DC |
1112 | } |
1113 | ||
1114 | if (devpriv) { | |
1115 | /* Shut down any board ops by resetting it */ | |
1116 | #ifdef USE_DMA | |
1117 | if (devpriv->lcfg) { | |
1118 | RtdDma0Control(dev, 0); /* disable DMA */ | |
1119 | RtdDma1Control(dev, 0); /* disable DMA */ | |
1120 | RtdPlxInterruptWrite(dev, ICS_PIE | ICS_PLIE); | |
1121 | } | |
1122 | #endif /* USE_DMA */ | |
1123 | if (devpriv->las0) { | |
1124 | RtdResetBoard(dev); | |
1125 | RtdInterruptMask(dev, 0); | |
1126 | RtdInterruptClearMask(dev, ~0); | |
1127 | RtdInterruptClear(dev); /* clears bits set by mask */ | |
1128 | } | |
1129 | #ifdef USE_DMA | |
1130 | /* release DMA */ | |
1131 | for (index = 0; index < DMA_CHAIN_COUNT; index++) { | |
1132 | if (NULL != devpriv->dma0Buff[index]) { | |
1133 | pci_free_consistent(devpriv->pci_dev, | |
0a85b6f0 MT |
1134 | sizeof(u16) * |
1135 | devpriv->fifoLen / 2, | |
1136 | devpriv->dma0Buff[index], | |
1137 | devpriv-> | |
1138 | dma0BuffPhysAddr[index]); | |
3d9f0739 DC |
1139 | devpriv->dma0Buff[index] = NULL; |
1140 | } | |
1141 | } | |
1142 | if (NULL != devpriv->dma0Chain) { | |
1143 | pci_free_consistent(devpriv->pci_dev, | |
0a85b6f0 MT |
1144 | sizeof(struct plx_dma_desc) * |
1145 | DMA_CHAIN_COUNT, devpriv->dma0Chain, | |
1146 | devpriv->dma0ChainPhysAddr); | |
3d9f0739 DC |
1147 | devpriv->dma0Chain = NULL; |
1148 | } | |
1149 | #endif /* USE_DMA */ | |
1150 | ||
1151 | /* release IRQ */ | |
1152 | if (dev->irq) { | |
1153 | /* disable interrupt controller */ | |
1154 | RtdPlxInterruptWrite(dev, RtdPlxInterruptRead(dev) | |
0a85b6f0 MT |
1155 | & ~(ICS_PLIE | ICS_DMA0_E | |
1156 | ICS_DMA1_E)); | |
5f74ea14 | 1157 | free_irq(dev->irq, dev); |
3d9f0739 DC |
1158 | } |
1159 | ||
1160 | /* release all regions that were allocated */ | |
bc8bf90a | 1161 | if (devpriv->las0) |
3d9f0739 | 1162 | iounmap(devpriv->las0); |
bc8bf90a NR |
1163 | |
1164 | if (devpriv->las1) | |
3d9f0739 | 1165 | iounmap(devpriv->las1); |
bc8bf90a NR |
1166 | |
1167 | if (devpriv->lcfg) | |
3d9f0739 | 1168 | iounmap(devpriv->lcfg); |
bc8bf90a | 1169 | |
3d9f0739 | 1170 | if (devpriv->pci_dev) { |
bc8bf90a | 1171 | if (devpriv->got_regions) |
3d9f0739 | 1172 | comedi_pci_disable(devpriv->pci_dev); |
bc8bf90a | 1173 | |
3d9f0739 DC |
1174 | pci_dev_put(devpriv->pci_dev); |
1175 | } | |
1176 | } | |
1177 | ||
bc8bf90a | 1178 | printk(KERN_INFO "comedi%d: rtd520: removed.\n", dev->minor); |
3d9f0739 DC |
1179 | |
1180 | return 0; | |
1181 | } | |
1182 | ||
1183 | /* | |
1184 | Convert a single comedi channel-gain entry to a RTD520 table entry | |
1185 | */ | |
71b5f4f1 | 1186 | static unsigned short rtdConvertChanGain(struct comedi_device *dev, |
0a85b6f0 | 1187 | unsigned int comediChan, int chanIndex) |
3d9f0739 DC |
1188 | { /* index in channel list */ |
1189 | unsigned int chan, range, aref; | |
1190 | unsigned short r = 0; | |
1191 | ||
1192 | chan = CR_CHAN(comediChan); | |
1193 | range = CR_RANGE(comediChan); | |
1194 | aref = CR_AREF(comediChan); | |
1195 | ||
1196 | r |= chan & 0xf; | |
1197 | ||
1198 | /* Note: we also setup the channel list bipolar flag array */ | |
1199 | if (range < thisboard->range10Start) { /* first batch are +-5 */ | |
1200 | r |= 0x000; /* +-5 range */ | |
1201 | r |= (range & 0x7) << 4; /* gain */ | |
1202 | CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex); | |
1203 | } else if (range < thisboard->rangeUniStart) { /* second batch are +-10 */ | |
1204 | r |= 0x100; /* +-10 range */ | |
1205 | r |= ((range - thisboard->range10Start) & 0x7) << 4; /* gain */ | |
1206 | CHAN_ARRAY_SET(devpriv->chanBipolar, chanIndex); | |
1207 | } else { /* last batch is +10 */ | |
1208 | r |= 0x200; /* +10 range */ | |
1209 | r |= ((range - thisboard->rangeUniStart) & 0x7) << 4; /* gain */ | |
1210 | CHAN_ARRAY_CLEAR(devpriv->chanBipolar, chanIndex); | |
1211 | } | |
1212 | ||
1213 | switch (aref) { | |
1214 | case AREF_GROUND: /* on-board ground */ | |
1215 | break; | |
1216 | ||
1217 | case AREF_COMMON: | |
1218 | r |= 0x80; /* ref external analog common */ | |
1219 | break; | |
1220 | ||
1221 | case AREF_DIFF: | |
1222 | r |= 0x400; /* differential inputs */ | |
1223 | break; | |
1224 | ||
1225 | case AREF_OTHER: /* ??? */ | |
1226 | break; | |
1227 | } | |
1228 | /*printk ("chan=%d r=%d a=%d -> 0x%x\n", | |
1229 | chan, range, aref, r); */ | |
1230 | return r; | |
1231 | } | |
1232 | ||
1233 | /* | |
1234 | Setup the channel-gain table from a comedi list | |
1235 | */ | |
71b5f4f1 | 1236 | static void rtd_load_channelgain_list(struct comedi_device *dev, |
0a85b6f0 | 1237 | unsigned int n_chan, unsigned int *list) |
3d9f0739 DC |
1238 | { |
1239 | if (n_chan > 1) { /* setup channel gain table */ | |
1240 | int ii; | |
1241 | RtdClearCGT(dev); | |
1242 | RtdEnableCGT(dev, 1); /* enable table */ | |
1243 | for (ii = 0; ii < n_chan; ii++) { | |
1244 | RtdWriteCGTable(dev, rtdConvertChanGain(dev, list[ii], | |
0a85b6f0 | 1245 | ii)); |
3d9f0739 DC |
1246 | } |
1247 | } else { /* just use the channel gain latch */ | |
1248 | RtdEnableCGT(dev, 0); /* disable table, enable latch */ | |
1249 | RtdWriteCGLatch(dev, rtdConvertChanGain(dev, list[0], 0)); | |
1250 | } | |
1251 | } | |
1252 | ||
1253 | /* determine fifo size by doing adc conversions until the fifo half | |
1254 | empty status flag clears */ | |
71b5f4f1 | 1255 | static int rtd520_probe_fifo_depth(struct comedi_device *dev) |
3d9f0739 | 1256 | { |
790c5541 | 1257 | unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND); |
3d9f0739 DC |
1258 | unsigned i; |
1259 | static const unsigned limit = 0x2000; | |
1260 | unsigned fifo_size = 0; | |
1261 | ||
1262 | RtdAdcClearFifo(dev); | |
1263 | rtd_load_channelgain_list(dev, 1, &chanspec); | |
1264 | RtdAdcConversionSource(dev, 0); /* software */ | |
1265 | /* convert samples */ | |
1266 | for (i = 0; i < limit; ++i) { | |
1267 | unsigned fifo_status; | |
1268 | /* trigger conversion */ | |
1269 | RtdAdcStart(dev); | |
5f74ea14 | 1270 | udelay(1); |
3d9f0739 | 1271 | fifo_status = RtdFifoStatus(dev); |
53106ae6 | 1272 | if ((fifo_status & FS_ADC_HEMPTY) == 0) { |
3d9f0739 DC |
1273 | fifo_size = 2 * i; |
1274 | break; | |
1275 | } | |
1276 | } | |
0a85b6f0 | 1277 | if (i == limit) { |
bc8bf90a | 1278 | printk(KERN_INFO "\ncomedi: %s: failed to probe fifo size.\n", DRV_NAME); |
3d9f0739 DC |
1279 | return -EIO; |
1280 | } | |
1281 | RtdAdcClearFifo(dev); | |
0a85b6f0 MT |
1282 | if (fifo_size != 0x400 && fifo_size != 0x2000) { |
1283 | printk | |
bc8bf90a | 1284 | (KERN_INFO "\ncomedi: %s: unexpected fifo size of %i, expected 1024 or 8192.\n", |
0a85b6f0 | 1285 | DRV_NAME, fifo_size); |
3d9f0739 DC |
1286 | return -EIO; |
1287 | } | |
1288 | return fifo_size; | |
1289 | } | |
1290 | ||
1291 | /* | |
1292 | "instructions" read/write data in "one-shot" or "software-triggered" | |
1293 | mode (simplest case). | |
25985edc | 1294 | This doesn't use interrupts. |
3d9f0739 DC |
1295 | |
1296 | Note, we don't do any settling delays. Use a instruction list to | |
1297 | select, delay, then read. | |
1298 | */ | |
71b5f4f1 | 1299 | static int rtd_ai_rinsn(struct comedi_device *dev, |
0a85b6f0 MT |
1300 | struct comedi_subdevice *s, struct comedi_insn *insn, |
1301 | unsigned int *data) | |
3d9f0739 DC |
1302 | { |
1303 | int n, ii; | |
1304 | int stat; | |
1305 | ||
1306 | /* clear any old fifo data */ | |
1307 | RtdAdcClearFifo(dev); | |
1308 | ||
1309 | /* write channel to multiplexer and clear channel gain table */ | |
1310 | rtd_load_channelgain_list(dev, 1, &insn->chanspec); | |
1311 | ||
1312 | /* set conversion source */ | |
1313 | RtdAdcConversionSource(dev, 0); /* software */ | |
1314 | ||
1315 | /* convert n samples */ | |
1316 | for (n = 0; n < insn->n; n++) { | |
1317 | s16 d; | |
1318 | /* trigger conversion */ | |
1319 | RtdAdcStart(dev); | |
1320 | ||
1321 | for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) { | |
1322 | stat = RtdFifoStatus(dev); | |
1323 | if (stat & FS_ADC_NOT_EMPTY) /* 1 -> not empty */ | |
1324 | break; | |
1325 | WAIT_QUIETLY; | |
1326 | } | |
1327 | if (ii >= RTD_ADC_TIMEOUT) { | |
0a85b6f0 MT |
1328 | DPRINTK |
1329 | ("rtd520: Error: ADC never finished! FifoStatus=0x%x\n", | |
1330 | stat ^ 0x6666); | |
3d9f0739 DC |
1331 | return -ETIMEDOUT; |
1332 | } | |
1333 | ||
1334 | /* read data */ | |
1335 | d = RtdAdcFifoGet(dev); /* get 2s comp value */ | |
1336 | /*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1); */ | |
1337 | d = d >> 3; /* low 3 bits are marker lines */ | |
bc8bf90a | 1338 | if (CHAN_ARRAY_TEST(devpriv->chanBipolar, 0)) |
3d9f0739 | 1339 | data[n] = d + 2048; /* convert to comedi unsigned data */ |
bc8bf90a | 1340 | else |
3d9f0739 | 1341 | data[n] = d; |
3d9f0739 DC |
1342 | } |
1343 | ||
1344 | /* return the number of samples read/written */ | |
1345 | return n; | |
1346 | } | |
1347 | ||
1348 | /* | |
1349 | Get what we know is there.... Fast! | |
1350 | This uses 1/2 the bus cycles of read_dregs (below). | |
1351 | ||
1352 | The manual claims that we can do a lword read, but it doesn't work here. | |
1353 | */ | |
0a85b6f0 MT |
1354 | static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s, |
1355 | int count) | |
3d9f0739 DC |
1356 | { |
1357 | int ii; | |
1358 | ||
1359 | for (ii = 0; ii < count; ii++) { | |
790c5541 | 1360 | short sample; |
3d9f0739 DC |
1361 | s16 d; |
1362 | ||
1363 | if (0 == devpriv->aiCount) { /* done */ | |
1364 | d = RtdAdcFifoGet(dev); /* Read N and discard */ | |
1365 | continue; | |
1366 | } | |
1367 | #if 0 | |
1368 | if (0 == (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY)) { /* DEBUG */ | |
1369 | DPRINTK("comedi: READ OOPS on %d of %d\n", ii + 1, | |
1370 | count); | |
1371 | break; | |
1372 | } | |
1373 | #endif | |
1374 | d = RtdAdcFifoGet(dev); /* get 2s comp value */ | |
1375 | ||
1376 | d = d >> 3; /* low 3 bits are marker lines */ | |
bc8bf90a | 1377 | if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) |
3d9f0739 | 1378 | sample = d + 2048; /* convert to comedi unsigned data */ |
bc8bf90a | 1379 | else |
3d9f0739 | 1380 | sample = d; |
bc8bf90a | 1381 | |
3d9f0739 DC |
1382 | if (!comedi_buf_put(s->async, sample)) |
1383 | return -1; | |
1384 | ||
1385 | if (devpriv->aiCount > 0) /* < 0, means read forever */ | |
1386 | devpriv->aiCount--; | |
1387 | } | |
1388 | return 0; | |
1389 | } | |
1390 | ||
1391 | /* | |
1392 | unknown amout of data is waiting in fifo. | |
1393 | */ | |
34c43922 | 1394 | static int ai_read_dregs(struct comedi_device *dev, struct comedi_subdevice *s) |
3d9f0739 DC |
1395 | { |
1396 | while (RtdFifoStatus(dev) & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */ | |
790c5541 | 1397 | short sample; |
3d9f0739 DC |
1398 | s16 d = RtdAdcFifoGet(dev); /* get 2s comp value */ |
1399 | ||
1400 | if (0 == devpriv->aiCount) { /* done */ | |
1401 | continue; /* read rest */ | |
1402 | } | |
1403 | ||
1404 | d = d >> 3; /* low 3 bits are marker lines */ | |
bc8bf90a | 1405 | if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) |
3d9f0739 | 1406 | sample = d + 2048; /* convert to comedi unsigned data */ |
bc8bf90a | 1407 | else |
3d9f0739 | 1408 | sample = d; |
bc8bf90a | 1409 | |
3d9f0739 DC |
1410 | if (!comedi_buf_put(s->async, sample)) |
1411 | return -1; | |
1412 | ||
1413 | if (devpriv->aiCount > 0) /* < 0, means read forever */ | |
1414 | devpriv->aiCount--; | |
1415 | } | |
1416 | return 0; | |
1417 | } | |
1418 | ||
1419 | #ifdef USE_DMA | |
1420 | /* | |
1421 | Terminate a DMA transfer and wait for everything to quiet down | |
1422 | */ | |
71b5f4f1 | 1423 | void abort_dma(struct comedi_device *dev, unsigned int channel) |
3d9f0739 DC |
1424 | { /* DMA channel 0, 1 */ |
1425 | unsigned long dma_cs_addr; /* the control/status register */ | |
1426 | uint8_t status; | |
1427 | unsigned int ii; | |
b6c77757 | 1428 | /* unsigned long flags; */ |
3d9f0739 DC |
1429 | |
1430 | dma_cs_addr = (unsigned long)devpriv->lcfg | |
0a85b6f0 | 1431 | + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1); |
3d9f0739 | 1432 | |
b6c77757 | 1433 | /* spinlock for plx dma control/status reg */ |
5f74ea14 | 1434 | /* spin_lock_irqsave( &dev->spinlock, flags ); */ |
3d9f0739 | 1435 | |
b6c77757 | 1436 | /* abort dma transfer if necessary */ |
3d9f0739 DC |
1437 | status = readb(dma_cs_addr); |
1438 | if ((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */ | |
1439 | DPRINTK("rtd520: AbortDma on non-active channel %d (0x%x)\n", | |
1440 | channel, status); | |
1441 | goto abortDmaExit; | |
1442 | } | |
1443 | ||
1444 | /* wait to make sure done bit is zero (needed?) */ | |
1445 | for (ii = 0; (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT; ii++) { | |
1446 | WAIT_QUIETLY; | |
1447 | status = readb(dma_cs_addr); | |
1448 | } | |
1449 | if (status & PLX_DMA_DONE_BIT) { | |
1450 | printk("rtd520: Timeout waiting for dma %i done clear\n", | |
0a85b6f0 | 1451 | channel); |
3d9f0739 DC |
1452 | goto abortDmaExit; |
1453 | } | |
1454 | ||
1455 | /* disable channel (required) */ | |
1456 | writeb(0, dma_cs_addr); | |
0a85b6f0 | 1457 | udelay(1); /* needed?? */ |
3d9f0739 DC |
1458 | /* set abort bit for channel */ |
1459 | writeb(PLX_DMA_ABORT_BIT, dma_cs_addr); | |
1460 | ||
b6c77757 | 1461 | /* wait for dma done bit to be set */ |
3d9f0739 DC |
1462 | status = readb(dma_cs_addr); |
1463 | for (ii = 0; | |
0a85b6f0 | 1464 | (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT; ii++) { |
3d9f0739 DC |
1465 | status = readb(dma_cs_addr); |
1466 | WAIT_QUIETLY; | |
1467 | } | |
1468 | if ((status & PLX_DMA_DONE_BIT) == 0) { | |
1469 | printk("rtd520: Timeout waiting for dma %i done set\n", | |
0a85b6f0 | 1470 | channel); |
3d9f0739 DC |
1471 | } |
1472 | ||
0a85b6f0 | 1473 | abortDmaExit: |
5f74ea14 | 1474 | /* spin_unlock_irqrestore( &dev->spinlock, flags ); */ |
3d9f0739 DC |
1475 | } |
1476 | ||
1477 | /* | |
1478 | Process what is in the DMA transfer buffer and pass to comedi | |
1479 | Note: this is not re-entrant | |
1480 | */ | |
34c43922 | 1481 | static int ai_process_dma(struct comedi_device *dev, struct comedi_subdevice *s) |
3d9f0739 DC |
1482 | { |
1483 | int ii, n; | |
1484 | s16 *dp; | |
1485 | ||
1486 | if (devpriv->aiCount == 0) /* transfer already complete */ | |
1487 | return 0; | |
1488 | ||
1489 | dp = devpriv->dma0Buff[devpriv->dma0Offset]; | |
1490 | for (ii = 0; ii < devpriv->fifoLen / 2;) { /* convert samples */ | |
790c5541 | 1491 | short sample; |
3d9f0739 DC |
1492 | |
1493 | if (CHAN_ARRAY_TEST(devpriv->chanBipolar, s->async->cur_chan)) { | |
1494 | sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */ | |
bc8bf90a | 1495 | else |
3d9f0739 | 1496 | sample = *dp >> 3; /* low 3 bits are marker lines */ |
bc8bf90a | 1497 | |
3d9f0739 DC |
1498 | *dp++ = sample; /* put processed value back */ |
1499 | ||
1500 | if (++s->async->cur_chan >= s->async->cmd.chanlist_len) | |
1501 | s->async->cur_chan = 0; | |
1502 | ||
1503 | ++ii; /* number ready to transfer */ | |
1504 | if (devpriv->aiCount > 0) { /* < 0, means read forever */ | |
1505 | if (--devpriv->aiCount == 0) { /* done */ | |
1506 | /*DPRINTK ("rtd520: Final %d samples\n", ii); */ | |
1507 | break; | |
1508 | } | |
1509 | } | |
1510 | } | |
1511 | ||
1512 | /* now pass the whole array to the comedi buffer */ | |
1513 | dp = devpriv->dma0Buff[devpriv->dma0Offset]; | |
1514 | n = comedi_buf_write_alloc(s->async, ii * sizeof(s16)); | |
1515 | if (n < (ii * sizeof(s16))) { /* any residual is an error */ | |
1516 | DPRINTK("rtd520:ai_process_dma buffer overflow %d samples!\n", | |
1517 | ii - (n / sizeof(s16))); | |
1518 | s->async->events |= COMEDI_CB_ERROR; | |
1519 | return -1; | |
1520 | } | |
1521 | comedi_buf_memcpy_to(s->async, 0, dp, n); | |
1522 | comedi_buf_write_free(s->async, n); | |
1523 | ||
1524 | /* always at least 1 scan -- 1/2 FIFO is larger than our max scan list */ | |
1525 | s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; | |
1526 | ||
1527 | if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */ | |
1528 | devpriv->dma0Offset = 0; | |
1529 | } | |
1530 | return 0; | |
1531 | } | |
1532 | #endif /* USE_DMA */ | |
1533 | ||
1534 | /* | |
1535 | Handle all rtd520 interrupts. | |
1536 | Runs atomically and is never re-entered. | |
1537 | This is a "slow handler"; other interrupts may be active. | |
1538 | The data conversion may someday happen in a "bottom half". | |
1539 | */ | |
1540 | static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */ | |
0a85b6f0 MT |
1541 | void *d) |
1542 | { /* our data *//* cpu context (ignored) */ | |
71b5f4f1 | 1543 | struct comedi_device *dev = d; /* must be called "dev" for devpriv */ |
3d9f0739 DC |
1544 | u16 status; |
1545 | u16 fifoStatus; | |
34c43922 | 1546 | struct comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */ |
3d9f0739 | 1547 | |
bc8bf90a | 1548 | if (!dev->attached) |
3d9f0739 | 1549 | return IRQ_NONE; |
3d9f0739 DC |
1550 | |
1551 | devpriv->intCount++; /* DEBUG statistics */ | |
1552 | ||
1553 | fifoStatus = RtdFifoStatus(dev); | |
1554 | /* check for FIFO full, this automatically halts the ADC! */ | |
1555 | if (!(fifoStatus & FS_ADC_NOT_FULL)) { /* 0 -> full */ | |
1556 | DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */ | |
1557 | goto abortTransfer; | |
1558 | } | |
1559 | #ifdef USE_DMA | |
1560 | if (devpriv->flags & DMA0_ACTIVE) { /* Check DMA */ | |
1561 | u32 istatus = RtdPlxInterruptRead(dev); | |
1562 | ||
1563 | if (istatus & ICS_DMA0_A) { | |
1564 | if (ai_process_dma(dev, s) < 0) { | |
0a85b6f0 MT |
1565 | DPRINTK |
1566 | ("rtd520: comedi read buffer overflow (DMA) with %ld to go!\n", | |
1567 | devpriv->aiCount); | |
3d9f0739 | 1568 | RtdDma0Control(dev, |
0a85b6f0 | 1569 | (devpriv->dma0Control & |
3d9f0739 | 1570 | ~PLX_DMA_START_BIT) |
0a85b6f0 | 1571 | | PLX_CLEAR_DMA_INTR_BIT); |
3d9f0739 DC |
1572 | goto abortTransfer; |
1573 | } | |
1574 | ||
1575 | /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n", | |
1576 | devpriv->aiCount, istatus); */ | |
1577 | RtdDma0Control(dev, | |
0a85b6f0 MT |
1578 | (devpriv-> |
1579 | dma0Control & ~PLX_DMA_START_BIT) | |
1580 | | PLX_CLEAR_DMA_INTR_BIT); | |
3d9f0739 DC |
1581 | if (0 == devpriv->aiCount) { /* counted down */ |
1582 | DPRINTK("rtd520: Samples Done (DMA).\n"); | |
1583 | goto transferDone; | |
1584 | } | |
1585 | comedi_event(dev, s); | |
1586 | } else { | |
1587 | /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus); */ | |
1588 | } | |
1589 | } | |
1590 | /* Fall through and check for other interrupt sources */ | |
1591 | #endif /* USE_DMA */ | |
1592 | ||
1593 | status = RtdInterruptStatus(dev); | |
1594 | /* if interrupt was not caused by our board, or handled above */ | |
bc8bf90a | 1595 | if (0 == status) |
3d9f0739 | 1596 | return IRQ_HANDLED; |
3d9f0739 DC |
1597 | |
1598 | if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */ | |
1599 | /* since the priority interrupt controller may have queued a sample | |
1600 | counter interrupt, even though we have already finished, | |
1601 | we must handle the possibility that there is no data here */ | |
1602 | if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */ | |
1603 | /*DPRINTK("rtd520: Sample int, reading 1/2FIFO. fifo_status 0x%x\n", | |
1604 | (fifoStatus ^ 0x6666) & 0x7777); */ | |
1605 | if (ai_read_n(dev, s, devpriv->fifoLen / 2) < 0) { | |
0a85b6f0 MT |
1606 | DPRINTK |
1607 | ("rtd520: comedi read buffer overflow (1/2FIFO) with %ld to go!\n", | |
1608 | devpriv->aiCount); | |
3d9f0739 DC |
1609 | goto abortTransfer; |
1610 | } | |
1611 | if (0 == devpriv->aiCount) { /* counted down */ | |
1612 | DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n", (fifoStatus ^ 0x6666) & 0x7777); /* should be all 0s */ | |
1613 | goto transferDone; | |
1614 | } | |
1615 | comedi_event(dev, s); | |
1616 | } else if (devpriv->transCount > 0) { /* read often */ | |
1617 | /*DPRINTK("rtd520: Sample int, reading %d fifo_status 0x%x\n", | |
1618 | devpriv->transCount, (fifoStatus ^ 0x6666) & 0x7777); */ | |
1619 | if (fifoStatus & FS_ADC_NOT_EMPTY) { /* 1 -> not empty */ | |
1620 | if (ai_read_n(dev, s, devpriv->transCount) < 0) { | |
0a85b6f0 MT |
1621 | DPRINTK |
1622 | ("rtd520: comedi read buffer overflow (N) with %ld to go!\n", | |
1623 | devpriv->aiCount); | |
3d9f0739 DC |
1624 | goto abortTransfer; |
1625 | } | |
1626 | if (0 == devpriv->aiCount) { /* counted down */ | |
0a85b6f0 MT |
1627 | DPRINTK |
1628 | ("rtd520: Samples Done (N). fifo_status was 0x%x\n", | |
1629 | (fifoStatus ^ 0x6666) & 0x7777); | |
3d9f0739 DC |
1630 | goto transferDone; |
1631 | } | |
1632 | comedi_event(dev, s); | |
1633 | } | |
1634 | } else { /* wait for 1/2 FIFO (old) */ | |
0a85b6f0 MT |
1635 | DPRINTK |
1636 | ("rtd520: Sample int. Wait for 1/2. fifo_status 0x%x\n", | |
1637 | (fifoStatus ^ 0x6666) & 0x7777); | |
3d9f0739 DC |
1638 | } |
1639 | } else { | |
1640 | DPRINTK("rtd520: unknown interrupt source!\n"); | |
1641 | } | |
1642 | ||
1643 | if (0xffff & RtdInterruptOverrunStatus(dev)) { /* interrupt overrun */ | |
0a85b6f0 MT |
1644 | DPRINTK |
1645 | ("rtd520: Interrupt overrun with %ld to go! over_status=0x%x\n", | |
1646 | devpriv->aiCount, 0xffff & RtdInterruptOverrunStatus(dev)); | |
3d9f0739 DC |
1647 | goto abortTransfer; |
1648 | } | |
1649 | ||
1650 | /* clear the interrupt */ | |
1651 | RtdInterruptClearMask(dev, status); | |
1652 | RtdInterruptClear(dev); | |
1653 | return IRQ_HANDLED; | |
1654 | ||
0a85b6f0 | 1655 | abortTransfer: |
3d9f0739 DC |
1656 | RtdAdcClearFifo(dev); /* clears full flag */ |
1657 | s->async->events |= COMEDI_CB_ERROR; | |
1658 | devpriv->aiCount = 0; /* stop and don't transfer any more */ | |
1659 | /* fall into transferDone */ | |
1660 | ||
0a85b6f0 | 1661 | transferDone: |
3d9f0739 DC |
1662 | RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */ |
1663 | RtdPacerStop(dev); /* Stop PACER */ | |
1664 | RtdAdcConversionSource(dev, 0); /* software trigger only */ | |
1665 | RtdInterruptMask(dev, 0); /* mask out SAMPLE */ | |
1666 | #ifdef USE_DMA | |
1667 | if (devpriv->flags & DMA0_ACTIVE) { | |
1668 | RtdPlxInterruptWrite(dev, /* disable any more interrupts */ | |
0a85b6f0 | 1669 | RtdPlxInterruptRead(dev) & ~ICS_DMA0_E); |
3d9f0739 DC |
1670 | abort_dma(dev, 0); |
1671 | devpriv->flags &= ~DMA0_ACTIVE; | |
1672 | /* if Using DMA, then we should have read everything by now */ | |
1673 | if (devpriv->aiCount > 0) { | |
1674 | DPRINTK("rtd520: Lost DMA data! %ld remain\n", | |
1675 | devpriv->aiCount); | |
1676 | } | |
1677 | } | |
1678 | #endif /* USE_DMA */ | |
1679 | ||
1680 | if (devpriv->aiCount > 0) { /* there shouldn't be anything left */ | |
1681 | fifoStatus = RtdFifoStatus(dev); | |
1682 | DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n", devpriv->aiCount, (fifoStatus ^ 0x6666) & 0x7777); /* should read all 0s */ | |
1683 | ai_read_dregs(dev, s); /* read anything left in FIFO */ | |
1684 | } | |
1685 | ||
1686 | s->async->events |= COMEDI_CB_EOA; /* signal end to comedi */ | |
1687 | comedi_event(dev, s); | |
1688 | ||
1689 | /* clear the interrupt */ | |
1690 | status = RtdInterruptStatus(dev); | |
1691 | RtdInterruptClearMask(dev, status); | |
1692 | RtdInterruptClear(dev); | |
1693 | ||
1694 | fifoStatus = RtdFifoStatus(dev); /* DEBUG */ | |
0a85b6f0 MT |
1695 | DPRINTK |
1696 | ("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n", | |
1697 | devpriv->intCount, status, | |
1698 | 0xffff & RtdInterruptOverrunStatus(dev)); | |
3d9f0739 DC |
1699 | |
1700 | return IRQ_HANDLED; | |
1701 | } | |
1702 | ||
1703 | #if 0 | |
1704 | /* | |
1705 | return the number of samples available | |
1706 | */ | |
34c43922 | 1707 | static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s) |
3d9f0739 DC |
1708 | { |
1709 | /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */ | |
1710 | /* Not sure what to do if DMA is active */ | |
1711 | return s->async->buf_write_count - s->async->buf_read_count; | |
1712 | } | |
1713 | #endif | |
1714 | ||
1715 | /* | |
1716 | cmdtest tests a particular command to see if it is valid. | |
1717 | Using the cmdtest ioctl, a user can create a valid cmd | |
1718 | and then have it executed by the cmd ioctl (asyncronously). | |
1719 | ||
1720 | cmdtest returns 1,2,3,4 or 0, depending on which tests | |
1721 | the command passes. | |
1722 | */ | |
1723 | ||
71b5f4f1 | 1724 | static int rtd_ai_cmdtest(struct comedi_device *dev, |
0a85b6f0 | 1725 | struct comedi_subdevice *s, struct comedi_cmd *cmd) |
3d9f0739 DC |
1726 | { |
1727 | int err = 0; | |
1728 | int tmp; | |
1729 | ||
1730 | /* step 1: make sure trigger sources are trivially valid */ | |
1731 | ||
1732 | tmp = cmd->start_src; | |
1733 | cmd->start_src &= TRIG_NOW; | |
bc8bf90a | 1734 | if (!cmd->start_src || tmp != cmd->start_src) |
3d9f0739 | 1735 | err++; |
3d9f0739 DC |
1736 | |
1737 | tmp = cmd->scan_begin_src; | |
1738 | cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT; | |
bc8bf90a | 1739 | if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) |
3d9f0739 | 1740 | err++; |
bc8bf90a | 1741 | |
3d9f0739 DC |
1742 | |
1743 | tmp = cmd->convert_src; | |
1744 | cmd->convert_src &= TRIG_TIMER | TRIG_EXT; | |
bc8bf90a | 1745 | if (!cmd->convert_src || tmp != cmd->convert_src) |
3d9f0739 | 1746 | err++; |
bc8bf90a | 1747 | |
3d9f0739 DC |
1748 | |
1749 | tmp = cmd->scan_end_src; | |
1750 | cmd->scan_end_src &= TRIG_COUNT; | |
bc8bf90a | 1751 | if (!cmd->scan_end_src || tmp != cmd->scan_end_src) |
3d9f0739 | 1752 | err++; |
bc8bf90a | 1753 | |
3d9f0739 DC |
1754 | |
1755 | tmp = cmd->stop_src; | |
1756 | cmd->stop_src &= TRIG_COUNT | TRIG_NONE; | |
bc8bf90a | 1757 | if (!cmd->stop_src || tmp != cmd->stop_src) |
3d9f0739 | 1758 | err++; |
bc8bf90a | 1759 | |
3d9f0739 DC |
1760 | |
1761 | if (err) | |
1762 | return 1; | |
1763 | ||
1764 | /* step 2: make sure trigger sources are unique | |
1765 | and mutually compatible */ | |
828684f9 | 1766 | /* note that mutual compatibility is not an issue here */ |
3d9f0739 | 1767 | if (cmd->scan_begin_src != TRIG_TIMER && |
0a85b6f0 | 1768 | cmd->scan_begin_src != TRIG_EXT) { |
3d9f0739 DC |
1769 | err++; |
1770 | } | |
bc8bf90a | 1771 | if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT) |
3d9f0739 | 1772 | err++; |
bc8bf90a NR |
1773 | |
1774 | if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE) | |
3d9f0739 | 1775 | err++; |
3d9f0739 | 1776 | |
bc8bf90a | 1777 | if (err) |
3d9f0739 | 1778 | return 2; |
3d9f0739 DC |
1779 | |
1780 | /* step 3: make sure arguments are trivially compatible */ | |
1781 | ||
1782 | if (cmd->start_arg != 0) { | |
1783 | cmd->start_arg = 0; | |
1784 | err++; | |
1785 | } | |
1786 | ||
1787 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1788 | /* Note: these are time periods, not actual rates */ | |
1789 | if (1 == cmd->chanlist_len) { /* no scanning */ | |
1790 | if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) { | |
1791 | cmd->scan_begin_arg = RTD_MAX_SPEED_1; | |
1792 | rtd_ns_to_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1793 | TRIG_ROUND_UP); |
3d9f0739 DC |
1794 | err++; |
1795 | } | |
1796 | if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) { | |
1797 | cmd->scan_begin_arg = RTD_MIN_SPEED_1; | |
1798 | rtd_ns_to_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1799 | TRIG_ROUND_DOWN); |
3d9f0739 DC |
1800 | err++; |
1801 | } | |
1802 | } else { | |
1803 | if (cmd->scan_begin_arg < RTD_MAX_SPEED) { | |
1804 | cmd->scan_begin_arg = RTD_MAX_SPEED; | |
1805 | rtd_ns_to_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1806 | TRIG_ROUND_UP); |
3d9f0739 DC |
1807 | err++; |
1808 | } | |
1809 | if (cmd->scan_begin_arg > RTD_MIN_SPEED) { | |
1810 | cmd->scan_begin_arg = RTD_MIN_SPEED; | |
1811 | rtd_ns_to_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1812 | TRIG_ROUND_DOWN); |
3d9f0739 DC |
1813 | err++; |
1814 | } | |
1815 | } | |
1816 | } else { | |
1817 | /* external trigger */ | |
1818 | /* should be level/edge, hi/lo specification here */ | |
1819 | /* should specify multiple external triggers */ | |
1820 | if (cmd->scan_begin_arg > 9) { | |
1821 | cmd->scan_begin_arg = 9; | |
1822 | err++; | |
1823 | } | |
1824 | } | |
1825 | if (cmd->convert_src == TRIG_TIMER) { | |
1826 | if (1 == cmd->chanlist_len) { /* no scanning */ | |
1827 | if (cmd->convert_arg < RTD_MAX_SPEED_1) { | |
1828 | cmd->convert_arg = RTD_MAX_SPEED_1; | |
1829 | rtd_ns_to_timer(&cmd->convert_arg, | |
0a85b6f0 | 1830 | TRIG_ROUND_UP); |
3d9f0739 DC |
1831 | err++; |
1832 | } | |
1833 | if (cmd->convert_arg > RTD_MIN_SPEED_1) { | |
1834 | cmd->convert_arg = RTD_MIN_SPEED_1; | |
1835 | rtd_ns_to_timer(&cmd->convert_arg, | |
0a85b6f0 | 1836 | TRIG_ROUND_DOWN); |
3d9f0739 DC |
1837 | err++; |
1838 | } | |
1839 | } else { | |
1840 | if (cmd->convert_arg < RTD_MAX_SPEED) { | |
1841 | cmd->convert_arg = RTD_MAX_SPEED; | |
1842 | rtd_ns_to_timer(&cmd->convert_arg, | |
0a85b6f0 | 1843 | TRIG_ROUND_UP); |
3d9f0739 DC |
1844 | err++; |
1845 | } | |
1846 | if (cmd->convert_arg > RTD_MIN_SPEED) { | |
1847 | cmd->convert_arg = RTD_MIN_SPEED; | |
1848 | rtd_ns_to_timer(&cmd->convert_arg, | |
0a85b6f0 | 1849 | TRIG_ROUND_DOWN); |
3d9f0739 DC |
1850 | err++; |
1851 | } | |
1852 | } | |
1853 | } else { | |
1854 | /* external trigger */ | |
1855 | /* see above */ | |
1856 | if (cmd->convert_arg > 9) { | |
1857 | cmd->convert_arg = 9; | |
1858 | err++; | |
1859 | } | |
1860 | } | |
1861 | ||
1862 | #if 0 | |
1863 | if (cmd->scan_end_arg != cmd->chanlist_len) { | |
1864 | cmd->scan_end_arg = cmd->chanlist_len; | |
1865 | err++; | |
1866 | } | |
1867 | #endif | |
1868 | if (cmd->stop_src == TRIG_COUNT) { | |
1869 | /* TODO check for rounding error due to counter wrap */ | |
1870 | ||
1871 | } else { | |
1872 | /* TRIG_NONE */ | |
1873 | if (cmd->stop_arg != 0) { | |
1874 | cmd->stop_arg = 0; | |
1875 | err++; | |
1876 | } | |
1877 | } | |
1878 | ||
bc8bf90a | 1879 | if (err) |
3d9f0739 | 1880 | return 3; |
bc8bf90a | 1881 | |
3d9f0739 DC |
1882 | |
1883 | /* step 4: fix up any arguments */ | |
1884 | ||
1885 | if (cmd->chanlist_len > RTD_MAX_CHANLIST) { | |
1886 | cmd->chanlist_len = RTD_MAX_CHANLIST; | |
1887 | err++; | |
1888 | } | |
1889 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
1890 | tmp = cmd->scan_begin_arg; | |
1891 | rtd_ns_to_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 1892 | cmd->flags & TRIG_ROUND_MASK); |
bc8bf90a | 1893 | if (tmp != cmd->scan_begin_arg) |
3d9f0739 | 1894 | err++; |
bc8bf90a | 1895 | |
3d9f0739 DC |
1896 | } |
1897 | if (cmd->convert_src == TRIG_TIMER) { | |
1898 | tmp = cmd->convert_arg; | |
1899 | rtd_ns_to_timer(&cmd->convert_arg, | |
0a85b6f0 | 1900 | cmd->flags & TRIG_ROUND_MASK); |
bc8bf90a | 1901 | if (tmp != cmd->convert_arg) |
3d9f0739 | 1902 | err++; |
bc8bf90a | 1903 | |
3d9f0739 | 1904 | if (cmd->scan_begin_src == TRIG_TIMER |
0a85b6f0 MT |
1905 | && (cmd->scan_begin_arg |
1906 | < (cmd->convert_arg * cmd->scan_end_arg))) { | |
3d9f0739 | 1907 | cmd->scan_begin_arg = |
0a85b6f0 | 1908 | cmd->convert_arg * cmd->scan_end_arg; |
3d9f0739 DC |
1909 | err++; |
1910 | } | |
1911 | } | |
1912 | ||
bc8bf90a | 1913 | if (err) |
3d9f0739 | 1914 | return 4; |
3d9f0739 DC |
1915 | |
1916 | return 0; | |
1917 | } | |
1918 | ||
1919 | /* | |
1920 | Execute a analog in command with many possible triggering options. | |
1921 | The data get stored in the async structure of the subdevice. | |
1922 | This is usually done by an interrupt handler. | |
1923 | Userland gets to the data using read calls. | |
1924 | */ | |
34c43922 | 1925 | static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
3d9f0739 | 1926 | { |
ea6d0d4c | 1927 | struct comedi_cmd *cmd = &s->async->cmd; |
3d9f0739 DC |
1928 | int timer; |
1929 | ||
1930 | /* stop anything currently running */ | |
1931 | RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */ | |
1932 | RtdPacerStop(dev); /* make sure PACER is stopped */ | |
1933 | RtdAdcConversionSource(dev, 0); /* software trigger only */ | |
1934 | RtdInterruptMask(dev, 0); | |
1935 | #ifdef USE_DMA | |
1936 | if (devpriv->flags & DMA0_ACTIVE) { /* cancel anything running */ | |
1937 | RtdPlxInterruptWrite(dev, /* disable any more interrupts */ | |
0a85b6f0 | 1938 | RtdPlxInterruptRead(dev) & ~ICS_DMA0_E); |
3d9f0739 DC |
1939 | abort_dma(dev, 0); |
1940 | devpriv->flags &= ~DMA0_ACTIVE; | |
1941 | if (RtdPlxInterruptRead(dev) & ICS_DMA0_A) { /*clear pending int */ | |
1942 | RtdDma0Control(dev, PLX_CLEAR_DMA_INTR_BIT); | |
1943 | } | |
1944 | } | |
1945 | RtdDma0Reset(dev); /* reset onboard state */ | |
1946 | #endif /* USE_DMA */ | |
1947 | RtdAdcClearFifo(dev); /* clear any old data */ | |
1948 | RtdInterruptOverrunClear(dev); | |
1949 | devpriv->intCount = 0; | |
1950 | ||
1951 | if (!dev->irq) { /* we need interrupts for this */ | |
1952 | DPRINTK("rtd520: ERROR! No interrupt available!\n"); | |
1953 | return -ENXIO; | |
1954 | } | |
1955 | ||
1956 | /* start configuration */ | |
1957 | /* load channel list and reset CGT */ | |
1958 | rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist); | |
1959 | ||
1960 | /* setup the common case and override if needed */ | |
1961 | if (cmd->chanlist_len > 1) { | |
1962 | /*DPRINTK ("rtd520: Multi channel setup\n"); */ | |
1963 | RtdPacerStartSource(dev, 0); /* software triggers pacer */ | |
1964 | RtdBurstStartSource(dev, 1); /* PACER triggers burst */ | |
1965 | RtdAdcConversionSource(dev, 2); /* BURST triggers ADC */ | |
1966 | } else { /* single channel */ | |
1967 | /*DPRINTK ("rtd520: single channel setup\n"); */ | |
1968 | RtdPacerStartSource(dev, 0); /* software triggers pacer */ | |
1969 | RtdAdcConversionSource(dev, 1); /* PACER triggers ADC */ | |
1970 | } | |
1971 | RtdAboutCounter(dev, devpriv->fifoLen / 2 - 1); /* 1/2 FIFO */ | |
1972 | ||
1973 | if (TRIG_TIMER == cmd->scan_begin_src) { | |
1974 | /* scan_begin_arg is in nanoseconds */ | |
1975 | /* find out how many samples to wait before transferring */ | |
1976 | if (cmd->flags & TRIG_WAKE_EOS) { | |
1977 | /* this may generate un-sustainable interrupt rates */ | |
1978 | /* the application is responsible for doing the right thing */ | |
1979 | devpriv->transCount = cmd->chanlist_len; | |
1980 | devpriv->flags |= SEND_EOS; | |
1981 | } else { | |
1982 | /* arrange to transfer data periodically */ | |
1983 | devpriv->transCount | |
0a85b6f0 MT |
1984 | = |
1985 | (TRANS_TARGET_PERIOD * cmd->chanlist_len) / | |
1986 | cmd->scan_begin_arg; | |
3d9f0739 DC |
1987 | if (devpriv->transCount < cmd->chanlist_len) { |
1988 | /* tranfer after each scan (and avoid 0) */ | |
1989 | devpriv->transCount = cmd->chanlist_len; | |
1990 | } else { /* make a multiple of scan length */ | |
1991 | devpriv->transCount = | |
0a85b6f0 MT |
1992 | (devpriv->transCount + |
1993 | cmd->chanlist_len - 1) | |
1994 | / cmd->chanlist_len; | |
3d9f0739 DC |
1995 | devpriv->transCount *= cmd->chanlist_len; |
1996 | } | |
1997 | devpriv->flags |= SEND_EOS; | |
1998 | } | |
1999 | if (devpriv->transCount >= (devpriv->fifoLen / 2)) { | |
2000 | /* out of counter range, use 1/2 fifo instead */ | |
2001 | devpriv->transCount = 0; | |
2002 | devpriv->flags &= ~SEND_EOS; | |
2003 | } else { | |
2004 | /* interrupt for each tranfer */ | |
2005 | RtdAboutCounter(dev, devpriv->transCount - 1); | |
2006 | } | |
2007 | ||
0a85b6f0 MT |
2008 | DPRINTK |
2009 | ("rtd520: scanLen=%d tranferCount=%d fifoLen=%d\n scanTime(ns)=%d flags=0x%x\n", | |
2010 | cmd->chanlist_len, devpriv->transCount, devpriv->fifoLen, | |
2011 | cmd->scan_begin_arg, devpriv->flags); | |
3d9f0739 DC |
2012 | } else { /* unknown timing, just use 1/2 FIFO */ |
2013 | devpriv->transCount = 0; | |
2014 | devpriv->flags &= ~SEND_EOS; | |
2015 | } | |
2016 | RtdPacerClockSource(dev, 1); /* use INTERNAL 8Mhz clock source */ | |
2017 | RtdAboutStopEnable(dev, 1); /* just interrupt, dont stop */ | |
2018 | ||
2019 | /* BUG??? these look like enumerated values, but they are bit fields */ | |
2020 | ||
2021 | /* First, setup when to stop */ | |
2022 | switch (cmd->stop_src) { | |
2023 | case TRIG_COUNT: /* stop after N scans */ | |
2024 | devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len; | |
2025 | if ((devpriv->transCount > 0) | |
0a85b6f0 | 2026 | && (devpriv->transCount > devpriv->aiCount)) { |
3d9f0739 DC |
2027 | devpriv->transCount = devpriv->aiCount; |
2028 | } | |
2029 | break; | |
2030 | ||
2031 | case TRIG_NONE: /* stop when cancel is called */ | |
2032 | devpriv->aiCount = -1; /* read forever */ | |
2033 | break; | |
2034 | ||
2035 | default: | |
2036 | DPRINTK("rtd520: Warning! ignoring stop_src mode %d\n", | |
2037 | cmd->stop_src); | |
2038 | } | |
2039 | ||
2040 | /* Scan timing */ | |
2041 | switch (cmd->scan_begin_src) { | |
2042 | case TRIG_TIMER: /* periodic scanning */ | |
2043 | timer = rtd_ns_to_timer(&cmd->scan_begin_arg, | |
0a85b6f0 | 2044 | TRIG_ROUND_NEAREST); |
3d9f0739 DC |
2045 | /* set PACER clock */ |
2046 | /*DPRINTK ("rtd520: loading %d into pacer\n", timer); */ | |
2047 | RtdPacerCounter(dev, timer); | |
2048 | ||
2049 | break; | |
2050 | ||
2051 | case TRIG_EXT: | |
2052 | RtdPacerStartSource(dev, 1); /* EXTERNALy trigger pacer */ | |
2053 | break; | |
2054 | ||
2055 | default: | |
2056 | DPRINTK("rtd520: Warning! ignoring scan_begin_src mode %d\n", | |
2057 | cmd->scan_begin_src); | |
2058 | } | |
2059 | ||
2060 | /* Sample timing within a scan */ | |
2061 | switch (cmd->convert_src) { | |
2062 | case TRIG_TIMER: /* periodic */ | |
2063 | if (cmd->chanlist_len > 1) { /* only needed for multi-channel */ | |
2064 | timer = rtd_ns_to_timer(&cmd->convert_arg, | |
0a85b6f0 | 2065 | TRIG_ROUND_NEAREST); |
3d9f0739 DC |
2066 | /* setup BURST clock */ |
2067 | /*DPRINTK ("rtd520: loading %d into burst\n", timer); */ | |
2068 | RtdBurstCounter(dev, timer); | |
2069 | } | |
2070 | ||
2071 | break; | |
2072 | ||
2073 | case TRIG_EXT: /* external */ | |
2074 | RtdBurstStartSource(dev, 2); /* EXTERNALy trigger burst */ | |
2075 | break; | |
2076 | ||
2077 | default: | |
2078 | DPRINTK("rtd520: Warning! ignoring convert_src mode %d\n", | |
2079 | cmd->convert_src); | |
2080 | } | |
2081 | /* end configuration */ | |
2082 | ||
2083 | /* This doesn't seem to work. There is no way to clear an interrupt | |
2084 | that the priority controller has queued! */ | |
2085 | RtdInterruptClearMask(dev, ~0); /* clear any existing flags */ | |
2086 | RtdInterruptClear(dev); | |
2087 | ||
2088 | /* TODO: allow multiple interrupt sources */ | |
2089 | if (devpriv->transCount > 0) { /* transfer every N samples */ | |
2090 | RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT); | |
2091 | DPRINTK("rtd520: Transferring every %d\n", devpriv->transCount); | |
2092 | } else { /* 1/2 FIFO transfers */ | |
2093 | #ifdef USE_DMA | |
2094 | devpriv->flags |= DMA0_ACTIVE; | |
2095 | ||
2096 | /* point to first transfer in ring */ | |
2097 | devpriv->dma0Offset = 0; | |
2098 | RtdDma0Mode(dev, DMA_MODE_BITS); | |
2099 | RtdDma0Next(dev, /* point to first block */ | |
0a85b6f0 | 2100 | devpriv->dma0Chain[DMA_CHAIN_COUNT - 1].next); |
3d9f0739 DC |
2101 | RtdDma0Source(dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */ |
2102 | ||
2103 | RtdPlxInterruptWrite(dev, /* enable interrupt */ | |
0a85b6f0 | 2104 | RtdPlxInterruptRead(dev) | ICS_DMA0_E); |
3d9f0739 DC |
2105 | /* Must be 2 steps. See PLX app note about "Starting a DMA transfer" */ |
2106 | RtdDma0Control(dev, PLX_DMA_EN_BIT); /* enable DMA (clear INTR?) */ | |
2107 | RtdDma0Control(dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT); /*start DMA */ | |
2108 | DPRINTK("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n", | |
2109 | RtdPlxInterruptRead(dev), devpriv->intMask); | |
2110 | #else /* USE_DMA */ | |
2111 | RtdInterruptMask(dev, IRQM_ADC_ABOUT_CNT); | |
2112 | DPRINTK("rtd520: Transferring every 1/2 FIFO\n"); | |
2113 | #endif /* USE_DMA */ | |
2114 | } | |
2115 | ||
2116 | /* BUG: start_src is ASSUMED to be TRIG_NOW */ | |
2117 | /* BUG? it seems like things are running before the "start" */ | |
2118 | RtdPacerStart(dev); /* Start PACER */ | |
2119 | return 0; | |
2120 | } | |
2121 | ||
2122 | /* | |
25985edc | 2123 | Stop a running data acquisition. |
3d9f0739 | 2124 | */ |
34c43922 | 2125 | static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
3d9f0739 DC |
2126 | { |
2127 | u16 status; | |
2128 | ||
2129 | RtdPacerStopSource(dev, 0); /* stop on SOFTWARE stop */ | |
2130 | RtdPacerStop(dev); /* Stop PACER */ | |
2131 | RtdAdcConversionSource(dev, 0); /* software trigger only */ | |
2132 | RtdInterruptMask(dev, 0); | |
2133 | devpriv->aiCount = 0; /* stop and don't transfer any more */ | |
2134 | #ifdef USE_DMA | |
2135 | if (devpriv->flags & DMA0_ACTIVE) { | |
2136 | RtdPlxInterruptWrite(dev, /* disable any more interrupts */ | |
0a85b6f0 | 2137 | RtdPlxInterruptRead(dev) & ~ICS_DMA0_E); |
3d9f0739 DC |
2138 | abort_dma(dev, 0); |
2139 | devpriv->flags &= ~DMA0_ACTIVE; | |
2140 | } | |
2141 | #endif /* USE_DMA */ | |
2142 | status = RtdInterruptStatus(dev); | |
0a85b6f0 MT |
2143 | DPRINTK |
2144 | ("rtd520: Acquisition canceled. %ld ints, intStat=%x, overStat=%x\n", | |
2145 | devpriv->intCount, status, | |
2146 | 0xffff & RtdInterruptOverrunStatus(dev)); | |
3d9f0739 DC |
2147 | return 0; |
2148 | } | |
2149 | ||
2150 | /* | |
2151 | Given a desired period and the clock period (both in ns), | |
2152 | return the proper counter value (divider-1). | |
2153 | Sets the original period to be the true value. | |
2154 | Note: you have to check if the value is larger than the counter range! | |
2155 | */ | |
2156 | static int rtd_ns_to_timer_base(unsigned int *nanosec, /* desired period (in ns) */ | |
0a85b6f0 | 2157 | int round_mode, int base) |
3d9f0739 DC |
2158 | { /* clock period (in ns) */ |
2159 | int divider; | |
2160 | ||
2161 | switch (round_mode) { | |
2162 | case TRIG_ROUND_NEAREST: | |
2163 | default: | |
2164 | divider = (*nanosec + base / 2) / base; | |
2165 | break; | |
2166 | case TRIG_ROUND_DOWN: | |
2167 | divider = (*nanosec) / base; | |
2168 | break; | |
2169 | case TRIG_ROUND_UP: | |
2170 | divider = (*nanosec + base - 1) / base; | |
2171 | break; | |
2172 | } | |
2173 | if (divider < 2) | |
2174 | divider = 2; /* min is divide by 2 */ | |
2175 | ||
2176 | /* Note: we don't check for max, because different timers | |
2177 | have different ranges */ | |
2178 | ||
2179 | *nanosec = base * divider; | |
2180 | return divider - 1; /* countdown is divisor+1 */ | |
2181 | } | |
2182 | ||
2183 | /* | |
2184 | Given a desired period (in ns), | |
2185 | return the proper counter value (divider-1) for the internal clock. | |
2186 | Sets the original period to be the true value. | |
2187 | */ | |
2188 | static int rtd_ns_to_timer(unsigned int *ns, int round_mode) | |
2189 | { | |
2190 | return rtd_ns_to_timer_base(ns, round_mode, RTD_CLOCK_BASE); | |
2191 | } | |
2192 | ||
2193 | /* | |
2194 | Output one (or more) analog values to a single port as fast as possible. | |
2195 | */ | |
71b5f4f1 | 2196 | static int rtd_ao_winsn(struct comedi_device *dev, |
0a85b6f0 MT |
2197 | struct comedi_subdevice *s, struct comedi_insn *insn, |
2198 | unsigned int *data) | |
3d9f0739 DC |
2199 | { |
2200 | int i; | |
2201 | int chan = CR_CHAN(insn->chanspec); | |
2202 | int range = CR_RANGE(insn->chanspec); | |
2203 | ||
2204 | /* Configure the output range (table index matches the range values) */ | |
2205 | RtdDacRange(dev, chan, range); | |
2206 | ||
2207 | /* Writing a list of values to an AO channel is probably not | |
2208 | * very useful, but that's how the interface is defined. */ | |
2209 | for (i = 0; i < insn->n; ++i) { | |
2210 | int val = data[i] << 3; | |
2211 | int stat = 0; /* initialize to avoid bogus warning */ | |
2212 | int ii; | |
2213 | ||
2214 | /* VERIFY: comedi range and offset conversions */ | |
2215 | ||
2216 | if ((range > 1) /* bipolar */ | |
bc8bf90a | 2217 | && (data[i] < 2048)) { |
3d9f0739 DC |
2218 | /* offset and sign extend */ |
2219 | val = (((int)data[i]) - 2048) << 3; | |
2220 | } else { /* unipolor */ | |
2221 | val = data[i] << 3; | |
2222 | } | |
2223 | ||
0a85b6f0 MT |
2224 | DPRINTK |
2225 | ("comedi: rtd520 DAC chan=%d range=%d writing %d as 0x%x\n", | |
2226 | chan, range, data[i], val); | |
3d9f0739 DC |
2227 | |
2228 | /* a typical programming sequence */ | |
2229 | RtdDacFifoPut(dev, chan, val); /* put the value in */ | |
2230 | RtdDacUpdate(dev, chan); /* trigger the conversion */ | |
2231 | ||
2232 | devpriv->aoValue[chan] = data[i]; /* save for read back */ | |
2233 | ||
2234 | for (ii = 0; ii < RTD_DAC_TIMEOUT; ++ii) { | |
2235 | stat = RtdFifoStatus(dev); | |
2236 | /* 1 -> not empty */ | |
2237 | if (stat & ((0 == chan) ? FS_DAC1_NOT_EMPTY : | |
0a85b6f0 | 2238 | FS_DAC2_NOT_EMPTY)) |
3d9f0739 DC |
2239 | break; |
2240 | WAIT_QUIETLY; | |
2241 | } | |
2242 | if (ii >= RTD_DAC_TIMEOUT) { | |
0a85b6f0 MT |
2243 | DPRINTK |
2244 | ("rtd520: Error: DAC never finished! FifoStatus=0x%x\n", | |
2245 | stat ^ 0x6666); | |
3d9f0739 DC |
2246 | return -ETIMEDOUT; |
2247 | } | |
2248 | } | |
2249 | ||
2250 | /* return the number of samples read/written */ | |
2251 | return i; | |
2252 | } | |
2253 | ||
2254 | /* AO subdevices should have a read insn as well as a write insn. | |
2255 | * Usually this means copying a value stored in devpriv. */ | |
71b5f4f1 | 2256 | static int rtd_ao_rinsn(struct comedi_device *dev, |
0a85b6f0 MT |
2257 | struct comedi_subdevice *s, struct comedi_insn *insn, |
2258 | unsigned int *data) | |
3d9f0739 DC |
2259 | { |
2260 | int i; | |
2261 | int chan = CR_CHAN(insn->chanspec); | |
2262 | ||
bc8bf90a | 2263 | for (i = 0; i < insn->n; i++) |
3d9f0739 | 2264 | data[i] = devpriv->aoValue[chan]; |
bc8bf90a | 2265 | |
3d9f0739 DC |
2266 | |
2267 | return i; | |
2268 | } | |
2269 | ||
2270 | /* | |
2271 | Write a masked set of bits and the read back the port. | |
2272 | We track what the bits should be (i.e. we don't read the port first). | |
2273 | ||
2274 | DIO devices are slightly special. Although it is possible to | |
2275 | * implement the insn_read/insn_write interface, it is much more | |
2276 | * useful to applications if you implement the insn_bits interface. | |
2277 | * This allows packed reading/writing of the DIO channels. The | |
2278 | * comedi core can convert between insn_bits and insn_read/write | |
2279 | */ | |
71b5f4f1 | 2280 | static int rtd_dio_insn_bits(struct comedi_device *dev, |
0a85b6f0 MT |
2281 | struct comedi_subdevice *s, |
2282 | struct comedi_insn *insn, unsigned int *data) | |
3d9f0739 DC |
2283 | { |
2284 | if (insn->n != 2) | |
2285 | return -EINVAL; | |
2286 | ||
2287 | /* The insn data is a mask in data[0] and the new data | |
2288 | * in data[1], each channel cooresponding to a bit. */ | |
2289 | if (data[0]) { | |
2290 | s->state &= ~data[0]; | |
2291 | s->state |= data[0] & data[1]; | |
2292 | ||
2293 | /* Write out the new digital output lines */ | |
2294 | RtdDio0Write(dev, s->state); | |
2295 | } | |
2296 | /* on return, data[1] contains the value of the digital | |
2297 | * input lines. */ | |
2298 | data[1] = RtdDio0Read(dev); | |
2299 | ||
2300 | /*DPRINTK("rtd520:port_0 wrote: 0x%x read: 0x%x\n", s->state, data[1]); */ | |
2301 | ||
2302 | return 2; | |
2303 | } | |
2304 | ||
2305 | /* | |
2306 | Configure one bit on a IO port as Input or Output (hence the name :-). | |
2307 | */ | |
71b5f4f1 | 2308 | static int rtd_dio_insn_config(struct comedi_device *dev, |
0a85b6f0 MT |
2309 | struct comedi_subdevice *s, |
2310 | struct comedi_insn *insn, unsigned int *data) | |
3d9f0739 DC |
2311 | { |
2312 | int chan = CR_CHAN(insn->chanspec); | |
2313 | ||
2314 | /* The input or output configuration of each digital line is | |
2315 | * configured by a special insn_config instruction. chanspec | |
2316 | * contains the channel to be changed, and data[0] contains the | |
2317 | * value COMEDI_INPUT or COMEDI_OUTPUT. */ | |
2318 | switch (data[0]) { | |
2319 | case INSN_CONFIG_DIO_OUTPUT: | |
2320 | s->io_bits |= 1 << chan; /* 1 means Out */ | |
2321 | break; | |
2322 | case INSN_CONFIG_DIO_INPUT: | |
2323 | s->io_bits &= ~(1 << chan); | |
2324 | break; | |
2325 | case INSN_CONFIG_DIO_QUERY: | |
2326 | data[1] = | |
0a85b6f0 | 2327 | (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT; |
3d9f0739 DC |
2328 | return insn->n; |
2329 | break; | |
2330 | default: | |
2331 | return -EINVAL; | |
2332 | } | |
2333 | ||
2334 | DPRINTK("rtd520: port_0_direction=0x%x (1 means out)\n", s->io_bits); | |
2335 | /* TODO support digital match interrupts and strobes */ | |
2336 | RtdDioStatusWrite(dev, 0x01); /* make Dio0Ctrl point to direction */ | |
2337 | RtdDio0CtrlWrite(dev, s->io_bits); /* set direction 1 means Out */ | |
2338 | RtdDioStatusWrite(dev, 0); /* make Dio0Ctrl clear interrupts */ | |
2339 | ||
2340 | /* port1 can only be all input or all output */ | |
2341 | ||
2342 | /* there are also 2 user input lines and 2 user output lines */ | |
2343 | ||
2344 | return 1; | |
2345 | } | |
2346 | ||
2347 | /* | |
2348 | * A convenient macro that defines init_module() and cleanup_module(), | |
2349 | * as necessary. | |
2350 | */ | |
727b286b AT |
2351 | static int __devinit rtd520Driver_pci_probe(struct pci_dev *dev, |
2352 | const struct pci_device_id *ent) | |
2353 | { | |
2354 | return comedi_pci_auto_config(dev, rtd520Driver.driver_name); | |
2355 | } | |
2356 | ||
2357 | static void __devexit rtd520Driver_pci_remove(struct pci_dev *dev) | |
2358 | { | |
2359 | comedi_pci_auto_unconfig(dev); | |
2360 | } | |
2361 | ||
2362 | static struct pci_driver rtd520Driver_pci_driver = { | |
2363 | .id_table = rtd520_pci_table, | |
2364 | .probe = &rtd520Driver_pci_probe, | |
2365 | .remove = __devexit_p(&rtd520Driver_pci_remove) | |
2366 | }; | |
2367 | ||
2368 | static int __init rtd520Driver_init_module(void) | |
2369 | { | |
2370 | int retval; | |
2371 | ||
2372 | retval = comedi_driver_register(&rtd520Driver); | |
2373 | if (retval < 0) | |
2374 | return retval; | |
2375 | ||
2376 | rtd520Driver_pci_driver.name = (char *)rtd520Driver.driver_name; | |
2377 | return pci_register_driver(&rtd520Driver_pci_driver); | |
2378 | } | |
2379 | ||
2380 | static void __exit rtd520Driver_cleanup_module(void) | |
2381 | { | |
2382 | pci_unregister_driver(&rtd520Driver_pci_driver); | |
2383 | comedi_driver_unregister(&rtd520Driver); | |
2384 | } | |
2385 | ||
2386 | module_init(rtd520Driver_init_module); | |
2387 | module_exit(rtd520Driver_cleanup_module); | |
90f703d3 AT |
2388 | |
2389 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
2390 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
2391 | MODULE_LICENSE("GPL"); |