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