Commit | Line | Data |
---|---|---|
fcdb427b MD |
1 | /* |
2 | * comedi/drivers/pcl812.c | |
3 | * | |
4 | * Author: Michal Dobes <dobes@tesnet.cz> | |
5 | * | |
6 | * hardware driver for Advantech cards | |
7 | * card: PCL-812, PCL-812PG, PCL-813, PCL-813B | |
8 | * driver: pcl812, pcl812pg, pcl813, pcl813b | |
9 | * and for ADlink cards | |
10 | * card: ACL-8112DG, ACL-8112HG, ACL-8112PG, ACL-8113, ACL-8216 | |
11 | * driver: acl8112dg, acl8112hg, acl8112pg, acl8113, acl8216 | |
12 | * and for ICP DAS cards | |
13 | * card: ISO-813, A-821PGH, A-821PGL, A-821PGL-NDA, A-822PGH, A-822PGL, | |
14 | * driver: iso813, a821pgh, a-821pgl, a-821pglnda, a822pgh, a822pgl, | |
15 | * card: A-823PGH, A-823PGL, A-826PG | |
16 | * driver: a823pgh, a823pgl, a826pg | |
17 | */ | |
18 | /* | |
19 | Driver: pcl812 | |
20 | Description: Advantech PCL-812/PG, PCL-813/B, | |
21 | ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216, | |
22 | ICP DAS A-821PGH/PGL/PGL-NDA, A-822PGH/PGL, A-823PGH/PGL, A-826PG, | |
23 | ICP DAS ISO-813 | |
24 | Author: Michal Dobes <dobes@tesnet.cz> | |
25 | Devices: [Advantech] PCL-812 (pcl812), PCL-812PG (pcl812pg), | |
26 | PCL-813 (pcl813), PCL-813B (pcl813b), [ADLink] ACL-8112DG (acl8112dg), | |
27 | ACL-8112HG (acl8112hg), ACL-8113 (acl-8113), ACL-8216 (acl8216), | |
28 | [ICP] ISO-813 (iso813), A-821PGH (a821pgh), A-821PGL (a821pgl), | |
29 | A-821PGL-NDA (a821pclnda), A-822PGH (a822pgh), A-822PGL (a822pgl), | |
30 | A-823PGH (a823pgh), A-823PGL (a823pgl), A-826PG (a826pg) | |
31 | Updated: Mon, 06 Aug 2007 12:03:15 +0100 | |
32 | Status: works (I hope. My board fire up under my hands | |
33 | and I cann't test all features.) | |
34 | ||
35 | This driver supports insn and cmd interfaces. Some boards support only insn | |
36 | becouse their hardware don't allow more (PCL-813/B, ACL-8113, ISO-813). | |
37 | Data transfer over DMA is supported only when you measure only one | |
38 | channel, this is too hardware limitation of these boards. | |
39 | ||
40 | Options for PCL-812: | |
41 | [0] - IO Base | |
42 | [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15) | |
43 | [2] - DMA (0=disable, 1, 3) | |
44 | [3] - 0=trigger source is internal 8253 with 2MHz clock | |
45 | 1=trigger source is external | |
46 | [4] - 0=A/D input range is +/-10V | |
47 | 1=A/D input range is +/-5V | |
48 | 2=A/D input range is +/-2.5V | |
49 | 3=A/D input range is +/-1.25V | |
50 | 4=A/D input range is +/-0.625V | |
51 | 5=A/D input range is +/-0.3125V | |
52 | [5] - 0=D/A outputs 0-5V (internal reference -5V) | |
53 | 1=D/A outputs 0-10V (internal reference -10V) | |
54 | 2=D/A outputs unknow (external reference) | |
55 | ||
56 | Options for PCL-812PG, ACL-8112PG: | |
57 | [0] - IO Base | |
58 | [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15) | |
59 | [2] - DMA (0=disable, 1, 3) | |
60 | [3] - 0=trigger source is internal 8253 with 2MHz clock | |
61 | 1=trigger source is external | |
62 | [4] - 0=A/D have max +/-5V input | |
63 | 1=A/D have max +/-10V input | |
64 | [5] - 0=D/A outputs 0-5V (internal reference -5V) | |
65 | 1=D/A outputs 0-10V (internal reference -10V) | |
66 | 2=D/A outputs unknow (external reference) | |
67 | ||
68 | Options for ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH, ACL-8216, A-826PG: | |
69 | [0] - IO Base | |
70 | [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15) | |
71 | [2] - DMA (0=disable, 1, 3) | |
72 | [3] - 0=trigger source is internal 8253 with 2MHz clock | |
73 | 1=trigger source is external | |
74 | [4] - 0=A/D channels are S.E. | |
75 | 1=A/D channels are DIFF | |
76 | [5] - 0=D/A outputs 0-5V (internal reference -5V) | |
77 | 1=D/A outputs 0-10V (internal reference -10V) | |
78 | 2=D/A outputs unknow (external reference) | |
79 | ||
80 | Options for A-821PGL/PGH: | |
81 | [0] - IO Base | |
82 | [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) | |
83 | [2] - 0=A/D channels are S.E. | |
84 | 1=A/D channels are DIFF | |
85 | [3] - 0=D/A output 0-5V (internal reference -5V) | |
86 | 1=D/A output 0-10V (internal reference -10V) | |
87 | ||
88 | Options for A-821PGL-NDA: | |
89 | [0] - IO Base | |
90 | [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) | |
91 | [2] - 0=A/D channels are S.E. | |
92 | 1=A/D channels are DIFF | |
93 | ||
94 | Options for PCL-813: | |
95 | [0] - IO Base | |
96 | ||
97 | Options for PCL-813B: | |
98 | [0] - IO Base | |
99 | [1] - 0= bipolar inputs | |
100 | 1= unipolar inputs | |
101 | ||
102 | Options for ACL-8113, ISO-813: | |
103 | [0] - IO Base | |
104 | [1] - 0= 10V bipolar inputs | |
105 | 1= 10V unipolar inputs | |
106 | 2= 20V bipolar inputs | |
107 | 3= 20V unipolar inputs | |
108 | */ | |
109 | ||
110 | #include "../comedidev.h" | |
111 | ||
112 | #include <linux/delay.h> | |
113 | #include <linux/ioport.h> | |
114 | #include <asm/dma.h> | |
115 | ||
116 | #include "8253.h" | |
117 | ||
118 | #undef PCL812_EXTDEBUG /* if this is defined then a lot of messages is printed */ | |
119 | ||
120 | // hardware types of the cards | |
121 | #define boardPCL812PG 0 /* and ACL-8112PG */ | |
122 | #define boardPCL813B 1 | |
123 | #define boardPCL812 2 | |
124 | #define boardPCL813 3 | |
125 | #define boardISO813 5 | |
126 | #define boardACL8113 6 | |
127 | #define boardACL8112 7 /* ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH */ | |
128 | #define boardACL8216 8 /* and ICP DAS A-826PG */ | |
129 | #define boardA821 9 /* PGH, PGL, PGL/NDA versions */ | |
130 | ||
131 | #define PCLx1x_IORANGE 16 | |
132 | ||
133 | #define PCL812_CTR0 0 | |
134 | #define PCL812_CTR1 1 | |
135 | #define PCL812_CTR2 2 | |
136 | #define PCL812_CTRCTL 3 | |
137 | #define PCL812_AD_LO 4 | |
138 | #define PCL812_DA1_LO 4 | |
139 | #define PCL812_AD_HI 5 | |
140 | #define PCL812_DA1_HI 5 | |
141 | #define PCL812_DA2_LO 6 | |
142 | #define PCL812_DI_LO 6 | |
143 | #define PCL812_DA2_HI 7 | |
144 | #define PCL812_DI_HI 7 | |
145 | #define PCL812_CLRINT 8 | |
146 | #define PCL812_GAIN 9 | |
147 | #define PCL812_MUX 10 | |
148 | #define PCL812_MODE 11 | |
149 | #define PCL812_CNTENABLE 10 | |
150 | #define PCL812_SOFTTRIG 12 | |
151 | #define PCL812_DO_LO 13 | |
152 | #define PCL812_DO_HI 14 | |
153 | ||
154 | #define PCL812_DRDY 0x10 /* =0 data ready */ | |
155 | ||
156 | #define ACL8216_STATUS 8 /* 5. bit signalize data ready */ | |
157 | ||
158 | #define ACL8216_DRDY 0x20 /* =0 data ready */ | |
159 | ||
160 | #define MAX_CHANLIST_LEN 256 /* length of scan list */ | |
161 | ||
162 | static const comedi_lrange range_pcl812pg_ai = { 5, { | |
163 | BIP_RANGE(5), | |
164 | BIP_RANGE(2.5), | |
165 | BIP_RANGE(1.25), | |
166 | BIP_RANGE(0.625), | |
167 | BIP_RANGE(0.3125), | |
168 | } | |
169 | }; | |
170 | static const comedi_lrange range_pcl812pg2_ai = { 5, { | |
171 | BIP_RANGE(10), | |
172 | BIP_RANGE(5), | |
173 | BIP_RANGE(2.5), | |
174 | BIP_RANGE(1.25), | |
175 | BIP_RANGE(0.625), | |
176 | } | |
177 | }; | |
178 | static const comedi_lrange range812_bipolar1_25 = { 1, { | |
179 | BIP_RANGE(1.25), | |
180 | } | |
181 | }; | |
182 | static const comedi_lrange range812_bipolar0_625 = { 1, { | |
183 | BIP_RANGE(0.625), | |
184 | } | |
185 | }; | |
186 | static const comedi_lrange range812_bipolar0_3125 = { 1, { | |
187 | BIP_RANGE(0.3125), | |
188 | } | |
189 | }; | |
190 | static const comedi_lrange range_pcl813b_ai = { 4, { | |
191 | BIP_RANGE(5), | |
192 | BIP_RANGE(2.5), | |
193 | BIP_RANGE(1.25), | |
194 | BIP_RANGE(0.625), | |
195 | } | |
196 | }; | |
197 | static const comedi_lrange range_pcl813b2_ai = { 4, { | |
198 | UNI_RANGE(10), | |
199 | UNI_RANGE(5), | |
200 | UNI_RANGE(2.5), | |
201 | UNI_RANGE(1.25), | |
202 | } | |
203 | }; | |
204 | static const comedi_lrange range_iso813_1_ai = { 5, { | |
205 | BIP_RANGE(5), | |
206 | BIP_RANGE(2.5), | |
207 | BIP_RANGE(1.25), | |
208 | BIP_RANGE(0.625), | |
209 | BIP_RANGE(0.3125), | |
210 | } | |
211 | }; | |
212 | static const comedi_lrange range_iso813_1_2_ai = { 5, { | |
213 | UNI_RANGE(10), | |
214 | UNI_RANGE(5), | |
215 | UNI_RANGE(2.5), | |
216 | UNI_RANGE(1.25), | |
217 | UNI_RANGE(0.625), | |
218 | } | |
219 | }; | |
220 | static const comedi_lrange range_iso813_2_ai = { 4, { | |
221 | BIP_RANGE(5), | |
222 | BIP_RANGE(2.5), | |
223 | BIP_RANGE(1.25), | |
224 | BIP_RANGE(0.625), | |
225 | } | |
226 | }; | |
227 | static const comedi_lrange range_iso813_2_2_ai = { 4, { | |
228 | UNI_RANGE(10), | |
229 | UNI_RANGE(5), | |
230 | UNI_RANGE(2.5), | |
231 | UNI_RANGE(1.25), | |
232 | } | |
233 | }; | |
234 | static const comedi_lrange range_acl8113_1_ai = { 4, { | |
235 | BIP_RANGE(5), | |
236 | BIP_RANGE(2.5), | |
237 | BIP_RANGE(1.25), | |
238 | BIP_RANGE(0.625), | |
239 | } | |
240 | }; | |
241 | static const comedi_lrange range_acl8113_1_2_ai = { 4, { | |
242 | UNI_RANGE(10), | |
243 | UNI_RANGE(5), | |
244 | UNI_RANGE(2.5), | |
245 | UNI_RANGE(1.25), | |
246 | } | |
247 | }; | |
248 | static const comedi_lrange range_acl8113_2_ai = { 3, { | |
249 | BIP_RANGE(5), | |
250 | BIP_RANGE(2.5), | |
251 | BIP_RANGE(1.25), | |
252 | } | |
253 | }; | |
254 | static const comedi_lrange range_acl8113_2_2_ai = { 3, { | |
255 | UNI_RANGE(10), | |
256 | UNI_RANGE(5), | |
257 | UNI_RANGE(2.5), | |
258 | } | |
259 | }; | |
260 | static const comedi_lrange range_acl8112dg_ai = { 9, { | |
261 | BIP_RANGE(5), | |
262 | BIP_RANGE(2.5), | |
263 | BIP_RANGE(1.25), | |
264 | BIP_RANGE(0.625), | |
265 | UNI_RANGE(10), | |
266 | UNI_RANGE(5), | |
267 | UNI_RANGE(2.5), | |
268 | UNI_RANGE(1.25), | |
269 | BIP_RANGE(10), | |
270 | } | |
271 | }; | |
272 | static const comedi_lrange range_acl8112hg_ai = { 12, { | |
273 | BIP_RANGE(5), | |
274 | BIP_RANGE(0.5), | |
275 | BIP_RANGE(0.05), | |
276 | BIP_RANGE(0.005), | |
277 | UNI_RANGE(10), | |
278 | UNI_RANGE(1), | |
279 | UNI_RANGE(0.1), | |
280 | UNI_RANGE(0.01), | |
281 | BIP_RANGE(10), | |
282 | BIP_RANGE(1), | |
283 | BIP_RANGE(0.1), | |
284 | BIP_RANGE(0.01), | |
285 | } | |
286 | }; | |
287 | static const comedi_lrange range_a821pgh_ai = { 4, { | |
288 | BIP_RANGE(5), | |
289 | BIP_RANGE(0.5), | |
290 | BIP_RANGE(0.05), | |
291 | BIP_RANGE(0.005), | |
292 | } | |
293 | }; | |
294 | ||
71b5f4f1 BP |
295 | static int pcl812_attach(struct comedi_device * dev, comedi_devconfig * it); |
296 | static int pcl812_detach(struct comedi_device * dev); | |
fcdb427b MD |
297 | |
298 | typedef struct { | |
299 | const char *name; // board name | |
300 | int board_type; // type of this board | |
301 | int n_aichan; // num of AI chans in S.E. | |
302 | int n_aichan_diff; // DIFF num of chans | |
303 | int n_aochan; // num of DA chans | |
304 | int n_dichan; // DI and DO chans | |
305 | int n_dochan; | |
306 | int ai_maxdata; // AI resolution | |
307 | unsigned int ai_ns_min; // max sample speed of card v ns | |
308 | unsigned int i8254_osc_base; // clock base | |
309 | const comedi_lrange *rangelist_ai; // rangelist for A/D | |
310 | const comedi_lrange *rangelist_ao; // rangelist for D/A | |
311 | unsigned int IRQbits; // allowed IRQ | |
312 | unsigned char DMAbits; // allowed DMA chans | |
313 | unsigned char io_range; // iorange for this board | |
314 | unsigned char haveMPC508; // 1=board use MPC508A multiplexor | |
315 | } boardtype; | |
316 | ||
317 | static const boardtype boardtypes[] = { | |
318 | {"pcl812", boardPCL812, 16, 0, 2, 16, 16, 0x0fff, | |
319 | 33000, 500, &range_bipolar10, &range_unipolar5, | |
320 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
321 | {"pcl812pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff, | |
322 | 33000, 500, &range_pcl812pg_ai, &range_unipolar5, | |
323 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
324 | {"acl8112pg", boardPCL812PG, 16, 0, 2, 16, 16, 0x0fff, | |
325 | 10000, 500, &range_pcl812pg_ai, &range_unipolar5, | |
326 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
327 | {"acl8112dg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, | |
328 | 10000, 500, &range_acl8112dg_ai, &range_unipolar5, | |
329 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 1}, | |
330 | {"acl8112hg", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, | |
331 | 10000, 500, &range_acl8112hg_ai, &range_unipolar5, | |
332 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 1}, | |
333 | {"a821pgl", boardA821, 16, 8, 1, 16, 16, 0x0fff, | |
334 | 10000, 500, &range_pcl813b_ai, &range_unipolar5, | |
335 | 0x000c, 0x00, PCLx1x_IORANGE, 0}, | |
336 | {"a821pglnda", boardA821, 16, 8, 0, 0, 0, 0x0fff, | |
337 | 10000, 500, &range_pcl813b_ai, NULL, | |
338 | 0x000c, 0x00, PCLx1x_IORANGE, 0}, | |
339 | {"a821pgh", boardA821, 16, 8, 1, 16, 16, 0x0fff, | |
340 | 10000, 500, &range_a821pgh_ai, &range_unipolar5, | |
341 | 0x000c, 0x00, PCLx1x_IORANGE, 0}, | |
342 | {"a822pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, | |
343 | 10000, 500, &range_acl8112dg_ai, &range_unipolar5, | |
344 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
345 | {"a822pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, | |
346 | 10000, 500, &range_acl8112hg_ai, &range_unipolar5, | |
347 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
348 | {"a823pgl", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, | |
349 | 8000, 500, &range_acl8112dg_ai, &range_unipolar5, | |
350 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
351 | {"a823pgh", boardACL8112, 16, 8, 2, 16, 16, 0x0fff, | |
352 | 8000, 500, &range_acl8112hg_ai, &range_unipolar5, | |
353 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
354 | {"pcl813", boardPCL813, 32, 0, 0, 0, 0, 0x0fff, | |
355 | 0, 0, &range_pcl813b_ai, NULL, | |
356 | 0x0000, 0x00, PCLx1x_IORANGE, 0}, | |
357 | {"pcl813b", boardPCL813B, 32, 0, 0, 0, 0, 0x0fff, | |
358 | 0, 0, &range_pcl813b_ai, NULL, | |
359 | 0x0000, 0x00, PCLx1x_IORANGE, 0}, | |
360 | {"acl8113", boardACL8113, 32, 0, 0, 0, 0, 0x0fff, | |
361 | 0, 0, &range_acl8113_1_ai, NULL, | |
362 | 0x0000, 0x00, PCLx1x_IORANGE, 0}, | |
363 | {"iso813", boardISO813, 32, 0, 0, 0, 0, 0x0fff, | |
364 | 0, 0, &range_iso813_1_ai, NULL, | |
365 | 0x0000, 0x00, PCLx1x_IORANGE, 0}, | |
366 | {"acl8216", boardACL8216, 16, 8, 2, 16, 16, 0xffff, | |
367 | 10000, 500, &range_pcl813b2_ai, &range_unipolar5, | |
368 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 1}, | |
369 | {"a826pg", boardACL8216, 16, 8, 2, 16, 16, 0xffff, | |
370 | 10000, 500, &range_pcl813b2_ai, &range_unipolar5, | |
371 | 0xdcfc, 0x0a, PCLx1x_IORANGE, 0}, | |
372 | }; | |
373 | ||
374 | #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) | |
375 | #define this_board ((const boardtype *)dev->board_ptr) | |
376 | ||
139dfbdf | 377 | static struct comedi_driver driver_pcl812 = { |
fcdb427b MD |
378 | driver_name:"pcl812", |
379 | module:THIS_MODULE, | |
380 | attach:pcl812_attach, | |
381 | detach:pcl812_detach, | |
382 | board_name:&boardtypes[0].name, | |
383 | num_names:n_boardtypes, | |
384 | offset:sizeof(boardtype), | |
385 | }; | |
386 | ||
387 | COMEDI_INITCLEANUP(driver_pcl812); | |
388 | ||
389 | typedef struct { | |
390 | unsigned char valid; // =1 device is OK | |
391 | unsigned char dma; // >0 use dma ( usedDMA channel) | |
392 | unsigned char use_diff; // =1 diff inputs | |
393 | unsigned char use_MPC; // 1=board uses MPC508A multiplexor | |
394 | unsigned char use_ext_trg; // 1=board uses external trigger | |
395 | unsigned char range_correction; // =1 we must add 1 to range number | |
396 | unsigned char old_chan_reg; // lastly used chan/gain pair | |
397 | unsigned char old_gain_reg; | |
398 | unsigned char mode_reg_int; // there is stored INT number for some card | |
399 | unsigned char ai_neverending; // =1 we do unlimited AI | |
400 | unsigned char ai_eos; // 1=EOS wake up | |
401 | unsigned char ai_dma; // =1 we use DMA | |
402 | unsigned int ai_poll_ptr; // how many sampes transfer poll | |
403 | unsigned int ai_scans; // len of scanlist | |
404 | unsigned int ai_act_scan; // how many scans we finished | |
405 | unsigned int ai_chanlist[MAX_CHANLIST_LEN]; // our copy of channel/range list | |
406 | unsigned int ai_n_chan; // how many channels is measured | |
407 | unsigned int ai_flags; // flaglist | |
408 | unsigned int ai_data_len; // len of data buffer | |
790c5541 | 409 | short *ai_data; // data buffer |
fcdb427b MD |
410 | unsigned int ai_is16b; // =1 we have 16 bit card |
411 | unsigned long dmabuf[2]; // PTR to DMA buf | |
412 | unsigned int dmapages[2]; // how many pages we have allocated | |
413 | unsigned int hwdmaptr[2]; // HW PTR to DMA buf | |
414 | unsigned int hwdmasize[2]; // DMA buf size in bytes | |
415 | unsigned int dmabytestomove[2]; // how many bytes DMA transfer | |
416 | int next_dma_buf; // which buffer is next to use | |
417 | unsigned int dma_runs_to_end; // how many times we must switch DMA buffers | |
418 | unsigned int last_dma_run; // how many bytes to transfer on last DMA buffer | |
419 | unsigned int max_812_ai_mode0_rangewait; // setling time for gain | |
790c5541 | 420 | unsigned int ao_readback[2]; // data for AO readback |
fcdb427b MD |
421 | } pcl812_private; |
422 | ||
423 | #define devpriv ((pcl812_private *)dev->private) | |
424 | ||
425 | /* | |
426 | ============================================================================== | |
427 | */ | |
71b5f4f1 | 428 | static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1, |
fcdb427b | 429 | unsigned int divisor2); |
34c43922 | 430 | static void setup_range_channel(struct comedi_device * dev, struct comedi_subdevice * s, |
fcdb427b | 431 | unsigned int rangechan, char wait); |
34c43922 | 432 | static int pcl812_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s); |
fcdb427b MD |
433 | /* |
434 | ============================================================================== | |
435 | */ | |
34c43922 | 436 | static int pcl812_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 437 | comedi_insn * insn, unsigned int * data) |
fcdb427b MD |
438 | { |
439 | int n; | |
440 | int timeout, hi; | |
441 | ||
442 | outb(devpriv->mode_reg_int | 1, dev->iobase + PCL812_MODE); /* select software trigger */ | |
443 | setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge | |
444 | for (n = 0; n < insn->n; n++) { | |
445 | outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */ | |
446 | comedi_udelay(5); | |
447 | timeout = 50; /* wait max 50us, it must finish under 33us */ | |
448 | while (timeout--) { | |
449 | hi = inb(dev->iobase + PCL812_AD_HI); | |
450 | if (!(hi & PCL812_DRDY)) | |
451 | goto conv_finish; | |
452 | comedi_udelay(1); | |
453 | } | |
454 | rt_printk | |
455 | ("comedi%d: pcl812: (%s at 0x%lx) A/D insn read timeout\n", | |
456 | dev->minor, dev->board_name, dev->iobase); | |
457 | outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE); | |
458 | return -ETIME; | |
459 | ||
460 | conv_finish: | |
461 | data[n] = ((hi & 0xf) << 8) | inb(dev->iobase + PCL812_AD_LO); | |
462 | } | |
463 | outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE); | |
464 | return n; | |
465 | } | |
466 | ||
467 | /* | |
468 | ============================================================================== | |
469 | */ | |
34c43922 | 470 | static int acl8216_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 471 | comedi_insn * insn, unsigned int * data) |
fcdb427b MD |
472 | { |
473 | int n; | |
474 | int timeout; | |
475 | ||
476 | outb(1, dev->iobase + PCL812_MODE); /* select software trigger */ | |
477 | setup_range_channel(dev, s, insn->chanspec, 1); // select channel and renge | |
478 | for (n = 0; n < insn->n; n++) { | |
479 | outb(255, dev->iobase + PCL812_SOFTTRIG); /* start conversion */ | |
480 | comedi_udelay(5); | |
481 | timeout = 50; /* wait max 50us, it must finish under 33us */ | |
482 | while (timeout--) { | |
483 | if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY)) | |
484 | goto conv_finish; | |
485 | comedi_udelay(1); | |
486 | } | |
487 | rt_printk | |
488 | ("comedi%d: pcl812: (%s at 0x%lx) A/D insn read timeout\n", | |
489 | dev->minor, dev->board_name, dev->iobase); | |
490 | outb(0, dev->iobase + PCL812_MODE); | |
491 | return -ETIME; | |
492 | ||
493 | conv_finish: | |
494 | data[n] = | |
495 | (inb(dev->iobase + | |
496 | PCL812_AD_HI) << 8) | inb(dev->iobase + | |
497 | PCL812_AD_LO); | |
498 | } | |
499 | outb(0, dev->iobase + PCL812_MODE); | |
500 | return n; | |
501 | } | |
502 | ||
503 | /* | |
504 | ============================================================================== | |
505 | */ | |
34c43922 | 506 | static int pcl812_ao_insn_write(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 507 | comedi_insn * insn, unsigned int * data) |
fcdb427b MD |
508 | { |
509 | int chan = CR_CHAN(insn->chanspec); | |
510 | int i; | |
511 | ||
512 | for (i = 0; i < insn->n; i++) { | |
513 | outb((data[i] & 0xff), | |
514 | dev->iobase + (chan ? PCL812_DA2_LO : PCL812_DA1_LO)); | |
515 | outb((data[i] >> 8) & 0x0f, | |
516 | dev->iobase + (chan ? PCL812_DA2_HI : PCL812_DA1_HI)); | |
517 | devpriv->ao_readback[chan] = data[i]; | |
518 | } | |
519 | ||
520 | return i; | |
521 | } | |
522 | ||
523 | /* | |
524 | ============================================================================== | |
525 | */ | |
34c43922 | 526 | static int pcl812_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 527 | comedi_insn * insn, unsigned int * data) |
fcdb427b MD |
528 | { |
529 | int chan = CR_CHAN(insn->chanspec); | |
530 | int i; | |
531 | ||
532 | for (i = 0; i < insn->n; i++) { | |
533 | data[i] = devpriv->ao_readback[chan]; | |
534 | } | |
535 | ||
536 | return i; | |
537 | } | |
538 | ||
539 | /* | |
540 | ============================================================================== | |
541 | */ | |
34c43922 | 542 | static int pcl812_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 543 | comedi_insn * insn, unsigned int * data) |
fcdb427b MD |
544 | { |
545 | if (insn->n != 2) | |
546 | return -EINVAL; | |
547 | ||
548 | data[1] = inb(dev->iobase + PCL812_DI_LO); | |
549 | data[1] |= inb(dev->iobase + PCL812_DI_HI) << 8; | |
550 | ||
551 | return 2; | |
552 | } | |
553 | ||
554 | /* | |
555 | ============================================================================== | |
556 | */ | |
34c43922 | 557 | static int pcl812_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 558 | comedi_insn * insn, unsigned int * data) |
fcdb427b MD |
559 | { |
560 | if (insn->n != 2) | |
561 | return -EINVAL; | |
562 | ||
563 | if (data[0]) { | |
564 | s->state &= ~data[0]; | |
565 | s->state |= data[0] & data[1]; | |
566 | outb(s->state & 0xff, dev->iobase + PCL812_DO_LO); | |
567 | outb((s->state >> 8), dev->iobase + PCL812_DO_HI); | |
568 | } | |
569 | data[1] = s->state; | |
570 | ||
571 | return 2; | |
572 | } | |
573 | ||
574 | #ifdef PCL812_EXTDEBUG | |
575 | /* | |
576 | ============================================================================== | |
577 | */ | |
578 | static void pcl812_cmdtest_out(int e, comedi_cmd * cmd) | |
579 | { | |
580 | rt_printk("pcl812 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e, | |
581 | cmd->start_src, cmd->scan_begin_src, cmd->convert_src); | |
582 | rt_printk("pcl812 e=%d startarg=%d scanarg=%d convarg=%d\n", e, | |
583 | cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg); | |
584 | rt_printk("pcl812 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src, | |
585 | cmd->scan_end_src); | |
586 | rt_printk("pcl812 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e, | |
587 | cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len); | |
588 | } | |
589 | #endif | |
590 | ||
591 | /* | |
592 | ============================================================================== | |
593 | */ | |
34c43922 | 594 | static int pcl812_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, |
fcdb427b MD |
595 | comedi_cmd * cmd) |
596 | { | |
597 | int err = 0; | |
598 | int tmp, divisor1, divisor2; | |
599 | ||
600 | #ifdef PCL812_EXTDEBUG | |
601 | rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...)\n"); | |
602 | pcl812_cmdtest_out(-1, cmd); | |
603 | #endif | |
604 | /* step 1: make sure trigger sources are trivially valid */ | |
605 | ||
606 | tmp = cmd->start_src; | |
607 | cmd->start_src &= TRIG_NOW; | |
608 | if (!cmd->start_src || tmp != cmd->start_src) | |
609 | err++; | |
610 | ||
611 | tmp = cmd->scan_begin_src; | |
612 | cmd->scan_begin_src &= TRIG_FOLLOW; | |
613 | if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) | |
614 | err++; | |
615 | ||
616 | tmp = cmd->convert_src; | |
617 | if (devpriv->use_ext_trg) { | |
618 | cmd->convert_src &= TRIG_EXT; | |
619 | } else { | |
620 | cmd->convert_src &= TRIG_TIMER; | |
621 | } | |
622 | if (!cmd->convert_src || tmp != cmd->convert_src) | |
623 | err++; | |
624 | ||
625 | tmp = cmd->scan_end_src; | |
626 | cmd->scan_end_src &= TRIG_COUNT; | |
627 | if (!cmd->scan_end_src || tmp != cmd->scan_end_src) | |
628 | err++; | |
629 | ||
630 | tmp = cmd->stop_src; | |
631 | cmd->stop_src &= TRIG_COUNT | TRIG_NONE; | |
632 | if (!cmd->stop_src || tmp != cmd->stop_src) | |
633 | err++; | |
634 | ||
635 | if (err) { | |
636 | #ifdef PCL812_EXTDEBUG | |
637 | pcl812_cmdtest_out(1, cmd); | |
638 | rt_printk | |
639 | ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=1\n", | |
640 | err); | |
641 | #endif | |
642 | return 1; | |
643 | } | |
644 | ||
645 | /* step 2: make sure trigger sources are unique and mutually compatible */ | |
646 | ||
647 | if (cmd->start_src != TRIG_NOW) { | |
648 | cmd->start_src = TRIG_NOW; | |
649 | err++; | |
650 | } | |
651 | ||
652 | if (cmd->scan_begin_src != TRIG_FOLLOW) { | |
653 | cmd->scan_begin_src = TRIG_FOLLOW; | |
654 | err++; | |
655 | } | |
656 | ||
657 | if (devpriv->use_ext_trg) { | |
658 | if (cmd->convert_src != TRIG_EXT) { | |
659 | cmd->convert_src = TRIG_EXT; | |
660 | err++; | |
661 | } | |
662 | } else { | |
663 | if (cmd->convert_src != TRIG_TIMER) { | |
664 | cmd->convert_src = TRIG_TIMER; | |
665 | err++; | |
666 | } | |
667 | } | |
668 | ||
669 | if (cmd->scan_end_src != TRIG_COUNT) { | |
670 | cmd->scan_end_src = TRIG_COUNT; | |
671 | err++; | |
672 | } | |
673 | ||
674 | if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT) | |
675 | err++; | |
676 | ||
677 | if (err) { | |
678 | #ifdef PCL812_EXTDEBUG | |
679 | pcl812_cmdtest_out(2, cmd); | |
680 | rt_printk | |
681 | ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=2\n", | |
682 | err); | |
683 | #endif | |
684 | return 2; | |
685 | } | |
686 | ||
687 | /* step 3: make sure arguments are trivially compatible */ | |
688 | ||
689 | if (cmd->start_arg != 0) { | |
690 | cmd->start_arg = 0; | |
691 | err++; | |
692 | } | |
693 | ||
694 | if (cmd->scan_begin_arg != 0) { | |
695 | cmd->scan_begin_arg = 0; | |
696 | err++; | |
697 | } | |
698 | ||
699 | if (cmd->convert_src == TRIG_TIMER) { | |
700 | if (cmd->convert_arg < this_board->ai_ns_min) { | |
701 | cmd->convert_arg = this_board->ai_ns_min; | |
702 | err++; | |
703 | } | |
704 | } else { /* TRIG_EXT */ | |
705 | if (cmd->convert_arg != 0) { | |
706 | cmd->convert_arg = 0; | |
707 | err++; | |
708 | } | |
709 | } | |
710 | ||
711 | if (!cmd->chanlist_len) { | |
712 | cmd->chanlist_len = 1; | |
713 | err++; | |
714 | } | |
715 | if (cmd->chanlist_len > MAX_CHANLIST_LEN) { | |
716 | cmd->chanlist_len = this_board->n_aichan; | |
717 | err++; | |
718 | } | |
719 | if (cmd->scan_end_arg != cmd->chanlist_len) { | |
720 | cmd->scan_end_arg = cmd->chanlist_len; | |
721 | err++; | |
722 | } | |
723 | if (cmd->stop_src == TRIG_COUNT) { | |
724 | if (!cmd->stop_arg) { | |
725 | cmd->stop_arg = 1; | |
726 | err++; | |
727 | } | |
728 | } else { /* TRIG_NONE */ | |
729 | if (cmd->stop_arg != 0) { | |
730 | cmd->stop_arg = 0; | |
731 | err++; | |
732 | } | |
733 | } | |
734 | ||
735 | if (err) { | |
736 | #ifdef PCL812_EXTDEBUG | |
737 | pcl812_cmdtest_out(3, cmd); | |
738 | rt_printk | |
739 | ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=3\n", | |
740 | err); | |
741 | #endif | |
742 | return 3; | |
743 | } | |
744 | ||
745 | /* step 4: fix up any arguments */ | |
746 | ||
747 | if (cmd->convert_src == TRIG_TIMER) { | |
748 | tmp = cmd->convert_arg; | |
749 | i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1, | |
750 | &divisor2, &cmd->convert_arg, | |
751 | cmd->flags & TRIG_ROUND_MASK); | |
752 | if (cmd->convert_arg < this_board->ai_ns_min) | |
753 | cmd->convert_arg = this_board->ai_ns_min; | |
754 | if (tmp != cmd->convert_arg) | |
755 | err++; | |
756 | } | |
757 | ||
758 | if (err) { | |
759 | #ifdef PCL812_EXTDEBUG | |
760 | rt_printk | |
761 | ("pcl812 EDBG: BGN: pcl812_ai_cmdtest(...) err=%d ret=4\n", | |
762 | err); | |
763 | #endif | |
764 | return 4; | |
765 | } | |
766 | ||
767 | return 0; | |
768 | } | |
769 | ||
770 | /* | |
771 | ============================================================================== | |
772 | */ | |
34c43922 | 773 | static int pcl812_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s) |
fcdb427b MD |
774 | { |
775 | unsigned int divisor1 = 0, divisor2 = 0, i, dma_flags, bytes; | |
776 | comedi_cmd *cmd = &s->async->cmd; | |
777 | ||
778 | #ifdef PCL812_EXTDEBUG | |
779 | rt_printk("pcl812 EDBG: BGN: pcl812_ai_cmd(...)\n"); | |
780 | #endif | |
781 | ||
782 | if (cmd->start_src != TRIG_NOW) | |
783 | return -EINVAL; | |
784 | if (cmd->scan_begin_src != TRIG_FOLLOW) | |
785 | return -EINVAL; | |
786 | if (devpriv->use_ext_trg) { | |
787 | if (cmd->convert_src != TRIG_EXT) | |
788 | return -EINVAL; | |
789 | } else { | |
790 | if (cmd->convert_src != TRIG_TIMER) | |
791 | return -EINVAL; | |
792 | } | |
793 | if (cmd->scan_end_src != TRIG_COUNT) | |
794 | return -EINVAL; | |
795 | if (cmd->scan_end_arg != cmd->chanlist_len) | |
796 | return -EINVAL; | |
797 | if (cmd->chanlist_len > MAX_CHANLIST_LEN) | |
798 | return -EINVAL; | |
799 | ||
800 | if (cmd->convert_src == TRIG_TIMER) { | |
801 | if (cmd->convert_arg < this_board->ai_ns_min) | |
802 | cmd->convert_arg = this_board->ai_ns_min; | |
803 | i8253_cascade_ns_to_timer(this_board->i8254_osc_base, | |
804 | &divisor1, &divisor2, &cmd->convert_arg, | |
805 | cmd->flags & TRIG_ROUND_MASK); | |
806 | } | |
807 | ||
808 | start_pacer(dev, -1, 0, 0); // stop pacer | |
809 | ||
810 | devpriv->ai_n_chan = cmd->chanlist_len; | |
811 | memcpy(devpriv->ai_chanlist, cmd->chanlist, | |
812 | sizeof(unsigned int) * cmd->scan_end_arg); | |
813 | setup_range_channel(dev, s, devpriv->ai_chanlist[0], 1); // select first channel and range | |
814 | ||
815 | if (devpriv->dma) { // check if we can use DMA transfer | |
816 | devpriv->ai_dma = 1; | |
817 | for (i = 1; i < devpriv->ai_n_chan; i++) | |
818 | if (devpriv->ai_chanlist[0] != devpriv->ai_chanlist[i]) { | |
819 | devpriv->ai_dma = 0; // we cann't use DMA :-( | |
820 | break; | |
821 | } | |
822 | } else | |
823 | devpriv->ai_dma = 0; | |
824 | ||
825 | devpriv->ai_flags = cmd->flags; | |
826 | devpriv->ai_data_len = s->async->prealloc_bufsz; | |
827 | devpriv->ai_data = s->async->prealloc_buf; | |
828 | if (cmd->stop_src == TRIG_COUNT) { | |
829 | devpriv->ai_scans = cmd->stop_arg; | |
830 | devpriv->ai_neverending = 0; | |
831 | } else { | |
832 | devpriv->ai_scans = 0; | |
833 | devpriv->ai_neverending = 1; | |
834 | } | |
835 | ||
836 | devpriv->ai_act_scan = 0; | |
837 | devpriv->ai_poll_ptr = 0; | |
838 | s->async->cur_chan = 0; | |
839 | ||
840 | if ((devpriv->ai_flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan? | |
841 | devpriv->ai_eos = 1; | |
842 | if (devpriv->ai_n_chan == 1) | |
843 | devpriv->ai_dma = 0; // DMA is useless for this situation | |
844 | } | |
845 | ||
846 | if (devpriv->ai_dma) { | |
847 | if (devpriv->ai_eos) { // we use EOS, so adapt DMA buffer to one scan | |
848 | devpriv->dmabytestomove[0] = | |
790c5541 | 849 | devpriv->ai_n_chan * sizeof(short); |
fcdb427b | 850 | devpriv->dmabytestomove[1] = |
790c5541 | 851 | devpriv->ai_n_chan * sizeof(short); |
fcdb427b MD |
852 | devpriv->dma_runs_to_end = 1; |
853 | } else { | |
854 | devpriv->dmabytestomove[0] = devpriv->hwdmasize[0]; | |
855 | devpriv->dmabytestomove[1] = devpriv->hwdmasize[1]; | |
856 | if (devpriv->ai_data_len < devpriv->hwdmasize[0]) | |
857 | devpriv->dmabytestomove[0] = | |
858 | devpriv->ai_data_len; | |
859 | if (devpriv->ai_data_len < devpriv->hwdmasize[1]) | |
860 | devpriv->dmabytestomove[1] = | |
861 | devpriv->ai_data_len; | |
862 | if (devpriv->ai_neverending) { | |
863 | devpriv->dma_runs_to_end = 1; | |
864 | } else { | |
790c5541 | 865 | bytes = devpriv->ai_n_chan * devpriv->ai_scans * sizeof(short); // how many samples we must transfer? |
fcdb427b MD |
866 | devpriv->dma_runs_to_end = bytes / devpriv->dmabytestomove[0]; // how many DMA pages we must fill |
867 | devpriv->last_dma_run = bytes % devpriv->dmabytestomove[0]; //on last dma transfer must be moved | |
868 | if (devpriv->dma_runs_to_end == 0) | |
869 | devpriv->dmabytestomove[0] = | |
870 | devpriv->last_dma_run; | |
871 | devpriv->dma_runs_to_end--; | |
872 | } | |
873 | } | |
874 | if (devpriv->dmabytestomove[0] > devpriv->hwdmasize[0]) { | |
875 | devpriv->dmabytestomove[0] = devpriv->hwdmasize[0]; | |
876 | devpriv->ai_eos = 0; | |
877 | } | |
878 | if (devpriv->dmabytestomove[1] > devpriv->hwdmasize[1]) { | |
879 | devpriv->dmabytestomove[1] = devpriv->hwdmasize[1]; | |
880 | devpriv->ai_eos = 0; | |
881 | } | |
882 | devpriv->next_dma_buf = 0; | |
883 | set_dma_mode(devpriv->dma, DMA_MODE_READ); | |
884 | dma_flags = claim_dma_lock(); | |
885 | clear_dma_ff(devpriv->dma); | |
886 | set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]); | |
887 | set_dma_count(devpriv->dma, devpriv->dmabytestomove[0]); | |
888 | release_dma_lock(dma_flags); | |
889 | enable_dma(devpriv->dma); | |
890 | #ifdef PCL812_EXTDEBUG | |
891 | rt_printk | |
892 | ("pcl812 EDBG: DMA %d PTR 0x%0x/0x%0x LEN %u/%u EOS %d\n", | |
893 | devpriv->dma, devpriv->hwdmaptr[0], | |
894 | devpriv->hwdmaptr[1], devpriv->dmabytestomove[0], | |
895 | devpriv->dmabytestomove[1], devpriv->ai_eos); | |
896 | #endif | |
897 | } | |
898 | ||
899 | switch (cmd->convert_src) { | |
900 | case TRIG_TIMER: | |
901 | start_pacer(dev, 1, divisor1, divisor2); | |
902 | break; | |
903 | } | |
904 | ||
905 | if (devpriv->ai_dma) { | |
906 | outb(devpriv->mode_reg_int | 2, dev->iobase + PCL812_MODE); // let's go! | |
907 | } else { | |
908 | outb(devpriv->mode_reg_int | 6, dev->iobase + PCL812_MODE); // let's go! | |
909 | } | |
910 | ||
911 | #ifdef PCL812_EXTDEBUG | |
912 | rt_printk("pcl812 EDBG: END: pcl812_ai_cmd(...)\n"); | |
913 | #endif | |
914 | ||
915 | return 0; | |
916 | } | |
917 | ||
918 | /* | |
919 | ============================================================================== | |
920 | */ | |
921 | static irqreturn_t interrupt_pcl812_ai_int(int irq, void *d) | |
922 | { | |
923 | char err = 1; | |
924 | unsigned int mask, timeout; | |
71b5f4f1 | 925 | struct comedi_device *dev = d; |
34c43922 | 926 | struct comedi_subdevice *s = dev->subdevices + 0; |
fcdb427b MD |
927 | |
928 | s->async->events = 0; | |
929 | ||
930 | timeout = 50; /* wait max 50us, it must finish under 33us */ | |
931 | if (devpriv->ai_is16b) { | |
932 | mask = 0xffff; | |
933 | while (timeout--) { | |
934 | if (!(inb(dev->iobase + ACL8216_STATUS) & ACL8216_DRDY)) { | |
935 | err = 0; | |
936 | break; | |
937 | } | |
938 | comedi_udelay(1); | |
939 | } | |
940 | } else { | |
941 | mask = 0x0fff; | |
942 | while (timeout--) { | |
943 | if (!(inb(dev->iobase + PCL812_AD_HI) & PCL812_DRDY)) { | |
944 | err = 0; | |
945 | break; | |
946 | } | |
947 | comedi_udelay(1); | |
948 | } | |
949 | } | |
950 | ||
951 | if (err) { | |
952 | rt_printk | |
953 | ("comedi%d: pcl812: (%s at 0x%lx) A/D cmd IRQ without DRDY!\n", | |
954 | dev->minor, dev->board_name, dev->iobase); | |
955 | pcl812_ai_cancel(dev, s); | |
956 | s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; | |
957 | comedi_event(dev, s); | |
958 | return IRQ_HANDLED; | |
959 | } | |
960 | ||
961 | comedi_buf_put(s->async, | |
962 | ((inb(dev->iobase + PCL812_AD_HI) << 8) | inb(dev->iobase + | |
963 | PCL812_AD_LO)) & mask); | |
964 | ||
965 | outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ | |
966 | ||
967 | if (s->async->cur_chan == 0) { /* one scan done */ | |
968 | devpriv->ai_act_scan++; | |
969 | if (!(devpriv->ai_neverending)) | |
970 | if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ | |
971 | pcl812_ai_cancel(dev, s); | |
972 | s->async->events |= COMEDI_CB_EOA; | |
973 | } | |
974 | } | |
975 | ||
976 | comedi_event(dev, s); | |
977 | return IRQ_HANDLED; | |
978 | } | |
979 | ||
980 | /* | |
981 | ============================================================================== | |
982 | */ | |
34c43922 | 983 | static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 984 | short * ptr, unsigned int bufptr, unsigned int len) |
fcdb427b MD |
985 | { |
986 | unsigned int i; | |
987 | ||
988 | s->async->events = 0; | |
989 | for (i = len; i; i--) { | |
990 | comedi_buf_put(s->async, ptr[bufptr++]); // get one sample | |
991 | ||
992 | if (s->async->cur_chan == 0) { | |
993 | devpriv->ai_act_scan++; | |
994 | if (!devpriv->ai_neverending) | |
995 | if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ | |
996 | pcl812_ai_cancel(dev, s); | |
997 | s->async->events |= COMEDI_CB_EOA; | |
998 | break; | |
999 | } | |
1000 | } | |
1001 | } | |
1002 | ||
1003 | comedi_event(dev, s); | |
1004 | } | |
1005 | ||
1006 | /* | |
1007 | ============================================================================== | |
1008 | */ | |
1009 | static irqreturn_t interrupt_pcl812_ai_dma(int irq, void *d) | |
1010 | { | |
71b5f4f1 | 1011 | struct comedi_device *dev = d; |
34c43922 | 1012 | struct comedi_subdevice *s = dev->subdevices + 0; |
fcdb427b MD |
1013 | unsigned long dma_flags; |
1014 | int len, bufptr; | |
790c5541 | 1015 | short *ptr; |
fcdb427b MD |
1016 | |
1017 | #ifdef PCL812_EXTDEBUG | |
1018 | rt_printk("pcl812 EDBG: BGN: interrupt_pcl812_ai_dma(...)\n"); | |
1019 | #endif | |
790c5541 | 1020 | ptr = (short *) devpriv->dmabuf[devpriv->next_dma_buf]; |
fcdb427b MD |
1021 | len = (devpriv->dmabytestomove[devpriv->next_dma_buf] >> 1) - |
1022 | devpriv->ai_poll_ptr; | |
1023 | ||
1024 | devpriv->next_dma_buf = 1 - devpriv->next_dma_buf; | |
1025 | disable_dma(devpriv->dma); | |
1026 | set_dma_mode(devpriv->dma, DMA_MODE_READ); | |
1027 | dma_flags = claim_dma_lock(); | |
1028 | set_dma_addr(devpriv->dma, devpriv->hwdmaptr[devpriv->next_dma_buf]); | |
1029 | if (devpriv->ai_eos) { | |
1030 | set_dma_count(devpriv->dma, | |
1031 | devpriv->dmabytestomove[devpriv->next_dma_buf]); | |
1032 | } else { | |
1033 | if (devpriv->dma_runs_to_end) { | |
1034 | set_dma_count(devpriv->dma, | |
1035 | devpriv->dmabytestomove[devpriv->next_dma_buf]); | |
1036 | } else { | |
1037 | set_dma_count(devpriv->dma, devpriv->last_dma_run); | |
1038 | } | |
1039 | devpriv->dma_runs_to_end--; | |
1040 | } | |
1041 | release_dma_lock(dma_flags); | |
1042 | enable_dma(devpriv->dma); | |
1043 | ||
1044 | outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ | |
1045 | ||
1046 | bufptr = devpriv->ai_poll_ptr; | |
1047 | devpriv->ai_poll_ptr = 0; | |
1048 | ||
1049 | transfer_from_dma_buf(dev, s, ptr, bufptr, len); | |
1050 | ||
1051 | #ifdef PCL812_EXTDEBUG | |
1052 | rt_printk("pcl812 EDBG: END: interrupt_pcl812_ai_dma(...)\n"); | |
1053 | #endif | |
1054 | return IRQ_HANDLED; | |
1055 | } | |
1056 | ||
1057 | /* | |
1058 | ============================================================================== | |
1059 | */ | |
1060 | static irqreturn_t interrupt_pcl812(int irq, void *d PT_REGS_ARG) | |
1061 | { | |
71b5f4f1 | 1062 | struct comedi_device *dev = d; |
fcdb427b MD |
1063 | |
1064 | if (!dev->attached) { | |
1065 | comedi_error(dev, "spurious interrupt"); | |
1066 | return IRQ_HANDLED; | |
1067 | } | |
1068 | if (devpriv->ai_dma) { | |
1069 | return interrupt_pcl812_ai_dma(irq, d); | |
1070 | } else { | |
1071 | return interrupt_pcl812_ai_int(irq, d); | |
1072 | }; | |
1073 | } | |
1074 | ||
1075 | /* | |
1076 | ============================================================================== | |
1077 | */ | |
34c43922 | 1078 | static int pcl812_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s) |
fcdb427b MD |
1079 | { |
1080 | unsigned long flags; | |
1081 | unsigned int top1, top2, i; | |
1082 | ||
1083 | if (!devpriv->ai_dma) | |
1084 | return 0; // poll is valid only for DMA transfer | |
1085 | ||
1086 | comedi_spin_lock_irqsave(&dev->spinlock, flags); | |
1087 | ||
1088 | for (i = 0; i < 10; i++) { | |
1089 | top1 = get_dma_residue(devpriv->ai_dma); // where is now DMA | |
1090 | top2 = get_dma_residue(devpriv->ai_dma); | |
1091 | if (top1 == top2) | |
1092 | break; | |
1093 | } | |
1094 | ||
1095 | if (top1 != top2) { | |
1096 | comedi_spin_unlock_irqrestore(&dev->spinlock, flags); | |
1097 | return 0; | |
1098 | } | |
1099 | ||
1100 | top1 = devpriv->dmabytestomove[1 - devpriv->next_dma_buf] - top1; // where is now DMA in buffer | |
1101 | top1 >>= 1; // sample position | |
1102 | top2 = top1 - devpriv->ai_poll_ptr; | |
1103 | if (top2 < 1) { // no new samples | |
1104 | comedi_spin_unlock_irqrestore(&dev->spinlock, flags); | |
1105 | return 0; | |
1106 | } | |
1107 | ||
1108 | transfer_from_dma_buf(dev, s, | |
1109 | (void *)devpriv->dmabuf[1 - devpriv->next_dma_buf], | |
1110 | devpriv->ai_poll_ptr, top2); | |
1111 | ||
1112 | devpriv->ai_poll_ptr = top1; // new buffer position | |
1113 | ||
1114 | comedi_spin_unlock_irqrestore(&dev->spinlock, flags); | |
1115 | ||
1116 | return s->async->buf_write_count - s->async->buf_read_count; | |
1117 | } | |
1118 | ||
1119 | /* | |
1120 | ============================================================================== | |
1121 | */ | |
34c43922 | 1122 | static void setup_range_channel(struct comedi_device * dev, struct comedi_subdevice * s, |
fcdb427b MD |
1123 | unsigned int rangechan, char wait) |
1124 | { | |
1125 | unsigned char chan_reg = CR_CHAN(rangechan); // normal board | |
1126 | unsigned char gain_reg = CR_RANGE(rangechan) + devpriv->range_correction; // gain index | |
1127 | ||
1128 | if ((chan_reg == devpriv->old_chan_reg) | |
1129 | && (gain_reg == devpriv->old_gain_reg)) | |
1130 | return; // we can return, no change | |
1131 | ||
1132 | devpriv->old_chan_reg = chan_reg; | |
1133 | devpriv->old_gain_reg = gain_reg; | |
1134 | ||
1135 | if (devpriv->use_MPC) { | |
1136 | if (devpriv->use_diff) { | |
1137 | chan_reg = chan_reg | 0x30; // DIFF inputs | |
1138 | } else { | |
1139 | if (chan_reg & 0x80) { | |
1140 | chan_reg = chan_reg | 0x20; // SE inputs 8-15 | |
1141 | } else { | |
1142 | chan_reg = chan_reg | 0x10; // SE inputs 0-7 | |
1143 | } | |
1144 | } | |
1145 | } | |
1146 | ||
1147 | outb(chan_reg, dev->iobase + PCL812_MUX); /* select channel */ | |
1148 | outb(gain_reg, dev->iobase + PCL812_GAIN); /* select gain */ | |
1149 | ||
1150 | if (wait) { | |
1151 | comedi_udelay(devpriv->max_812_ai_mode0_rangewait); // XXX this depends on selected range and can be very long for some high gain ranges! | |
1152 | } | |
1153 | } | |
1154 | ||
1155 | /* | |
1156 | ============================================================================== | |
1157 | */ | |
71b5f4f1 | 1158 | static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1, |
fcdb427b MD |
1159 | unsigned int divisor2) |
1160 | { | |
1161 | #ifdef PCL812_EXTDEBUG | |
1162 | rt_printk("pcl812 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode, divisor1, | |
1163 | divisor2); | |
1164 | #endif | |
1165 | outb(0xb4, dev->iobase + PCL812_CTRCTL); | |
1166 | outb(0x74, dev->iobase + PCL812_CTRCTL); | |
1167 | comedi_udelay(1); | |
1168 | ||
1169 | if (mode == 1) { | |
1170 | outb(divisor2 & 0xff, dev->iobase + PCL812_CTR2); | |
1171 | outb((divisor2 >> 8) & 0xff, dev->iobase + PCL812_CTR2); | |
1172 | outb(divisor1 & 0xff, dev->iobase + PCL812_CTR1); | |
1173 | outb((divisor1 >> 8) & 0xff, dev->iobase + PCL812_CTR1); | |
1174 | } | |
1175 | #ifdef PCL812_EXTDEBUG | |
1176 | rt_printk("pcl812 EDBG: END: start_pacer(...)\n"); | |
1177 | #endif | |
1178 | } | |
1179 | ||
1180 | /* | |
1181 | ============================================================================== | |
1182 | */ | |
71b5f4f1 | 1183 | static void free_resources(struct comedi_device * dev) |
fcdb427b MD |
1184 | { |
1185 | ||
1186 | if (dev->private) { | |
1187 | if (devpriv->dmabuf[0]) | |
1188 | free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]); | |
1189 | if (devpriv->dmabuf[1]) | |
1190 | free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]); | |
1191 | if (devpriv->dma) | |
1192 | free_dma(devpriv->dma); | |
1193 | } | |
1194 | if (dev->irq) | |
1195 | comedi_free_irq(dev->irq, dev); | |
1196 | if (dev->iobase) | |
1197 | release_region(dev->iobase, this_board->io_range); | |
1198 | } | |
1199 | ||
1200 | /* | |
1201 | ============================================================================== | |
1202 | */ | |
34c43922 | 1203 | static int pcl812_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s) |
fcdb427b MD |
1204 | { |
1205 | #ifdef PCL812_EXTDEBUG | |
1206 | rt_printk("pcl812 EDBG: BGN: pcl812_ai_cancel(...)\n"); | |
1207 | #endif | |
1208 | if (devpriv->ai_dma) | |
1209 | disable_dma(devpriv->dma); | |
1210 | outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ | |
1211 | outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE); /* Stop A/D */ | |
1212 | start_pacer(dev, -1, 0, 0); // stop 8254 | |
1213 | outb(0, dev->iobase + PCL812_CLRINT); /* clear INT request */ | |
1214 | #ifdef PCL812_EXTDEBUG | |
1215 | rt_printk("pcl812 EDBG: END: pcl812_ai_cancel(...)\n"); | |
1216 | #endif | |
1217 | return 0; | |
1218 | } | |
1219 | ||
1220 | /* | |
1221 | ============================================================================== | |
1222 | */ | |
71b5f4f1 | 1223 | static void pcl812_reset(struct comedi_device * dev) |
fcdb427b MD |
1224 | { |
1225 | #ifdef PCL812_EXTDEBUG | |
1226 | rt_printk("pcl812 EDBG: BGN: pcl812_reset(...)\n"); | |
1227 | #endif | |
1228 | outb(0, dev->iobase + PCL812_MUX); | |
1229 | outb(0 + devpriv->range_correction, dev->iobase + PCL812_GAIN); | |
1230 | devpriv->old_chan_reg = -1; // invalidate chain/gain memory | |
1231 | devpriv->old_gain_reg = -1; | |
1232 | ||
1233 | switch (this_board->board_type) { | |
1234 | case boardPCL812PG: | |
1235 | case boardPCL812: | |
1236 | case boardACL8112: | |
1237 | case boardACL8216: | |
1238 | outb(0, dev->iobase + PCL812_DA2_LO); | |
1239 | outb(0, dev->iobase + PCL812_DA2_HI); | |
1240 | case boardA821: | |
1241 | outb(0, dev->iobase + PCL812_DA1_LO); | |
1242 | outb(0, dev->iobase + PCL812_DA1_HI); | |
1243 | start_pacer(dev, -1, 0, 0); // stop 8254 | |
1244 | outb(0, dev->iobase + PCL812_DO_HI); | |
1245 | outb(0, dev->iobase + PCL812_DO_LO); | |
1246 | outb(devpriv->mode_reg_int | 0, dev->iobase + PCL812_MODE); | |
1247 | outb(0, dev->iobase + PCL812_CLRINT); | |
1248 | break; | |
1249 | case boardPCL813B: | |
1250 | case boardPCL813: | |
1251 | case boardISO813: | |
1252 | case boardACL8113: | |
1253 | comedi_udelay(5); | |
1254 | break; | |
1255 | } | |
1256 | comedi_udelay(5); | |
1257 | #ifdef PCL812_EXTDEBUG | |
1258 | rt_printk("pcl812 EDBG: END: pcl812_reset(...)\n"); | |
1259 | #endif | |
1260 | } | |
1261 | ||
1262 | /* | |
1263 | ============================================================================== | |
1264 | */ | |
71b5f4f1 | 1265 | static int pcl812_attach(struct comedi_device * dev, comedi_devconfig * it) |
fcdb427b MD |
1266 | { |
1267 | int ret, subdev; | |
1268 | unsigned long iobase; | |
1269 | unsigned int irq; | |
1270 | unsigned int dma; | |
1271 | unsigned long pages; | |
34c43922 | 1272 | struct comedi_subdevice *s; |
fcdb427b MD |
1273 | int n_subdevices; |
1274 | ||
1275 | iobase = it->options[0]; | |
1276 | printk("comedi%d: pcl812: board=%s, ioport=0x%03lx", dev->minor, | |
1277 | this_board->name, iobase); | |
1278 | ||
1279 | if (!request_region(iobase, this_board->io_range, "pcl812")) { | |
1280 | printk("I/O port conflict\n"); | |
1281 | return -EIO; | |
1282 | } | |
1283 | dev->iobase = iobase; | |
1284 | ||
1285 | if ((ret = alloc_private(dev, sizeof(pcl812_private))) < 0) { | |
1286 | free_resources(dev); | |
1287 | return ret; /* Can't alloc mem */ | |
1288 | } | |
1289 | ||
1290 | dev->board_name = this_board->name; | |
1291 | ||
1292 | irq = 0; | |
1293 | if (this_board->IRQbits != 0) { /* board support IRQ */ | |
1294 | irq = it->options[1]; | |
1295 | if (irq) { /* we want to use IRQ */ | |
1296 | if (((1 << irq) & this_board->IRQbits) == 0) { | |
1297 | printk(", IRQ %u is out of allowed range, DISABLING IT", irq); | |
1298 | irq = 0; /* Bad IRQ */ | |
1299 | } else { | |
1300 | if (comedi_request_irq(irq, interrupt_pcl812, 0, | |
1301 | "pcl812", dev)) { | |
1302 | printk(", unable to allocate IRQ %u, DISABLING IT", irq); | |
1303 | irq = 0; /* Can't use IRQ */ | |
1304 | } else { | |
1305 | printk(", irq=%u", irq); | |
1306 | } | |
1307 | } | |
1308 | } | |
1309 | } | |
1310 | ||
1311 | dev->irq = irq; | |
1312 | ||
1313 | dma = 0; | |
1314 | devpriv->dma = dma; | |
1315 | if (!dev->irq) | |
1316 | goto no_dma; /* if we haven't IRQ, we can't use DMA */ | |
1317 | if (this_board->DMAbits != 0) { /* board support DMA */ | |
1318 | dma = it->options[2]; | |
1319 | if (((1 << dma) & this_board->DMAbits) == 0) { | |
1320 | printk(", DMA is out of allowed range, FAIL!\n"); | |
1321 | return -EINVAL; /* Bad DMA */ | |
1322 | } | |
1323 | ret = request_dma(dma, "pcl812"); | |
1324 | if (ret) { | |
1325 | printk(", unable to allocate DMA %u, FAIL!\n", dma); | |
1326 | return -EBUSY; /* DMA isn't free */ | |
1327 | } | |
1328 | devpriv->dma = dma; | |
1329 | printk(", dma=%u", dma); | |
1330 | pages = 1; /* we want 8KB */ | |
1331 | devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages); | |
1332 | if (!devpriv->dmabuf[0]) { | |
1333 | printk(", unable to allocate DMA buffer, FAIL!\n"); | |
1334 | /* maybe experiment with try_to_free_pages() will help .... */ | |
1335 | free_resources(dev); | |
1336 | return -EBUSY; /* no buffer :-( */ | |
1337 | } | |
1338 | devpriv->dmapages[0] = pages; | |
1339 | devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]); | |
1340 | devpriv->hwdmasize[0] = PAGE_SIZE * (1 << pages); | |
1341 | devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages); | |
1342 | if (!devpriv->dmabuf[1]) { | |
1343 | printk(", unable to allocate DMA buffer, FAIL!\n"); | |
1344 | free_resources(dev); | |
1345 | return -EBUSY; | |
1346 | } | |
1347 | devpriv->dmapages[1] = pages; | |
1348 | devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]); | |
1349 | devpriv->hwdmasize[1] = PAGE_SIZE * (1 << pages); | |
1350 | } | |
1351 | no_dma: | |
1352 | ||
1353 | n_subdevices = 0; | |
1354 | if (this_board->n_aichan > 0) | |
1355 | n_subdevices++; | |
1356 | if (this_board->n_aochan > 0) | |
1357 | n_subdevices++; | |
1358 | if (this_board->n_dichan > 0) | |
1359 | n_subdevices++; | |
1360 | if (this_board->n_dochan > 0) | |
1361 | n_subdevices++; | |
1362 | ||
1363 | if ((ret = alloc_subdevices(dev, n_subdevices)) < 0) { | |
1364 | free_resources(dev); | |
1365 | return ret; | |
1366 | } | |
1367 | ||
1368 | subdev = 0; | |
1369 | ||
1370 | /* analog input */ | |
1371 | if (this_board->n_aichan > 0) { | |
1372 | s = dev->subdevices + subdev; | |
1373 | s->type = COMEDI_SUBD_AI; | |
1374 | s->subdev_flags = SDF_READABLE; | |
1375 | switch (this_board->board_type) { | |
1376 | case boardA821: | |
1377 | if (it->options[2] == 1) { | |
1378 | s->n_chan = this_board->n_aichan_diff; | |
1379 | s->subdev_flags |= SDF_DIFF; | |
1380 | devpriv->use_diff = 1; | |
1381 | } else { | |
1382 | s->n_chan = this_board->n_aichan; | |
1383 | s->subdev_flags |= SDF_GROUND; | |
1384 | } | |
1385 | break; | |
1386 | case boardACL8112: | |
1387 | case boardACL8216: | |
1388 | if (it->options[4] == 1) { | |
1389 | s->n_chan = this_board->n_aichan_diff; | |
1390 | s->subdev_flags |= SDF_DIFF; | |
1391 | devpriv->use_diff = 1; | |
1392 | } else { | |
1393 | s->n_chan = this_board->n_aichan; | |
1394 | s->subdev_flags |= SDF_GROUND; | |
1395 | } | |
1396 | break; | |
1397 | default: | |
1398 | s->n_chan = this_board->n_aichan; | |
1399 | s->subdev_flags |= SDF_GROUND; | |
1400 | break; | |
1401 | } | |
1402 | s->maxdata = this_board->ai_maxdata; | |
1403 | s->len_chanlist = MAX_CHANLIST_LEN; | |
1404 | s->range_table = this_board->rangelist_ai; | |
1405 | if (this_board->board_type == boardACL8216) { | |
1406 | s->insn_read = acl8216_ai_insn_read; | |
1407 | } else { | |
1408 | s->insn_read = pcl812_ai_insn_read; | |
1409 | } | |
1410 | devpriv->use_MPC = this_board->haveMPC508; | |
1411 | s->cancel = pcl812_ai_cancel; | |
1412 | if (dev->irq) { | |
1413 | dev->read_subdev = s; | |
1414 | s->subdev_flags |= SDF_CMD_READ; | |
1415 | s->do_cmdtest = pcl812_ai_cmdtest; | |
1416 | s->do_cmd = pcl812_ai_cmd; | |
1417 | s->poll = pcl812_ai_poll; | |
1418 | } | |
1419 | switch (this_board->board_type) { | |
1420 | case boardPCL812PG: | |
1421 | if (it->options[4] == 1) | |
1422 | s->range_table = &range_pcl812pg2_ai; | |
1423 | break; | |
1424 | case boardPCL812: | |
1425 | switch (it->options[4]) { | |
1426 | case 0: | |
1427 | s->range_table = &range_bipolar10; | |
1428 | break; | |
1429 | case 1: | |
1430 | s->range_table = &range_bipolar5; | |
1431 | break; | |
1432 | case 2: | |
1433 | s->range_table = &range_bipolar2_5; | |
1434 | break; | |
1435 | case 3: | |
1436 | s->range_table = &range812_bipolar1_25; | |
1437 | break; | |
1438 | case 4: | |
1439 | s->range_table = &range812_bipolar0_625; | |
1440 | break; | |
1441 | case 5: | |
1442 | s->range_table = &range812_bipolar0_3125; | |
1443 | break; | |
1444 | default: | |
1445 | s->range_table = &range_bipolar10; | |
1446 | break; | |
1447 | printk(", incorrect range number %d, changing to 0 (+/-10V)", it->options[4]); | |
1448 | break; | |
1449 | } | |
1450 | break; | |
1451 | break; | |
1452 | case boardPCL813B: | |
1453 | if (it->options[1] == 1) | |
1454 | s->range_table = &range_pcl813b2_ai; | |
1455 | break; | |
1456 | case boardISO813: | |
1457 | switch (it->options[1]) { | |
1458 | case 0: | |
1459 | s->range_table = &range_iso813_1_ai; | |
1460 | break; | |
1461 | case 1: | |
1462 | s->range_table = &range_iso813_1_2_ai; | |
1463 | break; | |
1464 | case 2: | |
1465 | s->range_table = &range_iso813_2_ai; | |
1466 | devpriv->range_correction = 1; | |
1467 | break; | |
1468 | case 3: | |
1469 | s->range_table = &range_iso813_2_2_ai; | |
1470 | devpriv->range_correction = 1; | |
1471 | break; | |
1472 | default: | |
1473 | s->range_table = &range_iso813_1_ai; | |
1474 | break; | |
1475 | printk(", incorrect range number %d, changing to 0 ", it->options[1]); | |
1476 | break; | |
1477 | } | |
1478 | break; | |
1479 | case boardACL8113: | |
1480 | switch (it->options[1]) { | |
1481 | case 0: | |
1482 | s->range_table = &range_acl8113_1_ai; | |
1483 | break; | |
1484 | case 1: | |
1485 | s->range_table = &range_acl8113_1_2_ai; | |
1486 | break; | |
1487 | case 2: | |
1488 | s->range_table = &range_acl8113_2_ai; | |
1489 | devpriv->range_correction = 1; | |
1490 | break; | |
1491 | case 3: | |
1492 | s->range_table = &range_acl8113_2_2_ai; | |
1493 | devpriv->range_correction = 1; | |
1494 | break; | |
1495 | default: | |
1496 | s->range_table = &range_acl8113_1_ai; | |
1497 | break; | |
1498 | printk(", incorrect range number %d, changing to 0 ", it->options[1]); | |
1499 | break; | |
1500 | } | |
1501 | break; | |
1502 | } | |
1503 | subdev++; | |
1504 | } | |
1505 | ||
1506 | /* analog output */ | |
1507 | if (this_board->n_aochan > 0) { | |
1508 | s = dev->subdevices + subdev; | |
1509 | s->type = COMEDI_SUBD_AO; | |
1510 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND; | |
1511 | s->n_chan = this_board->n_aochan; | |
1512 | s->maxdata = 0xfff; | |
1513 | s->len_chanlist = 1; | |
1514 | s->range_table = this_board->rangelist_ao; | |
1515 | s->insn_read = pcl812_ao_insn_read; | |
1516 | s->insn_write = pcl812_ao_insn_write; | |
1517 | switch (this_board->board_type) { | |
1518 | case boardA821: | |
1519 | if (it->options[3] == 1) | |
1520 | s->range_table = &range_unipolar10; | |
1521 | break; | |
1522 | case boardPCL812: | |
1523 | case boardACL8112: | |
1524 | case boardPCL812PG: | |
1525 | case boardACL8216: | |
1526 | if (it->options[5] == 1) | |
1527 | s->range_table = &range_unipolar10; | |
1528 | if (it->options[5] == 2) | |
1529 | s->range_table = &range_unknown; | |
1530 | break; | |
1531 | } | |
1532 | subdev++; | |
1533 | } | |
1534 | ||
1535 | /* digital input */ | |
1536 | if (this_board->n_dichan > 0) { | |
1537 | s = dev->subdevices + subdev; | |
1538 | s->type = COMEDI_SUBD_DI; | |
1539 | s->subdev_flags = SDF_READABLE; | |
1540 | s->n_chan = this_board->n_dichan; | |
1541 | s->maxdata = 1; | |
1542 | s->len_chanlist = this_board->n_dichan; | |
1543 | s->range_table = &range_digital; | |
1544 | s->insn_bits = pcl812_di_insn_bits; | |
1545 | subdev++; | |
1546 | } | |
1547 | ||
1548 | /* digital output */ | |
1549 | if (this_board->n_dochan > 0) { | |
1550 | s = dev->subdevices + subdev; | |
1551 | s->type = COMEDI_SUBD_DO; | |
1552 | s->subdev_flags = SDF_WRITABLE; | |
1553 | s->n_chan = this_board->n_dochan; | |
1554 | s->maxdata = 1; | |
1555 | s->len_chanlist = this_board->n_dochan; | |
1556 | s->range_table = &range_digital; | |
1557 | s->insn_bits = pcl812_do_insn_bits; | |
1558 | subdev++; | |
1559 | } | |
1560 | ||
1561 | switch (this_board->board_type) { | |
1562 | case boardACL8216: | |
1563 | devpriv->ai_is16b = 1; | |
1564 | case boardPCL812PG: | |
1565 | case boardPCL812: | |
1566 | case boardACL8112: | |
1567 | devpriv->max_812_ai_mode0_rangewait = 1; | |
1568 | if (it->options[3] > 0) | |
1569 | devpriv->use_ext_trg = 1; // we use external trigger | |
1570 | case boardA821: | |
1571 | devpriv->max_812_ai_mode0_rangewait = 1; | |
1572 | devpriv->mode_reg_int = (irq << 4) & 0xf0; | |
1573 | break; | |
1574 | case boardPCL813B: | |
1575 | case boardPCL813: | |
1576 | case boardISO813: | |
1577 | case boardACL8113: | |
1578 | devpriv->max_812_ai_mode0_rangewait = 5; /* maybe there must by greatest timeout */ | |
1579 | break; | |
1580 | } | |
1581 | ||
1582 | printk("\n"); | |
1583 | devpriv->valid = 1; | |
1584 | ||
1585 | pcl812_reset(dev); | |
1586 | ||
1587 | return 0; | |
1588 | } | |
1589 | ||
1590 | /* | |
1591 | ============================================================================== | |
1592 | */ | |
71b5f4f1 | 1593 | static int pcl812_detach(struct comedi_device * dev) |
fcdb427b MD |
1594 | { |
1595 | ||
1596 | #ifdef PCL812_EXTDEBUG | |
1597 | rt_printk("comedi%d: pcl812: remove\n", dev->minor); | |
1598 | #endif | |
1599 | free_resources(dev); | |
1600 | return 0; | |
1601 | } |