1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * addi_apci_3120.c |
4 | * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. |
5 | * |
6 | * ADDI-DATA GmbH |
7 | * Dieselstrasse 3 |
8 | * D-77833 Ottersweier |
9 | * Tel: +19(0)7223/9493-0 |
10 | * Fax: +49(0)7223/9493-92 |
11 | * http://www.addi-data.com |
12 | * info@addi-data.com |
13 | */ |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/comedi/comedi_pci.h> |
18 | |
19 | #include "amcc_s5933.h" |
20 | |
21 | /* |
22 | * PCI BAR 0 register map (devpriv->amcc) |
23 | * see amcc_s5933.h for register and bit defines |
24 | */ |
25 | #define APCI3120_FIFO_ADVANCE_ON_BYTE_2 BIT(29) |
26 | |
27 | /* |
28 | * PCI BAR 1 register map (dev->iobase) |
29 | */ |
30 | #define APCI3120_AI_FIFO_REG 0x00 |
31 | #define APCI3120_CTRL_REG 0x00 |
32 | #define APCI3120_CTRL_EXT_TRIG BIT(15) |
33 | #define APCI3120_CTRL_GATE(x) BIT(12 + (x)) |
34 | #define APCI3120_CTRL_PR(x) (((x) & 0xf) << 8) |
35 | #define APCI3120_CTRL_PA(x) (((x) & 0xf) << 0) |
36 | #define APCI3120_AI_SOFTTRIG_REG 0x02 |
37 | #define APCI3120_STATUS_REG 0x02 |
38 | #define APCI3120_STATUS_EOC_INT BIT(15) |
39 | #define APCI3120_STATUS_AMCC_INT BIT(14) |
40 | #define APCI3120_STATUS_EOS_INT BIT(13) |
41 | #define APCI3120_STATUS_TIMER2_INT BIT(12) |
42 | #define APCI3120_STATUS_INT_MASK (0xf << 12) |
43 | #define APCI3120_STATUS_TO_DI_BITS(x) (((x) >> 8) & 0xf) |
44 | #define APCI3120_STATUS_TO_VERSION(x) (((x) >> 4) & 0xf) |
45 | #define APCI3120_STATUS_FIFO_FULL BIT(2) |
46 | #define APCI3120_STATUS_FIFO_EMPTY BIT(1) |
47 | #define APCI3120_STATUS_DA_READY BIT(0) |
48 | #define APCI3120_TIMER_REG 0x04 |
49 | #define APCI3120_CHANLIST_REG 0x06 |
50 | #define APCI3120_CHANLIST_INDEX(x) (((x) & 0xf) << 8) |
51 | #define APCI3120_CHANLIST_UNIPOLAR BIT(7) |
52 | #define APCI3120_CHANLIST_GAIN(x) (((x) & 0x3) << 4) |
53 | #define APCI3120_CHANLIST_MUX(x) (((x) & 0xf) << 0) |
54 | #define APCI3120_AO_REG(x) (0x08 + (((x) / 4) * 2)) |
55 | #define APCI3120_AO_MUX(x) (((x) & 0x3) << 14) |
56 | #define APCI3120_AO_DATA(x) ((x) << 0) |
57 | #define APCI3120_TIMER_MODE_REG 0x0c |
58 | #define APCI3120_TIMER_MODE(_t, _m) ((_m) << ((_t) * 2)) |
59 | #define APCI3120_TIMER_MODE0 0 /* I8254_MODE0 */ |
60 | #define APCI3120_TIMER_MODE2 1 /* I8254_MODE2 */ |
61 | #define APCI3120_TIMER_MODE4 2 /* I8254_MODE4 */ |
62 | #define APCI3120_TIMER_MODE5 3 /* I8254_MODE5 */ |
63 | #define APCI3120_TIMER_MODE_MASK(_t) (3 << ((_t) * 2)) |
64 | #define APCI3120_CTR0_REG 0x0d |
65 | #define APCI3120_CTR0_DO_BITS(x) ((x) << 4) |
66 | #define APCI3120_CTR0_TIMER_SEL(x) ((x) << 0) |
67 | #define APCI3120_MODE_REG 0x0e |
68 | #define APCI3120_MODE_TIMER2_CLK(x) (((x) & 0x3) << 6) |
69 | #define APCI3120_MODE_TIMER2_CLK_OSC APCI3120_MODE_TIMER2_CLK(0) |
70 | #define APCI3120_MODE_TIMER2_CLK_OUT1 APCI3120_MODE_TIMER2_CLK(1) |
71 | #define APCI3120_MODE_TIMER2_CLK_EOC APCI3120_MODE_TIMER2_CLK(2) |
72 | #define APCI3120_MODE_TIMER2_CLK_EOS APCI3120_MODE_TIMER2_CLK(3) |
73 | #define APCI3120_MODE_TIMER2_CLK_MASK APCI3120_MODE_TIMER2_CLK(3) |
74 | #define APCI3120_MODE_TIMER2_AS(x) (((x) & 0x3) << 4) |
75 | #define APCI3120_MODE_TIMER2_AS_TIMER APCI3120_MODE_TIMER2_AS(0) |
76 | #define APCI3120_MODE_TIMER2_AS_COUNTER APCI3120_MODE_TIMER2_AS(1) |
77 | #define APCI3120_MODE_TIMER2_AS_WDOG APCI3120_MODE_TIMER2_AS(2) |
78 | #define APCI3120_MODE_TIMER2_AS_MASK APCI3120_MODE_TIMER2_AS(3) |
79 | #define APCI3120_MODE_SCAN_ENA BIT(3) |
80 | #define APCI3120_MODE_TIMER2_IRQ_ENA BIT(2) |
81 | #define APCI3120_MODE_EOS_IRQ_ENA BIT(1) |
82 | #define APCI3120_MODE_EOC_IRQ_ENA BIT(0) |
83 | |
84 | /* |
85 | * PCI BAR 2 register map (devpriv->addon) |
86 | */ |
87 | #define APCI3120_ADDON_ADDR_REG 0x00 |
88 | #define APCI3120_ADDON_DATA_REG 0x02 |
89 | #define APCI3120_ADDON_CTRL_REG 0x04 |
90 | #define APCI3120_ADDON_CTRL_AMWEN_ENA BIT(1) |
91 | #define APCI3120_ADDON_CTRL_A2P_FIFO_ENA BIT(0) |
92 | |
93 | /* |
94 | * Board revisions |
95 | */ |
96 | #define APCI3120_REVA 0xa |
97 | #define APCI3120_REVB 0xb |
98 | #define APCI3120_REVA_OSC_BASE 70 /* 70ns = 14.29MHz */ |
99 | #define APCI3120_REVB_OSC_BASE 50 /* 50ns = 20MHz */ |
100 | |
101 | static const struct comedi_lrange apci3120_ai_range = { |
102 | 8, { |
103 | BIP_RANGE(10), |
104 | BIP_RANGE(5), |
105 | BIP_RANGE(2), |
106 | BIP_RANGE(1), |
107 | UNI_RANGE(10), |
108 | UNI_RANGE(5), |
109 | UNI_RANGE(2), |
110 | UNI_RANGE(1) |
111 | } |
112 | }; |
113 | |
114 | enum apci3120_boardid { |
115 | BOARD_APCI3120, |
116 | BOARD_APCI3001, |
117 | }; |
118 | |
119 | struct apci3120_board { |
120 | const char *name; |
121 | unsigned int ai_is_16bit:1; |
122 | unsigned int has_ao:1; |
123 | }; |
124 | |
125 | static const struct apci3120_board apci3120_boardtypes[] = { |
126 | [BOARD_APCI3120] = { |
127 | .name = "apci3120" , |
128 | .ai_is_16bit = 1, |
129 | .has_ao = 1, |
130 | }, |
131 | [BOARD_APCI3001] = { |
132 | .name = "apci3001" , |
133 | }, |
134 | }; |
135 | |
136 | struct apci3120_dmabuf { |
137 | unsigned short *virt; |
138 | dma_addr_t hw; |
139 | unsigned int size; |
140 | unsigned int use_size; |
141 | }; |
142 | |
143 | struct apci3120_private { |
144 | unsigned long amcc; |
145 | unsigned long addon; |
146 | unsigned int osc_base; |
147 | unsigned int use_dma:1; |
148 | unsigned int use_double_buffer:1; |
149 | unsigned int cur_dmabuf:1; |
150 | struct apci3120_dmabuf dmabuf[2]; |
151 | unsigned char do_bits; |
152 | unsigned char timer_mode; |
153 | unsigned char mode; |
154 | unsigned short ctrl; |
155 | }; |
156 | |
157 | static void apci3120_addon_write(struct comedi_device *dev, |
158 | unsigned int val, unsigned int reg) |
159 | { |
160 | struct apci3120_private *devpriv = dev->private; |
161 | |
162 | /* 16-bit interface for AMCC add-on registers */ |
163 | |
164 | outw(value: reg, port: devpriv->addon + APCI3120_ADDON_ADDR_REG); |
165 | outw(value: val & 0xffff, port: devpriv->addon + APCI3120_ADDON_DATA_REG); |
166 | |
167 | outw(value: reg + 2, port: devpriv->addon + APCI3120_ADDON_ADDR_REG); |
168 | outw(value: (val >> 16) & 0xffff, port: devpriv->addon + APCI3120_ADDON_DATA_REG); |
169 | } |
170 | |
171 | static void apci3120_init_dma(struct comedi_device *dev, |
172 | struct apci3120_dmabuf *dmabuf) |
173 | { |
174 | struct apci3120_private *devpriv = dev->private; |
175 | |
176 | /* AMCC - enable transfer count and reset A2P FIFO */ |
177 | outl(AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO, |
178 | port: devpriv->amcc + AMCC_OP_REG_AGCSTS); |
179 | |
180 | /* Add-On - enable transfer count and reset A2P FIFO */ |
181 | apci3120_addon_write(dev, AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO, |
182 | AMCC_OP_REG_AGCSTS); |
183 | |
184 | /* AMCC - enable transfers and reset A2P flags */ |
185 | outl(RESET_A2P_FLAGS | EN_A2P_TRANSFERS, |
186 | port: devpriv->amcc + AMCC_OP_REG_MCSR); |
187 | |
188 | /* Add-On - DMA start address */ |
189 | apci3120_addon_write(dev, val: dmabuf->hw, AMCC_OP_REG_AMWAR); |
190 | |
191 | /* Add-On - Number of acquisitions */ |
192 | apci3120_addon_write(dev, val: dmabuf->use_size, AMCC_OP_REG_AMWTC); |
193 | |
194 | /* AMCC - enable write complete (DMA) and set FIFO advance */ |
195 | outl(APCI3120_FIFO_ADVANCE_ON_BYTE_2 | AINT_WRITE_COMPL, |
196 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
197 | |
198 | /* Add-On - enable DMA */ |
199 | outw(APCI3120_ADDON_CTRL_AMWEN_ENA | APCI3120_ADDON_CTRL_A2P_FIFO_ENA, |
200 | port: devpriv->addon + APCI3120_ADDON_CTRL_REG); |
201 | } |
202 | |
203 | static void apci3120_setup_dma(struct comedi_device *dev, |
204 | struct comedi_subdevice *s) |
205 | { |
206 | struct apci3120_private *devpriv = dev->private; |
207 | struct comedi_cmd *cmd = &s->async->cmd; |
208 | struct apci3120_dmabuf *dmabuf0 = &devpriv->dmabuf[0]; |
209 | struct apci3120_dmabuf *dmabuf1 = &devpriv->dmabuf[1]; |
210 | unsigned int dmalen0 = dmabuf0->size; |
211 | unsigned int dmalen1 = dmabuf1->size; |
212 | unsigned int scan_bytes; |
213 | |
214 | scan_bytes = comedi_samples_to_bytes(s, nsamples: cmd->scan_end_arg); |
215 | |
216 | if (cmd->stop_src == TRIG_COUNT) { |
217 | /* |
218 | * Must we fill full first buffer? And must we fill |
219 | * full second buffer when first is once filled? |
220 | */ |
221 | if (dmalen0 > (cmd->stop_arg * scan_bytes)) |
222 | dmalen0 = cmd->stop_arg * scan_bytes; |
223 | else if (dmalen1 > (cmd->stop_arg * scan_bytes - dmalen0)) |
224 | dmalen1 = cmd->stop_arg * scan_bytes - dmalen0; |
225 | } |
226 | |
227 | if (cmd->flags & CMDF_WAKE_EOS) { |
228 | /* don't we want wake up every scan? */ |
229 | if (dmalen0 > scan_bytes) { |
230 | dmalen0 = scan_bytes; |
231 | if (cmd->scan_end_arg & 1) |
232 | dmalen0 += 2; |
233 | } |
234 | if (dmalen1 > scan_bytes) { |
235 | dmalen1 = scan_bytes; |
236 | if (cmd->scan_end_arg & 1) |
237 | dmalen1 -= 2; |
238 | if (dmalen1 < 4) |
239 | dmalen1 = 4; |
240 | } |
241 | } else { |
242 | /* isn't output buff smaller that our DMA buff? */ |
243 | if (dmalen0 > s->async->prealloc_bufsz) |
244 | dmalen0 = s->async->prealloc_bufsz; |
245 | if (dmalen1 > s->async->prealloc_bufsz) |
246 | dmalen1 = s->async->prealloc_bufsz; |
247 | } |
248 | dmabuf0->use_size = dmalen0; |
249 | dmabuf1->use_size = dmalen1; |
250 | |
251 | apci3120_init_dma(dev, dmabuf: dmabuf0); |
252 | } |
253 | |
254 | /* |
255 | * There are three timers on the board. They all use the same base |
256 | * clock with a fixed prescaler for each timer. The base clock used |
257 | * depends on the board version and type. |
258 | * |
259 | * APCI-3120 Rev A boards OSC = 14.29MHz base clock (~70ns) |
260 | * APCI-3120 Rev B boards OSC = 20MHz base clock (50ns) |
261 | * APCI-3001 boards OSC = 20MHz base clock (50ns) |
262 | * |
263 | * The prescalers for each timer are: |
264 | * Timer 0 CLK = OSC/10 |
265 | * Timer 1 CLK = OSC/1000 |
266 | * Timer 2 CLK = OSC/1000 |
267 | */ |
268 | static unsigned int apci3120_ns_to_timer(struct comedi_device *dev, |
269 | unsigned int timer, |
270 | unsigned int ns, |
271 | unsigned int flags) |
272 | { |
273 | struct apci3120_private *devpriv = dev->private; |
274 | unsigned int prescale = (timer == 0) ? 10 : 1000; |
275 | unsigned int timer_base = devpriv->osc_base * prescale; |
276 | unsigned int divisor; |
277 | |
278 | switch (flags & CMDF_ROUND_MASK) { |
279 | case CMDF_ROUND_UP: |
280 | divisor = DIV_ROUND_UP(ns, timer_base); |
281 | break; |
282 | case CMDF_ROUND_DOWN: |
283 | divisor = ns / timer_base; |
284 | break; |
285 | case CMDF_ROUND_NEAREST: |
286 | default: |
287 | divisor = DIV_ROUND_CLOSEST(ns, timer_base); |
288 | break; |
289 | } |
290 | |
291 | if (timer == 2) { |
292 | /* timer 2 is 24-bits */ |
293 | if (divisor > 0x00ffffff) |
294 | divisor = 0x00ffffff; |
295 | } else { |
296 | /* timers 0 and 1 are 16-bits */ |
297 | if (divisor > 0xffff) |
298 | divisor = 0xffff; |
299 | } |
300 | /* the timers require a minimum divisor of 2 */ |
301 | if (divisor < 2) |
302 | divisor = 2; |
303 | |
304 | return divisor; |
305 | } |
306 | |
307 | static void apci3120_clr_timer2_interrupt(struct comedi_device *dev) |
308 | { |
309 | /* a dummy read of APCI3120_CTR0_REG clears the timer 2 interrupt */ |
310 | inb(port: dev->iobase + APCI3120_CTR0_REG); |
311 | } |
312 | |
313 | static void apci3120_timer_write(struct comedi_device *dev, |
314 | unsigned int timer, unsigned int val) |
315 | { |
316 | struct apci3120_private *devpriv = dev->private; |
317 | |
318 | /* write 16-bit value to timer (lower 16-bits of timer 2) */ |
319 | outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | |
320 | APCI3120_CTR0_TIMER_SEL(timer), |
321 | port: dev->iobase + APCI3120_CTR0_REG); |
322 | outw(value: val & 0xffff, port: dev->iobase + APCI3120_TIMER_REG); |
323 | |
324 | if (timer == 2) { |
325 | /* write upper 16-bits to timer 2 */ |
326 | outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | |
327 | APCI3120_CTR0_TIMER_SEL(timer + 1), |
328 | port: dev->iobase + APCI3120_CTR0_REG); |
329 | outw(value: (val >> 16) & 0xffff, port: dev->iobase + APCI3120_TIMER_REG); |
330 | } |
331 | } |
332 | |
333 | static unsigned int apci3120_timer_read(struct comedi_device *dev, |
334 | unsigned int timer) |
335 | { |
336 | struct apci3120_private *devpriv = dev->private; |
337 | unsigned int val; |
338 | |
339 | /* read 16-bit value from timer (lower 16-bits of timer 2) */ |
340 | outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | |
341 | APCI3120_CTR0_TIMER_SEL(timer), |
342 | port: dev->iobase + APCI3120_CTR0_REG); |
343 | val = inw(port: dev->iobase + APCI3120_TIMER_REG); |
344 | |
345 | if (timer == 2) { |
346 | /* read upper 16-bits from timer 2 */ |
347 | outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | |
348 | APCI3120_CTR0_TIMER_SEL(timer + 1), |
349 | port: dev->iobase + APCI3120_CTR0_REG); |
350 | val |= (inw(port: dev->iobase + APCI3120_TIMER_REG) << 16); |
351 | } |
352 | |
353 | return val; |
354 | } |
355 | |
356 | static void apci3120_timer_set_mode(struct comedi_device *dev, |
357 | unsigned int timer, unsigned int mode) |
358 | { |
359 | struct apci3120_private *devpriv = dev->private; |
360 | |
361 | devpriv->timer_mode &= ~APCI3120_TIMER_MODE_MASK(timer); |
362 | devpriv->timer_mode |= APCI3120_TIMER_MODE(timer, mode); |
363 | outb(value: devpriv->timer_mode, port: dev->iobase + APCI3120_TIMER_MODE_REG); |
364 | } |
365 | |
366 | static void apci3120_timer_enable(struct comedi_device *dev, |
367 | unsigned int timer, bool enable) |
368 | { |
369 | struct apci3120_private *devpriv = dev->private; |
370 | |
371 | if (enable) |
372 | devpriv->ctrl |= APCI3120_CTRL_GATE(timer); |
373 | else |
374 | devpriv->ctrl &= ~APCI3120_CTRL_GATE(timer); |
375 | outw(value: devpriv->ctrl, port: dev->iobase + APCI3120_CTRL_REG); |
376 | } |
377 | |
378 | static void apci3120_exttrig_enable(struct comedi_device *dev, bool enable) |
379 | { |
380 | struct apci3120_private *devpriv = dev->private; |
381 | |
382 | if (enable) |
383 | devpriv->ctrl |= APCI3120_CTRL_EXT_TRIG; |
384 | else |
385 | devpriv->ctrl &= ~APCI3120_CTRL_EXT_TRIG; |
386 | outw(value: devpriv->ctrl, port: dev->iobase + APCI3120_CTRL_REG); |
387 | } |
388 | |
389 | static void apci3120_set_chanlist(struct comedi_device *dev, |
390 | struct comedi_subdevice *s, |
391 | int n_chan, unsigned int *chanlist) |
392 | { |
393 | struct apci3120_private *devpriv = dev->private; |
394 | int i; |
395 | |
396 | /* set chanlist for scan */ |
397 | for (i = 0; i < n_chan; i++) { |
398 | unsigned int chan = CR_CHAN(chanlist[i]); |
399 | unsigned int range = CR_RANGE(chanlist[i]); |
400 | unsigned int val; |
401 | |
402 | val = APCI3120_CHANLIST_MUX(chan) | |
403 | APCI3120_CHANLIST_GAIN(range) | |
404 | APCI3120_CHANLIST_INDEX(i); |
405 | |
406 | if (comedi_range_is_unipolar(s, range)) |
407 | val |= APCI3120_CHANLIST_UNIPOLAR; |
408 | |
409 | outw(value: val, port: dev->iobase + APCI3120_CHANLIST_REG); |
410 | } |
411 | |
412 | /* a dummy read of APCI3120_TIMER_MODE_REG resets the ai FIFO */ |
413 | inw(port: dev->iobase + APCI3120_TIMER_MODE_REG); |
414 | |
415 | /* set scan length (PR) and scan start (PA) */ |
416 | devpriv->ctrl = APCI3120_CTRL_PR(n_chan - 1) | APCI3120_CTRL_PA(0); |
417 | outw(value: devpriv->ctrl, port: dev->iobase + APCI3120_CTRL_REG); |
418 | |
419 | /* enable chanlist scanning if necessary */ |
420 | if (n_chan > 1) |
421 | devpriv->mode |= APCI3120_MODE_SCAN_ENA; |
422 | } |
423 | |
424 | static void apci3120_interrupt_dma(struct comedi_device *dev, |
425 | struct comedi_subdevice *s) |
426 | { |
427 | struct apci3120_private *devpriv = dev->private; |
428 | struct comedi_async *async = s->async; |
429 | struct comedi_cmd *cmd = &async->cmd; |
430 | struct apci3120_dmabuf *dmabuf; |
431 | unsigned int nbytes; |
432 | unsigned int nsamples; |
433 | |
434 | dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf]; |
435 | |
436 | nbytes = dmabuf->use_size - inl(port: devpriv->amcc + AMCC_OP_REG_MWTC); |
437 | |
438 | if (nbytes < dmabuf->use_size) |
439 | dev_err(dev->class_dev, "Interrupted DMA transfer!\n" ); |
440 | if (nbytes & 1) { |
441 | dev_err(dev->class_dev, "Odd count of bytes in DMA ring!\n" ); |
442 | async->events |= COMEDI_CB_ERROR; |
443 | return; |
444 | } |
445 | |
446 | nsamples = comedi_bytes_to_samples(s, nbytes); |
447 | if (nsamples) { |
448 | comedi_buf_write_samples(s, data: dmabuf->virt, nsamples); |
449 | |
450 | if (!(cmd->flags & CMDF_WAKE_EOS)) |
451 | async->events |= COMEDI_CB_EOS; |
452 | } |
453 | |
454 | if ((async->events & COMEDI_CB_CANCEL_MASK) || |
455 | (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)) |
456 | return; |
457 | |
458 | if (devpriv->use_double_buffer) { |
459 | /* switch DMA buffers for next interrupt */ |
460 | devpriv->cur_dmabuf = !devpriv->cur_dmabuf; |
461 | dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf]; |
462 | apci3120_init_dma(dev, dmabuf); |
463 | } else { |
464 | /* restart DMA if not using double buffering */ |
465 | apci3120_init_dma(dev, dmabuf); |
466 | } |
467 | } |
468 | |
469 | static irqreturn_t apci3120_interrupt(int irq, void *d) |
470 | { |
471 | struct comedi_device *dev = d; |
472 | struct apci3120_private *devpriv = dev->private; |
473 | struct comedi_subdevice *s = dev->read_subdev; |
474 | struct comedi_async *async = s->async; |
475 | struct comedi_cmd *cmd = &async->cmd; |
476 | unsigned int status; |
477 | unsigned int int_amcc; |
478 | |
479 | status = inw(port: dev->iobase + APCI3120_STATUS_REG); |
480 | int_amcc = inl(port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
481 | |
482 | if (!(status & APCI3120_STATUS_INT_MASK) && |
483 | !(int_amcc & ANY_S593X_INT)) { |
484 | dev_err(dev->class_dev, "IRQ from unknown source\n" ); |
485 | return IRQ_NONE; |
486 | } |
487 | |
488 | outl(value: int_amcc | AINT_INT_MASK, port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
489 | |
490 | if (devpriv->ctrl & APCI3120_CTRL_EXT_TRIG) |
491 | apci3120_exttrig_enable(dev, enable: false); |
492 | |
493 | if (int_amcc & MASTER_ABORT_INT) |
494 | dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n" ); |
495 | if (int_amcc & TARGET_ABORT_INT) |
496 | dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n" ); |
497 | |
498 | if ((status & APCI3120_STATUS_EOS_INT) && |
499 | (devpriv->mode & APCI3120_MODE_EOS_IRQ_ENA)) { |
500 | unsigned short val; |
501 | int i; |
502 | |
503 | for (i = 0; i < cmd->chanlist_len; i++) { |
504 | val = inw(port: dev->iobase + APCI3120_AI_FIFO_REG); |
505 | comedi_buf_write_samples(s, data: &val, nsamples: 1); |
506 | } |
507 | |
508 | devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA; |
509 | outb(value: devpriv->mode, port: dev->iobase + APCI3120_MODE_REG); |
510 | } |
511 | |
512 | if (status & APCI3120_STATUS_TIMER2_INT) { |
513 | /* |
514 | * for safety... |
515 | * timer2 interrupts are not enabled in the driver |
516 | */ |
517 | apci3120_clr_timer2_interrupt(dev); |
518 | } |
519 | |
520 | if (status & APCI3120_STATUS_AMCC_INT) { |
521 | /* AMCC- Clear write complete interrupt (DMA) */ |
522 | outl(AINT_WT_COMPLETE, port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
523 | |
524 | /* do some data transfer */ |
525 | apci3120_interrupt_dma(dev, s); |
526 | } |
527 | |
528 | if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) |
529 | async->events |= COMEDI_CB_EOA; |
530 | |
531 | comedi_handle_events(dev, s); |
532 | |
533 | return IRQ_HANDLED; |
534 | } |
535 | |
536 | static int apci3120_ai_cmd(struct comedi_device *dev, |
537 | struct comedi_subdevice *s) |
538 | { |
539 | struct apci3120_private *devpriv = dev->private; |
540 | struct comedi_cmd *cmd = &s->async->cmd; |
541 | unsigned int divisor; |
542 | |
543 | /* set default mode bits */ |
544 | devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC | |
545 | APCI3120_MODE_TIMER2_AS_TIMER; |
546 | |
547 | /* AMCC- Clear write complete interrupt (DMA) */ |
548 | outl(AINT_WT_COMPLETE, port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
549 | |
550 | devpriv->cur_dmabuf = 0; |
551 | |
552 | /* load chanlist for command scan */ |
553 | apci3120_set_chanlist(dev, s, n_chan: cmd->chanlist_len, chanlist: cmd->chanlist); |
554 | |
555 | if (cmd->start_src == TRIG_EXT) |
556 | apci3120_exttrig_enable(dev, enable: true); |
557 | |
558 | if (cmd->scan_begin_src == TRIG_TIMER) { |
559 | /* |
560 | * Timer 1 is used in MODE2 (rate generator) to set the |
561 | * start time for each scan. |
562 | */ |
563 | divisor = apci3120_ns_to_timer(dev, timer: 1, ns: cmd->scan_begin_arg, |
564 | flags: cmd->flags); |
565 | apci3120_timer_set_mode(dev, timer: 1, APCI3120_TIMER_MODE2); |
566 | apci3120_timer_write(dev, timer: 1, val: divisor); |
567 | } |
568 | |
569 | /* |
570 | * Timer 0 is used in MODE2 (rate generator) to set the conversion |
571 | * time for each acquisition. |
572 | */ |
573 | divisor = apci3120_ns_to_timer(dev, timer: 0, ns: cmd->convert_arg, flags: cmd->flags); |
574 | apci3120_timer_set_mode(dev, timer: 0, APCI3120_TIMER_MODE2); |
575 | apci3120_timer_write(dev, timer: 0, val: divisor); |
576 | |
577 | if (devpriv->use_dma) |
578 | apci3120_setup_dma(dev, s); |
579 | else |
580 | devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA; |
581 | |
582 | /* set mode to enable acquisition */ |
583 | outb(value: devpriv->mode, port: dev->iobase + APCI3120_MODE_REG); |
584 | |
585 | if (cmd->scan_begin_src == TRIG_TIMER) |
586 | apci3120_timer_enable(dev, timer: 1, enable: true); |
587 | apci3120_timer_enable(dev, timer: 0, enable: true); |
588 | |
589 | return 0; |
590 | } |
591 | |
592 | static int apci3120_ai_cmdtest(struct comedi_device *dev, |
593 | struct comedi_subdevice *s, |
594 | struct comedi_cmd *cmd) |
595 | { |
596 | unsigned int arg; |
597 | int err = 0; |
598 | |
599 | /* Step 1 : check if triggers are trivially valid */ |
600 | |
601 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_NOW | TRIG_EXT); |
602 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
603 | TRIG_TIMER | TRIG_FOLLOW); |
604 | err |= comedi_check_trigger_src(src: &cmd->convert_src, TRIG_TIMER); |
605 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
606 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_COUNT | TRIG_NONE); |
607 | |
608 | if (err) |
609 | return 1; |
610 | |
611 | /* Step 2a : make sure trigger sources are unique */ |
612 | |
613 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
614 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
615 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
616 | |
617 | /* Step 2b : and mutually compatible */ |
618 | |
619 | if (err) |
620 | return 2; |
621 | |
622 | /* Step 3: check if arguments are trivially valid */ |
623 | |
624 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
625 | |
626 | if (cmd->scan_begin_src == TRIG_TIMER) { /* Test Delay timing */ |
627 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, |
628 | val: 100000); |
629 | } |
630 | |
631 | /* minimum conversion time per sample is 10us */ |
632 | err |= comedi_check_trigger_arg_min(arg: &cmd->convert_arg, val: 10000); |
633 | |
634 | err |= comedi_check_trigger_arg_min(arg: &cmd->chanlist_len, val: 1); |
635 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
636 | val: cmd->chanlist_len); |
637 | |
638 | if (cmd->stop_src == TRIG_COUNT) |
639 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
640 | else /* TRIG_NONE */ |
641 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
642 | |
643 | if (err) |
644 | return 3; |
645 | |
646 | /* Step 4: fix up any arguments */ |
647 | |
648 | if (cmd->scan_begin_src == TRIG_TIMER) { |
649 | /* scan begin must be larger than the scan time */ |
650 | arg = cmd->convert_arg * cmd->scan_end_arg; |
651 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, val: arg); |
652 | } |
653 | |
654 | if (err) |
655 | return 4; |
656 | |
657 | /* Step 5: check channel list if it exists */ |
658 | |
659 | return 0; |
660 | } |
661 | |
662 | static int apci3120_cancel(struct comedi_device *dev, |
663 | struct comedi_subdevice *s) |
664 | { |
665 | struct apci3120_private *devpriv = dev->private; |
666 | |
667 | /* Add-On - disable DMA */ |
668 | outw(value: 0, port: devpriv->addon + 4); |
669 | |
670 | /* Add-On - disable bus master */ |
671 | apci3120_addon_write(dev, val: 0, AMCC_OP_REG_AGCSTS); |
672 | |
673 | /* AMCC - disable bus master */ |
674 | outl(value: 0, port: devpriv->amcc + AMCC_OP_REG_MCSR); |
675 | |
676 | /* disable all counters, ext trigger, and reset scan */ |
677 | devpriv->ctrl = 0; |
678 | outw(value: devpriv->ctrl, port: dev->iobase + APCI3120_CTRL_REG); |
679 | |
680 | /* DISABLE_ALL_INTERRUPT */ |
681 | devpriv->mode = 0; |
682 | outb(value: devpriv->mode, port: dev->iobase + APCI3120_MODE_REG); |
683 | |
684 | inw(port: dev->iobase + APCI3120_STATUS_REG); |
685 | devpriv->cur_dmabuf = 0; |
686 | |
687 | return 0; |
688 | } |
689 | |
690 | static int apci3120_ai_eoc(struct comedi_device *dev, |
691 | struct comedi_subdevice *s, |
692 | struct comedi_insn *insn, |
693 | unsigned long context) |
694 | { |
695 | unsigned int status; |
696 | |
697 | status = inw(port: dev->iobase + APCI3120_STATUS_REG); |
698 | if ((status & APCI3120_STATUS_EOC_INT) == 0) |
699 | return 0; |
700 | return -EBUSY; |
701 | } |
702 | |
703 | static int apci3120_ai_insn_read(struct comedi_device *dev, |
704 | struct comedi_subdevice *s, |
705 | struct comedi_insn *insn, |
706 | unsigned int *data) |
707 | { |
708 | struct apci3120_private *devpriv = dev->private; |
709 | unsigned int divisor; |
710 | int ret; |
711 | int i; |
712 | |
713 | /* set mode for A/D conversions by software trigger with timer 0 */ |
714 | devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC | |
715 | APCI3120_MODE_TIMER2_AS_TIMER; |
716 | outb(value: devpriv->mode, port: dev->iobase + APCI3120_MODE_REG); |
717 | |
718 | /* load chanlist for single channel scan */ |
719 | apci3120_set_chanlist(dev, s, n_chan: 1, chanlist: &insn->chanspec); |
720 | |
721 | /* |
722 | * Timer 0 is used in MODE4 (software triggered strobe) to set the |
723 | * conversion time for each acquisition. Each conversion is triggered |
724 | * when the divisor is written to the timer, The conversion is done |
725 | * when the EOC bit in the status register is '0'. |
726 | */ |
727 | apci3120_timer_set_mode(dev, timer: 0, APCI3120_TIMER_MODE4); |
728 | apci3120_timer_enable(dev, timer: 0, enable: true); |
729 | |
730 | /* fixed conversion time of 10 us */ |
731 | divisor = apci3120_ns_to_timer(dev, timer: 0, ns: 10000, CMDF_ROUND_NEAREST); |
732 | |
733 | for (i = 0; i < insn->n; i++) { |
734 | /* trigger conversion */ |
735 | apci3120_timer_write(dev, timer: 0, val: divisor); |
736 | |
737 | ret = comedi_timeout(dev, s, insn, cb: apci3120_ai_eoc, context: 0); |
738 | if (ret) |
739 | return ret; |
740 | |
741 | data[i] = inw(port: dev->iobase + APCI3120_AI_FIFO_REG); |
742 | } |
743 | |
744 | return insn->n; |
745 | } |
746 | |
747 | static int apci3120_ao_ready(struct comedi_device *dev, |
748 | struct comedi_subdevice *s, |
749 | struct comedi_insn *insn, |
750 | unsigned long context) |
751 | { |
752 | unsigned int status; |
753 | |
754 | status = inw(port: dev->iobase + APCI3120_STATUS_REG); |
755 | if (status & APCI3120_STATUS_DA_READY) |
756 | return 0; |
757 | return -EBUSY; |
758 | } |
759 | |
760 | static int apci3120_ao_insn_write(struct comedi_device *dev, |
761 | struct comedi_subdevice *s, |
762 | struct comedi_insn *insn, |
763 | unsigned int *data) |
764 | { |
765 | unsigned int chan = CR_CHAN(insn->chanspec); |
766 | int i; |
767 | |
768 | for (i = 0; i < insn->n; i++) { |
769 | unsigned int val = data[i]; |
770 | int ret; |
771 | |
772 | ret = comedi_timeout(dev, s, insn, cb: apci3120_ao_ready, context: 0); |
773 | if (ret) |
774 | return ret; |
775 | |
776 | outw(APCI3120_AO_MUX(chan) | APCI3120_AO_DATA(val), |
777 | port: dev->iobase + APCI3120_AO_REG(chan)); |
778 | |
779 | s->readback[chan] = val; |
780 | } |
781 | |
782 | return insn->n; |
783 | } |
784 | |
785 | static int apci3120_di_insn_bits(struct comedi_device *dev, |
786 | struct comedi_subdevice *s, |
787 | struct comedi_insn *insn, |
788 | unsigned int *data) |
789 | { |
790 | unsigned int status; |
791 | |
792 | status = inw(port: dev->iobase + APCI3120_STATUS_REG); |
793 | data[1] = APCI3120_STATUS_TO_DI_BITS(status); |
794 | |
795 | return insn->n; |
796 | } |
797 | |
798 | static int apci3120_do_insn_bits(struct comedi_device *dev, |
799 | struct comedi_subdevice *s, |
800 | struct comedi_insn *insn, |
801 | unsigned int *data) |
802 | { |
803 | struct apci3120_private *devpriv = dev->private; |
804 | |
805 | if (comedi_dio_update_state(s, data)) { |
806 | devpriv->do_bits = s->state; |
807 | outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits), |
808 | port: dev->iobase + APCI3120_CTR0_REG); |
809 | } |
810 | |
811 | data[1] = s->state; |
812 | |
813 | return insn->n; |
814 | } |
815 | |
816 | static int apci3120_timer_insn_config(struct comedi_device *dev, |
817 | struct comedi_subdevice *s, |
818 | struct comedi_insn *insn, |
819 | unsigned int *data) |
820 | { |
821 | struct apci3120_private *devpriv = dev->private; |
822 | unsigned int divisor; |
823 | unsigned int status; |
824 | unsigned int mode; |
825 | unsigned int timer_mode; |
826 | |
827 | switch (data[0]) { |
828 | case INSN_CONFIG_ARM: |
829 | apci3120_clr_timer2_interrupt(dev); |
830 | divisor = apci3120_ns_to_timer(dev, timer: 2, ns: data[1], |
831 | CMDF_ROUND_DOWN); |
832 | apci3120_timer_write(dev, timer: 2, val: divisor); |
833 | apci3120_timer_enable(dev, timer: 2, enable: true); |
834 | break; |
835 | |
836 | case INSN_CONFIG_DISARM: |
837 | apci3120_timer_enable(dev, timer: 2, enable: false); |
838 | apci3120_clr_timer2_interrupt(dev); |
839 | break; |
840 | |
841 | case INSN_CONFIG_GET_COUNTER_STATUS: |
842 | data[1] = 0; |
843 | data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING | |
844 | COMEDI_COUNTER_TERMINAL_COUNT; |
845 | |
846 | if (devpriv->ctrl & APCI3120_CTRL_GATE(2)) { |
847 | data[1] |= COMEDI_COUNTER_ARMED; |
848 | data[1] |= COMEDI_COUNTER_COUNTING; |
849 | } |
850 | status = inw(port: dev->iobase + APCI3120_STATUS_REG); |
851 | if (status & APCI3120_STATUS_TIMER2_INT) { |
852 | data[1] &= ~COMEDI_COUNTER_COUNTING; |
853 | data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; |
854 | } |
855 | break; |
856 | |
857 | case INSN_CONFIG_SET_COUNTER_MODE: |
858 | switch (data[1]) { |
859 | case I8254_MODE0: |
860 | mode = APCI3120_MODE_TIMER2_AS_COUNTER; |
861 | timer_mode = APCI3120_TIMER_MODE0; |
862 | break; |
863 | case I8254_MODE2: |
864 | mode = APCI3120_MODE_TIMER2_AS_TIMER; |
865 | timer_mode = APCI3120_TIMER_MODE2; |
866 | break; |
867 | case I8254_MODE4: |
868 | mode = APCI3120_MODE_TIMER2_AS_TIMER; |
869 | timer_mode = APCI3120_TIMER_MODE4; |
870 | break; |
871 | case I8254_MODE5: |
872 | mode = APCI3120_MODE_TIMER2_AS_WDOG; |
873 | timer_mode = APCI3120_TIMER_MODE5; |
874 | break; |
875 | default: |
876 | return -EINVAL; |
877 | } |
878 | apci3120_timer_enable(dev, timer: 2, enable: false); |
879 | apci3120_clr_timer2_interrupt(dev); |
880 | apci3120_timer_set_mode(dev, timer: 2, mode: timer_mode); |
881 | devpriv->mode &= ~APCI3120_MODE_TIMER2_AS_MASK; |
882 | devpriv->mode |= mode; |
883 | outb(value: devpriv->mode, port: dev->iobase + APCI3120_MODE_REG); |
884 | break; |
885 | |
886 | default: |
887 | return -EINVAL; |
888 | } |
889 | |
890 | return insn->n; |
891 | } |
892 | |
893 | static int apci3120_timer_insn_read(struct comedi_device *dev, |
894 | struct comedi_subdevice *s, |
895 | struct comedi_insn *insn, |
896 | unsigned int *data) |
897 | { |
898 | int i; |
899 | |
900 | for (i = 0; i < insn->n; i++) |
901 | data[i] = apci3120_timer_read(dev, timer: 2); |
902 | |
903 | return insn->n; |
904 | } |
905 | |
906 | static void apci3120_dma_alloc(struct comedi_device *dev) |
907 | { |
908 | struct apci3120_private *devpriv = dev->private; |
909 | struct apci3120_dmabuf *dmabuf; |
910 | int order; |
911 | int i; |
912 | |
913 | for (i = 0; i < 2; i++) { |
914 | dmabuf = &devpriv->dmabuf[i]; |
915 | for (order = 2; order >= 0; order--) { |
916 | dmabuf->virt = dma_alloc_coherent(dev: dev->hw_dev, |
917 | PAGE_SIZE << order, |
918 | dma_handle: &dmabuf->hw, |
919 | GFP_KERNEL); |
920 | if (dmabuf->virt) |
921 | break; |
922 | } |
923 | if (!dmabuf->virt) |
924 | break; |
925 | dmabuf->size = PAGE_SIZE << order; |
926 | |
927 | if (i == 0) |
928 | devpriv->use_dma = 1; |
929 | if (i == 1) |
930 | devpriv->use_double_buffer = 1; |
931 | } |
932 | } |
933 | |
934 | static void apci3120_dma_free(struct comedi_device *dev) |
935 | { |
936 | struct apci3120_private *devpriv = dev->private; |
937 | struct apci3120_dmabuf *dmabuf; |
938 | int i; |
939 | |
940 | if (!devpriv) |
941 | return; |
942 | |
943 | for (i = 0; i < 2; i++) { |
944 | dmabuf = &devpriv->dmabuf[i]; |
945 | if (dmabuf->virt) { |
946 | dma_free_coherent(dev: dev->hw_dev, size: dmabuf->size, |
947 | cpu_addr: dmabuf->virt, dma_handle: dmabuf->hw); |
948 | } |
949 | } |
950 | } |
951 | |
952 | static void apci3120_reset(struct comedi_device *dev) |
953 | { |
954 | /* disable all interrupt sources */ |
955 | outb(value: 0, port: dev->iobase + APCI3120_MODE_REG); |
956 | |
957 | /* disable all counters, ext trigger, and reset scan */ |
958 | outw(value: 0, port: dev->iobase + APCI3120_CTRL_REG); |
959 | |
960 | /* clear interrupt status */ |
961 | inw(port: dev->iobase + APCI3120_STATUS_REG); |
962 | } |
963 | |
964 | static int apci3120_auto_attach(struct comedi_device *dev, |
965 | unsigned long context) |
966 | { |
967 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
968 | const struct apci3120_board *board = NULL; |
969 | struct apci3120_private *devpriv; |
970 | struct comedi_subdevice *s; |
971 | unsigned int status; |
972 | int ret; |
973 | |
974 | if (context < ARRAY_SIZE(apci3120_boardtypes)) |
975 | board = &apci3120_boardtypes[context]; |
976 | if (!board) |
977 | return -ENODEV; |
978 | dev->board_ptr = board; |
979 | dev->board_name = board->name; |
980 | |
981 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
982 | if (!devpriv) |
983 | return -ENOMEM; |
984 | |
985 | ret = comedi_pci_enable(dev); |
986 | if (ret) |
987 | return ret; |
988 | pci_set_master(dev: pcidev); |
989 | |
990 | dev->iobase = pci_resource_start(pcidev, 1); |
991 | devpriv->amcc = pci_resource_start(pcidev, 0); |
992 | devpriv->addon = pci_resource_start(pcidev, 2); |
993 | |
994 | apci3120_reset(dev); |
995 | |
996 | if (pcidev->irq > 0) { |
997 | ret = request_irq(irq: pcidev->irq, handler: apci3120_interrupt, IRQF_SHARED, |
998 | name: dev->board_name, dev); |
999 | if (ret == 0) { |
1000 | dev->irq = pcidev->irq; |
1001 | |
1002 | apci3120_dma_alloc(dev); |
1003 | } |
1004 | } |
1005 | |
1006 | status = inw(port: dev->iobase + APCI3120_STATUS_REG); |
1007 | if (APCI3120_STATUS_TO_VERSION(status) == APCI3120_REVB || |
1008 | context == BOARD_APCI3001) |
1009 | devpriv->osc_base = APCI3120_REVB_OSC_BASE; |
1010 | else |
1011 | devpriv->osc_base = APCI3120_REVA_OSC_BASE; |
1012 | |
1013 | ret = comedi_alloc_subdevices(dev, num_subdevices: 5); |
1014 | if (ret) |
1015 | return ret; |
1016 | |
1017 | /* Analog Input subdevice */ |
1018 | s = &dev->subdevices[0]; |
1019 | s->type = COMEDI_SUBD_AI; |
1020 | s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; |
1021 | s->n_chan = 16; |
1022 | s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; |
1023 | s->range_table = &apci3120_ai_range; |
1024 | s->insn_read = apci3120_ai_insn_read; |
1025 | if (dev->irq) { |
1026 | dev->read_subdev = s; |
1027 | s->subdev_flags |= SDF_CMD_READ; |
1028 | s->len_chanlist = s->n_chan; |
1029 | s->do_cmdtest = apci3120_ai_cmdtest; |
1030 | s->do_cmd = apci3120_ai_cmd; |
1031 | s->cancel = apci3120_cancel; |
1032 | } |
1033 | |
1034 | /* Analog Output subdevice */ |
1035 | s = &dev->subdevices[1]; |
1036 | if (board->has_ao) { |
1037 | s->type = COMEDI_SUBD_AO; |
1038 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; |
1039 | s->n_chan = 8; |
1040 | s->maxdata = 0x3fff; |
1041 | s->range_table = &range_bipolar10; |
1042 | s->insn_write = apci3120_ao_insn_write; |
1043 | |
1044 | ret = comedi_alloc_subdev_readback(s); |
1045 | if (ret) |
1046 | return ret; |
1047 | } else { |
1048 | s->type = COMEDI_SUBD_UNUSED; |
1049 | } |
1050 | |
1051 | /* Digital Input subdevice */ |
1052 | s = &dev->subdevices[2]; |
1053 | s->type = COMEDI_SUBD_DI; |
1054 | s->subdev_flags = SDF_READABLE; |
1055 | s->n_chan = 4; |
1056 | s->maxdata = 1; |
1057 | s->range_table = &range_digital; |
1058 | s->insn_bits = apci3120_di_insn_bits; |
1059 | |
1060 | /* Digital Output subdevice */ |
1061 | s = &dev->subdevices[3]; |
1062 | s->type = COMEDI_SUBD_DO; |
1063 | s->subdev_flags = SDF_WRITABLE; |
1064 | s->n_chan = 4; |
1065 | s->maxdata = 1; |
1066 | s->range_table = &range_digital; |
1067 | s->insn_bits = apci3120_do_insn_bits; |
1068 | |
1069 | /* Timer subdevice */ |
1070 | s = &dev->subdevices[4]; |
1071 | s->type = COMEDI_SUBD_TIMER; |
1072 | s->subdev_flags = SDF_READABLE; |
1073 | s->n_chan = 1; |
1074 | s->maxdata = 0x00ffffff; |
1075 | s->insn_config = apci3120_timer_insn_config; |
1076 | s->insn_read = apci3120_timer_insn_read; |
1077 | |
1078 | return 0; |
1079 | } |
1080 | |
1081 | static void apci3120_detach(struct comedi_device *dev) |
1082 | { |
1083 | comedi_pci_detach(dev); |
1084 | apci3120_dma_free(dev); |
1085 | } |
1086 | |
1087 | static struct comedi_driver apci3120_driver = { |
1088 | .driver_name = "addi_apci_3120" , |
1089 | .module = THIS_MODULE, |
1090 | .auto_attach = apci3120_auto_attach, |
1091 | .detach = apci3120_detach, |
1092 | }; |
1093 | |
1094 | static int apci3120_pci_probe(struct pci_dev *dev, |
1095 | const struct pci_device_id *id) |
1096 | { |
1097 | return comedi_pci_auto_config(pcidev: dev, driver: &apci3120_driver, context: id->driver_data); |
1098 | } |
1099 | |
1100 | static const struct pci_device_id apci3120_pci_table[] = { |
1101 | { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 }, |
1102 | { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 }, |
1103 | { 0 } |
1104 | }; |
1105 | MODULE_DEVICE_TABLE(pci, apci3120_pci_table); |
1106 | |
1107 | static struct pci_driver apci3120_pci_driver = { |
1108 | .name = "addi_apci_3120" , |
1109 | .id_table = apci3120_pci_table, |
1110 | .probe = apci3120_pci_probe, |
1111 | .remove = comedi_pci_auto_unconfig, |
1112 | }; |
1113 | module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver); |
1114 | |
1115 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
1116 | MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board" ); |
1117 | MODULE_LICENSE("GPL" ); |
1118 | |