]>
Commit | Line | Data |
---|---|---|
e184e2be | 1 | // SPDX-License-Identifier: GPL-2.0+ |
d8cdd97c HS |
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 | |
d8cdd97c HS |
13 | */ |
14 | ||
ce157f80 | 15 | #include <linux/module.h> |
4e068bec | 16 | #include <linux/interrupt.h> |
33782dd5 | 17 | |
b8c0a0ac | 18 | #include "../comedi_pci.h" |
bf36f012 | 19 | #include "amcc_s5933.h" |
3d41c443 | 20 | |
6174801d HS |
21 | /* |
22 | * PCI BAR 0 register map (devpriv->amcc) | |
23 | * see amcc_s5933.h for register and bit defines | |
24 | */ | |
6ed30c89 | 25 | #define APCI3120_FIFO_ADVANCE_ON_BYTE_2 BIT(29) |
6174801d HS |
26 | |
27 | /* | |
28 | * PCI BAR 1 register map (dev->iobase) | |
29 | */ | |
ecaf4c41 | 30 | #define APCI3120_AI_FIFO_REG 0x00 |
96257426 | 31 | #define APCI3120_CTRL_REG 0x00 |
6ed30c89 HS |
32 | #define APCI3120_CTRL_EXT_TRIG BIT(15) |
33 | #define APCI3120_CTRL_GATE(x) BIT(12 + (x)) | |
c24ca0e6 HS |
34 | #define APCI3120_CTRL_PR(x) (((x) & 0xf) << 8) |
35 | #define APCI3120_CTRL_PA(x) (((x) & 0xf) << 0) | |
dcb5e2c4 | 36 | #define APCI3120_AI_SOFTTRIG_REG 0x02 |
b0a0d0bc | 37 | #define APCI3120_STATUS_REG 0x02 |
6ed30c89 HS |
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) | |
3da98237 HS |
42 | #define APCI3120_STATUS_INT_MASK (0xf << 12) |
43 | #define APCI3120_STATUS_TO_DI_BITS(x) (((x) >> 8) & 0xf) | |
546bf338 | 44 | #define APCI3120_STATUS_TO_VERSION(x) (((x) >> 4) & 0xf) |
6ed30c89 HS |
45 | #define APCI3120_STATUS_FIFO_FULL BIT(2) |
46 | #define APCI3120_STATUS_FIFO_EMPTY BIT(1) | |
47 | #define APCI3120_STATUS_DA_READY BIT(0) | |
0c188e9c | 48 | #define APCI3120_TIMER_REG 0x04 |
9d5b0708 HS |
49 | #define APCI3120_CHANLIST_REG 0x06 |
50 | #define APCI3120_CHANLIST_INDEX(x) (((x) & 0xf) << 8) | |
6ed30c89 | 51 | #define APCI3120_CHANLIST_UNIPOLAR BIT(7) |
9d5b0708 HS |
52 | #define APCI3120_CHANLIST_GAIN(x) (((x) & 0x3) << 4) |
53 | #define APCI3120_CHANLIST_MUX(x) (((x) & 0xf) << 0) | |
6174801d HS |
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) | |
e6da4c6a HS |
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 */ | |
2016c534 | 63 | #define APCI3120_TIMER_MODE_MASK(_t) (3 << ((_t) * 2)) |
2e2e74e1 HS |
64 | #define APCI3120_CTR0_REG 0x0d |
65 | #define APCI3120_CTR0_DO_BITS(x) ((x) << 4) | |
ca578bce | 66 | #define APCI3120_CTR0_TIMER_SEL(x) ((x) << 0) |
aae8f725 | 67 | #define APCI3120_MODE_REG 0x0e |
6ed30c89 HS |
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) | |
6174801d HS |
83 | |
84 | /* | |
85 | * PCI BAR 2 register map (devpriv->addon) | |
86 | */ | |
0c63bd5a HS |
87 | #define APCI3120_ADDON_ADDR_REG 0x00 |
88 | #define APCI3120_ADDON_DATA_REG 0x02 | |
89 | #define APCI3120_ADDON_CTRL_REG 0x04 | |
6ed30c89 HS |
90 | #define APCI3120_ADDON_CTRL_AMWEN_ENA BIT(1) |
91 | #define APCI3120_ADDON_CTRL_A2P_FIFO_ENA BIT(0) | |
6174801d | 92 | |
546bf338 HS |
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 | ||
6ef68032 HS |
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 | ||
1df0e5b0 HS |
114 | enum apci3120_boardid { |
115 | BOARD_APCI3120, | |
116 | BOARD_APCI3001, | |
117 | }; | |
118 | ||
dbf502e4 HS |
119 | struct apci3120_board { |
120 | const char *name; | |
6a02623d | 121 | unsigned int ai_is_16bit:1; |
2cd6b2b2 | 122 | unsigned int has_ao:1; |
dbf502e4 HS |
123 | }; |
124 | ||
125 | static const struct apci3120_board apci3120_boardtypes[] = { | |
1df0e5b0 | 126 | [BOARD_APCI3120] = { |
6a02623d HS |
127 | .name = "apci3120", |
128 | .ai_is_16bit = 1, | |
129 | .has_ao = 1, | |
1df0e5b0 HS |
130 | }, |
131 | [BOARD_APCI3001] = { | |
6a02623d | 132 | .name = "apci3001", |
c0a053b8 HS |
133 | }, |
134 | }; | |
135 | ||
8f83d52d HS |
136 | struct apci3120_dmabuf { |
137 | unsigned short *virt; | |
138 | dma_addr_t hw; | |
139 | unsigned int size; | |
140 | unsigned int use_size; | |
141 | }; | |
142 | ||
4e068bec | 143 | struct apci3120_private { |
323503bf | 144 | unsigned long amcc; |
67941734 | 145 | unsigned long addon; |
546bf338 | 146 | unsigned int osc_base; |
e6539bbc | 147 | unsigned int use_dma:1; |
d65d73ff | 148 | unsigned int use_double_buffer:1; |
d895948e | 149 | unsigned int cur_dmabuf:1; |
8f83d52d | 150 | struct apci3120_dmabuf dmabuf[2]; |
36d553e4 | 151 | unsigned char do_bits; |
486beeb4 | 152 | unsigned char timer_mode; |
627e83a7 | 153 | unsigned char mode; |
5a1f46c8 | 154 | unsigned short ctrl; |
4e068bec HS |
155 | }; |
156 | ||
0b7defa7 HS |
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(reg, devpriv->addon + APCI3120_ADDON_ADDR_REG); | |
165 | outw(val & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG); | |
166 | ||
167 | outw(reg + 2, devpriv->addon + APCI3120_ADDON_ADDR_REG); | |
168 | outw((val >> 16) & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG); | |
169 | } | |
170 | ||
ee844c51 HS |
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 | 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 | devpriv->amcc + AMCC_OP_REG_MCSR); | |
187 | ||
188 | /* Add-On - DMA start address */ | |
189 | apci3120_addon_write(dev, dmabuf->hw, AMCC_OP_REG_AMWAR); | |
190 | ||
191 | /* Add-On - Number of acquisitions */ | |
192 | apci3120_addon_write(dev, 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 | 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 | devpriv->addon + APCI3120_ADDON_CTRL_REG); | |
201 | } | |
202 | ||
45062492 HS |
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, 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, dmabuf0); | |
252 | } | |
253 | ||
546bf338 HS |
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 | ||
24e18c85 HS |
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(dev->iobase + APCI3120_CTR0_REG); | |
311 | } | |
312 | ||
b3c8ee5c HS |
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 | dev->iobase + APCI3120_CTR0_REG); | |
322 | outw(val & 0xffff, 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 | dev->iobase + APCI3120_CTR0_REG); | |
329 | outw((val >> 16) & 0xffff, 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 | dev->iobase + APCI3120_CTR0_REG); | |
343 | val = inw(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 | dev->iobase + APCI3120_CTR0_REG); | |
350 | val |= (inw(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(devpriv->timer_mode, dev->iobase + APCI3120_TIMER_MODE_REG); | |
364 | } | |
365 | ||
a8f77b40 HS |
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(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); | |
376 | } | |
377 | ||
b17d3906 HS |
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(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); | |
387 | } | |
388 | ||
169f35e8 HS |
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 | ||
169f35e8 HS |
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(val, dev->iobase + APCI3120_CHANLIST_REG); | |
410 | } | |
15e920fb | 411 | |
34216792 HS |
412 | /* a dummy read of APCI3120_TIMER_MODE_REG resets the ai FIFO */ |
413 | inw(dev->iobase + APCI3120_TIMER_MODE_REG); | |
1022f64e HS |
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(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); | |
fdeb2f54 HS |
418 | |
419 | /* enable chanlist scanning if necessary */ | |
420 | if (n_chan > 1) | |
421 | devpriv->mode |= APCI3120_MODE_SCAN_ENA; | |
169f35e8 HS |
422 | } |
423 | ||
a93a0e9a HS |
424 | static void apci3120_interrupt_dma(struct comedi_device *dev, |
425 | struct comedi_subdevice *s) | |
cd68e0e2 | 426 | { |
cd68e0e2 | 427 | struct apci3120_private *devpriv = dev->private; |
cd68e0e2 HS |
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(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 | } | |
cd68e0e2 | 445 | |
f50205f0 | 446 | nsamples = comedi_bytes_to_samples(s, nbytes); |
cd68e0e2 HS |
447 | if (nsamples) { |
448 | comedi_buf_write_samples(s, 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) { | |
f50205f0 | 459 | /* switch DMA buffers for next interrupt */ |
cd68e0e2 | 460 | devpriv->cur_dmabuf = !devpriv->cur_dmabuf; |
f50205f0 HS |
461 | dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf]; |
462 | apci3120_init_dma(dev, dmabuf); | |
cd68e0e2 | 463 | } else { |
f50205f0 | 464 | /* restart DMA if not using double buffering */ |
cd68e0e2 HS |
465 | apci3120_init_dma(dev, dmabuf); |
466 | } | |
467 | } | |
4e068bec | 468 | |
76e60470 HS |
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(dev->iobase + APCI3120_STATUS_REG); | |
480 | int_amcc = inl(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(int_amcc | AINT_INT_MASK, devpriv->amcc + AMCC_OP_REG_INTCSR); | |
489 | ||
490 | if (devpriv->ctrl & APCI3120_CTRL_EXT_TRIG) | |
491 | apci3120_exttrig_enable(dev, 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 | ||
76e60470 HS |
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(dev->iobase + APCI3120_AI_FIFO_REG); | |
505 | comedi_buf_write_samples(s, &val, 1); | |
506 | } | |
507 | ||
508 | devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA; | |
509 | outb(devpriv->mode, 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, devpriv->amcc + AMCC_OP_REG_INTCSR); | |
523 | ||
524 | /* do some data transfer */ | |
a93a0e9a | 525 | apci3120_interrupt_dma(dev, s); |
76e60470 HS |
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 | ||
18e3b503 HS |
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, 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, cmd->chanlist_len, cmd->chanlist); | |
554 | ||
555 | if (cmd->start_src == TRIG_EXT) | |
556 | apci3120_exttrig_enable(dev, 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, 1, cmd->scan_begin_arg, | |
564 | cmd->flags); | |
565 | apci3120_timer_set_mode(dev, 1, APCI3120_TIMER_MODE2); | |
566 | apci3120_timer_write(dev, 1, 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, 0, cmd->convert_arg, cmd->flags); | |
574 | apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE2); | |
575 | apci3120_timer_write(dev, 0, 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(devpriv->mode, dev->iobase + APCI3120_MODE_REG); | |
584 | ||
585 | if (cmd->scan_begin_src == TRIG_TIMER) | |
586 | apci3120_timer_enable(dev, 1, true); | |
587 | apci3120_timer_enable(dev, 0, true); | |
588 | ||
589 | return 0; | |
590 | } | |
591 | ||
d32fc427 HS |
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 | ||
25e80464 IA |
601 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); |
602 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, | |
d32fc427 | 603 | TRIG_TIMER | TRIG_FOLLOW); |
25e80464 IA |
604 | err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER); |
605 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
606 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
d32fc427 HS |
607 | |
608 | if (err) | |
609 | return 1; | |
610 | ||
611 | /* Step 2a : make sure trigger sources are unique */ | |
612 | ||
25e80464 IA |
613 | err |= comedi_check_trigger_is_unique(cmd->start_src); |
614 | err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); | |
615 | err |= comedi_check_trigger_is_unique(cmd->stop_src); | |
d32fc427 HS |
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 | ||
25e80464 | 624 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
d32fc427 | 625 | |
25e80464 IA |
626 | if (cmd->scan_begin_src == TRIG_TIMER) { /* Test Delay timing */ |
627 | err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, | |
628 | 100000); | |
629 | } | |
d32fc427 HS |
630 | |
631 | /* minimum conversion time per sample is 10us */ | |
25e80464 | 632 | err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); |
d32fc427 | 633 | |
25e80464 IA |
634 | err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); |
635 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, | |
636 | cmd->chanlist_len); | |
d32fc427 HS |
637 | |
638 | if (cmd->stop_src == TRIG_COUNT) | |
25e80464 | 639 | err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); |
d32fc427 | 640 | else /* TRIG_NONE */ |
25e80464 | 641 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
d32fc427 HS |
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; | |
25e80464 | 651 | err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg); |
d32fc427 HS |
652 | } |
653 | ||
654 | if (err) | |
655 | return 4; | |
656 | ||
657 | /* Step 5: check channel list if it exists */ | |
658 | ||
659 | return 0; | |
660 | } | |
661 | ||
676caeda HS |
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(0, devpriv->addon + 4); | |
669 | ||
670 | /* Add-On - disable bus master */ | |
671 | apci3120_addon_write(dev, 0, AMCC_OP_REG_AGCSTS); | |
672 | ||
673 | /* AMCC - disable bus master */ | |
674 | outl(0, devpriv->amcc + AMCC_OP_REG_MCSR); | |
675 | ||
676 | /* disable all counters, ext trigger, and reset scan */ | |
677 | devpriv->ctrl = 0; | |
678 | outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); | |
679 | ||
680 | /* DISABLE_ALL_INTERRUPT */ | |
681 | devpriv->mode = 0; | |
682 | outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); | |
683 | ||
684 | inw(dev->iobase + APCI3120_STATUS_REG); | |
685 | devpriv->cur_dmabuf = 0; | |
686 | ||
687 | return 0; | |
688 | } | |
689 | ||
6a51cfc6 HS |
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(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(devpriv->mode, dev->iobase + APCI3120_MODE_REG); | |
717 | ||
718 | /* load chanlist for single channel scan */ | |
0a3e5181 | 719 | apci3120_set_chanlist(dev, s, 1, &insn->chanspec); |
6a51cfc6 HS |
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, 0, APCI3120_TIMER_MODE4); | |
728 | apci3120_timer_enable(dev, 0, true); | |
729 | ||
730 | /* fixed conversion time of 10 us */ | |
731 | divisor = apci3120_ns_to_timer(dev, 0, 10000, CMDF_ROUND_NEAREST); | |
732 | ||
6a51cfc6 HS |
733 | for (i = 0; i < insn->n; i++) { |
734 | /* trigger conversion */ | |
735 | apci3120_timer_write(dev, 0, divisor); | |
736 | ||
737 | ret = comedi_timeout(dev, s, insn, apci3120_ai_eoc, 0); | |
738 | if (ret) | |
739 | return ret; | |
740 | ||
ecaf4c41 | 741 | data[i] = inw(dev->iobase + APCI3120_AI_FIFO_REG); |
6a51cfc6 HS |
742 | } |
743 | ||
744 | return insn->n; | |
745 | } | |
746 | ||
5cf5fa95 HS |
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(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, apci3120_ao_ready, 0); | |
773 | if (ret) | |
774 | return ret; | |
775 | ||
776 | outw(APCI3120_AO_MUX(chan) | APCI3120_AO_DATA(val), | |
777 | dev->iobase + APCI3120_AO_REG(chan)); | |
778 | ||
779 | s->readback[chan] = val; | |
780 | } | |
781 | ||
782 | return insn->n; | |
783 | } | |
784 | ||
c737b2ce HS |
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(dev->iobase + APCI3120_STATUS_REG); | |
793 | data[1] = APCI3120_STATUS_TO_DI_BITS(status); | |
794 | ||
795 | return insn->n; | |
796 | } | |
797 | ||
ae12280d HS |
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 | dev->iobase + APCI3120_CTR0_REG); | |
809 | } | |
810 | ||
811 | data[1] = s->state; | |
812 | ||
813 | return insn->n; | |
814 | } | |
815 | ||
6d312740 HS |
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, 2, data[1], | |
831 | CMDF_ROUND_DOWN); | |
832 | apci3120_timer_write(dev, 2, divisor); | |
833 | apci3120_timer_enable(dev, 2, true); | |
834 | break; | |
835 | ||
836 | case INSN_CONFIG_DISARM: | |
837 | apci3120_timer_enable(dev, 2, 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(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, 2, false); | |
879 | apci3120_clr_timer2_interrupt(dev); | |
880 | apci3120_timer_set_mode(dev, 2, timer_mode); | |
881 | devpriv->mode &= ~APCI3120_MODE_TIMER2_AS_MASK; | |
882 | devpriv->mode |= mode; | |
883 | outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); | |
884 | break; | |
885 | ||
886 | default: | |
887 | return -EINVAL; | |
888 | } | |
889 | ||
890 | return insn->n; | |
891 | } | |
892 | ||
15cf0617 HS |
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, 2); | |
902 | ||
903 | return insn->n; | |
904 | } | |
905 | ||
1b90779b HS |
906 | static void apci3120_dma_alloc(struct comedi_device *dev) |
907 | { | |
908 | struct apci3120_private *devpriv = dev->private; | |
8f83d52d | 909 | struct apci3120_dmabuf *dmabuf; |
1b90779b HS |
910 | int order; |
911 | int i; | |
912 | ||
913 | for (i = 0; i < 2; i++) { | |
8f83d52d | 914 | dmabuf = &devpriv->dmabuf[i]; |
1b90779b | 915 | for (order = 2; order >= 0; order--) { |
8f83d52d HS |
916 | dmabuf->virt = dma_alloc_coherent(dev->hw_dev, |
917 | PAGE_SIZE << order, | |
918 | &dmabuf->hw, | |
919 | GFP_KERNEL); | |
920 | if (dmabuf->virt) | |
1b90779b HS |
921 | break; |
922 | } | |
8f83d52d | 923 | if (!dmabuf->virt) |
1b90779b | 924 | break; |
8f83d52d | 925 | dmabuf->size = PAGE_SIZE << order; |
1b90779b HS |
926 | |
927 | if (i == 0) | |
e6539bbc | 928 | devpriv->use_dma = 1; |
1b90779b | 929 | if (i == 1) |
d65d73ff | 930 | devpriv->use_double_buffer = 1; |
1b90779b HS |
931 | } |
932 | } | |
933 | ||
934 | static void apci3120_dma_free(struct comedi_device *dev) | |
935 | { | |
936 | struct apci3120_private *devpriv = dev->private; | |
8f83d52d | 937 | struct apci3120_dmabuf *dmabuf; |
1b90779b HS |
938 | int i; |
939 | ||
940 | if (!devpriv) | |
941 | return; | |
942 | ||
943 | for (i = 0; i < 2; i++) { | |
8f83d52d HS |
944 | dmabuf = &devpriv->dmabuf[i]; |
945 | if (dmabuf->virt) { | |
946 | dma_free_coherent(dev->hw_dev, dmabuf->size, | |
947 | dmabuf->virt, dmabuf->hw); | |
1b90779b HS |
948 | } |
949 | } | |
950 | } | |
951 | ||
ca26b42d HS |
952 | static void apci3120_reset(struct comedi_device *dev) |
953 | { | |
954 | /* disable all interrupt sources */ | |
955 | outb(0, dev->iobase + APCI3120_MODE_REG); | |
956 | ||
957 | /* disable all counters, ext trigger, and reset scan */ | |
958 | outw(0, dev->iobase + APCI3120_CTRL_REG); | |
959 | ||
960 | /* clear interrupt status */ | |
961 | inw(dev->iobase + APCI3120_STATUS_REG); | |
962 | } | |
963 | ||
a690b7e5 | 964 | static int apci3120_auto_attach(struct comedi_device *dev, |
1df0e5b0 | 965 | unsigned long context) |
bb6986f0 | 966 | { |
891e62c3 | 967 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
52a79403 | 968 | const struct apci3120_board *board = NULL; |
4e068bec | 969 | struct apci3120_private *devpriv; |
bb6986f0 | 970 | struct comedi_subdevice *s; |
546bf338 | 971 | unsigned int status; |
1b90779b | 972 | int ret; |
bb6986f0 | 973 | |
1df0e5b0 | 974 | if (context < ARRAY_SIZE(apci3120_boardtypes)) |
52a79403 HS |
975 | board = &apci3120_boardtypes[context]; |
976 | if (!board) | |
bb6986f0 | 977 | return -ENODEV; |
52a79403 HS |
978 | dev->board_ptr = board; |
979 | dev->board_name = board->name; | |
bb6986f0 | 980 | |
0bdab509 | 981 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
bb6986f0 HS |
982 | if (!devpriv) |
983 | return -ENOMEM; | |
bb6986f0 | 984 | |
818f569f | 985 | ret = comedi_pci_enable(dev); |
bb6986f0 HS |
986 | if (ret) |
987 | return ret; | |
4fbe36f2 | 988 | pci_set_master(pcidev); |
bb6986f0 | 989 | |
65fe75a6 | 990 | dev->iobase = pci_resource_start(pcidev, 1); |
323503bf | 991 | devpriv->amcc = pci_resource_start(pcidev, 0); |
67941734 | 992 | devpriv->addon = pci_resource_start(pcidev, 2); |
bb6986f0 | 993 | |
73ef1931 HS |
994 | apci3120_reset(dev); |
995 | ||
bb6986f0 | 996 | if (pcidev->irq > 0) { |
6c76d4e7 | 997 | ret = request_irq(pcidev->irq, apci3120_interrupt, IRQF_SHARED, |
bb6986f0 | 998 | dev->board_name, dev); |
1b90779b | 999 | if (ret == 0) { |
bb6986f0 | 1000 | dev->irq = pcidev->irq; |
4fbe36f2 | 1001 | |
1b90779b | 1002 | apci3120_dma_alloc(dev); |
4fbe36f2 | 1003 | } |
bb6986f0 HS |
1004 | } |
1005 | ||
b0a0d0bc | 1006 | status = inw(dev->iobase + APCI3120_STATUS_REG); |
546bf338 HS |
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 | ||
aea9c4e2 | 1013 | ret = comedi_alloc_subdevices(dev, 5); |
bb6986f0 HS |
1014 | if (ret) |
1015 | return ret; | |
1016 | ||
2a420f40 | 1017 | /* Analog Input subdevice */ |
bb6986f0 | 1018 | s = &dev->subdevices[0]; |
2a420f40 HS |
1019 | s->type = COMEDI_SUBD_AI; |
1020 | s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; | |
1021 | s->n_chan = 16; | |
52a79403 | 1022 | s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; |
6ef68032 | 1023 | s->range_table = &apci3120_ai_range; |
2a420f40 | 1024 | s->insn_read = apci3120_ai_insn_read; |
c58f9bb6 | 1025 | if (dev->irq) { |
2a420f40 HS |
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 | } | |
bb6986f0 | 1033 | |
f6dc9308 | 1034 | /* Analog Output subdevice */ |
bb6986f0 | 1035 | s = &dev->subdevices[1]; |
52a79403 | 1036 | if (board->has_ao) { |
f6dc9308 | 1037 | s->type = COMEDI_SUBD_AO; |
ef49d832 | 1038 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; |
f6dc9308 HS |
1039 | s->n_chan = 8; |
1040 | s->maxdata = 0x3fff; | |
1041 | s->range_table = &range_bipolar10; | |
1042 | s->insn_write = apci3120_ao_insn_write; | |
bf34c226 HS |
1043 | |
1044 | ret = comedi_alloc_subdev_readback(s); | |
1045 | if (ret) | |
1046 | return ret; | |
bb6986f0 | 1047 | } else { |
f6dc9308 | 1048 | s->type = COMEDI_SUBD_UNUSED; |
bb6986f0 | 1049 | } |
f2c872e1 | 1050 | |
dc842931 | 1051 | /* Digital Input subdevice */ |
bb6986f0 | 1052 | s = &dev->subdevices[2]; |
dc842931 HS |
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; | |
f2c872e1 | 1059 | |
1bed58e3 | 1060 | /* Digital Output subdevice */ |
bb6986f0 | 1061 | s = &dev->subdevices[3]; |
1bed58e3 | 1062 | s->type = COMEDI_SUBD_DO; |
ef49d832 | 1063 | s->subdev_flags = SDF_WRITABLE; |
1bed58e3 HS |
1064 | s->n_chan = 4; |
1065 | s->maxdata = 1; | |
1066 | s->range_table = &range_digital; | |
1067 | s->insn_bits = apci3120_do_insn_bits; | |
bb6986f0 | 1068 | |
75850519 | 1069 | /* Timer subdevice */ |
bb6986f0 | 1070 | s = &dev->subdevices[4]; |
75850519 | 1071 | s->type = COMEDI_SUBD_TIMER; |
6d312740 | 1072 | s->subdev_flags = SDF_READABLE; |
75850519 HS |
1073 | s->n_chan = 1; |
1074 | s->maxdata = 0x00ffffff; | |
6d312740 | 1075 | s->insn_config = apci3120_timer_insn_config; |
15cf0617 | 1076 | s->insn_read = apci3120_timer_insn_read; |
bb6986f0 | 1077 | |
bb6986f0 HS |
1078 | return 0; |
1079 | } | |
1080 | ||
1081 | static void apci3120_detach(struct comedi_device *dev) | |
1082 | { | |
aac307f9 | 1083 | comedi_pci_detach(dev); |
1b90779b | 1084 | apci3120_dma_free(dev); |
bb6986f0 HS |
1085 | } |
1086 | ||
20a22b70 HS |
1087 | static struct comedi_driver apci3120_driver = { |
1088 | .driver_name = "addi_apci_3120", | |
1089 | .module = THIS_MODULE, | |
891e62c3 | 1090 | .auto_attach = apci3120_auto_attach, |
bb6986f0 | 1091 | .detach = apci3120_detach, |
20a22b70 HS |
1092 | }; |
1093 | ||
a690b7e5 | 1094 | static int apci3120_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 1095 | const struct pci_device_id *id) |
20a22b70 | 1096 | { |
b8f4ac23 | 1097 | return comedi_pci_auto_config(dev, &apci3120_driver, id->driver_data); |
20a22b70 HS |
1098 | } |
1099 | ||
41e043fc | 1100 | static const struct pci_device_id apci3120_pci_table[] = { |
aee351b2 IA |
1101 | { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 }, |
1102 | { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 }, | |
317285d7 HS |
1103 | { 0 } |
1104 | }; | |
20a22b70 | 1105 | MODULE_DEVICE_TABLE(pci, apci3120_pci_table); |
317285d7 | 1106 | |
20a22b70 HS |
1107 | static struct pci_driver apci3120_pci_driver = { |
1108 | .name = "addi_apci_3120", | |
1109 | .id_table = apci3120_pci_table, | |
1110 | .probe = apci3120_pci_probe, | |
9901a4d7 | 1111 | .remove = comedi_pci_auto_unconfig, |
20a22b70 HS |
1112 | }; |
1113 | module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver); | |
90f703d3 AT |
1114 | |
1115 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
b5ebcaa8 | 1116 | MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board"); |
90f703d3 | 1117 | MODULE_LICENSE("GPL"); |