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