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