1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * comedi/drivers/adl_pci9118.c |
4 | * |
5 | * hardware driver for ADLink cards: |
6 | * card: PCI-9118DG, PCI-9118HG, PCI-9118HR |
7 | * driver: pci9118dg, pci9118hg, pci9118hr |
8 | * |
9 | * Author: Michal Dobes <dobes@tesnet.cz> |
10 | * |
11 | */ |
12 | |
13 | /* |
14 | * Driver: adl_pci9118 |
15 | * Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR |
16 | * Author: Michal Dobes <dobes@tesnet.cz> |
17 | * Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg), |
18 | * PCI-9118HR (pci9118hr) |
19 | * Status: works |
20 | * |
21 | * This driver supports AI, AO, DI and DO subdevices. |
22 | * AI subdevice supports cmd and insn interface, |
23 | * other subdevices support only insn interface. |
24 | * For AI: |
25 | * - If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46). |
26 | * - If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44). |
27 | * - If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46). |
28 | * - It is not necessary to have cmd.scan_end_arg=cmd.chanlist_len but |
29 | * cmd.scan_end_arg modulo cmd.chanlist_len must by 0. |
30 | * - If return value of cmdtest is 5 then you've bad channel list |
31 | * (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar |
32 | * ranges). |
33 | * |
34 | * There are some hardware limitations: |
35 | * a) You cann't use mixture of unipolar/bipoar ranges or differencial/single |
36 | * ended inputs. |
37 | * b) DMA transfers must have the length aligned to two samples (32 bit), |
38 | * so there is some problems if cmd->chanlist_len is odd. This driver tries |
39 | * bypass this with adding one sample to the end of the every scan and discard |
40 | * it on output but this can't be used if cmd->scan_begin_src=TRIG_FOLLOW |
41 | * and is used flag CMDF_WAKE_EOS, then driver switch to interrupt driven mode |
42 | * with interrupt after every sample. |
43 | * c) If isn't used DMA then you can use only mode where |
44 | * cmd->scan_begin_src=TRIG_FOLLOW. |
45 | * |
46 | * Configuration options: |
47 | * [0] - PCI bus of device (optional) |
48 | * [1] - PCI slot of device (optional) |
49 | * If bus/slot is not specified, then first available PCI |
50 | * card will be used. |
51 | * [2] - 0= standard 8 DIFF/16 SE channels configuration |
52 | * n = external multiplexer connected, 1 <= n <= 256 |
53 | * [3] - ignored |
54 | * [4] - sample&hold signal - card can generate signal for external S&H board |
55 | * 0 = use SSHO(pin 45) signal is generated in onboard hardware S&H logic |
56 | * 0 != use ADCHN7(pin 23) signal is generated from driver, number say how |
57 | * long delay is requested in ns and sign polarity of the hold |
58 | * (in this case external multiplexor can serve only 128 channels) |
59 | * [5] - ignored |
60 | */ |
61 | |
62 | /* |
63 | * FIXME |
64 | * |
65 | * All the supported boards have the same PCI vendor and device IDs, so |
66 | * auto-attachment of PCI devices will always find the first board type. |
67 | * |
68 | * Perhaps the boards have different subdevice IDs that we could use to |
69 | * distinguish them? |
70 | * |
71 | * Need some device attributes so the board type can be corrected after |
72 | * attachment if necessary, and possibly to set other options supported by |
73 | * manual attachment. |
74 | */ |
75 | |
76 | #include <linux/module.h> |
77 | #include <linux/delay.h> |
78 | #include <linux/gfp.h> |
79 | #include <linux/interrupt.h> |
80 | #include <linux/io.h> |
81 | #include <linux/comedi/comedi_pci.h> |
82 | #include <linux/comedi/comedi_8254.h> |
83 | |
84 | #include "amcc_s5933.h" |
85 | |
86 | /* |
87 | * PCI BAR2 Register map (dev->iobase) |
88 | */ |
89 | #define PCI9118_TIMER_BASE 0x00 |
90 | #define PCI9118_AI_FIFO_REG 0x10 |
91 | #define PCI9118_AO_REG(x) (0x10 + ((x) * 4)) |
92 | #define PCI9118_AI_STATUS_REG 0x18 |
93 | #define PCI9118_AI_STATUS_NFULL BIT(8) /* 0=FIFO full (fatal) */ |
94 | #define PCI9118_AI_STATUS_NHFULL BIT(7) /* 0=FIFO half full */ |
95 | #define PCI9118_AI_STATUS_NEPTY BIT(6) /* 0=FIFO empty */ |
96 | #define PCI9118_AI_STATUS_ACMP BIT(5) /* 1=about trigger complete */ |
97 | #define PCI9118_AI_STATUS_DTH BIT(4) /* 1=ext. digital trigger */ |
98 | #define PCI9118_AI_STATUS_BOVER BIT(3) /* 1=burst overrun (fatal) */ |
99 | #define PCI9118_AI_STATUS_ADOS BIT(2) /* 1=A/D over speed (warn) */ |
100 | #define PCI9118_AI_STATUS_ADOR BIT(1) /* 1=A/D overrun (fatal) */ |
101 | #define PCI9118_AI_STATUS_ADRDY BIT(0) /* 1=A/D ready */ |
102 | #define PCI9118_AI_CTRL_REG 0x18 |
103 | #define PCI9118_AI_CTRL_UNIP BIT(7) /* 1=unipolar */ |
104 | #define PCI9118_AI_CTRL_DIFF BIT(6) /* 1=differential inputs */ |
105 | #define PCI9118_AI_CTRL_SOFTG BIT(5) /* 1=8254 software gate */ |
106 | #define PCI9118_AI_CTRL_EXTG BIT(4) /* 1=8254 TGIN(pin 46) gate */ |
107 | #define PCI9118_AI_CTRL_EXTM BIT(3) /* 1=ext. trigger (pin 44) */ |
108 | #define PCI9118_AI_CTRL_TMRTR BIT(2) /* 1=8254 is trigger source */ |
109 | #define PCI9118_AI_CTRL_INT BIT(1) /* 1=enable interrupt */ |
110 | #define PCI9118_AI_CTRL_DMA BIT(0) /* 1=enable DMA */ |
111 | #define PCI9118_DIO_REG 0x1c |
112 | #define PCI9118_SOFTTRG_REG 0x20 |
113 | #define PCI9118_AI_CHANLIST_REG 0x24 |
114 | #define PCI9118_AI_CHANLIST_RANGE(x) (((x) & 0x3) << 8) |
115 | #define PCI9118_AI_CHANLIST_CHAN(x) ((x) << 0) |
116 | #define PCI9118_AI_BURST_NUM_REG 0x28 |
117 | #define PCI9118_AI_AUTOSCAN_MODE_REG 0x2c |
118 | #define PCI9118_AI_CFG_REG 0x30 |
119 | #define PCI9118_AI_CFG_PDTRG BIT(7) /* 1=positive trigger */ |
120 | #define PCI9118_AI_CFG_PETRG BIT(6) /* 1=positive ext. trigger */ |
121 | #define PCI9118_AI_CFG_BSSH BIT(5) /* 1=with sample & hold */ |
122 | #define PCI9118_AI_CFG_BM BIT(4) /* 1=burst mode */ |
123 | #define PCI9118_AI_CFG_BS BIT(3) /* 1=burst mode start */ |
124 | #define PCI9118_AI_CFG_PM BIT(2) /* 1=post trigger */ |
125 | #define PCI9118_AI_CFG_AM BIT(1) /* 1=about trigger */ |
126 | #define PCI9118_AI_CFG_START BIT(0) /* 1=trigger start */ |
127 | #define PCI9118_FIFO_RESET_REG 0x34 |
128 | #define PCI9118_INT_CTRL_REG 0x38 |
129 | #define PCI9118_INT_CTRL_TIMER BIT(3) /* timer interrupt */ |
130 | #define PCI9118_INT_CTRL_ABOUT BIT(2) /* about trigger complete */ |
131 | #define PCI9118_INT_CTRL_HFULL BIT(1) /* A/D FIFO half full */ |
132 | #define PCI9118_INT_CTRL_DTRG BIT(0) /* ext. digital trigger */ |
133 | |
134 | #define START_AI_EXT 0x01 /* start measure on external trigger */ |
135 | #define STOP_AI_EXT 0x02 /* stop measure on external trigger */ |
136 | #define STOP_AI_INT 0x08 /* stop measure on internal trigger */ |
137 | |
138 | static const struct comedi_lrange pci9118_ai_range = { |
139 | 8, { |
140 | BIP_RANGE(5), |
141 | BIP_RANGE(2.5), |
142 | BIP_RANGE(1.25), |
143 | BIP_RANGE(0.625), |
144 | UNI_RANGE(10), |
145 | UNI_RANGE(5), |
146 | UNI_RANGE(2.5), |
147 | UNI_RANGE(1.25) |
148 | } |
149 | }; |
150 | |
151 | static const struct comedi_lrange pci9118hg_ai_range = { |
152 | 8, { |
153 | BIP_RANGE(5), |
154 | BIP_RANGE(0.5), |
155 | BIP_RANGE(0.05), |
156 | BIP_RANGE(0.005), |
157 | UNI_RANGE(10), |
158 | UNI_RANGE(1), |
159 | UNI_RANGE(0.1), |
160 | UNI_RANGE(0.01) |
161 | } |
162 | }; |
163 | |
164 | enum pci9118_boardid { |
165 | BOARD_PCI9118DG, |
166 | BOARD_PCI9118HG, |
167 | BOARD_PCI9118HR, |
168 | }; |
169 | |
170 | struct pci9118_boardinfo { |
171 | const char *name; |
172 | unsigned int ai_is_16bit:1; |
173 | unsigned int is_hg:1; |
174 | }; |
175 | |
176 | static const struct pci9118_boardinfo pci9118_boards[] = { |
177 | [BOARD_PCI9118DG] = { |
178 | .name = "pci9118dg" , |
179 | }, |
180 | [BOARD_PCI9118HG] = { |
181 | .name = "pci9118hg" , |
182 | .is_hg = 1, |
183 | }, |
184 | [BOARD_PCI9118HR] = { |
185 | .name = "pci9118hr" , |
186 | .ai_is_16bit = 1, |
187 | }, |
188 | }; |
189 | |
190 | struct pci9118_dmabuf { |
191 | unsigned short *virt; /* virtual address of buffer */ |
192 | dma_addr_t hw; /* hardware (bus) address of buffer */ |
193 | unsigned int size; /* size of dma buffer in bytes */ |
194 | unsigned int use_size; /* which size we may now use for transfer */ |
195 | }; |
196 | |
197 | struct pci9118_private { |
198 | unsigned long iobase_a; /* base+size for AMCC chip */ |
199 | unsigned int master:1; |
200 | unsigned int dma_doublebuf:1; |
201 | unsigned int ai_neverending:1; |
202 | unsigned int usedma:1; |
203 | unsigned int usemux:1; |
204 | unsigned char ai_ctrl; |
205 | unsigned char int_ctrl; |
206 | unsigned char ai_cfg; |
207 | unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */ |
208 | unsigned int ai_n_realscanlen; /* |
209 | * what we must transfer for one |
210 | * outgoing scan include front/back adds |
211 | */ |
212 | unsigned int ai_act_dmapos; /* position in actual real stream */ |
213 | unsigned int ai_add_front; /* |
214 | * how many channels we must add |
215 | * before scan to satisfy S&H? |
216 | */ |
217 | unsigned int ai_add_back; /* |
218 | * how many channels we must add |
219 | * before scan to satisfy DMA? |
220 | */ |
221 | unsigned int ai_flags; |
222 | char ai12_startstop; /* |
223 | * measure can start/stop |
224 | * on external trigger |
225 | */ |
226 | unsigned int dma_actbuf; /* which buffer is used now */ |
227 | struct pci9118_dmabuf dmabuf[2]; |
228 | int softsshdelay; /* |
229 | * >0 use software S&H, |
230 | * numer is requested delay in ns |
231 | */ |
232 | unsigned char softsshsample; /* |
233 | * polarity of S&H signal |
234 | * in sample state |
235 | */ |
236 | unsigned char softsshhold; /* |
237 | * polarity of S&H signal |
238 | * in hold state |
239 | */ |
240 | unsigned int ai_ns_min; |
241 | }; |
242 | |
243 | static void pci9118_amcc_setup_dma(struct comedi_device *dev, unsigned int buf) |
244 | { |
245 | struct pci9118_private *devpriv = dev->private; |
246 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[buf]; |
247 | |
248 | /* set the master write address and transfer count */ |
249 | outl(value: dmabuf->hw, port: devpriv->iobase_a + AMCC_OP_REG_MWAR); |
250 | outl(value: dmabuf->use_size, port: devpriv->iobase_a + AMCC_OP_REG_MWTC); |
251 | } |
252 | |
253 | static void pci9118_amcc_dma_ena(struct comedi_device *dev, bool enable) |
254 | { |
255 | struct pci9118_private *devpriv = dev->private; |
256 | unsigned int mcsr; |
257 | |
258 | mcsr = inl(port: devpriv->iobase_a + AMCC_OP_REG_MCSR); |
259 | if (enable) |
260 | mcsr |= RESET_A2P_FLAGS | A2P_HI_PRIORITY | EN_A2P_TRANSFERS; |
261 | else |
262 | mcsr &= ~EN_A2P_TRANSFERS; |
263 | outl(value: mcsr, port: devpriv->iobase_a + AMCC_OP_REG_MCSR); |
264 | } |
265 | |
266 | static void pci9118_amcc_int_ena(struct comedi_device *dev, bool enable) |
267 | { |
268 | struct pci9118_private *devpriv = dev->private; |
269 | unsigned int intcsr; |
270 | |
271 | /* enable/disable interrupt for AMCC Incoming Mailbox 4 (32-bit) */ |
272 | intcsr = inl(port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
273 | if (enable) |
274 | intcsr |= 0x1f00; |
275 | else |
276 | intcsr &= ~0x1f00; |
277 | outl(value: intcsr, port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
278 | } |
279 | |
280 | static void pci9118_ai_reset_fifo(struct comedi_device *dev) |
281 | { |
282 | /* writing any value resets the A/D FIFO */ |
283 | outl(value: 0, port: dev->iobase + PCI9118_FIFO_RESET_REG); |
284 | } |
285 | |
286 | static int pci9118_ai_check_chanlist(struct comedi_device *dev, |
287 | struct comedi_subdevice *s, |
288 | struct comedi_cmd *cmd) |
289 | { |
290 | struct pci9118_private *devpriv = dev->private; |
291 | unsigned int range0 = CR_RANGE(cmd->chanlist[0]); |
292 | unsigned int aref0 = CR_AREF(cmd->chanlist[0]); |
293 | int i; |
294 | |
295 | /* single channel scans are always ok */ |
296 | if (cmd->chanlist_len == 1) |
297 | return 0; |
298 | |
299 | for (i = 1; i < cmd->chanlist_len; i++) { |
300 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
301 | unsigned int range = CR_RANGE(cmd->chanlist[i]); |
302 | unsigned int aref = CR_AREF(cmd->chanlist[i]); |
303 | |
304 | if (aref != aref0) { |
305 | dev_err(dev->class_dev, |
306 | "Differential and single ended inputs can't be mixed!\n" ); |
307 | return -EINVAL; |
308 | } |
309 | if (comedi_range_is_bipolar(s, range) != |
310 | comedi_range_is_bipolar(s, range: range0)) { |
311 | dev_err(dev->class_dev, |
312 | "Bipolar and unipolar ranges can't be mixed!\n" ); |
313 | return -EINVAL; |
314 | } |
315 | if (!devpriv->usemux && aref == AREF_DIFF && |
316 | (chan >= (s->n_chan / 2))) { |
317 | dev_err(dev->class_dev, |
318 | "AREF_DIFF is only available for the first 8 channels!\n" ); |
319 | return -EINVAL; |
320 | } |
321 | } |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static void pci9118_set_chanlist(struct comedi_device *dev, |
327 | struct comedi_subdevice *s, |
328 | int n_chan, unsigned int *chanlist, |
329 | int frontadd, int backadd) |
330 | { |
331 | struct pci9118_private *devpriv = dev->private; |
332 | unsigned int chan0 = CR_CHAN(chanlist[0]); |
333 | unsigned int range0 = CR_RANGE(chanlist[0]); |
334 | unsigned int aref0 = CR_AREF(chanlist[0]); |
335 | unsigned int ssh = 0x00; |
336 | unsigned int val; |
337 | int i; |
338 | |
339 | /* |
340 | * Configure analog input based on the first chanlist entry. |
341 | * All entries are either unipolar or bipolar and single-ended |
342 | * or differential. |
343 | */ |
344 | devpriv->ai_ctrl = 0; |
345 | if (comedi_range_is_unipolar(s, range: range0)) |
346 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_UNIP; |
347 | if (aref0 == AREF_DIFF) |
348 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_DIFF; |
349 | outl(value: devpriv->ai_ctrl, port: dev->iobase + PCI9118_AI_CTRL_REG); |
350 | |
351 | /* gods know why this sequence! */ |
352 | outl(value: 2, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
353 | outl(value: 0, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
354 | outl(value: 1, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
355 | |
356 | /* insert channels for S&H */ |
357 | if (frontadd) { |
358 | val = PCI9118_AI_CHANLIST_CHAN(chan0) | |
359 | PCI9118_AI_CHANLIST_RANGE(range0); |
360 | ssh = devpriv->softsshsample; |
361 | for (i = 0; i < frontadd; i++) { |
362 | outl(value: val | ssh, port: dev->iobase + PCI9118_AI_CHANLIST_REG); |
363 | ssh = devpriv->softsshhold; |
364 | } |
365 | } |
366 | |
367 | /* store chanlist */ |
368 | for (i = 0; i < n_chan; i++) { |
369 | unsigned int chan = CR_CHAN(chanlist[i]); |
370 | unsigned int range = CR_RANGE(chanlist[i]); |
371 | |
372 | val = PCI9118_AI_CHANLIST_CHAN(chan) | |
373 | PCI9118_AI_CHANLIST_RANGE(range); |
374 | outl(value: val | ssh, port: dev->iobase + PCI9118_AI_CHANLIST_REG); |
375 | } |
376 | |
377 | /* insert channels to fit onto 32bit DMA */ |
378 | if (backadd) { |
379 | val = PCI9118_AI_CHANLIST_CHAN(chan0) | |
380 | PCI9118_AI_CHANLIST_RANGE(range0); |
381 | for (i = 0; i < backadd; i++) |
382 | outl(value: val | ssh, port: dev->iobase + PCI9118_AI_CHANLIST_REG); |
383 | } |
384 | /* close scan queue */ |
385 | outl(value: 0, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
386 | /* udelay(100); important delay, or first sample will be crippled */ |
387 | } |
388 | |
389 | static void pci9118_ai_mode4_switch(struct comedi_device *dev, |
390 | unsigned int next_buf) |
391 | { |
392 | struct pci9118_private *devpriv = dev->private; |
393 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[next_buf]; |
394 | |
395 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG | |
396 | PCI9118_AI_CFG_AM; |
397 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
398 | comedi_8254_load(i8254: dev->pacer, counter: 0, val: dmabuf->hw >> 1, |
399 | mode: I8254_MODE0 | I8254_BINARY); |
400 | devpriv->ai_cfg |= PCI9118_AI_CFG_START; |
401 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
402 | } |
403 | |
404 | static unsigned int pci9118_ai_samples_ready(struct comedi_device *dev, |
405 | struct comedi_subdevice *s, |
406 | unsigned int n_raw_samples) |
407 | { |
408 | struct pci9118_private *devpriv = dev->private; |
409 | struct comedi_cmd *cmd = &s->async->cmd; |
410 | unsigned int start_pos = devpriv->ai_add_front; |
411 | unsigned int stop_pos = start_pos + cmd->chanlist_len; |
412 | unsigned int span_len = stop_pos + devpriv->ai_add_back; |
413 | unsigned int dma_pos = devpriv->ai_act_dmapos; |
414 | unsigned int whole_spans, n_samples, x; |
415 | |
416 | if (span_len == cmd->chanlist_len) |
417 | return n_raw_samples; /* use all samples */ |
418 | |
419 | /* |
420 | * Not all samples are to be used. Buffer contents consist of a |
421 | * possibly non-whole number of spans and a region of each span |
422 | * is to be used. |
423 | * |
424 | * Account for samples in whole number of spans. |
425 | */ |
426 | whole_spans = n_raw_samples / span_len; |
427 | n_samples = whole_spans * cmd->chanlist_len; |
428 | n_raw_samples -= whole_spans * span_len; |
429 | |
430 | /* |
431 | * Deal with remaining samples which could overlap up to two spans. |
432 | */ |
433 | while (n_raw_samples) { |
434 | if (dma_pos < start_pos) { |
435 | /* Skip samples before start position. */ |
436 | x = start_pos - dma_pos; |
437 | if (x > n_raw_samples) |
438 | x = n_raw_samples; |
439 | dma_pos += x; |
440 | n_raw_samples -= x; |
441 | if (!n_raw_samples) |
442 | break; |
443 | } |
444 | if (dma_pos < stop_pos) { |
445 | /* Include samples before stop position. */ |
446 | x = stop_pos - dma_pos; |
447 | if (x > n_raw_samples) |
448 | x = n_raw_samples; |
449 | n_samples += x; |
450 | dma_pos += x; |
451 | n_raw_samples -= x; |
452 | } |
453 | /* Advance to next span. */ |
454 | start_pos += span_len; |
455 | stop_pos += span_len; |
456 | } |
457 | return n_samples; |
458 | } |
459 | |
460 | static void pci9118_ai_dma_xfer(struct comedi_device *dev, |
461 | struct comedi_subdevice *s, |
462 | unsigned short *dma_buffer, |
463 | unsigned int n_raw_samples) |
464 | { |
465 | struct pci9118_private *devpriv = dev->private; |
466 | struct comedi_cmd *cmd = &s->async->cmd; |
467 | unsigned int start_pos = devpriv->ai_add_front; |
468 | unsigned int stop_pos = start_pos + cmd->chanlist_len; |
469 | unsigned int span_len = stop_pos + devpriv->ai_add_back; |
470 | unsigned int dma_pos = devpriv->ai_act_dmapos; |
471 | unsigned int x; |
472 | |
473 | if (span_len == cmd->chanlist_len) { |
474 | /* All samples are to be copied. */ |
475 | comedi_buf_write_samples(s, data: dma_buffer, nsamples: n_raw_samples); |
476 | dma_pos += n_raw_samples; |
477 | } else { |
478 | /* |
479 | * Not all samples are to be copied. Buffer contents consist |
480 | * of a possibly non-whole number of spans and a region of |
481 | * each span is to be copied. |
482 | */ |
483 | while (n_raw_samples) { |
484 | if (dma_pos < start_pos) { |
485 | /* Skip samples before start position. */ |
486 | x = start_pos - dma_pos; |
487 | if (x > n_raw_samples) |
488 | x = n_raw_samples; |
489 | dma_pos += x; |
490 | n_raw_samples -= x; |
491 | if (!n_raw_samples) |
492 | break; |
493 | } |
494 | if (dma_pos < stop_pos) { |
495 | /* Copy samples before stop position. */ |
496 | x = stop_pos - dma_pos; |
497 | if (x > n_raw_samples) |
498 | x = n_raw_samples; |
499 | comedi_buf_write_samples(s, data: dma_buffer, nsamples: x); |
500 | dma_pos += x; |
501 | n_raw_samples -= x; |
502 | } |
503 | /* Advance to next span. */ |
504 | start_pos += span_len; |
505 | stop_pos += span_len; |
506 | } |
507 | } |
508 | /* Update position in span for next time. */ |
509 | devpriv->ai_act_dmapos = dma_pos % span_len; |
510 | } |
511 | |
512 | static void pci9118_exttrg_enable(struct comedi_device *dev, bool enable) |
513 | { |
514 | struct pci9118_private *devpriv = dev->private; |
515 | |
516 | if (enable) |
517 | devpriv->int_ctrl |= PCI9118_INT_CTRL_DTRG; |
518 | else |
519 | devpriv->int_ctrl &= ~PCI9118_INT_CTRL_DTRG; |
520 | outl(value: devpriv->int_ctrl, port: dev->iobase + PCI9118_INT_CTRL_REG); |
521 | |
522 | if (devpriv->int_ctrl) |
523 | pci9118_amcc_int_ena(dev, enable: true); |
524 | else |
525 | pci9118_amcc_int_ena(dev, enable: false); |
526 | } |
527 | |
528 | static void pci9118_calc_divisors(struct comedi_device *dev, |
529 | struct comedi_subdevice *s, |
530 | unsigned int *tim1, unsigned int *tim2, |
531 | unsigned int flags, int chans, |
532 | unsigned int *div1, unsigned int *div2, |
533 | unsigned int chnsshfront) |
534 | { |
535 | struct comedi_8254 *pacer = dev->pacer; |
536 | struct comedi_cmd *cmd = &s->async->cmd; |
537 | |
538 | *div1 = *tim2 / pacer->osc_base; /* convert timer (burst) */ |
539 | *div2 = *tim1 / pacer->osc_base; /* scan timer */ |
540 | *div2 = *div2 / *div1; /* major timer is c1*c2 */ |
541 | if (*div2 < chans) |
542 | *div2 = chans; |
543 | |
544 | *tim2 = *div1 * pacer->osc_base; /* real convert timer */ |
545 | |
546 | if (cmd->convert_src == TRIG_NOW && !chnsshfront) { |
547 | /* use BSSH signal */ |
548 | if (*div2 < (chans + 2)) |
549 | *div2 = chans + 2; |
550 | } |
551 | |
552 | *tim1 = *div1 * *div2 * pacer->osc_base; |
553 | } |
554 | |
555 | static void pci9118_start_pacer(struct comedi_device *dev, int mode) |
556 | { |
557 | if (mode == 1 || mode == 2 || mode == 4) |
558 | comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: true); |
559 | } |
560 | |
561 | static int pci9118_ai_cancel(struct comedi_device *dev, |
562 | struct comedi_subdevice *s) |
563 | { |
564 | struct pci9118_private *devpriv = dev->private; |
565 | |
566 | if (devpriv->usedma) |
567 | pci9118_amcc_dma_ena(dev, enable: false); |
568 | pci9118_exttrg_enable(dev, enable: false); |
569 | comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: false); |
570 | /* set default config (disable burst and triggers) */ |
571 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; |
572 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
573 | /* reset acquisition control */ |
574 | devpriv->ai_ctrl = 0; |
575 | outl(value: devpriv->ai_ctrl, port: dev->iobase + PCI9118_AI_CTRL_REG); |
576 | outl(value: 0, port: dev->iobase + PCI9118_AI_BURST_NUM_REG); |
577 | /* reset scan queue */ |
578 | outl(value: 1, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
579 | outl(value: 2, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
580 | pci9118_ai_reset_fifo(dev); |
581 | |
582 | devpriv->int_ctrl = 0; |
583 | outl(value: devpriv->int_ctrl, port: dev->iobase + PCI9118_INT_CTRL_REG); |
584 | pci9118_amcc_int_ena(dev, enable: false); |
585 | |
586 | devpriv->ai_do = 0; |
587 | devpriv->usedma = 0; |
588 | |
589 | devpriv->ai_act_dmapos = 0; |
590 | s->async->inttrig = NULL; |
591 | devpriv->ai_neverending = 0; |
592 | devpriv->dma_actbuf = 0; |
593 | |
594 | return 0; |
595 | } |
596 | |
597 | static void pci9118_ai_munge(struct comedi_device *dev, |
598 | struct comedi_subdevice *s, void *data, |
599 | unsigned int num_bytes, |
600 | unsigned int start_chan_index) |
601 | { |
602 | struct pci9118_private *devpriv = dev->private; |
603 | unsigned short *array = data; |
604 | unsigned int num_samples = comedi_bytes_to_samples(s, nbytes: num_bytes); |
605 | unsigned int i; |
606 | __be16 *barray = data; |
607 | |
608 | for (i = 0; i < num_samples; i++) { |
609 | if (devpriv->usedma) |
610 | array[i] = be16_to_cpu(barray[i]); |
611 | if (s->maxdata == 0xffff) |
612 | array[i] ^= 0x8000; |
613 | else |
614 | array[i] = (array[i] >> 4) & 0x0fff; |
615 | } |
616 | } |
617 | |
618 | static void pci9118_ai_get_onesample(struct comedi_device *dev, |
619 | struct comedi_subdevice *s) |
620 | { |
621 | struct pci9118_private *devpriv = dev->private; |
622 | struct comedi_cmd *cmd = &s->async->cmd; |
623 | unsigned short sampl; |
624 | |
625 | sampl = inl(port: dev->iobase + PCI9118_AI_FIFO_REG); |
626 | |
627 | comedi_buf_write_samples(s, data: &sampl, nsamples: 1); |
628 | |
629 | if (!devpriv->ai_neverending) { |
630 | if (s->async->scans_done >= cmd->stop_arg) |
631 | s->async->events |= COMEDI_CB_EOA; |
632 | } |
633 | } |
634 | |
635 | static void pci9118_ai_get_dma(struct comedi_device *dev, |
636 | struct comedi_subdevice *s) |
637 | { |
638 | struct pci9118_private *devpriv = dev->private; |
639 | struct comedi_cmd *cmd = &s->async->cmd; |
640 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[devpriv->dma_actbuf]; |
641 | unsigned int n_all = comedi_bytes_to_samples(s, nbytes: dmabuf->use_size); |
642 | unsigned int n_valid; |
643 | bool more_dma; |
644 | |
645 | /* determine whether more DMA buffers to do after this one */ |
646 | n_valid = pci9118_ai_samples_ready(dev, s, n_raw_samples: n_all); |
647 | more_dma = n_valid < comedi_nsamples_left(s, nsamples: n_valid + 1); |
648 | |
649 | /* switch DMA buffers and restart DMA if double buffering */ |
650 | if (more_dma && devpriv->dma_doublebuf) { |
651 | devpriv->dma_actbuf = 1 - devpriv->dma_actbuf; |
652 | pci9118_amcc_setup_dma(dev, buf: devpriv->dma_actbuf); |
653 | if (devpriv->ai_do == 4) |
654 | pci9118_ai_mode4_switch(dev, next_buf: devpriv->dma_actbuf); |
655 | } |
656 | |
657 | if (n_all) |
658 | pci9118_ai_dma_xfer(dev, s, dma_buffer: dmabuf->virt, n_raw_samples: n_all); |
659 | |
660 | if (!devpriv->ai_neverending) { |
661 | if (s->async->scans_done >= cmd->stop_arg) |
662 | s->async->events |= COMEDI_CB_EOA; |
663 | } |
664 | |
665 | if (s->async->events & COMEDI_CB_CANCEL_MASK) |
666 | more_dma = false; |
667 | |
668 | /* restart DMA if not double buffering */ |
669 | if (more_dma && !devpriv->dma_doublebuf) { |
670 | pci9118_amcc_setup_dma(dev, buf: 0); |
671 | if (devpriv->ai_do == 4) |
672 | pci9118_ai_mode4_switch(dev, next_buf: 0); |
673 | } |
674 | } |
675 | |
676 | static irqreturn_t pci9118_interrupt(int irq, void *d) |
677 | { |
678 | struct comedi_device *dev = d; |
679 | struct comedi_subdevice *s = dev->read_subdev; |
680 | struct pci9118_private *devpriv = dev->private; |
681 | unsigned int intsrc; /* IRQ reasons from card */ |
682 | unsigned int intcsr; /* INT register from AMCC chip */ |
683 | unsigned int adstat; /* STATUS register */ |
684 | |
685 | if (!dev->attached) |
686 | return IRQ_NONE; |
687 | |
688 | intsrc = inl(port: dev->iobase + PCI9118_INT_CTRL_REG) & 0xf; |
689 | intcsr = inl(port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
690 | |
691 | if (!intsrc && !(intcsr & ANY_S593X_INT)) |
692 | return IRQ_NONE; |
693 | |
694 | outl(value: intcsr | 0x00ff0000, port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
695 | |
696 | if (intcsr & MASTER_ABORT_INT) { |
697 | dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n" ); |
698 | s->async->events |= COMEDI_CB_ERROR; |
699 | goto interrupt_exit; |
700 | } |
701 | |
702 | if (intcsr & TARGET_ABORT_INT) { |
703 | dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n" ); |
704 | s->async->events |= COMEDI_CB_ERROR; |
705 | goto interrupt_exit; |
706 | } |
707 | |
708 | adstat = inl(port: dev->iobase + PCI9118_AI_STATUS_REG); |
709 | if ((adstat & PCI9118_AI_STATUS_NFULL) == 0) { |
710 | dev_err(dev->class_dev, |
711 | "A/D FIFO Full status (Fatal Error!)\n" ); |
712 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; |
713 | goto interrupt_exit; |
714 | } |
715 | if (adstat & PCI9118_AI_STATUS_BOVER) { |
716 | dev_err(dev->class_dev, |
717 | "A/D Burst Mode Overrun Status (Fatal Error!)\n" ); |
718 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; |
719 | goto interrupt_exit; |
720 | } |
721 | if (adstat & PCI9118_AI_STATUS_ADOS) { |
722 | dev_err(dev->class_dev, "A/D Over Speed Status (Warning!)\n" ); |
723 | s->async->events |= COMEDI_CB_ERROR; |
724 | goto interrupt_exit; |
725 | } |
726 | if (adstat & PCI9118_AI_STATUS_ADOR) { |
727 | dev_err(dev->class_dev, "A/D Overrun Status (Fatal Error!)\n" ); |
728 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; |
729 | goto interrupt_exit; |
730 | } |
731 | |
732 | if (!devpriv->ai_do) |
733 | return IRQ_HANDLED; |
734 | |
735 | if (devpriv->ai12_startstop) { |
736 | if ((adstat & PCI9118_AI_STATUS_DTH) && |
737 | (intsrc & PCI9118_INT_CTRL_DTRG)) { |
738 | /* start/stop of measure */ |
739 | if (devpriv->ai12_startstop & START_AI_EXT) { |
740 | /* deactivate EXT trigger */ |
741 | devpriv->ai12_startstop &= ~START_AI_EXT; |
742 | if (!(devpriv->ai12_startstop & STOP_AI_EXT)) |
743 | pci9118_exttrg_enable(dev, enable: false); |
744 | |
745 | /* start pacer */ |
746 | pci9118_start_pacer(dev, mode: devpriv->ai_do); |
747 | outl(value: devpriv->ai_ctrl, |
748 | port: dev->iobase + PCI9118_AI_CTRL_REG); |
749 | } else if (devpriv->ai12_startstop & STOP_AI_EXT) { |
750 | /* deactivate EXT trigger */ |
751 | devpriv->ai12_startstop &= ~STOP_AI_EXT; |
752 | pci9118_exttrg_enable(dev, enable: false); |
753 | |
754 | /* on next interrupt measure will stop */ |
755 | devpriv->ai_neverending = 0; |
756 | } |
757 | } |
758 | } |
759 | |
760 | if (devpriv->usedma) |
761 | pci9118_ai_get_dma(dev, s); |
762 | else |
763 | pci9118_ai_get_onesample(dev, s); |
764 | |
765 | interrupt_exit: |
766 | comedi_handle_events(dev, s); |
767 | return IRQ_HANDLED; |
768 | } |
769 | |
770 | static void pci9118_ai_cmd_start(struct comedi_device *dev) |
771 | { |
772 | struct pci9118_private *devpriv = dev->private; |
773 | |
774 | outl(value: devpriv->int_ctrl, port: dev->iobase + PCI9118_INT_CTRL_REG); |
775 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
776 | if (devpriv->ai_do != 3) { |
777 | pci9118_start_pacer(dev, mode: devpriv->ai_do); |
778 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_SOFTG; |
779 | } |
780 | outl(value: devpriv->ai_ctrl, port: dev->iobase + PCI9118_AI_CTRL_REG); |
781 | } |
782 | |
783 | static int pci9118_ai_inttrig(struct comedi_device *dev, |
784 | struct comedi_subdevice *s, |
785 | unsigned int trig_num) |
786 | { |
787 | struct comedi_cmd *cmd = &s->async->cmd; |
788 | |
789 | if (trig_num != cmd->start_arg) |
790 | return -EINVAL; |
791 | |
792 | s->async->inttrig = NULL; |
793 | pci9118_ai_cmd_start(dev); |
794 | |
795 | return 1; |
796 | } |
797 | |
798 | static int pci9118_ai_setup_dma(struct comedi_device *dev, |
799 | struct comedi_subdevice *s) |
800 | { |
801 | struct pci9118_private *devpriv = dev->private; |
802 | struct comedi_cmd *cmd = &s->async->cmd; |
803 | struct pci9118_dmabuf *dmabuf0 = &devpriv->dmabuf[0]; |
804 | struct pci9118_dmabuf *dmabuf1 = &devpriv->dmabuf[1]; |
805 | unsigned int dmalen0 = dmabuf0->size; |
806 | unsigned int dmalen1 = dmabuf1->size; |
807 | unsigned int scan_bytes = devpriv->ai_n_realscanlen * |
808 | comedi_bytes_per_sample(s); |
809 | |
810 | /* isn't output buff smaller that our DMA buff? */ |
811 | if (dmalen0 > s->async->prealloc_bufsz) { |
812 | /* align to 32bit down */ |
813 | dmalen0 = s->async->prealloc_bufsz & ~3L; |
814 | } |
815 | if (dmalen1 > s->async->prealloc_bufsz) { |
816 | /* align to 32bit down */ |
817 | dmalen1 = s->async->prealloc_bufsz & ~3L; |
818 | } |
819 | |
820 | /* we want wake up every scan? */ |
821 | if (devpriv->ai_flags & CMDF_WAKE_EOS) { |
822 | if (dmalen0 < scan_bytes) { |
823 | /* uff, too short DMA buffer, disable EOS support! */ |
824 | devpriv->ai_flags &= (~CMDF_WAKE_EOS); |
825 | dev_info(dev->class_dev, |
826 | "WAR: DMA0 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n" , |
827 | dmalen0, scan_bytes); |
828 | } else { |
829 | /* short first DMA buffer to one scan */ |
830 | dmalen0 = scan_bytes; |
831 | if (dmalen0 < 4) { |
832 | dev_info(dev->class_dev, |
833 | "ERR: DMA0 buf len bug? (%d<4)\n" , |
834 | dmalen0); |
835 | dmalen0 = 4; |
836 | } |
837 | } |
838 | } |
839 | if (devpriv->ai_flags & CMDF_WAKE_EOS) { |
840 | if (dmalen1 < scan_bytes) { |
841 | /* uff, too short DMA buffer, disable EOS support! */ |
842 | devpriv->ai_flags &= (~CMDF_WAKE_EOS); |
843 | dev_info(dev->class_dev, |
844 | "WAR: DMA1 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n" , |
845 | dmalen1, scan_bytes); |
846 | } else { |
847 | /* short second DMA buffer to one scan */ |
848 | dmalen1 = scan_bytes; |
849 | if (dmalen1 < 4) { |
850 | dev_info(dev->class_dev, |
851 | "ERR: DMA1 buf len bug? (%d<4)\n" , |
852 | dmalen1); |
853 | dmalen1 = 4; |
854 | } |
855 | } |
856 | } |
857 | |
858 | /* transfer without CMDF_WAKE_EOS */ |
859 | if (!(devpriv->ai_flags & CMDF_WAKE_EOS)) { |
860 | unsigned int tmp; |
861 | |
862 | /* if it's possible then align DMA buffers to length of scan */ |
863 | tmp = dmalen0; |
864 | dmalen0 = (dmalen0 / scan_bytes) * scan_bytes; |
865 | dmalen0 &= ~3L; |
866 | if (!dmalen0) |
867 | dmalen0 = tmp; /* uff. very long scan? */ |
868 | tmp = dmalen1; |
869 | dmalen1 = (dmalen1 / scan_bytes) * scan_bytes; |
870 | dmalen1 &= ~3L; |
871 | if (!dmalen1) |
872 | dmalen1 = tmp; /* uff. very long scan? */ |
873 | /* |
874 | * if measure isn't neverending then test, if it fits whole |
875 | * into one or two DMA buffers |
876 | */ |
877 | if (!devpriv->ai_neverending) { |
878 | unsigned long long scanlen; |
879 | |
880 | scanlen = (unsigned long long)scan_bytes * |
881 | cmd->stop_arg; |
882 | |
883 | /* fits whole measure into one DMA buffer? */ |
884 | if (dmalen0 > scanlen) { |
885 | dmalen0 = scanlen; |
886 | dmalen0 &= ~3L; |
887 | } else { |
888 | /* fits whole measure into two DMA buffer? */ |
889 | if (dmalen1 > (scanlen - dmalen0)) { |
890 | dmalen1 = scanlen - dmalen0; |
891 | dmalen1 &= ~3L; |
892 | } |
893 | } |
894 | } |
895 | } |
896 | |
897 | /* these DMA buffer size will be used */ |
898 | devpriv->dma_actbuf = 0; |
899 | dmabuf0->use_size = dmalen0; |
900 | dmabuf1->use_size = dmalen1; |
901 | |
902 | pci9118_amcc_dma_ena(dev, enable: false); |
903 | pci9118_amcc_setup_dma(dev, buf: 0); |
904 | /* init DMA transfer */ |
905 | outl(value: 0x00000000 | AINT_WRITE_COMPL, |
906 | port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
907 | /* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */ |
908 | pci9118_amcc_dma_ena(dev, enable: true); |
909 | outl(inl(port: devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS, |
910 | port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
911 | /* allow bus mastering */ |
912 | |
913 | return 0; |
914 | } |
915 | |
916 | static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
917 | { |
918 | struct pci9118_private *devpriv = dev->private; |
919 | struct comedi_8254 *pacer = dev->pacer; |
920 | struct comedi_cmd *cmd = &s->async->cmd; |
921 | unsigned int addchans = 0; |
922 | unsigned int scanlen; |
923 | |
924 | devpriv->ai12_startstop = 0; |
925 | devpriv->ai_flags = cmd->flags; |
926 | devpriv->ai_add_front = 0; |
927 | devpriv->ai_add_back = 0; |
928 | |
929 | /* prepare for start/stop conditions */ |
930 | if (cmd->start_src == TRIG_EXT) |
931 | devpriv->ai12_startstop |= START_AI_EXT; |
932 | if (cmd->stop_src == TRIG_EXT) { |
933 | devpriv->ai_neverending = 1; |
934 | devpriv->ai12_startstop |= STOP_AI_EXT; |
935 | } |
936 | if (cmd->stop_src == TRIG_NONE) |
937 | devpriv->ai_neverending = 1; |
938 | if (cmd->stop_src == TRIG_COUNT) |
939 | devpriv->ai_neverending = 0; |
940 | |
941 | /* |
942 | * use additional sample at end of every scan |
943 | * to satisty DMA 32 bit transfer? |
944 | */ |
945 | devpriv->ai_add_front = 0; |
946 | devpriv->ai_add_back = 0; |
947 | if (devpriv->master) { |
948 | devpriv->usedma = 1; |
949 | if ((cmd->flags & CMDF_WAKE_EOS) && |
950 | (cmd->scan_end_arg == 1)) { |
951 | if (cmd->convert_src == TRIG_NOW) |
952 | devpriv->ai_add_back = 1; |
953 | if (cmd->convert_src == TRIG_TIMER) { |
954 | devpriv->usedma = 0; |
955 | /* |
956 | * use INT transfer if scanlist |
957 | * have only one channel |
958 | */ |
959 | } |
960 | } |
961 | if ((cmd->flags & CMDF_WAKE_EOS) && |
962 | (cmd->scan_end_arg & 1) && |
963 | (cmd->scan_end_arg > 1)) { |
964 | if (cmd->scan_begin_src == TRIG_FOLLOW) { |
965 | devpriv->usedma = 0; |
966 | /* |
967 | * XXX maybe can be corrected to use 16 bit DMA |
968 | */ |
969 | } else { /* |
970 | * well, we must insert one sample |
971 | * to end of EOS to meet 32 bit transfer |
972 | */ |
973 | devpriv->ai_add_back = 1; |
974 | } |
975 | } |
976 | } else { /* interrupt transfer don't need any correction */ |
977 | devpriv->usedma = 0; |
978 | } |
979 | |
980 | /* |
981 | * we need software S&H signal? |
982 | * It adds two samples before every scan as minimum |
983 | */ |
984 | if (cmd->convert_src == TRIG_NOW && devpriv->softsshdelay) { |
985 | devpriv->ai_add_front = 2; |
986 | if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) { |
987 | /* move it to front */ |
988 | devpriv->ai_add_front++; |
989 | devpriv->ai_add_back = 0; |
990 | } |
991 | if (cmd->convert_arg < devpriv->ai_ns_min) |
992 | cmd->convert_arg = devpriv->ai_ns_min; |
993 | addchans = devpriv->softsshdelay / cmd->convert_arg; |
994 | if (devpriv->softsshdelay % cmd->convert_arg) |
995 | addchans++; |
996 | if (addchans > (devpriv->ai_add_front - 1)) { |
997 | /* uff, still short */ |
998 | devpriv->ai_add_front = addchans + 1; |
999 | if (devpriv->usedma == 1) |
1000 | if ((devpriv->ai_add_front + |
1001 | cmd->chanlist_len + |
1002 | devpriv->ai_add_back) & 1) |
1003 | devpriv->ai_add_front++; |
1004 | /* round up to 32 bit */ |
1005 | } |
1006 | } |
1007 | /* well, we now know what must be all added */ |
1008 | scanlen = devpriv->ai_add_front + cmd->chanlist_len + |
1009 | devpriv->ai_add_back; |
1010 | /* |
1011 | * what we must take from card in real to have cmd->scan_end_arg |
1012 | * on output? |
1013 | */ |
1014 | devpriv->ai_n_realscanlen = scanlen * |
1015 | (cmd->scan_end_arg / cmd->chanlist_len); |
1016 | |
1017 | if (scanlen > s->len_chanlist) { |
1018 | dev_err(dev->class_dev, |
1019 | "range/channel list is too long for actual configuration!\n" ); |
1020 | return -EINVAL; |
1021 | } |
1022 | |
1023 | /* |
1024 | * Configure analog input and load the chanlist. |
1025 | * The acquisition control bits are enabled later. |
1026 | */ |
1027 | pci9118_set_chanlist(dev, s, n_chan: cmd->chanlist_len, chanlist: cmd->chanlist, |
1028 | frontadd: devpriv->ai_add_front, backadd: devpriv->ai_add_back); |
1029 | |
1030 | /* Determine acquisition mode and calculate timing */ |
1031 | devpriv->ai_do = 0; |
1032 | if (cmd->scan_begin_src != TRIG_TIMER && |
1033 | cmd->convert_src == TRIG_TIMER) { |
1034 | /* cascaded timers 1 and 2 are used for convert timing */ |
1035 | if (cmd->scan_begin_src == TRIG_EXT) |
1036 | devpriv->ai_do = 4; |
1037 | else |
1038 | devpriv->ai_do = 1; |
1039 | |
1040 | comedi_8254_cascade_ns_to_timer(i8254: pacer, nanosec: &cmd->convert_arg, |
1041 | flags: devpriv->ai_flags & |
1042 | CMDF_ROUND_NEAREST); |
1043 | comedi_8254_update_divisors(i8254: pacer); |
1044 | |
1045 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR; |
1046 | |
1047 | if (!devpriv->usedma) { |
1048 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_INT; |
1049 | devpriv->int_ctrl |= PCI9118_INT_CTRL_TIMER; |
1050 | } |
1051 | |
1052 | if (cmd->scan_begin_src == TRIG_EXT) { |
1053 | struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[0]; |
1054 | |
1055 | devpriv->ai_cfg |= PCI9118_AI_CFG_AM; |
1056 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
1057 | comedi_8254_load(i8254: pacer, counter: 0, val: dmabuf->hw >> 1, |
1058 | mode: I8254_MODE0 | I8254_BINARY); |
1059 | devpriv->ai_cfg |= PCI9118_AI_CFG_START; |
1060 | } |
1061 | } |
1062 | |
1063 | if (cmd->scan_begin_src == TRIG_TIMER && |
1064 | cmd->convert_src != TRIG_EXT) { |
1065 | if (!devpriv->usedma) { |
1066 | dev_err(dev->class_dev, |
1067 | "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!\n" ); |
1068 | return -EIO; |
1069 | } |
1070 | |
1071 | /* double timed action */ |
1072 | devpriv->ai_do = 2; |
1073 | |
1074 | pci9118_calc_divisors(dev, s, |
1075 | tim1: &cmd->scan_begin_arg, tim2: &cmd->convert_arg, |
1076 | flags: devpriv->ai_flags, |
1077 | chans: devpriv->ai_n_realscanlen, |
1078 | div1: &pacer->divisor1, |
1079 | div2: &pacer->divisor2, |
1080 | chnsshfront: devpriv->ai_add_front); |
1081 | |
1082 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR; |
1083 | devpriv->ai_cfg |= PCI9118_AI_CFG_BM | PCI9118_AI_CFG_BS; |
1084 | if (cmd->convert_src == TRIG_NOW && !devpriv->softsshdelay) |
1085 | devpriv->ai_cfg |= PCI9118_AI_CFG_BSSH; |
1086 | outl(value: devpriv->ai_n_realscanlen, |
1087 | port: dev->iobase + PCI9118_AI_BURST_NUM_REG); |
1088 | } |
1089 | |
1090 | if (cmd->scan_begin_src == TRIG_FOLLOW && |
1091 | cmd->convert_src == TRIG_EXT) { |
1092 | /* external trigger conversion */ |
1093 | devpriv->ai_do = 3; |
1094 | |
1095 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_EXTM; |
1096 | } |
1097 | |
1098 | if (devpriv->ai_do == 0) { |
1099 | dev_err(dev->class_dev, |
1100 | "Unable to determine acquisition mode! BUG in (*do_cmdtest)?\n" ); |
1101 | return -EINVAL; |
1102 | } |
1103 | |
1104 | if (devpriv->usedma) |
1105 | devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA; |
1106 | |
1107 | /* set default config (disable burst and triggers) */ |
1108 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; |
1109 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
1110 | udelay(1); |
1111 | pci9118_ai_reset_fifo(dev); |
1112 | |
1113 | /* clear A/D and INT status registers */ |
1114 | inl(port: dev->iobase + PCI9118_AI_STATUS_REG); |
1115 | inl(port: dev->iobase + PCI9118_INT_CTRL_REG); |
1116 | |
1117 | devpriv->ai_act_dmapos = 0; |
1118 | |
1119 | if (devpriv->usedma) { |
1120 | pci9118_ai_setup_dma(dev, s); |
1121 | |
1122 | outl(value: 0x02000000 | AINT_WRITE_COMPL, |
1123 | port: devpriv->iobase_a + AMCC_OP_REG_INTCSR); |
1124 | } else { |
1125 | pci9118_amcc_int_ena(dev, enable: true); |
1126 | } |
1127 | |
1128 | /* start async command now or wait for internal trigger */ |
1129 | if (cmd->start_src == TRIG_NOW) |
1130 | pci9118_ai_cmd_start(dev); |
1131 | else if (cmd->start_src == TRIG_INT) |
1132 | s->async->inttrig = pci9118_ai_inttrig; |
1133 | |
1134 | /* enable external trigger for command start/stop */ |
1135 | if (cmd->start_src == TRIG_EXT || cmd->stop_src == TRIG_EXT) |
1136 | pci9118_exttrg_enable(dev, enable: true); |
1137 | |
1138 | return 0; |
1139 | } |
1140 | |
1141 | static int pci9118_ai_cmdtest(struct comedi_device *dev, |
1142 | struct comedi_subdevice *s, |
1143 | struct comedi_cmd *cmd) |
1144 | { |
1145 | struct pci9118_private *devpriv = dev->private; |
1146 | int err = 0; |
1147 | unsigned int flags; |
1148 | unsigned int arg; |
1149 | |
1150 | /* Step 1 : check if triggers are trivially valid */ |
1151 | |
1152 | err |= comedi_check_trigger_src(src: &cmd->start_src, |
1153 | TRIG_NOW | TRIG_EXT | TRIG_INT); |
1154 | |
1155 | flags = TRIG_FOLLOW; |
1156 | if (devpriv->master) |
1157 | flags |= TRIG_TIMER | TRIG_EXT; |
1158 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, flags); |
1159 | |
1160 | flags = TRIG_TIMER | TRIG_EXT; |
1161 | if (devpriv->master) |
1162 | flags |= TRIG_NOW; |
1163 | err |= comedi_check_trigger_src(src: &cmd->convert_src, flags); |
1164 | |
1165 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
1166 | err |= comedi_check_trigger_src(src: &cmd->stop_src, |
1167 | TRIG_COUNT | TRIG_NONE | TRIG_EXT); |
1168 | |
1169 | if (err) |
1170 | return 1; |
1171 | |
1172 | /* Step 2a : make sure trigger sources are unique */ |
1173 | |
1174 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
1175 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
1176 | err |= comedi_check_trigger_is_unique(src: cmd->convert_src); |
1177 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
1178 | |
1179 | /* Step 2b : and mutually compatible */ |
1180 | |
1181 | if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) |
1182 | err |= -EINVAL; |
1183 | |
1184 | if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) && |
1185 | (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) |
1186 | err |= -EINVAL; |
1187 | |
1188 | if ((cmd->scan_begin_src == TRIG_FOLLOW) && |
1189 | (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) |
1190 | err |= -EINVAL; |
1191 | |
1192 | if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) |
1193 | err |= -EINVAL; |
1194 | |
1195 | if (err) |
1196 | return 2; |
1197 | |
1198 | /* Step 3: check if arguments are trivially valid */ |
1199 | |
1200 | switch (cmd->start_src) { |
1201 | case TRIG_NOW: |
1202 | case TRIG_EXT: |
1203 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
1204 | break; |
1205 | case TRIG_INT: |
1206 | /* start_arg is the internal trigger (any value) */ |
1207 | break; |
1208 | } |
1209 | |
1210 | if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT)) |
1211 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: 0); |
1212 | |
1213 | if ((cmd->scan_begin_src == TRIG_TIMER) && |
1214 | (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) { |
1215 | cmd->scan_begin_src = TRIG_FOLLOW; |
1216 | cmd->convert_arg = cmd->scan_begin_arg; |
1217 | cmd->scan_begin_arg = 0; |
1218 | } |
1219 | |
1220 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1221 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, |
1222 | val: devpriv->ai_ns_min); |
1223 | } |
1224 | |
1225 | if (cmd->scan_begin_src == TRIG_EXT) { |
1226 | if (cmd->scan_begin_arg) { |
1227 | cmd->scan_begin_arg = 0; |
1228 | err |= -EINVAL; |
1229 | err |= comedi_check_trigger_arg_max(arg: &cmd->scan_end_arg, |
1230 | val: 65535); |
1231 | } |
1232 | } |
1233 | |
1234 | if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { |
1235 | err |= comedi_check_trigger_arg_min(arg: &cmd->convert_arg, |
1236 | val: devpriv->ai_ns_min); |
1237 | } |
1238 | |
1239 | if (cmd->convert_src == TRIG_EXT) |
1240 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: 0); |
1241 | |
1242 | if (cmd->stop_src == TRIG_COUNT) |
1243 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
1244 | else /* TRIG_NONE */ |
1245 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
1246 | |
1247 | err |= comedi_check_trigger_arg_min(arg: &cmd->chanlist_len, val: 1); |
1248 | |
1249 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_end_arg, |
1250 | val: cmd->chanlist_len); |
1251 | |
1252 | if ((cmd->scan_end_arg % cmd->chanlist_len)) { |
1253 | cmd->scan_end_arg = |
1254 | cmd->chanlist_len * (cmd->scan_end_arg / cmd->chanlist_len); |
1255 | err |= -EINVAL; |
1256 | } |
1257 | |
1258 | if (err) |
1259 | return 3; |
1260 | |
1261 | /* step 4: fix up any arguments */ |
1262 | |
1263 | if (cmd->scan_begin_src == TRIG_TIMER) { |
1264 | arg = cmd->scan_begin_arg; |
1265 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
1266 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: arg); |
1267 | } |
1268 | |
1269 | if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) { |
1270 | arg = cmd->convert_arg; |
1271 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
1272 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: arg); |
1273 | |
1274 | if (cmd->scan_begin_src == TRIG_TIMER && |
1275 | cmd->convert_src == TRIG_NOW) { |
1276 | if (cmd->convert_arg == 0) { |
1277 | arg = devpriv->ai_ns_min * |
1278 | (cmd->scan_end_arg + 2); |
1279 | } else { |
1280 | arg = cmd->convert_arg * cmd->chanlist_len; |
1281 | } |
1282 | err |= comedi_check_trigger_arg_min( |
1283 | arg: &cmd->scan_begin_arg, val: arg); |
1284 | } |
1285 | } |
1286 | |
1287 | if (err) |
1288 | return 4; |
1289 | |
1290 | /* Step 5: check channel list if it exists */ |
1291 | |
1292 | if (cmd->chanlist) |
1293 | err |= pci9118_ai_check_chanlist(dev, s, cmd); |
1294 | |
1295 | if (err) |
1296 | return 5; |
1297 | |
1298 | return 0; |
1299 | } |
1300 | |
1301 | static int pci9118_ai_eoc(struct comedi_device *dev, |
1302 | struct comedi_subdevice *s, |
1303 | struct comedi_insn *insn, |
1304 | unsigned long context) |
1305 | { |
1306 | unsigned int status; |
1307 | |
1308 | status = inl(port: dev->iobase + PCI9118_AI_STATUS_REG); |
1309 | if (status & PCI9118_AI_STATUS_ADRDY) |
1310 | return 0; |
1311 | return -EBUSY; |
1312 | } |
1313 | |
1314 | static void pci9118_ai_start_conv(struct comedi_device *dev) |
1315 | { |
1316 | /* writing any value triggers an A/D conversion */ |
1317 | outl(value: 0, port: dev->iobase + PCI9118_SOFTTRG_REG); |
1318 | } |
1319 | |
1320 | static int pci9118_ai_insn_read(struct comedi_device *dev, |
1321 | struct comedi_subdevice *s, |
1322 | struct comedi_insn *insn, |
1323 | unsigned int *data) |
1324 | { |
1325 | struct pci9118_private *devpriv = dev->private; |
1326 | unsigned int val; |
1327 | int ret; |
1328 | int i; |
1329 | |
1330 | /* |
1331 | * Configure analog input based on the chanspec. |
1332 | * Acqusition is software controlled without interrupts. |
1333 | */ |
1334 | pci9118_set_chanlist(dev, s, n_chan: 1, chanlist: &insn->chanspec, frontadd: 0, backadd: 0); |
1335 | |
1336 | /* set default config (disable burst and triggers) */ |
1337 | devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG; |
1338 | outl(value: devpriv->ai_cfg, port: dev->iobase + PCI9118_AI_CFG_REG); |
1339 | |
1340 | pci9118_ai_reset_fifo(dev); |
1341 | |
1342 | for (i = 0; i < insn->n; i++) { |
1343 | pci9118_ai_start_conv(dev); |
1344 | |
1345 | ret = comedi_timeout(dev, s, insn, cb: pci9118_ai_eoc, context: 0); |
1346 | if (ret) |
1347 | return ret; |
1348 | |
1349 | val = inl(port: dev->iobase + PCI9118_AI_FIFO_REG); |
1350 | if (s->maxdata == 0xffff) |
1351 | data[i] = (val & 0xffff) ^ 0x8000; |
1352 | else |
1353 | data[i] = (val >> 4) & 0xfff; |
1354 | } |
1355 | |
1356 | return insn->n; |
1357 | } |
1358 | |
1359 | static int pci9118_ao_insn_write(struct comedi_device *dev, |
1360 | struct comedi_subdevice *s, |
1361 | struct comedi_insn *insn, |
1362 | unsigned int *data) |
1363 | { |
1364 | unsigned int chan = CR_CHAN(insn->chanspec); |
1365 | unsigned int val = s->readback[chan]; |
1366 | int i; |
1367 | |
1368 | for (i = 0; i < insn->n; i++) { |
1369 | val = data[i]; |
1370 | outl(value: val, port: dev->iobase + PCI9118_AO_REG(chan)); |
1371 | } |
1372 | s->readback[chan] = val; |
1373 | |
1374 | return insn->n; |
1375 | } |
1376 | |
1377 | static int pci9118_di_insn_bits(struct comedi_device *dev, |
1378 | struct comedi_subdevice *s, |
1379 | struct comedi_insn *insn, |
1380 | unsigned int *data) |
1381 | { |
1382 | /* |
1383 | * The digital inputs and outputs share the read register. |
1384 | * bits [7:4] are the digital outputs |
1385 | * bits [3:0] are the digital inputs |
1386 | */ |
1387 | data[1] = inl(port: dev->iobase + PCI9118_DIO_REG) & 0xf; |
1388 | |
1389 | return insn->n; |
1390 | } |
1391 | |
1392 | static int pci9118_do_insn_bits(struct comedi_device *dev, |
1393 | struct comedi_subdevice *s, |
1394 | struct comedi_insn *insn, |
1395 | unsigned int *data) |
1396 | { |
1397 | /* |
1398 | * The digital outputs are set with the same register that |
1399 | * the digital inputs and outputs are read from. But the |
1400 | * outputs are set with bits [3:0] so we can simply write |
1401 | * the s->state to set them. |
1402 | */ |
1403 | if (comedi_dio_update_state(s, data)) |
1404 | outl(value: s->state, port: dev->iobase + PCI9118_DIO_REG); |
1405 | |
1406 | data[1] = s->state; |
1407 | |
1408 | return insn->n; |
1409 | } |
1410 | |
1411 | static void pci9118_reset(struct comedi_device *dev) |
1412 | { |
1413 | /* reset analog input subsystem */ |
1414 | outl(value: 0, port: dev->iobase + PCI9118_INT_CTRL_REG); |
1415 | outl(value: 0, port: dev->iobase + PCI9118_AI_CTRL_REG); |
1416 | outl(value: 0, port: dev->iobase + PCI9118_AI_CFG_REG); |
1417 | pci9118_ai_reset_fifo(dev); |
1418 | |
1419 | /* clear any pending interrupts and status */ |
1420 | inl(port: dev->iobase + PCI9118_INT_CTRL_REG); |
1421 | inl(port: dev->iobase + PCI9118_AI_STATUS_REG); |
1422 | |
1423 | /* reset DMA and scan queue */ |
1424 | outl(value: 0, port: dev->iobase + PCI9118_AI_BURST_NUM_REG); |
1425 | outl(value: 1, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
1426 | outl(value: 2, port: dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG); |
1427 | |
1428 | /* reset analog outputs to 0V */ |
1429 | outl(value: 2047, port: dev->iobase + PCI9118_AO_REG(0)); |
1430 | outl(value: 2047, port: dev->iobase + PCI9118_AO_REG(1)); |
1431 | } |
1432 | |
1433 | static struct pci_dev *pci9118_find_pci(struct comedi_device *dev, |
1434 | struct comedi_devconfig *it) |
1435 | { |
1436 | struct pci_dev *pcidev = NULL; |
1437 | int bus = it->options[0]; |
1438 | int slot = it->options[1]; |
1439 | |
1440 | for_each_pci_dev(pcidev) { |
1441 | if (pcidev->vendor != PCI_VENDOR_ID_AMCC) |
1442 | continue; |
1443 | if (pcidev->device != 0x80d9) |
1444 | continue; |
1445 | if (bus || slot) { |
1446 | /* requested particular bus/slot */ |
1447 | if (pcidev->bus->number != bus || |
1448 | PCI_SLOT(pcidev->devfn) != slot) |
1449 | continue; |
1450 | } |
1451 | return pcidev; |
1452 | } |
1453 | dev_err(dev->class_dev, |
1454 | "no supported board found! (req. bus/slot : %d/%d)\n" , |
1455 | bus, slot); |
1456 | return NULL; |
1457 | } |
1458 | |
1459 | static void pci9118_alloc_dma(struct comedi_device *dev) |
1460 | { |
1461 | struct pci9118_private *devpriv = dev->private; |
1462 | struct pci9118_dmabuf *dmabuf; |
1463 | int order; |
1464 | int i; |
1465 | |
1466 | for (i = 0; i < 2; i++) { |
1467 | dmabuf = &devpriv->dmabuf[i]; |
1468 | for (order = 2; order >= 0; order--) { |
1469 | dmabuf->virt = |
1470 | dma_alloc_coherent(dev: dev->hw_dev, PAGE_SIZE << order, |
1471 | dma_handle: &dmabuf->hw, GFP_KERNEL); |
1472 | if (dmabuf->virt) |
1473 | break; |
1474 | } |
1475 | if (!dmabuf->virt) |
1476 | break; |
1477 | dmabuf->size = PAGE_SIZE << order; |
1478 | |
1479 | if (i == 0) |
1480 | devpriv->master = 1; |
1481 | if (i == 1) |
1482 | devpriv->dma_doublebuf = 1; |
1483 | } |
1484 | } |
1485 | |
1486 | static void pci9118_free_dma(struct comedi_device *dev) |
1487 | { |
1488 | struct pci9118_private *devpriv = dev->private; |
1489 | struct pci9118_dmabuf *dmabuf; |
1490 | int i; |
1491 | |
1492 | if (!devpriv) |
1493 | return; |
1494 | |
1495 | for (i = 0; i < 2; i++) { |
1496 | dmabuf = &devpriv->dmabuf[i]; |
1497 | if (dmabuf->virt) { |
1498 | dma_free_coherent(dev: dev->hw_dev, size: dmabuf->size, |
1499 | cpu_addr: dmabuf->virt, dma_handle: dmabuf->hw); |
1500 | } |
1501 | } |
1502 | } |
1503 | |
1504 | static int pci9118_common_attach(struct comedi_device *dev, |
1505 | int ext_mux, int softsshdelay) |
1506 | { |
1507 | const struct pci9118_boardinfo *board = dev->board_ptr; |
1508 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1509 | struct pci9118_private *devpriv; |
1510 | struct comedi_subdevice *s; |
1511 | int ret; |
1512 | int i; |
1513 | u16 u16w; |
1514 | |
1515 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
1516 | if (!devpriv) |
1517 | return -ENOMEM; |
1518 | |
1519 | ret = comedi_pci_enable(dev); |
1520 | if (ret) |
1521 | return ret; |
1522 | pci_set_master(dev: pcidev); |
1523 | |
1524 | devpriv->iobase_a = pci_resource_start(pcidev, 0); |
1525 | dev->iobase = pci_resource_start(pcidev, 2); |
1526 | |
1527 | dev->pacer = comedi_8254_io_alloc(iobase: dev->iobase + PCI9118_TIMER_BASE, |
1528 | I8254_OSC_BASE_4MHZ, I8254_IO32, regshift: 0); |
1529 | if (IS_ERR(ptr: dev->pacer)) |
1530 | return PTR_ERR(ptr: dev->pacer); |
1531 | |
1532 | pci9118_reset(dev); |
1533 | |
1534 | if (pcidev->irq) { |
1535 | ret = request_irq(irq: pcidev->irq, handler: pci9118_interrupt, IRQF_SHARED, |
1536 | name: dev->board_name, dev); |
1537 | if (ret == 0) { |
1538 | dev->irq = pcidev->irq; |
1539 | |
1540 | pci9118_alloc_dma(dev); |
1541 | } |
1542 | } |
1543 | |
1544 | if (ext_mux > 0) { |
1545 | if (ext_mux > 256) |
1546 | ext_mux = 256; /* max 256 channels! */ |
1547 | if (softsshdelay > 0) |
1548 | if (ext_mux > 128) |
1549 | ext_mux = 128; |
1550 | devpriv->usemux = 1; |
1551 | } else { |
1552 | devpriv->usemux = 0; |
1553 | } |
1554 | |
1555 | if (softsshdelay < 0) { |
1556 | /* select sample&hold signal polarity */ |
1557 | devpriv->softsshdelay = -softsshdelay; |
1558 | devpriv->softsshsample = 0x80; |
1559 | devpriv->softsshhold = 0x00; |
1560 | } else { |
1561 | devpriv->softsshdelay = softsshdelay; |
1562 | devpriv->softsshsample = 0x00; |
1563 | devpriv->softsshhold = 0x80; |
1564 | } |
1565 | |
1566 | pci_read_config_word(dev: pcidev, PCI_COMMAND, val: &u16w); |
1567 | pci_write_config_word(dev: pcidev, PCI_COMMAND, val: u16w | 64); |
1568 | /* Enable parity check for parity error */ |
1569 | |
1570 | ret = comedi_alloc_subdevices(dev, num_subdevices: 4); |
1571 | if (ret) |
1572 | return ret; |
1573 | |
1574 | /* Analog Input subdevice */ |
1575 | s = &dev->subdevices[0]; |
1576 | s->type = COMEDI_SUBD_AI; |
1577 | s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; |
1578 | s->n_chan = (devpriv->usemux) ? ext_mux : 16; |
1579 | s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; |
1580 | s->range_table = board->is_hg ? &pci9118hg_ai_range |
1581 | : &pci9118_ai_range; |
1582 | s->insn_read = pci9118_ai_insn_read; |
1583 | if (dev->irq) { |
1584 | dev->read_subdev = s; |
1585 | s->subdev_flags |= SDF_CMD_READ; |
1586 | s->len_chanlist = 255; |
1587 | s->do_cmdtest = pci9118_ai_cmdtest; |
1588 | s->do_cmd = pci9118_ai_cmd; |
1589 | s->cancel = pci9118_ai_cancel; |
1590 | s->munge = pci9118_ai_munge; |
1591 | } |
1592 | |
1593 | if (s->maxdata == 0xffff) { |
1594 | /* |
1595 | * 16-bit samples are from an ADS7805 A/D converter. |
1596 | * Minimum sampling rate is 10us. |
1597 | */ |
1598 | devpriv->ai_ns_min = 10000; |
1599 | } else { |
1600 | /* |
1601 | * 12-bit samples are from an ADS7800 A/D converter. |
1602 | * Minimum sampling rate is 3us. |
1603 | */ |
1604 | devpriv->ai_ns_min = 3000; |
1605 | } |
1606 | |
1607 | /* Analog Output subdevice */ |
1608 | s = &dev->subdevices[1]; |
1609 | s->type = COMEDI_SUBD_AO; |
1610 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; |
1611 | s->n_chan = 2; |
1612 | s->maxdata = 0x0fff; |
1613 | s->range_table = &range_bipolar10; |
1614 | s->insn_write = pci9118_ao_insn_write; |
1615 | |
1616 | ret = comedi_alloc_subdev_readback(s); |
1617 | if (ret) |
1618 | return ret; |
1619 | |
1620 | /* the analog outputs were reset to 0V, make the readback match */ |
1621 | for (i = 0; i < s->n_chan; i++) |
1622 | s->readback[i] = 2047; |
1623 | |
1624 | /* Digital Input subdevice */ |
1625 | s = &dev->subdevices[2]; |
1626 | s->type = COMEDI_SUBD_DI; |
1627 | s->subdev_flags = SDF_READABLE; |
1628 | s->n_chan = 4; |
1629 | s->maxdata = 1; |
1630 | s->range_table = &range_digital; |
1631 | s->insn_bits = pci9118_di_insn_bits; |
1632 | |
1633 | /* Digital Output subdevice */ |
1634 | s = &dev->subdevices[3]; |
1635 | s->type = COMEDI_SUBD_DO; |
1636 | s->subdev_flags = SDF_WRITABLE; |
1637 | s->n_chan = 4; |
1638 | s->maxdata = 1; |
1639 | s->range_table = &range_digital; |
1640 | s->insn_bits = pci9118_do_insn_bits; |
1641 | |
1642 | /* get the current state of the digital outputs */ |
1643 | s->state = inl(port: dev->iobase + PCI9118_DIO_REG) >> 4; |
1644 | |
1645 | return 0; |
1646 | } |
1647 | |
1648 | static int pci9118_attach(struct comedi_device *dev, |
1649 | struct comedi_devconfig *it) |
1650 | { |
1651 | struct pci_dev *pcidev; |
1652 | int ext_mux, softsshdelay; |
1653 | |
1654 | ext_mux = it->options[2]; |
1655 | softsshdelay = it->options[4]; |
1656 | |
1657 | pcidev = pci9118_find_pci(dev, it); |
1658 | if (!pcidev) |
1659 | return -EIO; |
1660 | comedi_set_hw_dev(dev, hw_dev: &pcidev->dev); |
1661 | |
1662 | return pci9118_common_attach(dev, ext_mux, softsshdelay); |
1663 | } |
1664 | |
1665 | static int pci9118_auto_attach(struct comedi_device *dev, |
1666 | unsigned long context) |
1667 | { |
1668 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1669 | const struct pci9118_boardinfo *board = NULL; |
1670 | |
1671 | if (context < ARRAY_SIZE(pci9118_boards)) |
1672 | board = &pci9118_boards[context]; |
1673 | if (!board) |
1674 | return -ENODEV; |
1675 | dev->board_ptr = board; |
1676 | dev->board_name = board->name; |
1677 | |
1678 | /* |
1679 | * Need to 'get' the PCI device to match the 'put' in pci9118_detach(). |
1680 | * (The 'put' also matches the implicit 'get' by pci9118_find_pci().) |
1681 | */ |
1682 | pci_dev_get(dev: pcidev); |
1683 | /* no external mux, no sample-hold delay */ |
1684 | return pci9118_common_attach(dev, ext_mux: 0, softsshdelay: 0); |
1685 | } |
1686 | |
1687 | static void pci9118_detach(struct comedi_device *dev) |
1688 | { |
1689 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
1690 | |
1691 | if (dev->iobase) |
1692 | pci9118_reset(dev); |
1693 | comedi_pci_detach(dev); |
1694 | pci9118_free_dma(dev); |
1695 | pci_dev_put(dev: pcidev); |
1696 | } |
1697 | |
1698 | static struct comedi_driver adl_pci9118_driver = { |
1699 | .driver_name = "adl_pci9118" , |
1700 | .module = THIS_MODULE, |
1701 | .attach = pci9118_attach, |
1702 | .auto_attach = pci9118_auto_attach, |
1703 | .detach = pci9118_detach, |
1704 | .num_names = ARRAY_SIZE(pci9118_boards), |
1705 | .board_name = &pci9118_boards[0].name, |
1706 | .offset = sizeof(struct pci9118_boardinfo), |
1707 | }; |
1708 | |
1709 | static int adl_pci9118_pci_probe(struct pci_dev *dev, |
1710 | const struct pci_device_id *id) |
1711 | { |
1712 | return comedi_pci_auto_config(pcidev: dev, driver: &adl_pci9118_driver, |
1713 | context: id->driver_data); |
1714 | } |
1715 | |
1716 | /* FIXME: All the supported board types have the same device ID! */ |
1717 | static const struct pci_device_id adl_pci9118_pci_table[] = { |
1718 | { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118DG }, |
1719 | /* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HG }, */ |
1720 | /* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HR }, */ |
1721 | { 0 } |
1722 | }; |
1723 | MODULE_DEVICE_TABLE(pci, adl_pci9118_pci_table); |
1724 | |
1725 | static struct pci_driver adl_pci9118_pci_driver = { |
1726 | .name = "adl_pci9118" , |
1727 | .id_table = adl_pci9118_pci_table, |
1728 | .probe = adl_pci9118_pci_probe, |
1729 | .remove = comedi_pci_auto_unconfig, |
1730 | }; |
1731 | module_comedi_pci_driver(adl_pci9118_driver, adl_pci9118_pci_driver); |
1732 | |
1733 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
1734 | MODULE_DESCRIPTION("Comedi low-level driver" ); |
1735 | MODULE_LICENSE("GPL" ); |
1736 | |