1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Comedi driver for Keithley DAS-1700/DAS-1800 series boards |
4 | * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net> |
5 | * |
6 | * COMEDI - Linux Control and Measurement Device Interface |
7 | * Copyright (C) 2000 David A. Schleef <ds@schleef.org> |
8 | */ |
9 | |
10 | /* |
11 | * Driver: das1800 |
12 | * Description: Keithley Metrabyte DAS1800 (& compatibles) |
13 | * Author: Frank Mori Hess <fmhess@users.sourceforge.net> |
14 | * Devices: [Keithley Metrabyte] DAS-1701ST (das-1701st), |
15 | * DAS-1701ST-DA (das-1701st-da), DAS-1701/AO (das-1701ao), |
16 | * DAS-1702ST (das-1702st), DAS-1702ST-DA (das-1702st-da), |
17 | * DAS-1702HR (das-1702hr), DAS-1702HR-DA (das-1702hr-da), |
18 | * DAS-1702/AO (das-1702ao), DAS-1801ST (das-1801st), |
19 | * DAS-1801ST-DA (das-1801st-da), DAS-1801HC (das-1801hc), |
20 | * DAS-1801AO (das-1801ao), DAS-1802ST (das-1802st), |
21 | * DAS-1802ST-DA (das-1802st-da), DAS-1802HR (das-1802hr), |
22 | * DAS-1802HR-DA (das-1802hr-da), DAS-1802HC (das-1802hc), |
23 | * DAS-1802AO (das-1802ao) |
24 | * Status: works |
25 | * |
26 | * Configuration options: |
27 | * [0] - I/O port base address |
28 | * [1] - IRQ (optional, required for analog input cmd support) |
29 | * [2] - DMA0 (optional, requires irq) |
30 | * [3] - DMA1 (optional, requires irq and dma0) |
31 | * |
32 | * analog input cmd triggers supported: |
33 | * |
34 | * start_src TRIG_NOW command starts immediately |
35 | * TRIG_EXT command starts on external pin TGIN |
36 | * |
37 | * scan_begin_src TRIG_FOLLOW paced/external scans start immediately |
38 | * TRIG_TIMER burst scans start periodically |
39 | * TRIG_EXT burst scans start on external pin XPCLK |
40 | * |
41 | * scan_end_src TRIG_COUNT scan ends after last channel |
42 | * |
43 | * convert_src TRIG_TIMER paced/burst conversions are timed |
44 | * TRIG_EXT conversions on external pin XPCLK |
45 | * (requires scan_begin_src == TRIG_FOLLOW) |
46 | * |
47 | * stop_src TRIG_COUNT command stops after stop_arg scans |
48 | * TRIG_EXT command stops on external pin TGIN |
49 | * TRIG_NONE command runs until canceled |
50 | * |
51 | * If TRIG_EXT is used for both the start_src and stop_src, the first TGIN |
52 | * trigger starts the command, and the second trigger will stop it. If only |
53 | * one is TRIG_EXT, the first trigger will either stop or start the command. |
54 | * The external pin TGIN is normally set for negative edge triggering. It |
55 | * can be set to positive edge with the CR_INVERT flag. If TRIG_EXT is used |
56 | * for both the start_src and stop_src they must have the same polarity. |
57 | * |
58 | * Minimum conversion speed is limited to 64 microseconds (convert_arg <= 64000) |
59 | * for 'burst' scans. This limitation does not apply for 'paced' scans. The |
60 | * maximum conversion speed is limited by the board (convert_arg >= ai_speed). |
61 | * Maximum conversion speeds are not always achievable depending on the |
62 | * board setup (see user manual). |
63 | * |
64 | * NOTES: |
65 | * Only the DAS-1801ST has been tested by me. |
66 | * Unipolar and bipolar ranges cannot be mixed in the channel/gain list. |
67 | * |
68 | * The waveform analog output on the 'ao' cards is not supported. |
69 | * If you need it, send me (Frank Hess) an email. |
70 | */ |
71 | |
72 | #include <linux/module.h> |
73 | #include <linux/interrupt.h> |
74 | #include <linux/slab.h> |
75 | #include <linux/io.h> |
76 | #include <linux/comedi/comedidev.h> |
77 | #include <linux/comedi/comedi_8254.h> |
78 | #include <linux/comedi/comedi_isadma.h> |
79 | |
80 | /* misc. defines */ |
81 | #define DAS1800_SIZE 16 /* uses 16 io addresses */ |
82 | #define FIFO_SIZE 1024 /* 1024 sample fifo */ |
83 | #define DMA_BUF_SIZE 0x1ff00 /* size in bytes of dma buffers */ |
84 | |
85 | /* Registers for the das1800 */ |
86 | #define DAS1800_FIFO 0x0 |
87 | #define DAS1800_QRAM 0x0 |
88 | #define DAS1800_DAC 0x0 |
89 | #define DAS1800_SELECT 0x2 |
90 | #define ADC 0x0 |
91 | #define QRAM 0x1 |
92 | #define DAC(a) (0x2 + a) |
93 | #define DAS1800_DIGITAL 0x3 |
94 | #define DAS1800_CONTROL_A 0x4 |
95 | #define FFEN 0x1 |
96 | #define CGEN 0x4 |
97 | #define CGSL 0x8 |
98 | #define TGEN 0x10 |
99 | #define TGSL 0x20 |
100 | #define TGPL 0x40 |
101 | #define ATEN 0x80 |
102 | #define DAS1800_CONTROL_B 0x5 |
103 | #define DMA_CH5 0x1 |
104 | #define DMA_CH6 0x2 |
105 | #define DMA_CH7 0x3 |
106 | #define DMA_CH5_CH6 0x5 |
107 | #define DMA_CH6_CH7 0x6 |
108 | #define DMA_CH7_CH5 0x7 |
109 | #define DMA_ENABLED 0x3 |
110 | #define DMA_DUAL 0x4 |
111 | #define IRQ3 0x8 |
112 | #define IRQ5 0x10 |
113 | #define IRQ7 0x18 |
114 | #define IRQ10 0x28 |
115 | #define IRQ11 0x30 |
116 | #define IRQ15 0x38 |
117 | #define FIMD 0x40 |
118 | #define DAS1800_CONTROL_C 0X6 |
119 | #define IPCLK 0x1 |
120 | #define XPCLK 0x3 |
121 | #define BMDE 0x4 |
122 | #define CMEN 0x8 |
123 | #define UQEN 0x10 |
124 | #define SD 0x40 |
125 | #define UB 0x80 |
126 | #define DAS1800_STATUS 0x7 |
127 | #define INT 0x1 |
128 | #define DMATC 0x2 |
129 | #define CT0TC 0x8 |
130 | #define OVF 0x10 |
131 | #define FHF 0x20 |
132 | #define FNE 0x40 |
133 | #define CVEN 0x80 |
134 | #define CVEN_MASK 0x40 |
135 | #define CLEAR_INTR_MASK (CVEN_MASK | 0x1f) |
136 | #define DAS1800_BURST_LENGTH 0x8 |
137 | #define DAS1800_BURST_RATE 0x9 |
138 | #define DAS1800_QRAM_ADDRESS 0xa |
139 | #define DAS1800_COUNTER 0xc |
140 | |
141 | #define IOBASE2 0x400 |
142 | |
143 | static const struct comedi_lrange das1801_ai_range = { |
144 | 8, { |
145 | BIP_RANGE(5), /* bipolar gain = 1 */ |
146 | BIP_RANGE(1), /* bipolar gain = 10 */ |
147 | BIP_RANGE(0.1), /* bipolar gain = 50 */ |
148 | BIP_RANGE(0.02), /* bipolar gain = 250 */ |
149 | UNI_RANGE(5), /* unipolar gain = 1 */ |
150 | UNI_RANGE(1), /* unipolar gain = 10 */ |
151 | UNI_RANGE(0.1), /* unipolar gain = 50 */ |
152 | UNI_RANGE(0.02) /* unipolar gain = 250 */ |
153 | } |
154 | }; |
155 | |
156 | static const struct comedi_lrange das1802_ai_range = { |
157 | 8, { |
158 | BIP_RANGE(10), /* bipolar gain = 1 */ |
159 | BIP_RANGE(5), /* bipolar gain = 2 */ |
160 | BIP_RANGE(2.5), /* bipolar gain = 4 */ |
161 | BIP_RANGE(1.25), /* bipolar gain = 8 */ |
162 | UNI_RANGE(10), /* unipolar gain = 1 */ |
163 | UNI_RANGE(5), /* unipolar gain = 2 */ |
164 | UNI_RANGE(2.5), /* unipolar gain = 4 */ |
165 | UNI_RANGE(1.25) /* unipolar gain = 8 */ |
166 | } |
167 | }; |
168 | |
169 | /* |
170 | * The waveform analog outputs on the 'ao' boards are not currently |
171 | * supported. They have a comedi_lrange of: |
172 | * { 2, { BIP_RANGE(10), BIP_RANGE(5) } } |
173 | */ |
174 | |
175 | enum das1800_boardid { |
176 | BOARD_DAS1701ST, |
177 | BOARD_DAS1701ST_DA, |
178 | BOARD_DAS1702ST, |
179 | BOARD_DAS1702ST_DA, |
180 | BOARD_DAS1702HR, |
181 | BOARD_DAS1702HR_DA, |
182 | BOARD_DAS1701AO, |
183 | BOARD_DAS1702AO, |
184 | BOARD_DAS1801ST, |
185 | BOARD_DAS1801ST_DA, |
186 | BOARD_DAS1802ST, |
187 | BOARD_DAS1802ST_DA, |
188 | BOARD_DAS1802HR, |
189 | BOARD_DAS1802HR_DA, |
190 | BOARD_DAS1801HC, |
191 | BOARD_DAS1802HC, |
192 | BOARD_DAS1801AO, |
193 | BOARD_DAS1802AO |
194 | }; |
195 | |
196 | /* board probe id values (hi byte of the digital input register) */ |
197 | #define DAS1800_ID_ST_DA 0x3 |
198 | #define DAS1800_ID_HR_DA 0x4 |
199 | #define DAS1800_ID_AO 0x5 |
200 | #define DAS1800_ID_HR 0x6 |
201 | #define DAS1800_ID_ST 0x7 |
202 | #define DAS1800_ID_HC 0x8 |
203 | |
204 | struct das1800_board { |
205 | const char *name; |
206 | unsigned char id; |
207 | unsigned int ai_speed; |
208 | unsigned int is_01_series:1; |
209 | }; |
210 | |
211 | static const struct das1800_board das1800_boards[] = { |
212 | [BOARD_DAS1701ST] = { |
213 | .name = "das-1701st" , |
214 | .id = DAS1800_ID_ST, |
215 | .ai_speed = 6250, |
216 | .is_01_series = 1, |
217 | }, |
218 | [BOARD_DAS1701ST_DA] = { |
219 | .name = "das-1701st-da" , |
220 | .id = DAS1800_ID_ST_DA, |
221 | .ai_speed = 6250, |
222 | .is_01_series = 1, |
223 | }, |
224 | [BOARD_DAS1702ST] = { |
225 | .name = "das-1702st" , |
226 | .id = DAS1800_ID_ST, |
227 | .ai_speed = 6250, |
228 | }, |
229 | [BOARD_DAS1702ST_DA] = { |
230 | .name = "das-1702st-da" , |
231 | .id = DAS1800_ID_ST_DA, |
232 | .ai_speed = 6250, |
233 | }, |
234 | [BOARD_DAS1702HR] = { |
235 | .name = "das-1702hr" , |
236 | .id = DAS1800_ID_HR, |
237 | .ai_speed = 20000, |
238 | }, |
239 | [BOARD_DAS1702HR_DA] = { |
240 | .name = "das-1702hr-da" , |
241 | .id = DAS1800_ID_HR_DA, |
242 | .ai_speed = 20000, |
243 | }, |
244 | [BOARD_DAS1701AO] = { |
245 | .name = "das-1701ao" , |
246 | .id = DAS1800_ID_AO, |
247 | .ai_speed = 6250, |
248 | .is_01_series = 1, |
249 | }, |
250 | [BOARD_DAS1702AO] = { |
251 | .name = "das-1702ao" , |
252 | .id = DAS1800_ID_AO, |
253 | .ai_speed = 6250, |
254 | }, |
255 | [BOARD_DAS1801ST] = { |
256 | .name = "das-1801st" , |
257 | .id = DAS1800_ID_ST, |
258 | .ai_speed = 3000, |
259 | .is_01_series = 1, |
260 | }, |
261 | [BOARD_DAS1801ST_DA] = { |
262 | .name = "das-1801st-da" , |
263 | .id = DAS1800_ID_ST_DA, |
264 | .ai_speed = 3000, |
265 | .is_01_series = 1, |
266 | }, |
267 | [BOARD_DAS1802ST] = { |
268 | .name = "das-1802st" , |
269 | .id = DAS1800_ID_ST, |
270 | .ai_speed = 3000, |
271 | }, |
272 | [BOARD_DAS1802ST_DA] = { |
273 | .name = "das-1802st-da" , |
274 | .id = DAS1800_ID_ST_DA, |
275 | .ai_speed = 3000, |
276 | }, |
277 | [BOARD_DAS1802HR] = { |
278 | .name = "das-1802hr" , |
279 | .id = DAS1800_ID_HR, |
280 | .ai_speed = 10000, |
281 | }, |
282 | [BOARD_DAS1802HR_DA] = { |
283 | .name = "das-1802hr-da" , |
284 | .id = DAS1800_ID_HR_DA, |
285 | .ai_speed = 10000, |
286 | }, |
287 | [BOARD_DAS1801HC] = { |
288 | .name = "das-1801hc" , |
289 | .id = DAS1800_ID_HC, |
290 | .ai_speed = 3000, |
291 | .is_01_series = 1, |
292 | }, |
293 | [BOARD_DAS1802HC] = { |
294 | .name = "das-1802hc" , |
295 | .id = DAS1800_ID_HC, |
296 | .ai_speed = 3000, |
297 | }, |
298 | [BOARD_DAS1801AO] = { |
299 | .name = "das-1801ao" , |
300 | .id = DAS1800_ID_AO, |
301 | .ai_speed = 3000, |
302 | .is_01_series = 1, |
303 | }, |
304 | [BOARD_DAS1802AO] = { |
305 | .name = "das-1802ao" , |
306 | .id = DAS1800_ID_AO, |
307 | .ai_speed = 3000, |
308 | }, |
309 | }; |
310 | |
311 | struct das1800_private { |
312 | struct comedi_isadma *dma; |
313 | int irq_dma_bits; |
314 | int dma_bits; |
315 | unsigned short *fifo_buf; |
316 | unsigned long iobase2; |
317 | bool ai_is_unipolar; |
318 | }; |
319 | |
320 | static void das1800_ai_munge(struct comedi_device *dev, |
321 | struct comedi_subdevice *s, |
322 | void *data, unsigned int num_bytes, |
323 | unsigned int start_chan_index) |
324 | { |
325 | struct das1800_private *devpriv = dev->private; |
326 | unsigned short *array = data; |
327 | unsigned int num_samples = comedi_bytes_to_samples(s, nbytes: num_bytes); |
328 | unsigned int i; |
329 | |
330 | if (devpriv->ai_is_unipolar) |
331 | return; |
332 | |
333 | for (i = 0; i < num_samples; i++) |
334 | array[i] = comedi_offset_munge(s, val: array[i]); |
335 | } |
336 | |
337 | static void das1800_handle_fifo_half_full(struct comedi_device *dev, |
338 | struct comedi_subdevice *s) |
339 | { |
340 | struct das1800_private *devpriv = dev->private; |
341 | unsigned int nsamples = comedi_nsamples_left(s, FIFO_SIZE / 2); |
342 | |
343 | insw(port: dev->iobase + DAS1800_FIFO, addr: devpriv->fifo_buf, count: nsamples); |
344 | comedi_buf_write_samples(s, data: devpriv->fifo_buf, nsamples); |
345 | } |
346 | |
347 | static void das1800_handle_fifo_not_empty(struct comedi_device *dev, |
348 | struct comedi_subdevice *s) |
349 | { |
350 | struct comedi_cmd *cmd = &s->async->cmd; |
351 | unsigned short dpnt; |
352 | |
353 | while (inb(port: dev->iobase + DAS1800_STATUS) & FNE) { |
354 | dpnt = inw(port: dev->iobase + DAS1800_FIFO); |
355 | comedi_buf_write_samples(s, data: &dpnt, nsamples: 1); |
356 | |
357 | if (cmd->stop_src == TRIG_COUNT && |
358 | s->async->scans_done >= cmd->stop_arg) |
359 | break; |
360 | } |
361 | } |
362 | |
363 | static void das1800_flush_dma_channel(struct comedi_device *dev, |
364 | struct comedi_subdevice *s, |
365 | struct comedi_isadma_desc *desc) |
366 | { |
367 | unsigned int residue = comedi_isadma_disable(dma_chan: desc->chan); |
368 | unsigned int nbytes = desc->size - residue; |
369 | unsigned int nsamples; |
370 | |
371 | /* figure out how many points to read */ |
372 | nsamples = comedi_bytes_to_samples(s, nbytes); |
373 | nsamples = comedi_nsamples_left(s, nsamples); |
374 | |
375 | comedi_buf_write_samples(s, data: desc->virt_addr, nsamples); |
376 | } |
377 | |
378 | static void das1800_flush_dma(struct comedi_device *dev, |
379 | struct comedi_subdevice *s) |
380 | { |
381 | struct das1800_private *devpriv = dev->private; |
382 | struct comedi_isadma *dma = devpriv->dma; |
383 | struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; |
384 | const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL; |
385 | |
386 | das1800_flush_dma_channel(dev, s, desc); |
387 | |
388 | if (dual_dma) { |
389 | /* switch to other channel and flush it */ |
390 | dma->cur_dma = 1 - dma->cur_dma; |
391 | desc = &dma->desc[dma->cur_dma]; |
392 | das1800_flush_dma_channel(dev, s, desc); |
393 | } |
394 | |
395 | /* get any remaining samples in fifo */ |
396 | das1800_handle_fifo_not_empty(dev, s); |
397 | } |
398 | |
399 | static void das1800_handle_dma(struct comedi_device *dev, |
400 | struct comedi_subdevice *s, unsigned int status) |
401 | { |
402 | struct das1800_private *devpriv = dev->private; |
403 | struct comedi_isadma *dma = devpriv->dma; |
404 | struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma]; |
405 | const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL; |
406 | |
407 | das1800_flush_dma_channel(dev, s, desc); |
408 | |
409 | /* re-enable dma channel */ |
410 | comedi_isadma_program(desc); |
411 | |
412 | if (status & DMATC) { |
413 | /* clear DMATC interrupt bit */ |
414 | outb(CLEAR_INTR_MASK & ~DMATC, port: dev->iobase + DAS1800_STATUS); |
415 | /* switch dma channels for next time, if appropriate */ |
416 | if (dual_dma) |
417 | dma->cur_dma = 1 - dma->cur_dma; |
418 | } |
419 | } |
420 | |
421 | static int das1800_ai_cancel(struct comedi_device *dev, |
422 | struct comedi_subdevice *s) |
423 | { |
424 | struct das1800_private *devpriv = dev->private; |
425 | struct comedi_isadma *dma = devpriv->dma; |
426 | struct comedi_isadma_desc *desc; |
427 | int i; |
428 | |
429 | /* disable and stop conversions */ |
430 | outb(value: 0x0, port: dev->iobase + DAS1800_STATUS); |
431 | outb(value: 0x0, port: dev->iobase + DAS1800_CONTROL_B); |
432 | outb(value: 0x0, port: dev->iobase + DAS1800_CONTROL_A); |
433 | |
434 | if (dma) { |
435 | for (i = 0; i < 2; i++) { |
436 | desc = &dma->desc[i]; |
437 | if (desc->chan) |
438 | comedi_isadma_disable(dma_chan: desc->chan); |
439 | } |
440 | } |
441 | |
442 | return 0; |
443 | } |
444 | |
445 | static void das1800_ai_handler(struct comedi_device *dev) |
446 | { |
447 | struct das1800_private *devpriv = dev->private; |
448 | struct comedi_subdevice *s = dev->read_subdev; |
449 | struct comedi_async *async = s->async; |
450 | struct comedi_cmd *cmd = &async->cmd; |
451 | unsigned int status = inb(port: dev->iobase + DAS1800_STATUS); |
452 | |
453 | /* select adc register (spinlock is already held) */ |
454 | outb(ADC, port: dev->iobase + DAS1800_SELECT); |
455 | |
456 | /* get samples with dma, fifo, or polled as necessary */ |
457 | if (devpriv->irq_dma_bits & DMA_ENABLED) |
458 | das1800_handle_dma(dev, s, status); |
459 | else if (status & FHF) |
460 | das1800_handle_fifo_half_full(dev, s); |
461 | else if (status & FNE) |
462 | das1800_handle_fifo_not_empty(dev, s); |
463 | |
464 | /* if the card's fifo has overflowed */ |
465 | if (status & OVF) { |
466 | /* clear OVF interrupt bit */ |
467 | outb(CLEAR_INTR_MASK & ~OVF, port: dev->iobase + DAS1800_STATUS); |
468 | dev_err(dev->class_dev, "FIFO overflow\n" ); |
469 | async->events |= COMEDI_CB_ERROR; |
470 | comedi_handle_events(dev, s); |
471 | return; |
472 | } |
473 | /* stop taking data if appropriate */ |
474 | /* stop_src TRIG_EXT */ |
475 | if (status & CT0TC) { |
476 | /* clear CT0TC interrupt bit */ |
477 | outb(CLEAR_INTR_MASK & ~CT0TC, port: dev->iobase + DAS1800_STATUS); |
478 | /* get all remaining samples before quitting */ |
479 | if (devpriv->irq_dma_bits & DMA_ENABLED) |
480 | das1800_flush_dma(dev, s); |
481 | else |
482 | das1800_handle_fifo_not_empty(dev, s); |
483 | async->events |= COMEDI_CB_EOA; |
484 | } else if (cmd->stop_src == TRIG_COUNT && |
485 | async->scans_done >= cmd->stop_arg) { |
486 | async->events |= COMEDI_CB_EOA; |
487 | } |
488 | |
489 | comedi_handle_events(dev, s); |
490 | } |
491 | |
492 | static int das1800_ai_poll(struct comedi_device *dev, |
493 | struct comedi_subdevice *s) |
494 | { |
495 | unsigned long flags; |
496 | |
497 | /* |
498 | * Protects the indirect addressing selected by DAS1800_SELECT |
499 | * in das1800_ai_handler() also prevents race with das1800_interrupt(). |
500 | */ |
501 | spin_lock_irqsave(&dev->spinlock, flags); |
502 | |
503 | das1800_ai_handler(dev); |
504 | |
505 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
506 | |
507 | return comedi_buf_n_bytes_ready(s); |
508 | } |
509 | |
510 | static irqreturn_t das1800_interrupt(int irq, void *d) |
511 | { |
512 | struct comedi_device *dev = d; |
513 | unsigned int status; |
514 | |
515 | if (!dev->attached) { |
516 | dev_err(dev->class_dev, "premature interrupt\n" ); |
517 | return IRQ_HANDLED; |
518 | } |
519 | |
520 | /* |
521 | * Protects the indirect addressing selected by DAS1800_SELECT |
522 | * in das1800_ai_handler() also prevents race with das1800_ai_poll(). |
523 | */ |
524 | spin_lock(lock: &dev->spinlock); |
525 | |
526 | status = inb(port: dev->iobase + DAS1800_STATUS); |
527 | |
528 | /* if interrupt was not caused by das-1800 */ |
529 | if (!(status & INT)) { |
530 | spin_unlock(lock: &dev->spinlock); |
531 | return IRQ_NONE; |
532 | } |
533 | /* clear the interrupt status bit INT */ |
534 | outb(CLEAR_INTR_MASK & ~INT, port: dev->iobase + DAS1800_STATUS); |
535 | /* handle interrupt */ |
536 | das1800_ai_handler(dev); |
537 | |
538 | spin_unlock(lock: &dev->spinlock); |
539 | return IRQ_HANDLED; |
540 | } |
541 | |
542 | static int das1800_ai_fixup_paced_timing(struct comedi_device *dev, |
543 | struct comedi_cmd *cmd) |
544 | { |
545 | unsigned int arg = cmd->convert_arg; |
546 | |
547 | /* |
548 | * Paced mode: |
549 | * scan_begin_src is TRIG_FOLLOW |
550 | * convert_src is TRIG_TIMER |
551 | * |
552 | * The convert_arg sets the pacer sample acquisition time. |
553 | * The max acquisition speed is limited to the boards |
554 | * 'ai_speed' (this was already verified). The min speed is |
555 | * limited by the cascaded 8254 timer. |
556 | */ |
557 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
558 | return comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: arg); |
559 | } |
560 | |
561 | static int das1800_ai_fixup_burst_timing(struct comedi_device *dev, |
562 | struct comedi_cmd *cmd) |
563 | { |
564 | unsigned int arg = cmd->convert_arg; |
565 | int err = 0; |
566 | |
567 | /* |
568 | * Burst mode: |
569 | * scan_begin_src is TRIG_TIMER or TRIG_EXT |
570 | * convert_src is TRIG_TIMER |
571 | * |
572 | * The convert_arg sets burst sample acquisition time. |
573 | * The max acquisition speed is limited to the boards |
574 | * 'ai_speed' (this was already verified). The min speed is |
575 | * limiited to 64 microseconds, |
576 | */ |
577 | err |= comedi_check_trigger_arg_max(arg: &arg, val: 64000); |
578 | |
579 | /* round to microseconds then verify */ |
580 | switch (cmd->flags & CMDF_ROUND_MASK) { |
581 | case CMDF_ROUND_NEAREST: |
582 | default: |
583 | arg = DIV_ROUND_CLOSEST(arg, 1000); |
584 | break; |
585 | case CMDF_ROUND_DOWN: |
586 | arg = arg / 1000; |
587 | break; |
588 | case CMDF_ROUND_UP: |
589 | arg = DIV_ROUND_UP(arg, 1000); |
590 | break; |
591 | } |
592 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: arg * 1000); |
593 | |
594 | /* |
595 | * The pacer can be used to set the scan sample rate. The max scan |
596 | * speed is limited by the conversion speed and the number of channels |
597 | * to convert. The min speed is limited by the cascaded 8254 timer. |
598 | */ |
599 | if (cmd->scan_begin_src == TRIG_TIMER) { |
600 | arg = cmd->convert_arg * cmd->chanlist_len; |
601 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, val: arg); |
602 | |
603 | arg = cmd->scan_begin_arg; |
604 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
605 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: arg); |
606 | } |
607 | |
608 | return err; |
609 | } |
610 | |
611 | static int das1800_ai_check_chanlist(struct comedi_device *dev, |
612 | struct comedi_subdevice *s, |
613 | struct comedi_cmd *cmd) |
614 | { |
615 | unsigned int range = CR_RANGE(cmd->chanlist[0]); |
616 | bool unipolar0 = comedi_range_is_unipolar(s, range); |
617 | int i; |
618 | |
619 | for (i = 1; i < cmd->chanlist_len; i++) { |
620 | range = CR_RANGE(cmd->chanlist[i]); |
621 | |
622 | if (unipolar0 != comedi_range_is_unipolar(s, range)) { |
623 | dev_dbg(dev->class_dev, |
624 | "unipolar and bipolar ranges cannot be mixed in the chanlist\n" ); |
625 | return -EINVAL; |
626 | } |
627 | } |
628 | |
629 | return 0; |
630 | } |
631 | |
632 | static int das1800_ai_cmdtest(struct comedi_device *dev, |
633 | struct comedi_subdevice *s, |
634 | struct comedi_cmd *cmd) |
635 | { |
636 | const struct das1800_board *board = dev->board_ptr; |
637 | int err = 0; |
638 | |
639 | /* Step 1 : check if triggers are trivially valid */ |
640 | |
641 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_NOW | TRIG_EXT); |
642 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
643 | TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT); |
644 | err |= comedi_check_trigger_src(src: &cmd->convert_src, |
645 | TRIG_TIMER | TRIG_EXT); |
646 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
647 | err |= comedi_check_trigger_src(src: &cmd->stop_src, |
648 | TRIG_COUNT | TRIG_EXT | TRIG_NONE); |
649 | |
650 | if (err) |
651 | return 1; |
652 | |
653 | /* Step 2a : make sure trigger sources are unique */ |
654 | |
655 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
656 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
657 | err |= comedi_check_trigger_is_unique(src: cmd->convert_src); |
658 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
659 | |
660 | /* Step 2b : and mutually compatible */ |
661 | |
662 | /* burst scans must use timed conversions */ |
663 | if (cmd->scan_begin_src != TRIG_FOLLOW && |
664 | cmd->convert_src != TRIG_TIMER) |
665 | err |= -EINVAL; |
666 | |
667 | /* the external pin TGIN must use the same polarity */ |
668 | if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT) |
669 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, |
670 | val: cmd->stop_arg); |
671 | |
672 | if (err) |
673 | return 2; |
674 | |
675 | /* Step 3: check if arguments are trivially valid */ |
676 | |
677 | if (cmd->start_arg == TRIG_NOW) |
678 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
679 | |
680 | if (cmd->convert_src == TRIG_TIMER) { |
681 | err |= comedi_check_trigger_arg_min(arg: &cmd->convert_arg, |
682 | val: board->ai_speed); |
683 | } |
684 | |
685 | err |= comedi_check_trigger_arg_min(arg: &cmd->chanlist_len, val: 1); |
686 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
687 | val: cmd->chanlist_len); |
688 | |
689 | switch (cmd->stop_src) { |
690 | case TRIG_COUNT: |
691 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
692 | break; |
693 | case TRIG_NONE: |
694 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
695 | break; |
696 | default: |
697 | break; |
698 | } |
699 | |
700 | if (err) |
701 | return 3; |
702 | |
703 | /* Step 4: fix up any arguments */ |
704 | |
705 | if (cmd->convert_src == TRIG_TIMER) { |
706 | if (cmd->scan_begin_src == TRIG_FOLLOW) |
707 | err |= das1800_ai_fixup_paced_timing(dev, cmd); |
708 | else /* TRIG_TIMER or TRIG_EXT */ |
709 | err |= das1800_ai_fixup_burst_timing(dev, cmd); |
710 | } |
711 | |
712 | if (err) |
713 | return 4; |
714 | |
715 | /* Step 5: check channel list if it exists */ |
716 | if (cmd->chanlist && cmd->chanlist_len > 0) |
717 | err |= das1800_ai_check_chanlist(dev, s, cmd); |
718 | |
719 | if (err) |
720 | return 5; |
721 | |
722 | return 0; |
723 | } |
724 | |
725 | static unsigned char das1800_ai_chanspec_bits(struct comedi_subdevice *s, |
726 | unsigned int chanspec) |
727 | { |
728 | unsigned int range = CR_RANGE(chanspec); |
729 | unsigned int aref = CR_AREF(chanspec); |
730 | unsigned char bits; |
731 | |
732 | bits = UQEN; |
733 | if (aref != AREF_DIFF) |
734 | bits |= SD; |
735 | if (aref == AREF_COMMON) |
736 | bits |= CMEN; |
737 | if (comedi_range_is_unipolar(s, range)) |
738 | bits |= UB; |
739 | |
740 | return bits; |
741 | } |
742 | |
743 | static unsigned int das1800_ai_transfer_size(struct comedi_device *dev, |
744 | struct comedi_subdevice *s, |
745 | unsigned int maxbytes, |
746 | unsigned int ns) |
747 | { |
748 | struct comedi_cmd *cmd = &s->async->cmd; |
749 | unsigned int max_samples = comedi_bytes_to_samples(s, nbytes: maxbytes); |
750 | unsigned int samples; |
751 | |
752 | samples = max_samples; |
753 | |
754 | /* for timed modes, make dma buffer fill in 'ns' time */ |
755 | switch (cmd->scan_begin_src) { |
756 | case TRIG_FOLLOW: /* not in burst mode */ |
757 | if (cmd->convert_src == TRIG_TIMER) |
758 | samples = ns / cmd->convert_arg; |
759 | break; |
760 | case TRIG_TIMER: |
761 | samples = ns / (cmd->scan_begin_arg * cmd->chanlist_len); |
762 | break; |
763 | } |
764 | |
765 | /* limit samples to what is remaining in the command */ |
766 | samples = comedi_nsamples_left(s, nsamples: samples); |
767 | |
768 | if (samples > max_samples) |
769 | samples = max_samples; |
770 | if (samples < 1) |
771 | samples = 1; |
772 | |
773 | return comedi_samples_to_bytes(s, nsamples: samples); |
774 | } |
775 | |
776 | static void das1800_ai_setup_dma(struct comedi_device *dev, |
777 | struct comedi_subdevice *s) |
778 | { |
779 | struct das1800_private *devpriv = dev->private; |
780 | struct comedi_isadma *dma = devpriv->dma; |
781 | struct comedi_isadma_desc *desc; |
782 | unsigned int bytes; |
783 | |
784 | if ((devpriv->irq_dma_bits & DMA_ENABLED) == 0) |
785 | return; |
786 | |
787 | dma->cur_dma = 0; |
788 | desc = &dma->desc[0]; |
789 | |
790 | /* determine a dma transfer size to fill buffer in 0.3 sec */ |
791 | bytes = das1800_ai_transfer_size(dev, s, maxbytes: desc->maxsize, ns: 300000000); |
792 | |
793 | desc->size = bytes; |
794 | comedi_isadma_program(desc); |
795 | |
796 | /* set up dual dma if appropriate */ |
797 | if (devpriv->irq_dma_bits & DMA_DUAL) { |
798 | desc = &dma->desc[1]; |
799 | desc->size = bytes; |
800 | comedi_isadma_program(desc); |
801 | } |
802 | } |
803 | |
804 | static void das1800_ai_set_chanlist(struct comedi_device *dev, |
805 | unsigned int *chanlist, unsigned int len) |
806 | { |
807 | unsigned long flags; |
808 | unsigned int i; |
809 | |
810 | /* protects the indirect addressing selected by DAS1800_SELECT */ |
811 | spin_lock_irqsave(&dev->spinlock, flags); |
812 | |
813 | /* select QRAM register and set start address */ |
814 | outb(QRAM, port: dev->iobase + DAS1800_SELECT); |
815 | outb(value: len - 1, port: dev->iobase + DAS1800_QRAM_ADDRESS); |
816 | |
817 | /* make channel / gain list */ |
818 | for (i = 0; i < len; i++) { |
819 | unsigned int chan = CR_CHAN(chanlist[i]); |
820 | unsigned int range = CR_RANGE(chanlist[i]); |
821 | unsigned short val; |
822 | |
823 | val = chan | ((range & 0x3) << 8); |
824 | outw(value: val, port: dev->iobase + DAS1800_QRAM); |
825 | } |
826 | |
827 | /* finish write to QRAM */ |
828 | outb(value: len - 1, port: dev->iobase + DAS1800_QRAM_ADDRESS); |
829 | |
830 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
831 | } |
832 | |
833 | static int das1800_ai_cmd(struct comedi_device *dev, |
834 | struct comedi_subdevice *s) |
835 | { |
836 | struct das1800_private *devpriv = dev->private; |
837 | int control_a, control_c; |
838 | struct comedi_async *async = s->async; |
839 | const struct comedi_cmd *cmd = &async->cmd; |
840 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); |
841 | |
842 | /* |
843 | * Disable dma on CMDF_WAKE_EOS, or CMDF_PRIORITY (because dma in |
844 | * handler is unsafe at hard real-time priority). |
845 | */ |
846 | if (cmd->flags & (CMDF_WAKE_EOS | CMDF_PRIORITY)) |
847 | devpriv->irq_dma_bits &= ~DMA_ENABLED; |
848 | else |
849 | devpriv->irq_dma_bits |= devpriv->dma_bits; |
850 | /* interrupt on end of conversion for CMDF_WAKE_EOS */ |
851 | if (cmd->flags & CMDF_WAKE_EOS) { |
852 | /* interrupt fifo not empty */ |
853 | devpriv->irq_dma_bits &= ~FIMD; |
854 | } else { |
855 | /* interrupt fifo half full */ |
856 | devpriv->irq_dma_bits |= FIMD; |
857 | } |
858 | |
859 | das1800_ai_cancel(dev, s); |
860 | |
861 | devpriv->ai_is_unipolar = comedi_range_is_unipolar(s, range: range0); |
862 | |
863 | control_a = FFEN; |
864 | if (cmd->stop_src == TRIG_EXT) |
865 | control_a |= ATEN; |
866 | if (cmd->start_src == TRIG_EXT) |
867 | control_a |= TGEN | CGSL; |
868 | else /* TRIG_NOW */ |
869 | control_a |= CGEN; |
870 | if (control_a & (ATEN | TGEN)) { |
871 | if ((cmd->start_arg & CR_INVERT) || (cmd->stop_arg & CR_INVERT)) |
872 | control_a |= TGPL; |
873 | } |
874 | |
875 | control_c = das1800_ai_chanspec_bits(s, chanspec: cmd->chanlist[0]); |
876 | /* set clock source to internal or external */ |
877 | if (cmd->scan_begin_src == TRIG_FOLLOW) { |
878 | /* not in burst mode */ |
879 | if (cmd->convert_src == TRIG_TIMER) { |
880 | /* trig on cascaded counters */ |
881 | control_c |= IPCLK; |
882 | } else { /* TRIG_EXT */ |
883 | /* trig on falling edge of external trigger */ |
884 | control_c |= XPCLK; |
885 | } |
886 | } else if (cmd->scan_begin_src == TRIG_TIMER) { |
887 | /* burst mode with internal pacer clock */ |
888 | control_c |= BMDE | IPCLK; |
889 | } else { /* TRIG_EXT */ |
890 | /* burst mode with external trigger */ |
891 | control_c |= BMDE | XPCLK; |
892 | } |
893 | |
894 | das1800_ai_set_chanlist(dev, chanlist: cmd->chanlist, len: cmd->chanlist_len); |
895 | |
896 | /* setup cascaded counters for conversion/scan frequency */ |
897 | if ((cmd->scan_begin_src == TRIG_FOLLOW || |
898 | cmd->scan_begin_src == TRIG_TIMER) && |
899 | cmd->convert_src == TRIG_TIMER) { |
900 | comedi_8254_update_divisors(i8254: dev->pacer); |
901 | comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: true); |
902 | } |
903 | |
904 | /* setup counter 0 for 'about triggering' */ |
905 | if (cmd->stop_src == TRIG_EXT) |
906 | comedi_8254_load(i8254: dev->pacer, counter: 0, val: 1, mode: I8254_MODE0 | I8254_BINARY); |
907 | |
908 | das1800_ai_setup_dma(dev, s); |
909 | outb(value: control_c, port: dev->iobase + DAS1800_CONTROL_C); |
910 | /* set conversion rate and length for burst mode */ |
911 | if (control_c & BMDE) { |
912 | outb(value: cmd->convert_arg / 1000 - 1, /* microseconds - 1 */ |
913 | port: dev->iobase + DAS1800_BURST_RATE); |
914 | outb(value: cmd->chanlist_len - 1, port: dev->iobase + DAS1800_BURST_LENGTH); |
915 | } |
916 | |
917 | /* enable and start conversions */ |
918 | outb(value: devpriv->irq_dma_bits, port: dev->iobase + DAS1800_CONTROL_B); |
919 | outb(value: control_a, port: dev->iobase + DAS1800_CONTROL_A); |
920 | outb(CVEN, port: dev->iobase + DAS1800_STATUS); |
921 | |
922 | return 0; |
923 | } |
924 | |
925 | static int das1800_ai_eoc(struct comedi_device *dev, |
926 | struct comedi_subdevice *s, |
927 | struct comedi_insn *insn, |
928 | unsigned long context) |
929 | { |
930 | unsigned char status; |
931 | |
932 | status = inb(port: dev->iobase + DAS1800_STATUS); |
933 | if (status & FNE) |
934 | return 0; |
935 | return -EBUSY; |
936 | } |
937 | |
938 | static int das1800_ai_insn_read(struct comedi_device *dev, |
939 | struct comedi_subdevice *s, |
940 | struct comedi_insn *insn, |
941 | unsigned int *data) |
942 | { |
943 | unsigned int range = CR_RANGE(insn->chanspec); |
944 | bool is_unipolar = comedi_range_is_unipolar(s, range); |
945 | int ret = 0; |
946 | int n; |
947 | unsigned short dpnt; |
948 | unsigned long flags; |
949 | |
950 | outb(value: das1800_ai_chanspec_bits(s, chanspec: insn->chanspec), |
951 | port: dev->iobase + DAS1800_CONTROL_C); /* software pacer */ |
952 | outb(CVEN, port: dev->iobase + DAS1800_STATUS); /* enable conversions */ |
953 | outb(value: 0x0, port: dev->iobase + DAS1800_CONTROL_A); /* reset fifo */ |
954 | outb(FFEN, port: dev->iobase + DAS1800_CONTROL_A); |
955 | |
956 | das1800_ai_set_chanlist(dev, chanlist: &insn->chanspec, len: 1); |
957 | |
958 | /* protects the indirect addressing selected by DAS1800_SELECT */ |
959 | spin_lock_irqsave(&dev->spinlock, flags); |
960 | |
961 | /* select ai fifo register */ |
962 | outb(ADC, port: dev->iobase + DAS1800_SELECT); |
963 | |
964 | for (n = 0; n < insn->n; n++) { |
965 | /* trigger conversion */ |
966 | outb(value: 0, port: dev->iobase + DAS1800_FIFO); |
967 | |
968 | ret = comedi_timeout(dev, s, insn, cb: das1800_ai_eoc, context: 0); |
969 | if (ret) |
970 | break; |
971 | |
972 | dpnt = inw(port: dev->iobase + DAS1800_FIFO); |
973 | if (!is_unipolar) |
974 | dpnt = comedi_offset_munge(s, val: dpnt); |
975 | data[n] = dpnt; |
976 | } |
977 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
978 | |
979 | return ret ? ret : insn->n; |
980 | } |
981 | |
982 | static int das1800_ao_insn_write(struct comedi_device *dev, |
983 | struct comedi_subdevice *s, |
984 | struct comedi_insn *insn, |
985 | unsigned int *data) |
986 | { |
987 | unsigned int chan = CR_CHAN(insn->chanspec); |
988 | unsigned int update_chan = s->n_chan - 1; |
989 | unsigned long flags; |
990 | int i; |
991 | |
992 | /* protects the indirect addressing selected by DAS1800_SELECT */ |
993 | spin_lock_irqsave(&dev->spinlock, flags); |
994 | |
995 | for (i = 0; i < insn->n; i++) { |
996 | unsigned int val = data[i]; |
997 | |
998 | s->readback[chan] = val; |
999 | |
1000 | val = comedi_offset_munge(s, val); |
1001 | |
1002 | /* load this channel (and update if it's the last channel) */ |
1003 | outb(DAC(chan), port: dev->iobase + DAS1800_SELECT); |
1004 | outw(value: val, port: dev->iobase + DAS1800_DAC); |
1005 | |
1006 | /* update all channels */ |
1007 | if (chan != update_chan) { |
1008 | val = comedi_offset_munge(s, val: s->readback[update_chan]); |
1009 | |
1010 | outb(DAC(update_chan), port: dev->iobase + DAS1800_SELECT); |
1011 | outw(value: val, port: dev->iobase + DAS1800_DAC); |
1012 | } |
1013 | } |
1014 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
1015 | |
1016 | return insn->n; |
1017 | } |
1018 | |
1019 | static int das1800_di_insn_bits(struct comedi_device *dev, |
1020 | struct comedi_subdevice *s, |
1021 | struct comedi_insn *insn, |
1022 | unsigned int *data) |
1023 | { |
1024 | data[1] = inb(port: dev->iobase + DAS1800_DIGITAL) & 0xf; |
1025 | data[0] = 0; |
1026 | |
1027 | return insn->n; |
1028 | } |
1029 | |
1030 | static int das1800_do_insn_bits(struct comedi_device *dev, |
1031 | struct comedi_subdevice *s, |
1032 | struct comedi_insn *insn, |
1033 | unsigned int *data) |
1034 | { |
1035 | if (comedi_dio_update_state(s, data)) |
1036 | outb(value: s->state, port: dev->iobase + DAS1800_DIGITAL); |
1037 | |
1038 | data[1] = s->state; |
1039 | |
1040 | return insn->n; |
1041 | } |
1042 | |
1043 | static void das1800_init_dma(struct comedi_device *dev, |
1044 | struct comedi_devconfig *it) |
1045 | { |
1046 | struct das1800_private *devpriv = dev->private; |
1047 | unsigned int *dma_chan; |
1048 | |
1049 | /* |
1050 | * it->options[2] is DMA channel 0 |
1051 | * it->options[3] is DMA channel 1 |
1052 | * |
1053 | * Encode the DMA channels into 2 digit hexadecimal for switch. |
1054 | */ |
1055 | dma_chan = &it->options[2]; |
1056 | |
1057 | switch ((dma_chan[0] & 0x7) | (dma_chan[1] << 4)) { |
1058 | case 0x5: /* dma0 == 5 */ |
1059 | devpriv->dma_bits = DMA_CH5; |
1060 | break; |
1061 | case 0x6: /* dma0 == 6 */ |
1062 | devpriv->dma_bits = DMA_CH6; |
1063 | break; |
1064 | case 0x7: /* dma0 == 7 */ |
1065 | devpriv->dma_bits = DMA_CH7; |
1066 | break; |
1067 | case 0x65: /* dma0 == 5, dma1 == 6 */ |
1068 | devpriv->dma_bits = DMA_CH5_CH6; |
1069 | break; |
1070 | case 0x76: /* dma0 == 6, dma1 == 7 */ |
1071 | devpriv->dma_bits = DMA_CH6_CH7; |
1072 | break; |
1073 | case 0x57: /* dma0 == 7, dma1 == 5 */ |
1074 | devpriv->dma_bits = DMA_CH7_CH5; |
1075 | break; |
1076 | default: |
1077 | return; |
1078 | } |
1079 | |
1080 | /* DMA can use 1 or 2 buffers, each with a separate channel */ |
1081 | devpriv->dma = comedi_isadma_alloc(dev, n_desc: dma_chan[1] ? 2 : 1, |
1082 | dma_chan1: dma_chan[0], dma_chan2: dma_chan[1], |
1083 | DMA_BUF_SIZE, COMEDI_ISADMA_READ); |
1084 | if (!devpriv->dma) |
1085 | devpriv->dma_bits = 0; |
1086 | } |
1087 | |
1088 | static void das1800_free_dma(struct comedi_device *dev) |
1089 | { |
1090 | struct das1800_private *devpriv = dev->private; |
1091 | |
1092 | if (devpriv) |
1093 | comedi_isadma_free(dma: devpriv->dma); |
1094 | } |
1095 | |
1096 | static int das1800_probe(struct comedi_device *dev) |
1097 | { |
1098 | const struct das1800_board *board = dev->board_ptr; |
1099 | unsigned char id; |
1100 | |
1101 | id = (inb(port: dev->iobase + DAS1800_DIGITAL) >> 4) & 0xf; |
1102 | |
1103 | /* |
1104 | * The dev->board_ptr will be set by comedi_device_attach() if the |
1105 | * board name provided by the user matches a board->name in this |
1106 | * driver. If so, this function sanity checks the id to verify that |
1107 | * the board is correct. |
1108 | */ |
1109 | if (board) { |
1110 | if (board->id == id) |
1111 | return 0; |
1112 | dev_err(dev->class_dev, |
1113 | "probed id does not match board id (0x%x != 0x%x)\n" , |
1114 | id, board->id); |
1115 | return -ENODEV; |
1116 | } |
1117 | |
1118 | /* |
1119 | * If the dev->board_ptr is not set, the user is trying to attach |
1120 | * an unspecified board to this driver. In this case the id is used |
1121 | * to 'probe' for the dev->board_ptr. |
1122 | */ |
1123 | switch (id) { |
1124 | case DAS1800_ID_ST_DA: |
1125 | /* das-1701st-da, das-1702st-da, das-1801st-da, das-1802st-da */ |
1126 | board = &das1800_boards[BOARD_DAS1801ST_DA]; |
1127 | break; |
1128 | case DAS1800_ID_HR_DA: |
1129 | /* das-1702hr-da, das-1802hr-da */ |
1130 | board = &das1800_boards[BOARD_DAS1802HR_DA]; |
1131 | break; |
1132 | case DAS1800_ID_AO: |
1133 | /* das-1701ao, das-1702ao, das-1801ao, das-1802ao */ |
1134 | board = &das1800_boards[BOARD_DAS1801AO]; |
1135 | break; |
1136 | case DAS1800_ID_HR: |
1137 | /* das-1702hr, das-1802hr */ |
1138 | board = &das1800_boards[BOARD_DAS1802HR]; |
1139 | break; |
1140 | case DAS1800_ID_ST: |
1141 | /* das-1701st, das-1702st, das-1801st, das-1802st */ |
1142 | board = &das1800_boards[BOARD_DAS1801ST]; |
1143 | break; |
1144 | case DAS1800_ID_HC: |
1145 | /* das-1801hc, das-1802hc */ |
1146 | board = &das1800_boards[BOARD_DAS1801HC]; |
1147 | break; |
1148 | default: |
1149 | dev_err(dev->class_dev, "invalid probe id 0x%x\n" , id); |
1150 | return -ENODEV; |
1151 | } |
1152 | dev->board_ptr = board; |
1153 | dev->board_name = board->name; |
1154 | dev_warn(dev->class_dev, |
1155 | "probed id 0x%0x: %s series (not recommended)\n" , |
1156 | id, board->name); |
1157 | return 0; |
1158 | } |
1159 | |
1160 | static int das1800_attach(struct comedi_device *dev, |
1161 | struct comedi_devconfig *it) |
1162 | { |
1163 | const struct das1800_board *board; |
1164 | struct das1800_private *devpriv; |
1165 | struct comedi_subdevice *s; |
1166 | unsigned int irq = it->options[1]; |
1167 | bool is_16bit; |
1168 | int ret; |
1169 | int i; |
1170 | |
1171 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
1172 | if (!devpriv) |
1173 | return -ENOMEM; |
1174 | |
1175 | ret = comedi_request_region(dev, start: it->options[0], DAS1800_SIZE); |
1176 | if (ret) |
1177 | return ret; |
1178 | |
1179 | ret = das1800_probe(dev); |
1180 | if (ret) |
1181 | return ret; |
1182 | board = dev->board_ptr; |
1183 | |
1184 | is_16bit = board->id == DAS1800_ID_HR || board->id == DAS1800_ID_HR_DA; |
1185 | |
1186 | /* waveform 'ao' boards have additional io ports */ |
1187 | if (board->id == DAS1800_ID_AO) { |
1188 | unsigned long iobase2 = dev->iobase + IOBASE2; |
1189 | |
1190 | ret = __comedi_request_region(dev, start: iobase2, DAS1800_SIZE); |
1191 | if (ret) |
1192 | return ret; |
1193 | devpriv->iobase2 = iobase2; |
1194 | } |
1195 | |
1196 | if (irq == 3 || irq == 5 || irq == 7 || irq == 10 || irq == 11 || |
1197 | irq == 15) { |
1198 | ret = request_irq(irq, handler: das1800_interrupt, flags: 0, |
1199 | name: dev->board_name, dev); |
1200 | if (ret == 0) { |
1201 | dev->irq = irq; |
1202 | |
1203 | switch (irq) { |
1204 | case 3: |
1205 | devpriv->irq_dma_bits |= 0x8; |
1206 | break; |
1207 | case 5: |
1208 | devpriv->irq_dma_bits |= 0x10; |
1209 | break; |
1210 | case 7: |
1211 | devpriv->irq_dma_bits |= 0x18; |
1212 | break; |
1213 | case 10: |
1214 | devpriv->irq_dma_bits |= 0x28; |
1215 | break; |
1216 | case 11: |
1217 | devpriv->irq_dma_bits |= 0x30; |
1218 | break; |
1219 | case 15: |
1220 | devpriv->irq_dma_bits |= 0x38; |
1221 | break; |
1222 | } |
1223 | } |
1224 | } |
1225 | |
1226 | /* an irq and one dma channel is required to use dma */ |
1227 | if (dev->irq & it->options[2]) |
1228 | das1800_init_dma(dev, it); |
1229 | |
1230 | devpriv->fifo_buf = kmalloc_array(FIFO_SIZE, |
1231 | size: sizeof(*devpriv->fifo_buf), |
1232 | GFP_KERNEL); |
1233 | if (!devpriv->fifo_buf) |
1234 | return -ENOMEM; |
1235 | |
1236 | dev->pacer = comedi_8254_io_alloc(iobase: dev->iobase + DAS1800_COUNTER, |
1237 | I8254_OSC_BASE_5MHZ, I8254_IO8, regshift: 0); |
1238 | if (IS_ERR(ptr: dev->pacer)) |
1239 | return PTR_ERR(ptr: dev->pacer); |
1240 | |
1241 | ret = comedi_alloc_subdevices(dev, num_subdevices: 4); |
1242 | if (ret) |
1243 | return ret; |
1244 | |
1245 | /* |
1246 | * Analog Input subdevice |
1247 | * |
1248 | * The "hc" type boards have 64 analog input channels and a 64 |
1249 | * entry QRAM fifo. |
1250 | * |
1251 | * All the other board types have 16 on-board channels. Each channel |
1252 | * can be expanded to 16 channels with the addition of an EXP-1800 |
1253 | * expansion board for a total of 256 channels. The QRAM fifo on |
1254 | * these boards has 256 entries. |
1255 | * |
1256 | * From the datasheets it's not clear what the comedi channel to |
1257 | * actual physical channel mapping is when EXP-1800 boards are used. |
1258 | */ |
1259 | s = &dev->subdevices[0]; |
1260 | s->type = COMEDI_SUBD_AI; |
1261 | s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND; |
1262 | if (board->id != DAS1800_ID_HC) |
1263 | s->subdev_flags |= SDF_COMMON; |
1264 | s->n_chan = (board->id == DAS1800_ID_HC) ? 64 : 256; |
1265 | s->maxdata = is_16bit ? 0xffff : 0x0fff; |
1266 | s->range_table = board->is_01_series ? &das1801_ai_range |
1267 | : &das1802_ai_range; |
1268 | s->insn_read = das1800_ai_insn_read; |
1269 | if (dev->irq) { |
1270 | dev->read_subdev = s; |
1271 | s->subdev_flags |= SDF_CMD_READ; |
1272 | s->len_chanlist = s->n_chan; |
1273 | s->do_cmd = das1800_ai_cmd; |
1274 | s->do_cmdtest = das1800_ai_cmdtest; |
1275 | s->poll = das1800_ai_poll; |
1276 | s->cancel = das1800_ai_cancel; |
1277 | s->munge = das1800_ai_munge; |
1278 | } |
1279 | |
1280 | /* Analog Output subdevice */ |
1281 | s = &dev->subdevices[1]; |
1282 | if (board->id == DAS1800_ID_ST_DA || board->id == DAS1800_ID_HR_DA) { |
1283 | s->type = COMEDI_SUBD_AO; |
1284 | s->subdev_flags = SDF_WRITABLE; |
1285 | s->n_chan = (board->id == DAS1800_ID_ST_DA) ? 4 : 2; |
1286 | s->maxdata = is_16bit ? 0xffff : 0x0fff; |
1287 | s->range_table = &range_bipolar10; |
1288 | s->insn_write = das1800_ao_insn_write; |
1289 | |
1290 | ret = comedi_alloc_subdev_readback(s); |
1291 | if (ret) |
1292 | return ret; |
1293 | |
1294 | /* initialize all channels to 0V */ |
1295 | for (i = 0; i < s->n_chan; i++) { |
1296 | /* spinlock is not necessary during the attach */ |
1297 | outb(DAC(i), port: dev->iobase + DAS1800_SELECT); |
1298 | outw(value: 0, port: dev->iobase + DAS1800_DAC); |
1299 | } |
1300 | } else if (board->id == DAS1800_ID_AO) { |
1301 | /* |
1302 | * 'ao' boards have waveform analog outputs that are not |
1303 | * currently supported. |
1304 | */ |
1305 | s->type = COMEDI_SUBD_UNUSED; |
1306 | } else { |
1307 | s->type = COMEDI_SUBD_UNUSED; |
1308 | } |
1309 | |
1310 | /* Digital Input subdevice */ |
1311 | s = &dev->subdevices[2]; |
1312 | s->type = COMEDI_SUBD_DI; |
1313 | s->subdev_flags = SDF_READABLE; |
1314 | s->n_chan = 4; |
1315 | s->maxdata = 1; |
1316 | s->range_table = &range_digital; |
1317 | s->insn_bits = das1800_di_insn_bits; |
1318 | |
1319 | /* Digital Output subdevice */ |
1320 | s = &dev->subdevices[3]; |
1321 | s->type = COMEDI_SUBD_DO; |
1322 | s->subdev_flags = SDF_WRITABLE; |
1323 | s->n_chan = (board->id == DAS1800_ID_HC) ? 8 : 4; |
1324 | s->maxdata = 1; |
1325 | s->range_table = &range_digital; |
1326 | s->insn_bits = das1800_do_insn_bits; |
1327 | |
1328 | das1800_ai_cancel(dev, s: dev->read_subdev); |
1329 | |
1330 | /* initialize digital out channels */ |
1331 | outb(value: 0, port: dev->iobase + DAS1800_DIGITAL); |
1332 | |
1333 | return 0; |
1334 | }; |
1335 | |
1336 | static void das1800_detach(struct comedi_device *dev) |
1337 | { |
1338 | struct das1800_private *devpriv = dev->private; |
1339 | |
1340 | das1800_free_dma(dev); |
1341 | if (devpriv) { |
1342 | kfree(objp: devpriv->fifo_buf); |
1343 | if (devpriv->iobase2) |
1344 | release_region(devpriv->iobase2, DAS1800_SIZE); |
1345 | } |
1346 | comedi_legacy_detach(dev); |
1347 | } |
1348 | |
1349 | static struct comedi_driver das1800_driver = { |
1350 | .driver_name = "das1800" , |
1351 | .module = THIS_MODULE, |
1352 | .attach = das1800_attach, |
1353 | .detach = das1800_detach, |
1354 | .num_names = ARRAY_SIZE(das1800_boards), |
1355 | .board_name = &das1800_boards[0].name, |
1356 | .offset = sizeof(struct das1800_board), |
1357 | }; |
1358 | module_comedi_driver(das1800_driver); |
1359 | |
1360 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
1361 | MODULE_DESCRIPTION("Comedi driver for DAS1800 compatible ISA boards" ); |
1362 | MODULE_LICENSE("GPL" ); |
1363 | |