Commit | Line | Data |
---|---|---|
3d9f0739 | 1 | /* |
9880d313 HS |
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. | |
9880d313 | 17 | */ |
3d9f0739 | 18 | |
3d9f0739 | 19 | /* |
9880d313 HS |
20 | * Driver: rtd520 |
21 | * Description: Real Time Devices PCI4520/DM7520 | |
9c3ac117 IA |
22 | * Devices: [Real Time Devices] DM7520HR-1 (DM7520), DM7520HR-8, |
23 | * PCI4520 (PCI4520), PCI4520-8 | |
9880d313 HS |
24 | * Author: Dan Christian |
25 | * Status: Works. Only tested on DM7520-8. Not SMP safe. | |
26 | * | |
27 | * Configuration options: not applicable, uses PCI auto config | |
28 | */ | |
3d9f0739 | 29 | |
3d9f0739 | 30 | /* |
9880d313 HS |
31 | * Created by Dan Christian, NASA Ames Research Center. |
32 | * | |
33 | * The PCI4520 is a PCI card. The DM7520 is a PC/104-plus card. | |
34 | * Both have: | |
35 | * 8/16 12 bit ADC with FIFO and channel gain table | |
36 | * 8 bits high speed digital out (for external MUX) (or 8 in or 8 out) | |
37 | * 8 bits high speed digital in with FIFO and interrupt on change (or 8 IO) | |
38 | * 2 12 bit DACs with FIFOs | |
39 | * 2 bits output | |
40 | * 2 bits input | |
41 | * bus mastering DMA | |
42 | * timers: ADC sample, pacer, burst, about, delay, DA1, DA2 | |
43 | * sample counter | |
44 | * 3 user timer/counters (8254) | |
45 | * external interrupt | |
46 | * | |
47 | * The DM7520 has slightly fewer features (fewer gain steps). | |
48 | * | |
49 | * These boards can support external multiplexors and multi-board | |
50 | * synchronization, but this driver doesn't support that. | |
51 | * | |
52 | * Board docs: http://www.rtdusa.com/PC104/DM/analog%20IO/dm7520.htm | |
53 | * Data sheet: http://www.rtdusa.com/pdf/dm7520.pdf | |
54 | * Example source: http://www.rtdusa.com/examples/dm/dm7520.zip | |
55 | * Call them and ask for the register level manual. | |
56 | * PCI chip: http://www.plxtech.com/products/io/pci9080 | |
57 | * | |
58 | * Notes: | |
59 | * This board is memory mapped. There is some IO stuff, but it isn't needed. | |
60 | * | |
61 | * I use a pretty loose naming style within the driver (rtd_blah). | |
62 | * All externally visible names should be rtd520_blah. | |
63 | * I use camelCase for structures (and inside them). | |
64 | * I may also use upper CamelCase for function names (old habit). | |
65 | * | |
66 | * This board is somewhat related to the RTD PCI4400 board. | |
67 | * | |
68 | * I borrowed heavily from the ni_mio_common, ni_atmio16d, mite, and | |
69 | * das1800, since they have the best documented code. Driver cb_pcidas64.c | |
70 | * uses the same DMA controller. | |
71 | * | |
72 | * As far as I can tell, the About interrupt doesn't work if Sample is | |
73 | * also enabled. It turns out that About really isn't needed, since | |
74 | * we always count down samples read. | |
9880d313 | 75 | */ |
3d9f0739 DC |
76 | |
77 | /* | |
9880d313 HS |
78 | * driver status: |
79 | * | |
80 | * Analog-In supports instruction and command mode. | |
81 | * | |
82 | * With DMA, you can sample at 1.15Mhz with 70% idle on a 400Mhz K6-2 | |
83 | * (single channel, 64K read buffer). I get random system lockups when | |
84 | * using DMA with ALI-15xx based systems. I haven't been able to test | |
85 | * any other chipsets. The lockups happen soon after the start of an | |
86 | * acquistion, not in the middle of a long run. | |
87 | * | |
88 | * Without DMA, you can do 620Khz sampling with 20% idle on a 400Mhz K6-2 | |
89 | * (with a 256K read buffer). | |
90 | * | |
91 | * Digital-IO and Analog-Out only support instruction mode. | |
92 | */ | |
3d9f0739 | 93 | |
ce157f80 | 94 | #include <linux/module.h> |
3d9f0739 | 95 | #include <linux/delay.h> |
33782dd5 | 96 | #include <linux/interrupt.h> |
3d9f0739 | 97 | |
9fca154c | 98 | #include "../comedi_pci.h" |
3d9f0739 | 99 | |
bc86e82d | 100 | #include "comedi_8254.h" |
fe559c02 | 101 | #include "plx9080.h" |
27020ffe | 102 | |
f88bb22f HS |
103 | /* |
104 | * Local Address Space 0 Offsets | |
105 | */ | |
106 | #define LAS0_USER_IO 0x0008 /* User I/O */ | |
107 | #define LAS0_ADC 0x0010 /* FIFO Status/Software A/D Start */ | |
267d2e07 HS |
108 | #define FS_DAC1_NOT_EMPTY BIT(0) /* DAC1 FIFO not empty */ |
109 | #define FS_DAC1_HEMPTY BIT(1) /* DAC1 FIFO half empty */ | |
110 | #define FS_DAC1_NOT_FULL BIT(2) /* DAC1 FIFO not full */ | |
111 | #define FS_DAC2_NOT_EMPTY BIT(4) /* DAC2 FIFO not empty */ | |
112 | #define FS_DAC2_HEMPTY BIT(5) /* DAC2 FIFO half empty */ | |
113 | #define FS_DAC2_NOT_FULL BIT(6) /* DAC2 FIFO not full */ | |
114 | #define FS_ADC_NOT_EMPTY BIT(8) /* ADC FIFO not empty */ | |
115 | #define FS_ADC_HEMPTY BIT(9) /* ADC FIFO half empty */ | |
116 | #define FS_ADC_NOT_FULL BIT(10) /* ADC FIFO not full */ | |
117 | #define FS_DIN_NOT_EMPTY BIT(12) /* DIN FIFO not empty */ | |
118 | #define FS_DIN_HEMPTY BIT(13) /* DIN FIFO half empty */ | |
119 | #define FS_DIN_NOT_FULL BIT(14) /* DIN FIFO not full */ | |
78c1652c | 120 | #define LAS0_UPDATE_DAC(x) (0x0014 + ((x) * 0x4)) /* D/Ax Update (w) */ |
f88bb22f HS |
121 | #define LAS0_DAC 0x0024 /* Software Simultaneous Update (w) */ |
122 | #define LAS0_PACER 0x0028 /* Software Pacer Start/Stop */ | |
123 | #define LAS0_TIMER 0x002c /* Timer Status/HDIN Software Trig. */ | |
124 | #define LAS0_IT 0x0030 /* Interrupt Status/Enable */ | |
267d2e07 HS |
125 | #define IRQM_ADC_FIFO_WRITE BIT(0) /* ADC FIFO Write */ |
126 | #define IRQM_CGT_RESET BIT(1) /* Reset CGT */ | |
127 | #define IRQM_CGT_PAUSE BIT(3) /* Pause CGT */ | |
128 | #define IRQM_ADC_ABOUT_CNT BIT(4) /* About Counter out */ | |
129 | #define IRQM_ADC_DELAY_CNT BIT(5) /* Delay Counter out */ | |
130 | #define IRQM_ADC_SAMPLE_CNT BIT(6) /* ADC Sample Counter */ | |
131 | #define IRQM_DAC1_UCNT BIT(7) /* DAC1 Update Counter */ | |
132 | #define IRQM_DAC2_UCNT BIT(8) /* DAC2 Update Counter */ | |
133 | #define IRQM_UTC1 BIT(9) /* User TC1 out */ | |
134 | #define IRQM_UTC1_INV BIT(10) /* User TC1 out, inverted */ | |
135 | #define IRQM_UTC2 BIT(11) /* User TC2 out */ | |
136 | #define IRQM_DIGITAL_IT BIT(12) /* Digital Interrupt */ | |
137 | #define IRQM_EXTERNAL_IT BIT(13) /* External Interrupt */ | |
138 | #define IRQM_ETRIG_RISING BIT(14) /* Ext Trigger rising-edge */ | |
139 | #define IRQM_ETRIG_FALLING BIT(15) /* Ext Trigger falling-edge */ | |
f88bb22f HS |
140 | #define LAS0_CLEAR 0x0034 /* Clear/Set Interrupt Clear Mask */ |
141 | #define LAS0_OVERRUN 0x0038 /* Pending interrupts/Clear Overrun */ | |
142 | #define LAS0_PCLK 0x0040 /* Pacer Clock (24bit) */ | |
143 | #define LAS0_BCLK 0x0044 /* Burst Clock (10bit) */ | |
144 | #define LAS0_ADC_SCNT 0x0048 /* A/D Sample counter (10bit) */ | |
145 | #define LAS0_DAC1_UCNT 0x004c /* D/A1 Update counter (10 bit) */ | |
146 | #define LAS0_DAC2_UCNT 0x0050 /* D/A2 Update counter (10 bit) */ | |
147 | #define LAS0_DCNT 0x0054 /* Delay counter (16 bit) */ | |
148 | #define LAS0_ACNT 0x0058 /* About counter (16 bit) */ | |
149 | #define LAS0_DAC_CLK 0x005c /* DAC clock (16bit) */ | |
bc86e82d | 150 | #define LAS0_8254_TIMER_BASE 0x0060 /* 8254 timer/counter base */ |
f88bb22f HS |
151 | #define LAS0_DIO0 0x0070 /* Digital I/O Port 0 */ |
152 | #define LAS0_DIO1 0x0074 /* Digital I/O Port 1 */ | |
153 | #define LAS0_DIO0_CTRL 0x0078 /* Digital I/O Control */ | |
154 | #define LAS0_DIO_STATUS 0x007c /* Digital I/O Status */ | |
155 | #define LAS0_BOARD_RESET 0x0100 /* Board reset */ | |
156 | #define LAS0_DMA0_SRC 0x0104 /* DMA 0 Sources select */ | |
157 | #define LAS0_DMA1_SRC 0x0108 /* DMA 1 Sources select */ | |
158 | #define LAS0_ADC_CONVERSION 0x010c /* A/D Conversion Signal select */ | |
159 | #define LAS0_BURST_START 0x0110 /* Burst Clock Start Trigger select */ | |
160 | #define LAS0_PACER_START 0x0114 /* Pacer Clock Start Trigger select */ | |
161 | #define LAS0_PACER_STOP 0x0118 /* Pacer Clock Stop Trigger select */ | |
162 | #define LAS0_ACNT_STOP_ENABLE 0x011c /* About Counter Stop Enable */ | |
163 | #define LAS0_PACER_REPEAT 0x0120 /* Pacer Start Trigger Mode select */ | |
164 | #define LAS0_DIN_START 0x0124 /* HiSpd DI Sampling Signal select */ | |
165 | #define LAS0_DIN_FIFO_CLEAR 0x0128 /* Digital Input FIFO Clear */ | |
166 | #define LAS0_ADC_FIFO_CLEAR 0x012c /* A/D FIFO Clear */ | |
167 | #define LAS0_CGT_WRITE 0x0130 /* Channel Gain Table Write */ | |
168 | #define LAS0_CGL_WRITE 0x0134 /* Channel Gain Latch Write */ | |
169 | #define LAS0_CG_DATA 0x0138 /* Digital Table Write */ | |
170 | #define LAS0_CGT_ENABLE 0x013c /* Channel Gain Table Enable */ | |
171 | #define LAS0_CG_ENABLE 0x0140 /* Digital Table Enable */ | |
172 | #define LAS0_CGT_PAUSE 0x0144 /* Table Pause Enable */ | |
173 | #define LAS0_CGT_RESET 0x0148 /* Reset Channel Gain Table */ | |
174 | #define LAS0_CGT_CLEAR 0x014c /* Clear Channel Gain Table */ | |
78c1652c HS |
175 | #define LAS0_DAC_CTRL(x) (0x0150 + ((x) * 0x14)) /* D/Ax type/range */ |
176 | #define LAS0_DAC_SRC(x) (0x0154 + ((x) * 0x14)) /* D/Ax update source */ | |
177 | #define LAS0_DAC_CYCLE(x) (0x0158 + ((x) * 0x14)) /* D/Ax cycle mode */ | |
178 | #define LAS0_DAC_RESET(x) (0x015c + ((x) * 0x14)) /* D/Ax FIFO reset */ | |
179 | #define LAS0_DAC_FIFO_CLEAR(x) (0x0160 + ((x) * 0x14)) /* D/Ax FIFO clear */ | |
f88bb22f HS |
180 | #define LAS0_ADC_SCNT_SRC 0x0178 /* A/D Sample Counter Source select */ |
181 | #define LAS0_PACER_SELECT 0x0180 /* Pacer Clock select */ | |
182 | #define LAS0_SBUS0_SRC 0x0184 /* SyncBus 0 Source select */ | |
183 | #define LAS0_SBUS0_ENABLE 0x0188 /* SyncBus 0 enable */ | |
184 | #define LAS0_SBUS1_SRC 0x018c /* SyncBus 1 Source select */ | |
185 | #define LAS0_SBUS1_ENABLE 0x0190 /* SyncBus 1 enable */ | |
186 | #define LAS0_SBUS2_SRC 0x0198 /* SyncBus 2 Source select */ | |
187 | #define LAS0_SBUS2_ENABLE 0x019c /* SyncBus 2 enable */ | |
188 | #define LAS0_ETRG_POLARITY 0x01a4 /* Ext. Trigger polarity select */ | |
189 | #define LAS0_EINT_POLARITY 0x01a8 /* Ext. Interrupt polarity select */ | |
bc86e82d HS |
190 | #define LAS0_8254_CLK_SEL(x) (0x01ac + ((x) * 0x8)) /* 8254 clock select */ |
191 | #define LAS0_8254_GATE_SEL(x) (0x01b0 + ((x) * 0x8)) /* 8254 gate select */ | |
f88bb22f HS |
192 | #define LAS0_UOUT0_SELECT 0x01c4 /* User Output 0 source select */ |
193 | #define LAS0_UOUT1_SELECT 0x01c8 /* User Output 1 source select */ | |
194 | #define LAS0_DMA0_RESET 0x01cc /* DMA0 Request state machine reset */ | |
195 | #define LAS0_DMA1_RESET 0x01d0 /* DMA1 Request state machine reset */ | |
196 | ||
197 | /* | |
198 | * Local Address Space 1 Offsets | |
199 | */ | |
200 | #define LAS1_ADC_FIFO 0x0000 /* A/D FIFO (16bit) */ | |
201 | #define LAS1_HDIO_FIFO 0x0004 /* HiSpd DI FIFO (16bit) */ | |
78c1652c | 202 | #define LAS1_DAC_FIFO(x) (0x0008 + ((x) * 0x4)) /* D/Ax FIFO (16bit) */ |
f88bb22f | 203 | |
640da603 HS |
204 | /* |
205 | * Driver specific stuff (tunable) | |
206 | */ | |
3d9f0739 | 207 | |
640da603 HS |
208 | /* |
209 | * We really only need 2 buffers. More than that means being much | |
210 | * smarter about knowing which ones are full. | |
211 | */ | |
3d9f0739 DC |
212 | #define DMA_CHAIN_COUNT 2 /* max DMA segments/buffers in a ring (min 2) */ |
213 | ||
214 | /* Target period for periodic transfers. This sets the user read latency. */ | |
215 | /* Note: There are certain rates where we give this up and transfer 1/2 FIFO */ | |
216 | /* If this is too low, efficiency is poor */ | |
217 | #define TRANS_TARGET_PERIOD 10000000 /* 10 ms (in nanoseconds) */ | |
218 | ||
219 | /* Set a practical limit on how long a list to support (affects memory use) */ | |
220 | /* The board support a channel list up to the FIFO length (1K or 8K) */ | |
221 | #define RTD_MAX_CHANLIST 128 /* max channel list that we allow */ | |
222 | ||
640da603 HS |
223 | /* |
224 | * Board specific stuff | |
225 | */ | |
3d9f0739 | 226 | |
3d9f0739 DC |
227 | #define RTD_CLOCK_RATE 8000000 /* 8Mhz onboard clock */ |
228 | #define RTD_CLOCK_BASE 125 /* clock period in ns */ | |
229 | ||
230 | /* Note: these speed are slower than the spec, but fit the counter resolution*/ | |
231 | #define RTD_MAX_SPEED 1625 /* when sampling, in nanoseconds */ | |
232 | /* max speed if we don't have to wait for settling */ | |
233 | #define RTD_MAX_SPEED_1 875 /* if single channel, in nanoseconds */ | |
234 | ||
235 | #define RTD_MIN_SPEED 2097151875 /* (24bit counter) in nanoseconds */ | |
236 | /* min speed when only 1 channel (no burst counter) */ | |
237 | #define RTD_MIN_SPEED_1 5000000 /* 200Hz, in nanoseconds */ | |
238 | ||
3d9f0739 DC |
239 | /* Setup continuous ring of 1/2 FIFO transfers. See RTD manual p91 */ |
240 | #define DMA_MODE_BITS (\ | |
241 | PLX_LOCAL_BUS_16_WIDE_BITS \ | |
242 | | PLX_DMA_EN_READYIN_BIT \ | |
243 | | PLX_DMA_LOCAL_BURST_EN_BIT \ | |
244 | | PLX_EN_CHAIN_BIT \ | |
245 | | PLX_DMA_INTR_PCI_BIT \ | |
246 | | PLX_LOCAL_ADDR_CONST_BIT \ | |
247 | | PLX_DEMAND_MODE_BIT) | |
248 | ||
249 | #define DMA_TRANSFER_BITS (\ | |
6e882d47 | 250 | /* descriptors in PCI memory*/ PLX_DESC_IN_PCI_BIT \ |
3d9f0739 DC |
251 | /* interrupt at end of block */ | PLX_INTR_TERM_COUNT \ |
252 | /* from board to PCI */ | PLX_XFER_LOCAL_TO_PCI) | |
253 | ||
640da603 HS |
254 | /* |
255 | * Comedi specific stuff | |
256 | */ | |
3d9f0739 DC |
257 | |
258 | /* | |
feb153f3 HS |
259 | * The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128) |
260 | */ | |
261 | static const struct comedi_lrange rtd_ai_7520_range = { | |
262 | 18, { | |
263 | /* +-5V input range gain steps */ | |
264 | BIP_RANGE(5.0), | |
265 | BIP_RANGE(5.0 / 2), | |
266 | BIP_RANGE(5.0 / 4), | |
267 | BIP_RANGE(5.0 / 8), | |
268 | BIP_RANGE(5.0 / 16), | |
269 | BIP_RANGE(5.0 / 32), | |
270 | /* +-10V input range gain steps */ | |
271 | BIP_RANGE(10.0), | |
272 | BIP_RANGE(10.0 / 2), | |
273 | BIP_RANGE(10.0 / 4), | |
274 | BIP_RANGE(10.0 / 8), | |
275 | BIP_RANGE(10.0 / 16), | |
276 | BIP_RANGE(10.0 / 32), | |
277 | /* +10V input range gain steps */ | |
278 | UNI_RANGE(10.0), | |
279 | UNI_RANGE(10.0 / 2), | |
280 | UNI_RANGE(10.0 / 4), | |
281 | UNI_RANGE(10.0 / 8), | |
282 | UNI_RANGE(10.0 / 16), | |
283 | UNI_RANGE(10.0 / 32), | |
284 | } | |
3d9f0739 DC |
285 | }; |
286 | ||
287 | /* PCI4520 has two more gains (6 more entries) */ | |
feb153f3 HS |
288 | static const struct comedi_lrange rtd_ai_4520_range = { |
289 | 24, { | |
290 | /* +-5V input range gain steps */ | |
291 | BIP_RANGE(5.0), | |
292 | BIP_RANGE(5.0 / 2), | |
293 | BIP_RANGE(5.0 / 4), | |
294 | BIP_RANGE(5.0 / 8), | |
295 | BIP_RANGE(5.0 / 16), | |
296 | BIP_RANGE(5.0 / 32), | |
297 | BIP_RANGE(5.0 / 64), | |
298 | BIP_RANGE(5.0 / 128), | |
299 | /* +-10V input range gain steps */ | |
300 | BIP_RANGE(10.0), | |
301 | BIP_RANGE(10.0 / 2), | |
302 | BIP_RANGE(10.0 / 4), | |
303 | BIP_RANGE(10.0 / 8), | |
304 | BIP_RANGE(10.0 / 16), | |
305 | BIP_RANGE(10.0 / 32), | |
306 | BIP_RANGE(10.0 / 64), | |
307 | BIP_RANGE(10.0 / 128), | |
308 | /* +10V input range gain steps */ | |
309 | UNI_RANGE(10.0), | |
310 | UNI_RANGE(10.0 / 2), | |
311 | UNI_RANGE(10.0 / 4), | |
312 | UNI_RANGE(10.0 / 8), | |
313 | UNI_RANGE(10.0 / 16), | |
314 | UNI_RANGE(10.0 / 32), | |
315 | UNI_RANGE(10.0 / 64), | |
316 | UNI_RANGE(10.0 / 128), | |
317 | } | |
3d9f0739 DC |
318 | }; |
319 | ||
320 | /* Table order matches range values */ | |
feb153f3 HS |
321 | static const struct comedi_lrange rtd_ao_range = { |
322 | 4, { | |
323 | UNI_RANGE(5), | |
324 | UNI_RANGE(10), | |
325 | BIP_RANGE(5), | |
326 | BIP_RANGE(10), | |
327 | } | |
3d9f0739 DC |
328 | }; |
329 | ||
b3322d42 HS |
330 | enum rtd_boardid { |
331 | BOARD_DM7520, | |
332 | BOARD_PCI4520, | |
333 | }; | |
334 | ||
dcc2358f | 335 | struct rtd_boardinfo { |
3cdefc92 | 336 | const char *name; |
dcc2358f HS |
337 | int range_bip10; /* start of +-10V range */ |
338 | int range_uni10; /* start of +10V range */ | |
8a799460 | 339 | const struct comedi_lrange *ai_range; |
d80235ce | 340 | }; |
3d9f0739 | 341 | |
fd62ef00 | 342 | static const struct rtd_boardinfo rtd520_boards[] = { |
b3322d42 | 343 | [BOARD_DM7520] = { |
3cdefc92 | 344 | .name = "DM7520", |
dcc2358f HS |
345 | .range_bip10 = 6, |
346 | .range_uni10 = 12, | |
8a799460 | 347 | .ai_range = &rtd_ai_7520_range, |
b3322d42 HS |
348 | }, |
349 | [BOARD_PCI4520] = { | |
3cdefc92 | 350 | .name = "PCI4520", |
dcc2358f HS |
351 | .range_bip10 = 8, |
352 | .range_uni10 = 16, | |
8a799460 | 353 | .ai_range = &rtd_ai_4520_range, |
3cdefc92 | 354 | }, |
3d9f0739 DC |
355 | }; |
356 | ||
4686a0e6 | 357 | struct rtd_private { |
3d9f0739 | 358 | /* memory mapped board structures */ |
54fe68a8 HS |
359 | void __iomem *las1; |
360 | void __iomem *lcfg; | |
3d9f0739 | 361 | |
8bc1bd1f | 362 | long ai_count; /* total transfer size (samples) */ |
2d7b8b94 | 363 | int xfer_count; /* # to transfer data. 0->1/2FIFO */ |
3d9f0739 | 364 | int flags; /* flag event modes */ |
f7ede00d | 365 | unsigned int fifosz; |
bc86e82d HS |
366 | |
367 | /* 8254 Timer/Counter gate and clock sources */ | |
368 | unsigned char timer_gate_src[3]; | |
369 | unsigned char timer_clk_src[3]; | |
d80235ce | 370 | }; |
3d9f0739 DC |
371 | |
372 | /* bit defines for "flags" */ | |
373 | #define SEND_EOS 0x01 /* send End Of Scan events */ | |
374 | #define DMA0_ACTIVE 0x02 /* DMA0 is active */ | |
375 | #define DMA1_ACTIVE 0x04 /* DMA1 is active */ | |
376 | ||
5bb196ad | 377 | /* |
640da603 HS |
378 | * Given a desired period and the clock period (both in ns), return the |
379 | * proper counter value (divider-1). Sets the original period to be the | |
380 | * true value. | |
381 | * Note: you have to check if the value is larger than the counter range! | |
382 | */ | |
3ff20ef3 | 383 | static int rtd_ns_to_timer_base(unsigned int *nanosec, |
a207c12f | 384 | unsigned int flags, int base) |
3ff20ef3 | 385 | { |
c98d90fd HS |
386 | int divider; |
387 | ||
2ed2182e IA |
388 | switch (flags & CMDF_ROUND_MASK) { |
389 | case CMDF_ROUND_NEAREST: | |
c98d90fd | 390 | default: |
da687773 | 391 | divider = DIV_ROUND_CLOSEST(*nanosec, base); |
c98d90fd | 392 | break; |
2ed2182e | 393 | case CMDF_ROUND_DOWN: |
c98d90fd HS |
394 | divider = (*nanosec) / base; |
395 | break; | |
2ed2182e | 396 | case CMDF_ROUND_UP: |
da687773 | 397 | divider = DIV_ROUND_UP(*nanosec, base); |
c98d90fd HS |
398 | break; |
399 | } | |
400 | if (divider < 2) | |
401 | divider = 2; /* min is divide by 2 */ | |
402 | ||
640da603 HS |
403 | /* |
404 | * Note: we don't check for max, because different timers | |
405 | * have different ranges | |
406 | */ | |
c98d90fd HS |
407 | |
408 | *nanosec = base * divider; | |
409 | return divider - 1; /* countdown is divisor+1 */ | |
410 | } | |
3d9f0739 DC |
411 | |
412 | /* | |
640da603 HS |
413 | * Given a desired period (in ns), return the proper counter value |
414 | * (divider-1) for the internal clock. Sets the original period to | |
415 | * be the true value. | |
416 | */ | |
a207c12f | 417 | static int rtd_ns_to_timer(unsigned int *ns, unsigned int flags) |
c98d90fd | 418 | { |
a207c12f | 419 | return rtd_ns_to_timer_base(ns, flags, RTD_CLOCK_BASE); |
c98d90fd | 420 | } |
3d9f0739 | 421 | |
640da603 | 422 | /* Convert a single comedi channel-gain entry to a RTD520 table entry */ |
b0ae4368 HS |
423 | static unsigned short rtd_convert_chan_gain(struct comedi_device *dev, |
424 | unsigned int chanspec, int index) | |
425 | { | |
3afadfa5 | 426 | const struct rtd_boardinfo *board = dev->board_ptr; |
b0ae4368 HS |
427 | unsigned int chan = CR_CHAN(chanspec); |
428 | unsigned int range = CR_RANGE(chanspec); | |
429 | unsigned int aref = CR_AREF(chanspec); | |
c98d90fd | 430 | unsigned short r = 0; |
3d9f0739 | 431 | |
c98d90fd | 432 | r |= chan & 0xf; |
3d9f0739 | 433 | |
c98d90fd | 434 | /* Note: we also setup the channel list bipolar flag array */ |
e9e7023c | 435 | if (range < board->range_bip10) { |
3ff20ef3 HS |
436 | /* +-5 range */ |
437 | r |= 0x000; | |
438 | r |= (range & 0x7) << 4; | |
e9e7023c | 439 | } else if (range < board->range_uni10) { |
3ff20ef3 HS |
440 | /* +-10 range */ |
441 | r |= 0x100; | |
e9e7023c | 442 | r |= ((range - board->range_bip10) & 0x7) << 4; |
3ff20ef3 HS |
443 | } else { |
444 | /* +10 range */ | |
445 | r |= 0x200; | |
e9e7023c | 446 | r |= ((range - board->range_uni10) & 0x7) << 4; |
c98d90fd | 447 | } |
3d9f0739 | 448 | |
c98d90fd HS |
449 | switch (aref) { |
450 | case AREF_GROUND: /* on-board ground */ | |
451 | break; | |
452 | ||
453 | case AREF_COMMON: | |
454 | r |= 0x80; /* ref external analog common */ | |
455 | break; | |
456 | ||
457 | case AREF_DIFF: | |
458 | r |= 0x400; /* differential inputs */ | |
459 | break; | |
460 | ||
461 | case AREF_OTHER: /* ??? */ | |
462 | break; | |
463 | } | |
c98d90fd HS |
464 | return r; |
465 | } | |
466 | ||
640da603 | 467 | /* Setup the channel-gain table from a comedi list */ |
c98d90fd HS |
468 | static void rtd_load_channelgain_list(struct comedi_device *dev, |
469 | unsigned int n_chan, unsigned int *list) | |
470 | { | |
471 | if (n_chan > 1) { /* setup channel gain table */ | |
472 | int ii; | |
67e50c6c | 473 | |
c5930d66 HS |
474 | writel(0, dev->mmio + LAS0_CGT_CLEAR); |
475 | writel(1, dev->mmio + LAS0_CGT_ENABLE); | |
c98d90fd | 476 | for (ii = 0; ii < n_chan; ii++) { |
b0ae4368 | 477 | writel(rtd_convert_chan_gain(dev, list[ii], ii), |
c5930d66 | 478 | dev->mmio + LAS0_CGT_WRITE); |
3d9f0739 | 479 | } |
c98d90fd | 480 | } else { /* just use the channel gain latch */ |
c5930d66 | 481 | writel(0, dev->mmio + LAS0_CGT_ENABLE); |
b0ae4368 | 482 | writel(rtd_convert_chan_gain(dev, list[0], 0), |
c5930d66 | 483 | dev->mmio + LAS0_CGL_WRITE); |
3d9f0739 | 484 | } |
c98d90fd HS |
485 | } |
486 | ||
640da603 HS |
487 | /* |
488 | * Determine fifo size by doing adc conversions until the fifo half | |
489 | * empty status flag clears. | |
490 | */ | |
c98d90fd HS |
491 | static int rtd520_probe_fifo_depth(struct comedi_device *dev) |
492 | { | |
493 | unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND); | |
f7ede00d NE |
494 | unsigned int i; |
495 | static const unsigned int limit = 0x2000; | |
496 | unsigned int fifo_size = 0; | |
c98d90fd | 497 | |
c5930d66 | 498 | writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); |
c98d90fd | 499 | rtd_load_channelgain_list(dev, 1, &chanspec); |
9a1f7223 | 500 | /* ADC conversion trigger source: SOFTWARE */ |
c5930d66 | 501 | writel(0, dev->mmio + LAS0_ADC_CONVERSION); |
c98d90fd HS |
502 | /* convert samples */ |
503 | for (i = 0; i < limit; ++i) { | |
f7ede00d | 504 | unsigned int fifo_status; |
c98d90fd | 505 | /* trigger conversion */ |
c5930d66 | 506 | writew(0, dev->mmio + LAS0_ADC); |
120bdac7 | 507 | usleep_range(1, 1000); |
c5930d66 | 508 | fifo_status = readl(dev->mmio + LAS0_ADC); |
c98d90fd HS |
509 | if ((fifo_status & FS_ADC_HEMPTY) == 0) { |
510 | fifo_size = 2 * i; | |
511 | break; | |
3d9f0739 | 512 | } |
c98d90fd HS |
513 | } |
514 | if (i == limit) { | |
4370e80f | 515 | dev_info(dev->class_dev, "failed to probe fifo size.\n"); |
3d9f0739 DC |
516 | return -EIO; |
517 | } | |
c5930d66 | 518 | writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); |
c98d90fd | 519 | if (fifo_size != 0x400 && fifo_size != 0x2000) { |
4370e80f YT |
520 | dev_info(dev->class_dev, |
521 | "unexpected fifo size of %i, expected 1024 or 8192.\n", | |
522 | fifo_size); | |
c98d90fd | 523 | return -EIO; |
3d9f0739 | 524 | } |
c98d90fd HS |
525 | return fifo_size; |
526 | } | |
3d9f0739 | 527 | |
051448c5 HS |
528 | static int rtd_ai_eoc(struct comedi_device *dev, |
529 | struct comedi_subdevice *s, | |
530 | struct comedi_insn *insn, | |
531 | unsigned long context) | |
532 | { | |
051448c5 HS |
533 | unsigned int status; |
534 | ||
c5930d66 | 535 | status = readl(dev->mmio + LAS0_ADC); |
051448c5 HS |
536 | if (status & FS_ADC_NOT_EMPTY) |
537 | return 0; | |
538 | return -EBUSY; | |
539 | } | |
bc8bf90a | 540 | |
c98d90fd HS |
541 | static int rtd_ai_rinsn(struct comedi_device *dev, |
542 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
543 | unsigned int *data) | |
544 | { | |
4686a0e6 | 545 | struct rtd_private *devpriv = dev->private; |
c234566f | 546 | unsigned int range = CR_RANGE(insn->chanspec); |
051448c5 HS |
547 | int ret; |
548 | int n; | |
3d9f0739 | 549 | |
c98d90fd | 550 | /* clear any old fifo data */ |
c5930d66 | 551 | writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); |
3d9f0739 | 552 | |
c98d90fd HS |
553 | /* write channel to multiplexer and clear channel gain table */ |
554 | rtd_load_channelgain_list(dev, 1, &insn->chanspec); | |
3d9f0739 | 555 | |
9a1f7223 | 556 | /* ADC conversion trigger source: SOFTWARE */ |
c5930d66 | 557 | writel(0, dev->mmio + LAS0_ADC_CONVERSION); |
3d9f0739 | 558 | |
c98d90fd HS |
559 | /* convert n samples */ |
560 | for (n = 0; n < insn->n; n++) { | |
055a1e2c | 561 | unsigned short d; |
c98d90fd | 562 | /* trigger conversion */ |
c5930d66 | 563 | writew(0, dev->mmio + LAS0_ADC); |
c98d90fd | 564 | |
051448c5 HS |
565 | ret = comedi_timeout(dev, s, insn, rtd_ai_eoc, 0); |
566 | if (ret) | |
567 | return ret; | |
c98d90fd HS |
568 | |
569 | /* read data */ | |
d2e1aebe | 570 | d = readw(devpriv->las1 + LAS1_ADC_FIFO); |
f559108b | 571 | d >>= 3; /* low 3 bits are marker lines */ |
c234566f HS |
572 | |
573 | /* convert bipolar data to comedi unsigned data */ | |
574 | if (comedi_range_is_bipolar(s, range)) | |
055a1e2c | 575 | d = comedi_offset_munge(s, d); |
c234566f | 576 | |
055a1e2c | 577 | data[n] = d & s->maxdata; |
3d9f0739 DC |
578 | } |
579 | ||
c98d90fd HS |
580 | /* return the number of samples read/written */ |
581 | return n; | |
582 | } | |
3d9f0739 | 583 | |
c98d90fd HS |
584 | static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s, |
585 | int count) | |
586 | { | |
4686a0e6 | 587 | struct rtd_private *devpriv = dev->private; |
0c8c1c0a HS |
588 | struct comedi_async *async = s->async; |
589 | struct comedi_cmd *cmd = &async->cmd; | |
c98d90fd | 590 | int ii; |
3d9f0739 | 591 | |
c98d90fd | 592 | for (ii = 0; ii < count; ii++) { |
c234566f | 593 | unsigned int range = CR_RANGE(cmd->chanlist[async->cur_chan]); |
055a1e2c | 594 | unsigned short d; |
bc8bf90a | 595 | |
e7211492 | 596 | if (devpriv->ai_count == 0) { /* done */ |
d2e1aebe | 597 | d = readw(devpriv->las1 + LAS1_ADC_FIFO); |
c98d90fd HS |
598 | continue; |
599 | } | |
3d9f0739 | 600 | |
5133f127 | 601 | d = readw(devpriv->las1 + LAS1_ADC_FIFO); |
f559108b | 602 | d >>= 3; /* low 3 bits are marker lines */ |
c234566f HS |
603 | |
604 | /* convert bipolar data to comedi unsigned data */ | |
605 | if (comedi_range_is_bipolar(s, range)) | |
055a1e2c IA |
606 | d = comedi_offset_munge(s, d); |
607 | d &= s->maxdata; | |
3d9f0739 | 608 | |
5023a4bb | 609 | if (!comedi_buf_write_samples(s, &d, 1)) |
c98d90fd | 610 | return -1; |
3d9f0739 | 611 | |
8bc1bd1f HS |
612 | if (devpriv->ai_count > 0) /* < 0, means read forever */ |
613 | devpriv->ai_count--; | |
c98d90fd HS |
614 | } |
615 | return 0; | |
616 | } | |
3d9f0739 | 617 | |
2f021034 HS |
618 | static irqreturn_t rtd_interrupt(int irq, void *d) |
619 | { | |
f782a255 | 620 | struct comedi_device *dev = d; |
9eba3a27 | 621 | struct comedi_subdevice *s = dev->read_subdev; |
4686a0e6 | 622 | struct rtd_private *devpriv = dev->private; |
4c977b86 | 623 | u32 overrun; |
c98d90fd | 624 | u16 status; |
2f021034 | 625 | u16 fifo_status; |
bc8bf90a | 626 | |
c98d90fd HS |
627 | if (!dev->attached) |
628 | return IRQ_NONE; | |
bc8bf90a | 629 | |
c5930d66 | 630 | fifo_status = readl(dev->mmio + LAS0_ADC); |
c98d90fd | 631 | /* check for FIFO full, this automatically halts the ADC! */ |
2f021034 HS |
632 | if (!(fifo_status & FS_ADC_NOT_FULL)) /* 0 -> full */ |
633 | goto xfer_abort; | |
c98d90fd | 634 | |
c5930d66 | 635 | status = readw(dev->mmio + LAS0_IT); |
c98d90fd | 636 | /* if interrupt was not caused by our board, or handled above */ |
e7211492 | 637 | if (status == 0) |
c98d90fd HS |
638 | return IRQ_HANDLED; |
639 | ||
640 | if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */ | |
3ff20ef3 HS |
641 | /* |
642 | * since the priority interrupt controller may have queued | |
643 | * a sample counter interrupt, even though we have already | |
644 | * finished, we must handle the possibility that there is | |
645 | * no data here | |
646 | */ | |
2f021034 | 647 | if (!(fifo_status & FS_ADC_HEMPTY)) { |
3ff20ef3 | 648 | /* FIFO half full */ |
cd5e2d06 | 649 | if (ai_read_n(dev, s, devpriv->fifosz / 2) < 0) |
2f021034 | 650 | goto xfer_abort; |
d62bc468 | 651 | |
e7211492 | 652 | if (devpriv->ai_count == 0) |
2f021034 | 653 | goto xfer_done; |
2d7b8b94 | 654 | } else if (devpriv->xfer_count > 0) { |
2f021034 | 655 | if (fifo_status & FS_ADC_NOT_EMPTY) { |
3ff20ef3 | 656 | /* FIFO not empty */ |
2d7b8b94 | 657 | if (ai_read_n(dev, s, devpriv->xfer_count) < 0) |
2f021034 | 658 | goto xfer_abort; |
d62bc468 | 659 | |
e7211492 | 660 | if (devpriv->ai_count == 0) |
2f021034 | 661 | goto xfer_done; |
c98d90fd | 662 | } |
3d9f0739 DC |
663 | } |
664 | } | |
3d9f0739 | 665 | |
c5930d66 | 666 | overrun = readl(dev->mmio + LAS0_OVERRUN) & 0xffff; |
d62bc468 | 667 | if (overrun) |
2f021034 | 668 | goto xfer_abort; |
3d9f0739 | 669 | |
c98d90fd | 670 | /* clear the interrupt */ |
c5930d66 HS |
671 | writew(status, dev->mmio + LAS0_CLEAR); |
672 | readw(dev->mmio + LAS0_CLEAR); | |
a708e091 HS |
673 | |
674 | comedi_handle_events(dev, s); | |
675 | ||
c98d90fd | 676 | return IRQ_HANDLED; |
3d9f0739 | 677 | |
2f021034 | 678 | xfer_abort: |
c98d90fd | 679 | s->async->events |= COMEDI_CB_ERROR; |
3d9f0739 | 680 | |
2f021034 | 681 | xfer_done: |
a708e091 | 682 | s->async->events |= COMEDI_CB_EOA; |
3d9f0739 | 683 | |
c98d90fd | 684 | /* clear the interrupt */ |
c5930d66 HS |
685 | status = readw(dev->mmio + LAS0_IT); |
686 | writew(status, dev->mmio + LAS0_CLEAR); | |
687 | readw(dev->mmio + LAS0_CLEAR); | |
3d9f0739 | 688 | |
c5930d66 HS |
689 | fifo_status = readl(dev->mmio + LAS0_ADC); |
690 | overrun = readl(dev->mmio + LAS0_OVERRUN) & 0xffff; | |
c98d90fd | 691 | |
a708e091 HS |
692 | comedi_handle_events(dev, s); |
693 | ||
c98d90fd | 694 | return IRQ_HANDLED; |
3d9f0739 DC |
695 | } |
696 | ||
c98d90fd HS |
697 | static int rtd_ai_cmdtest(struct comedi_device *dev, |
698 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
3d9f0739 | 699 | { |
c98d90fd | 700 | int err = 0; |
3d6a4d11 | 701 | unsigned int arg; |
3d9f0739 | 702 | |
27020ffe | 703 | /* Step 1 : check if triggers are trivially valid */ |
c98d90fd | 704 | |
57fab5a2 IA |
705 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); |
706 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, | |
27020ffe | 707 | TRIG_TIMER | TRIG_EXT); |
57fab5a2 IA |
708 | err |= comedi_check_trigger_src(&cmd->convert_src, |
709 | TRIG_TIMER | TRIG_EXT); | |
710 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
711 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
c98d90fd HS |
712 | |
713 | if (err) | |
714 | return 1; | |
715 | ||
27020ffe | 716 | /* Step 2a : make sure trigger sources are unique */ |
3d9f0739 | 717 | |
57fab5a2 IA |
718 | err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); |
719 | err |= comedi_check_trigger_is_unique(cmd->convert_src); | |
720 | err |= comedi_check_trigger_is_unique(cmd->stop_src); | |
27020ffe HS |
721 | |
722 | /* Step 2b : and mutually compatible */ | |
3d9f0739 | 723 | |
c98d90fd HS |
724 | if (err) |
725 | return 2; | |
3d9f0739 | 726 | |
8a2b08ec | 727 | /* Step 3: check if arguments are trivially valid */ |
3d9f0739 | 728 | |
57fab5a2 | 729 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
3d9f0739 | 730 | |
c98d90fd HS |
731 | if (cmd->scan_begin_src == TRIG_TIMER) { |
732 | /* Note: these are time periods, not actual rates */ | |
e7211492 | 733 | if (cmd->chanlist_len == 1) { /* no scanning */ |
57fab5a2 IA |
734 | if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg, |
735 | RTD_MAX_SPEED_1)) { | |
c98d90fd | 736 | rtd_ns_to_timer(&cmd->scan_begin_arg, |
2ed2182e | 737 | CMDF_ROUND_UP); |
8a2b08ec | 738 | err |= -EINVAL; |
c98d90fd | 739 | } |
57fab5a2 IA |
740 | if (comedi_check_trigger_arg_max(&cmd->scan_begin_arg, |
741 | RTD_MIN_SPEED_1)) { | |
c98d90fd | 742 | rtd_ns_to_timer(&cmd->scan_begin_arg, |
2ed2182e | 743 | CMDF_ROUND_DOWN); |
8a2b08ec | 744 | err |= -EINVAL; |
c98d90fd HS |
745 | } |
746 | } else { | |
57fab5a2 IA |
747 | if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg, |
748 | RTD_MAX_SPEED)) { | |
c98d90fd | 749 | rtd_ns_to_timer(&cmd->scan_begin_arg, |
2ed2182e | 750 | CMDF_ROUND_UP); |
8a2b08ec | 751 | err |= -EINVAL; |
c98d90fd | 752 | } |
57fab5a2 IA |
753 | if (comedi_check_trigger_arg_max(&cmd->scan_begin_arg, |
754 | RTD_MIN_SPEED)) { | |
c98d90fd | 755 | rtd_ns_to_timer(&cmd->scan_begin_arg, |
2ed2182e | 756 | CMDF_ROUND_DOWN); |
8a2b08ec | 757 | err |= -EINVAL; |
c98d90fd | 758 | } |
3d9f0739 | 759 | } |
c98d90fd HS |
760 | } else { |
761 | /* external trigger */ | |
762 | /* should be level/edge, hi/lo specification here */ | |
763 | /* should specify multiple external triggers */ | |
57fab5a2 | 764 | err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 9); |
c98d90fd | 765 | } |
8a2b08ec | 766 | |
c98d90fd | 767 | if (cmd->convert_src == TRIG_TIMER) { |
e7211492 | 768 | if (cmd->chanlist_len == 1) { /* no scanning */ |
57fab5a2 IA |
769 | if (comedi_check_trigger_arg_min(&cmd->convert_arg, |
770 | RTD_MAX_SPEED_1)) { | |
c98d90fd | 771 | rtd_ns_to_timer(&cmd->convert_arg, |
2ed2182e | 772 | CMDF_ROUND_UP); |
8a2b08ec | 773 | err |= -EINVAL; |
c98d90fd | 774 | } |
57fab5a2 IA |
775 | if (comedi_check_trigger_arg_max(&cmd->convert_arg, |
776 | RTD_MIN_SPEED_1)) { | |
c98d90fd | 777 | rtd_ns_to_timer(&cmd->convert_arg, |
2ed2182e | 778 | CMDF_ROUND_DOWN); |
8a2b08ec | 779 | err |= -EINVAL; |
c98d90fd HS |
780 | } |
781 | } else { | |
57fab5a2 IA |
782 | if (comedi_check_trigger_arg_min(&cmd->convert_arg, |
783 | RTD_MAX_SPEED)) { | |
c98d90fd | 784 | rtd_ns_to_timer(&cmd->convert_arg, |
2ed2182e | 785 | CMDF_ROUND_UP); |
8a2b08ec | 786 | err |= -EINVAL; |
c98d90fd | 787 | } |
57fab5a2 IA |
788 | if (comedi_check_trigger_arg_max(&cmd->convert_arg, |
789 | RTD_MIN_SPEED)) { | |
c98d90fd | 790 | rtd_ns_to_timer(&cmd->convert_arg, |
2ed2182e | 791 | CMDF_ROUND_DOWN); |
8a2b08ec | 792 | err |= -EINVAL; |
c98d90fd HS |
793 | } |
794 | } | |
795 | } else { | |
796 | /* external trigger */ | |
797 | /* see above */ | |
57fab5a2 | 798 | err |= comedi_check_trigger_arg_max(&cmd->convert_arg, 9); |
c98d90fd HS |
799 | } |
800 | ||
57fab5a2 IA |
801 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, |
802 | cmd->chanlist_len); | |
f50cebb9 | 803 | |
8c602c49 | 804 | if (cmd->stop_src == TRIG_COUNT) |
57fab5a2 | 805 | err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); |
8c602c49 | 806 | else /* TRIG_NONE */ |
57fab5a2 | 807 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
bc8bf90a | 808 | |
c98d90fd HS |
809 | if (err) |
810 | return 3; | |
3d9f0739 | 811 | |
c98d90fd HS |
812 | /* step 4: fix up any arguments */ |
813 | ||
c98d90fd | 814 | if (cmd->scan_begin_src == TRIG_TIMER) { |
3d6a4d11 | 815 | arg = cmd->scan_begin_arg; |
a207c12f | 816 | rtd_ns_to_timer(&arg, cmd->flags); |
57fab5a2 | 817 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); |
c98d90fd | 818 | } |
3d6a4d11 | 819 | |
c98d90fd | 820 | if (cmd->convert_src == TRIG_TIMER) { |
3d6a4d11 | 821 | arg = cmd->convert_arg; |
a207c12f | 822 | rtd_ns_to_timer(&arg, cmd->flags); |
57fab5a2 | 823 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); |
3d6a4d11 HS |
824 | |
825 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
826 | arg = cmd->convert_arg * cmd->scan_end_arg; | |
57fab5a2 IA |
827 | err |= comedi_check_trigger_arg_min(&cmd-> |
828 | scan_begin_arg, | |
829 | arg); | |
3d9f0739 | 830 | } |
c98d90fd | 831 | } |
3d9f0739 | 832 | |
c98d90fd HS |
833 | if (err) |
834 | return 4; | |
3d9f0739 | 835 | |
3d9f0739 DC |
836 | return 0; |
837 | } | |
838 | ||
c98d90fd HS |
839 | static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
840 | { | |
4686a0e6 | 841 | struct rtd_private *devpriv = dev->private; |
c98d90fd HS |
842 | struct comedi_cmd *cmd = &s->async->cmd; |
843 | int timer; | |
3d9f0739 | 844 | |
c98d90fd | 845 | /* stop anything currently running */ |
9a1f7223 | 846 | /* pacer stop source: SOFTWARE */ |
c5930d66 HS |
847 | writel(0, dev->mmio + LAS0_PACER_STOP); |
848 | writel(0, dev->mmio + LAS0_PACER); /* stop pacer */ | |
849 | writel(0, dev->mmio + LAS0_ADC_CONVERSION); | |
850 | writew(0, dev->mmio + LAS0_IT); | |
851 | writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); | |
852 | writel(0, dev->mmio + LAS0_OVERRUN); | |
3d9f0739 | 853 | |
c98d90fd HS |
854 | /* start configuration */ |
855 | /* load channel list and reset CGT */ | |
856 | rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist); | |
3d9f0739 | 857 | |
c98d90fd HS |
858 | /* setup the common case and override if needed */ |
859 | if (cmd->chanlist_len > 1) { | |
9a1f7223 | 860 | /* pacer start source: SOFTWARE */ |
c5930d66 | 861 | writel(0, dev->mmio + LAS0_PACER_START); |
9a1f7223 | 862 | /* burst trigger source: PACER */ |
c5930d66 | 863 | writel(1, dev->mmio + LAS0_BURST_START); |
9a1f7223 | 864 | /* ADC conversion trigger source: BURST */ |
c5930d66 | 865 | writel(2, dev->mmio + LAS0_ADC_CONVERSION); |
c98d90fd | 866 | } else { /* single channel */ |
9a1f7223 | 867 | /* pacer start source: SOFTWARE */ |
c5930d66 | 868 | writel(0, dev->mmio + LAS0_PACER_START); |
9a1f7223 | 869 | /* ADC conversion trigger source: PACER */ |
c5930d66 | 870 | writel(1, dev->mmio + LAS0_ADC_CONVERSION); |
3d9f0739 | 871 | } |
c5930d66 | 872 | writel((devpriv->fifosz / 2 - 1) & 0xffff, dev->mmio + LAS0_ACNT); |
c98d90fd | 873 | |
e7211492 | 874 | if (cmd->scan_begin_src == TRIG_TIMER) { |
c98d90fd HS |
875 | /* scan_begin_arg is in nanoseconds */ |
876 | /* find out how many samples to wait before transferring */ | |
6681d3da | 877 | if (cmd->flags & CMDF_WAKE_EOS) { |
3ff20ef3 HS |
878 | /* |
879 | * this may generate un-sustainable interrupt rates | |
880 | * the application is responsible for doing the | |
881 | * right thing | |
882 | */ | |
2d7b8b94 | 883 | devpriv->xfer_count = cmd->chanlist_len; |
c98d90fd HS |
884 | devpriv->flags |= SEND_EOS; |
885 | } else { | |
886 | /* arrange to transfer data periodically */ | |
2d7b8b94 | 887 | devpriv->xfer_count = |
c98d90fd HS |
888 | (TRANS_TARGET_PERIOD * cmd->chanlist_len) / |
889 | cmd->scan_begin_arg; | |
2d7b8b94 | 890 | if (devpriv->xfer_count < cmd->chanlist_len) { |
c98d90fd | 891 | /* transfer after each scan (and avoid 0) */ |
2d7b8b94 | 892 | devpriv->xfer_count = cmd->chanlist_len; |
c98d90fd | 893 | } else { /* make a multiple of scan length */ |
2d7b8b94 | 894 | devpriv->xfer_count = |
01163176 BS |
895 | DIV_ROUND_UP(devpriv->xfer_count, |
896 | cmd->chanlist_len); | |
2d7b8b94 | 897 | devpriv->xfer_count *= cmd->chanlist_len; |
c98d90fd HS |
898 | } |
899 | devpriv->flags |= SEND_EOS; | |
900 | } | |
cd5e2d06 | 901 | if (devpriv->xfer_count >= (devpriv->fifosz / 2)) { |
c98d90fd | 902 | /* out of counter range, use 1/2 fifo instead */ |
2d7b8b94 | 903 | devpriv->xfer_count = 0; |
c98d90fd HS |
904 | devpriv->flags &= ~SEND_EOS; |
905 | } else { | |
906 | /* interrupt for each transfer */ | |
2d7b8b94 | 907 | writel((devpriv->xfer_count - 1) & 0xffff, |
c5930d66 | 908 | dev->mmio + LAS0_ACNT); |
c98d90fd | 909 | } |
c98d90fd | 910 | } else { /* unknown timing, just use 1/2 FIFO */ |
2d7b8b94 | 911 | devpriv->xfer_count = 0; |
c98d90fd | 912 | devpriv->flags &= ~SEND_EOS; |
3d9f0739 | 913 | } |
9a1f7223 | 914 | /* pacer clock source: INTERNAL 8MHz */ |
c5930d66 | 915 | writel(1, dev->mmio + LAS0_PACER_SELECT); |
9a1f7223 | 916 | /* just interrupt, don't stop */ |
c5930d66 | 917 | writel(1, dev->mmio + LAS0_ACNT_STOP_ENABLE); |
3d9f0739 | 918 | |
c98d90fd | 919 | /* BUG??? these look like enumerated values, but they are bit fields */ |
3d9f0739 | 920 | |
c98d90fd HS |
921 | /* First, setup when to stop */ |
922 | switch (cmd->stop_src) { | |
923 | case TRIG_COUNT: /* stop after N scans */ | |
8bc1bd1f | 924 | devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len; |
c3aea012 HS |
925 | if ((devpriv->xfer_count > 0) && |
926 | (devpriv->xfer_count > devpriv->ai_count)) { | |
8bc1bd1f | 927 | devpriv->xfer_count = devpriv->ai_count; |
c98d90fd HS |
928 | } |
929 | break; | |
3d9f0739 | 930 | |
c98d90fd | 931 | case TRIG_NONE: /* stop when cancel is called */ |
8bc1bd1f | 932 | devpriv->ai_count = -1; /* read forever */ |
c98d90fd | 933 | break; |
c98d90fd HS |
934 | } |
935 | ||
936 | /* Scan timing */ | |
937 | switch (cmd->scan_begin_src) { | |
938 | case TRIG_TIMER: /* periodic scanning */ | |
939 | timer = rtd_ns_to_timer(&cmd->scan_begin_arg, | |
2ed2182e | 940 | CMDF_ROUND_NEAREST); |
c98d90fd | 941 | /* set PACER clock */ |
c5930d66 | 942 | writel(timer & 0xffffff, dev->mmio + LAS0_PCLK); |
3d9f0739 | 943 | |
c98d90fd | 944 | break; |
bc8bf90a | 945 | |
c98d90fd | 946 | case TRIG_EXT: |
9a1f7223 | 947 | /* pacer start source: EXTERNAL */ |
c5930d66 | 948 | writel(1, dev->mmio + LAS0_PACER_START); |
c98d90fd | 949 | break; |
c98d90fd | 950 | } |
3d9f0739 | 951 | |
c98d90fd HS |
952 | /* Sample timing within a scan */ |
953 | switch (cmd->convert_src) { | |
954 | case TRIG_TIMER: /* periodic */ | |
3ff20ef3 HS |
955 | if (cmd->chanlist_len > 1) { |
956 | /* only needed for multi-channel */ | |
c98d90fd | 957 | timer = rtd_ns_to_timer(&cmd->convert_arg, |
2ed2182e | 958 | CMDF_ROUND_NEAREST); |
c98d90fd | 959 | /* setup BURST clock */ |
c5930d66 | 960 | writel(timer & 0x3ff, dev->mmio + LAS0_BCLK); |
3d9f0739 | 961 | } |
3d9f0739 | 962 | |
c98d90fd | 963 | break; |
3d9f0739 | 964 | |
c98d90fd | 965 | case TRIG_EXT: /* external */ |
9a1f7223 | 966 | /* burst trigger source: EXTERNAL */ |
c5930d66 | 967 | writel(2, dev->mmio + LAS0_BURST_START); |
c98d90fd | 968 | break; |
3d9f0739 | 969 | } |
c98d90fd | 970 | /* end configuration */ |
3d9f0739 | 971 | |
640da603 HS |
972 | /* |
973 | * This doesn't seem to work. There is no way to clear an interrupt | |
974 | * that the priority controller has queued! | |
975 | */ | |
c5930d66 HS |
976 | writew(~0, dev->mmio + LAS0_CLEAR); |
977 | readw(dev->mmio + LAS0_CLEAR); | |
3d9f0739 | 978 | |
c98d90fd | 979 | /* TODO: allow multiple interrupt sources */ |
b15f0277 NMG |
980 | /* transfer every N samples */ |
981 | writew(IRQM_ADC_ABOUT_CNT, dev->mmio + LAS0_IT); | |
3d9f0739 | 982 | |
c98d90fd HS |
983 | /* BUG: start_src is ASSUMED to be TRIG_NOW */ |
984 | /* BUG? it seems like things are running before the "start" */ | |
c5930d66 | 985 | readl(dev->mmio + LAS0_PACER); /* start pacer */ |
c98d90fd HS |
986 | return 0; |
987 | } | |
3d9f0739 | 988 | |
c98d90fd HS |
989 | static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
990 | { | |
4686a0e6 | 991 | struct rtd_private *devpriv = dev->private; |
3d9f0739 | 992 | |
9a1f7223 | 993 | /* pacer stop source: SOFTWARE */ |
c5930d66 HS |
994 | writel(0, dev->mmio + LAS0_PACER_STOP); |
995 | writel(0, dev->mmio + LAS0_PACER); /* stop pacer */ | |
996 | writel(0, dev->mmio + LAS0_ADC_CONVERSION); | |
997 | writew(0, dev->mmio + LAS0_IT); | |
8bc1bd1f | 998 | devpriv->ai_count = 0; /* stop and don't transfer any more */ |
351a7bff | 999 | writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); |
c98d90fd | 1000 | return 0; |
3d9f0739 | 1001 | } |
3d9f0739 | 1002 | |
051448c5 HS |
1003 | static int rtd_ao_eoc(struct comedi_device *dev, |
1004 | struct comedi_subdevice *s, | |
1005 | struct comedi_insn *insn, | |
1006 | unsigned long context) | |
1007 | { | |
051448c5 HS |
1008 | unsigned int chan = CR_CHAN(insn->chanspec); |
1009 | unsigned int bit = (chan == 0) ? FS_DAC1_NOT_EMPTY : FS_DAC2_NOT_EMPTY; | |
1010 | unsigned int status; | |
1011 | ||
c5930d66 | 1012 | status = readl(dev->mmio + LAS0_ADC); |
051448c5 HS |
1013 | if (status & bit) |
1014 | return 0; | |
1015 | return -EBUSY; | |
1016 | } | |
1017 | ||
8e0768b1 HS |
1018 | static int rtd_ao_insn_write(struct comedi_device *dev, |
1019 | struct comedi_subdevice *s, | |
1020 | struct comedi_insn *insn, | |
1021 | unsigned int *data) | |
3d9f0739 | 1022 | { |
4686a0e6 | 1023 | struct rtd_private *devpriv = dev->private; |
8e0768b1 HS |
1024 | unsigned int chan = CR_CHAN(insn->chanspec); |
1025 | unsigned int range = CR_RANGE(insn->chanspec); | |
051448c5 | 1026 | int ret; |
8e0768b1 | 1027 | int i; |
bc8bf90a | 1028 | |
c98d90fd | 1029 | /* Configure the output range (table index matches the range values) */ |
78c1652c | 1030 | writew(range & 7, dev->mmio + LAS0_DAC_CTRL(chan)); |
3d9f0739 | 1031 | |
c98d90fd | 1032 | for (i = 0; i < insn->n; ++i) { |
8e0768b1 | 1033 | unsigned int val = data[i]; |
bc8bf90a | 1034 | |
8e0768b1 HS |
1035 | /* bipolar uses 2's complement values with an extended sign */ |
1036 | if (comedi_range_is_bipolar(s, range)) { | |
1037 | val = comedi_offset_munge(s, val); | |
1038 | val |= (val & ((s->maxdata + 1) >> 1)) << 1; | |
c98d90fd | 1039 | } |
3d9f0739 | 1040 | |
8e0768b1 HS |
1041 | /* shift the 12-bit data (+ sign) to match the register */ |
1042 | val <<= 3; | |
1043 | ||
78c1652c HS |
1044 | writew(val, devpriv->las1 + LAS1_DAC_FIFO(chan)); |
1045 | writew(0, dev->mmio + LAS0_UPDATE_DAC(chan)); | |
3d9f0739 | 1046 | |
051448c5 HS |
1047 | ret = comedi_timeout(dev, s, insn, rtd_ao_eoc, 0); |
1048 | if (ret) | |
1049 | return ret; | |
8e0768b1 HS |
1050 | |
1051 | s->readback[chan] = data[i]; | |
3d9f0739 DC |
1052 | } |
1053 | ||
8e0768b1 | 1054 | return insn->n; |
c98d90fd | 1055 | } |
3d9f0739 | 1056 | |
c98d90fd HS |
1057 | static int rtd_dio_insn_bits(struct comedi_device *dev, |
1058 | struct comedi_subdevice *s, | |
45b6937a HS |
1059 | struct comedi_insn *insn, |
1060 | unsigned int *data) | |
c98d90fd | 1061 | { |
97f4289a | 1062 | if (comedi_dio_update_state(s, data)) |
c5930d66 | 1063 | writew(s->state & 0xff, dev->mmio + LAS0_DIO0); |
45b6937a | 1064 | |
c5930d66 | 1065 | data[1] = readw(dev->mmio + LAS0_DIO0) & 0xff; |
3d9f0739 | 1066 | |
a2714e3e | 1067 | return insn->n; |
3d9f0739 DC |
1068 | } |
1069 | ||
c98d90fd HS |
1070 | static int rtd_dio_insn_config(struct comedi_device *dev, |
1071 | struct comedi_subdevice *s, | |
efa5b326 HS |
1072 | struct comedi_insn *insn, |
1073 | unsigned int *data) | |
3d9f0739 | 1074 | { |
ddf62f2c | 1075 | int ret; |
3d9f0739 | 1076 | |
ddf62f2c HS |
1077 | ret = comedi_dio_insn_config(dev, s, insn, data, 0); |
1078 | if (ret) | |
1079 | return ret; | |
3d9f0739 | 1080 | |
c98d90fd | 1081 | /* TODO support digital match interrupts and strobes */ |
360235af HS |
1082 | |
1083 | /* set direction */ | |
c5930d66 HS |
1084 | writew(0x01, dev->mmio + LAS0_DIO_STATUS); |
1085 | writew(s->io_bits & 0xff, dev->mmio + LAS0_DIO0_CTRL); | |
360235af HS |
1086 | |
1087 | /* clear interrupts */ | |
c5930d66 | 1088 | writew(0x00, dev->mmio + LAS0_DIO_STATUS); |
3d9f0739 | 1089 | |
c98d90fd | 1090 | /* port1 can only be all input or all output */ |
3d9f0739 | 1091 | |
c98d90fd | 1092 | /* there are also 2 user input lines and 2 user output lines */ |
3d9f0739 | 1093 | |
efa5b326 | 1094 | return insn->n; |
c98d90fd | 1095 | } |
3d9f0739 | 1096 | |
bc86e82d HS |
1097 | static int rtd_counter_insn_config(struct comedi_device *dev, |
1098 | struct comedi_subdevice *s, | |
1099 | struct comedi_insn *insn, | |
1100 | unsigned int *data) | |
1101 | { | |
1102 | struct rtd_private *devpriv = dev->private; | |
1103 | unsigned int chan = CR_CHAN(insn->chanspec); | |
1104 | unsigned int max_src; | |
1105 | unsigned int src; | |
1106 | ||
1107 | switch (data[0]) { | |
1108 | case INSN_CONFIG_SET_GATE_SRC: | |
1109 | /* | |
1110 | * 8254 Timer/Counter gate sources: | |
1111 | * | |
1112 | * 0 = Not gated, free running (reset state) | |
1113 | * 1 = Gated, off | |
1114 | * 2 = Ext. TC Gate 1 | |
1115 | * 3 = Ext. TC Gate 2 | |
1116 | * 4 = Previous TC out (chan 1 and 2 only) | |
1117 | */ | |
1118 | src = data[2]; | |
1119 | max_src = (chan == 0) ? 3 : 4; | |
1120 | if (src > max_src) | |
1121 | return -EINVAL; | |
1122 | ||
1123 | devpriv->timer_gate_src[chan] = src; | |
1124 | writeb(src, dev->mmio + LAS0_8254_GATE_SEL(chan)); | |
1125 | break; | |
1126 | case INSN_CONFIG_GET_GATE_SRC: | |
1127 | data[2] = devpriv->timer_gate_src[chan]; | |
1128 | break; | |
1129 | case INSN_CONFIG_SET_CLOCK_SRC: | |
1130 | /* | |
1131 | * 8254 Timer/Counter clock sources: | |
1132 | * | |
1133 | * 0 = 8 MHz (reset state) | |
1134 | * 1 = Ext. TC Clock 1 | |
1135 | * 2 = Ext. TX Clock 2 | |
1136 | * 3 = Ext. Pacer Clock | |
1137 | * 4 = Previous TC out (chan 1 and 2 only) | |
1138 | * 5 = High-Speed Digital Input Sampling signal (chan 1 only) | |
1139 | */ | |
1140 | src = data[1]; | |
1141 | switch (chan) { | |
1142 | case 0: | |
1143 | max_src = 3; | |
1144 | break; | |
1145 | case 1: | |
1146 | max_src = 5; | |
1147 | break; | |
1148 | case 2: | |
1149 | max_src = 4; | |
1150 | break; | |
1151 | default: | |
1152 | return -EINVAL; | |
1153 | } | |
1154 | if (src > max_src) | |
1155 | return -EINVAL; | |
1156 | ||
1157 | devpriv->timer_clk_src[chan] = src; | |
1158 | writeb(src, dev->mmio + LAS0_8254_CLK_SEL(chan)); | |
1159 | break; | |
1160 | case INSN_CONFIG_GET_CLOCK_SRC: | |
1161 | src = devpriv->timer_clk_src[chan]; | |
1162 | data[1] = devpriv->timer_clk_src[chan]; | |
1163 | data[2] = (src == 0) ? RTD_CLOCK_BASE : 0; | |
1164 | break; | |
1165 | default: | |
1166 | return -EINVAL; | |
1167 | } | |
1168 | ||
1169 | return insn->n; | |
1170 | } | |
1171 | ||
09d93a18 | 1172 | static void rtd_reset(struct comedi_device *dev) |
b155c5fa | 1173 | { |
4686a0e6 | 1174 | struct rtd_private *devpriv = dev->private; |
b155c5fa | 1175 | |
c5930d66 | 1176 | writel(0, dev->mmio + LAS0_BOARD_RESET); |
120bdac7 | 1177 | usleep_range(100, 1000); /* needed? */ |
c644a11a | 1178 | writel(0, devpriv->lcfg + PLX_REG_INTCSR); |
c5930d66 HS |
1179 | writew(0, dev->mmio + LAS0_IT); |
1180 | writew(~0, dev->mmio + LAS0_CLEAR); | |
1181 | readw(dev->mmio + LAS0_CLEAR); | |
09d93a18 HS |
1182 | } |
1183 | ||
1184 | /* | |
1185 | * initialize board, per RTD spec | |
1186 | * also, initialize shadow registers | |
1187 | */ | |
1188 | static void rtd_init_board(struct comedi_device *dev) | |
1189 | { | |
09d93a18 HS |
1190 | rtd_reset(dev); |
1191 | ||
c5930d66 HS |
1192 | writel(0, dev->mmio + LAS0_OVERRUN); |
1193 | writel(0, dev->mmio + LAS0_CGT_CLEAR); | |
1194 | writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); | |
78c1652c HS |
1195 | writel(0, dev->mmio + LAS0_DAC_RESET(0)); |
1196 | writel(0, dev->mmio + LAS0_DAC_RESET(1)); | |
b155c5fa | 1197 | /* clear digital IO fifo */ |
c5930d66 | 1198 | writew(0, dev->mmio + LAS0_DIO_STATUS); |
b155c5fa HS |
1199 | /* TODO: set user out source ??? */ |
1200 | } | |
1201 | ||
3d7e4416 HS |
1202 | /* The RTD driver does this */ |
1203 | static void rtd_pci_latency_quirk(struct comedi_device *dev, | |
1204 | struct pci_dev *pcidev) | |
1205 | { | |
1206 | unsigned char pci_latency; | |
3d7e4416 HS |
1207 | |
1208 | pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency); | |
1209 | if (pci_latency < 32) { | |
1210 | dev_info(dev->class_dev, | |
6c7d2c8b HS |
1211 | "PCI latency changed from %d to %d\n", |
1212 | pci_latency, 32); | |
3d7e4416 | 1213 | pci_write_config_byte(pcidev, PCI_LATENCY_TIMER, 32); |
3d7e4416 | 1214 | } |
3d7e4416 HS |
1215 | } |
1216 | ||
a690b7e5 | 1217 | static int rtd_auto_attach(struct comedi_device *dev, |
b3322d42 | 1218 | unsigned long context) |
825ca08e | 1219 | { |
5df39540 | 1220 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
e9e7023c | 1221 | const struct rtd_boardinfo *board = NULL; |
4686a0e6 | 1222 | struct rtd_private *devpriv; |
c98d90fd | 1223 | struct comedi_subdevice *s; |
c98d90fd | 1224 | int ret; |
3d9f0739 | 1225 | |
fd62ef00 HS |
1226 | if (context < ARRAY_SIZE(rtd520_boards)) |
1227 | board = &rtd520_boards[context]; | |
e9e7023c | 1228 | if (!board) |
825ca08e | 1229 | return -ENODEV; |
e9e7023c HS |
1230 | dev->board_ptr = board; |
1231 | dev->board_name = board->name; | |
825ca08e | 1232 | |
0bdab509 | 1233 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
1234 | if (!devpriv) |
1235 | return -ENOMEM; | |
3d9f0739 | 1236 | |
818f569f | 1237 | ret = comedi_pci_enable(dev); |
edecbd06 | 1238 | if (ret) |
c98d90fd | 1239 | return ret; |
a26f4dd0 | 1240 | |
c5930d66 | 1241 | dev->mmio = pci_ioremap_bar(pcidev, 2); |
dde4196e HS |
1242 | devpriv->las1 = pci_ioremap_bar(pcidev, 3); |
1243 | devpriv->lcfg = pci_ioremap_bar(pcidev, 0); | |
c5930d66 | 1244 | if (!dev->mmio || !devpriv->las1 || !devpriv->lcfg) |
c98d90fd | 1245 | return -ENOMEM; |
3d9f0739 | 1246 | |
3d7e4416 | 1247 | rtd_pci_latency_quirk(dev, pcidev); |
3d9f0739 | 1248 | |
90973498 HS |
1249 | if (pcidev->irq) { |
1250 | ret = request_irq(pcidev->irq, rtd_interrupt, IRQF_SHARED, | |
1251 | dev->board_name, dev); | |
1252 | if (ret == 0) | |
1253 | dev->irq = pcidev->irq; | |
1254 | } | |
1255 | ||
8b6c5694 HS |
1256 | ret = comedi_alloc_subdevices(dev, 4); |
1257 | if (ret) | |
1258 | return ret; | |
3d9f0739 | 1259 | |
58f20459 | 1260 | s = &dev->subdevices[0]; |
c98d90fd | 1261 | /* analog input subdevice */ |
37f97e50 HS |
1262 | s->type = COMEDI_SUBD_AI; |
1263 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF; | |
1264 | s->n_chan = 16; | |
1265 | s->maxdata = 0x0fff; | |
e9e7023c | 1266 | s->range_table = board->ai_range; |
37f97e50 HS |
1267 | s->len_chanlist = RTD_MAX_CHANLIST; |
1268 | s->insn_read = rtd_ai_rinsn; | |
90973498 HS |
1269 | if (dev->irq) { |
1270 | dev->read_subdev = s; | |
37f97e50 HS |
1271 | s->subdev_flags |= SDF_CMD_READ; |
1272 | s->do_cmd = rtd_ai_cmd; | |
1273 | s->do_cmdtest = rtd_ai_cmdtest; | |
1274 | s->cancel = rtd_ai_cancel; | |
90973498 | 1275 | } |
3d9f0739 | 1276 | |
58f20459 | 1277 | s = &dev->subdevices[1]; |
c98d90fd | 1278 | /* analog output subdevice */ |
37f97e50 HS |
1279 | s->type = COMEDI_SUBD_AO; |
1280 | s->subdev_flags = SDF_WRITABLE; | |
1281 | s->n_chan = 2; | |
1282 | s->maxdata = 0x0fff; | |
1283 | s->range_table = &rtd_ao_range; | |
8e0768b1 | 1284 | s->insn_write = rtd_ao_insn_write; |
3caaf7b7 HS |
1285 | |
1286 | ret = comedi_alloc_subdev_readback(s); | |
1287 | if (ret) | |
1288 | return ret; | |
3d9f0739 | 1289 | |
58f20459 | 1290 | s = &dev->subdevices[2]; |
c98d90fd | 1291 | /* digital i/o subdevice */ |
37f97e50 HS |
1292 | s->type = COMEDI_SUBD_DIO; |
1293 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
c98d90fd | 1294 | /* we only support port 0 right now. Ignoring port 1 and user IO */ |
37f97e50 HS |
1295 | s->n_chan = 8; |
1296 | s->maxdata = 1; | |
1297 | s->range_table = &range_digital; | |
1298 | s->insn_bits = rtd_dio_insn_bits; | |
1299 | s->insn_config = rtd_dio_insn_config; | |
3d9f0739 | 1300 | |
bc86e82d | 1301 | /* 8254 Timer/Counter subdevice */ |
58f20459 | 1302 | s = &dev->subdevices[3]; |
bc86e82d HS |
1303 | dev->pacer = comedi_8254_mm_init(dev->mmio + LAS0_8254_TIMER_BASE, |
1304 | RTD_CLOCK_BASE, I8254_IO8, 2); | |
1305 | if (!dev->pacer) | |
1306 | return -ENOMEM; | |
1307 | ||
1308 | comedi_8254_subdevice_init(s, dev->pacer); | |
1309 | dev->pacer->insn_config = rtd_counter_insn_config; | |
3d9f0739 | 1310 | |
b155c5fa | 1311 | rtd_init_board(dev); |
3d9f0739 | 1312 | |
c98d90fd HS |
1313 | ret = rtd520_probe_fifo_depth(dev); |
1314 | if (ret < 0) | |
1315 | return ret; | |
cd5e2d06 | 1316 | devpriv->fifosz = ret; |
c98d90fd | 1317 | |
a5ca9475 | 1318 | if (dev->irq) |
9dc53852 IA |
1319 | writel(PLX_INTCSR_PIEN | PLX_INTCSR_PLIEN, |
1320 | devpriv->lcfg + PLX_REG_INTCSR); | |
bc8bf90a | 1321 | |
edecbd06 | 1322 | return 0; |
3d9f0739 DC |
1323 | } |
1324 | ||
c98d90fd | 1325 | static void rtd_detach(struct comedi_device *dev) |
3d9f0739 | 1326 | { |
4686a0e6 | 1327 | struct rtd_private *devpriv = dev->private; |
3d9f0739 | 1328 | |
c98d90fd HS |
1329 | if (devpriv) { |
1330 | /* Shut down any board ops by resetting it */ | |
c5930d66 | 1331 | if (dev->mmio && devpriv->lcfg) |
09d93a18 | 1332 | rtd_reset(dev); |
b7d6b43b | 1333 | if (dev->irq) |
c98d90fd | 1334 | free_irq(dev->irq, dev); |
c5930d66 HS |
1335 | if (dev->mmio) |
1336 | iounmap(dev->mmio); | |
c98d90fd HS |
1337 | if (devpriv->las1) |
1338 | iounmap(devpriv->las1); | |
1339 | if (devpriv->lcfg) | |
1340 | iounmap(devpriv->lcfg); | |
843c042d | 1341 | } |
7f072f54 | 1342 | comedi_pci_disable(dev); |
3d9f0739 DC |
1343 | } |
1344 | ||
75e6301b HS |
1345 | static struct comedi_driver rtd520_driver = { |
1346 | .driver_name = "rtd520", | |
6beb8160 | 1347 | .module = THIS_MODULE, |
5df39540 | 1348 | .auto_attach = rtd_auto_attach, |
6beb8160 HS |
1349 | .detach = rtd_detach, |
1350 | }; | |
1351 | ||
a690b7e5 | 1352 | static int rtd520_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 1353 | const struct pci_device_id *id) |
727b286b | 1354 | { |
b8f4ac23 | 1355 | return comedi_pci_auto_config(dev, &rtd520_driver, id->driver_data); |
727b286b AT |
1356 | } |
1357 | ||
41e043fc | 1358 | static const struct pci_device_id rtd520_pci_table[] = { |
b3322d42 HS |
1359 | { PCI_VDEVICE(RTD, 0x7520), BOARD_DM7520 }, |
1360 | { PCI_VDEVICE(RTD, 0x4520), BOARD_PCI4520 }, | |
6beb8160 HS |
1361 | { 0 } |
1362 | }; | |
1363 | MODULE_DEVICE_TABLE(pci, rtd520_pci_table); | |
1364 | ||
75e6301b HS |
1365 | static struct pci_driver rtd520_pci_driver = { |
1366 | .name = "rtd520", | |
6beb8160 | 1367 | .id_table = rtd520_pci_table, |
75e6301b | 1368 | .probe = rtd520_pci_probe, |
9901a4d7 | 1369 | .remove = comedi_pci_auto_unconfig, |
727b286b | 1370 | }; |
75e6301b | 1371 | module_comedi_pci_driver(rtd520_driver, rtd520_pci_driver); |
90f703d3 AT |
1372 | |
1373 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
1374 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
1375 | MODULE_LICENSE("GPL"); |