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