1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * comedi/drivers/amplc_pci224.c |
4 | * Driver for Amplicon PCI224 and PCI234 AO boards. |
5 | * |
6 | * Copyright (C) 2005 MEV Ltd. <https://www.mev.co.uk/> |
7 | * |
8 | * COMEDI - Linux Control and Measurement Device Interface |
9 | * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org> |
10 | */ |
11 | |
12 | /* |
13 | * Driver: amplc_pci224 |
14 | * Description: Amplicon PCI224, PCI234 |
15 | * Author: Ian Abbott <abbotti@mev.co.uk> |
16 | * Devices: [Amplicon] PCI224 (amplc_pci224), PCI234 |
17 | * Updated: Thu, 31 Jul 2014 11:08:03 +0000 |
18 | * Status: works, but see caveats |
19 | * |
20 | * Supports: |
21 | * |
22 | * - ao_insn read/write |
23 | * - ao_do_cmd mode with the following sources: |
24 | * |
25 | * - start_src TRIG_INT TRIG_EXT |
26 | * - scan_begin_src TRIG_TIMER TRIG_EXT |
27 | * - convert_src TRIG_NOW |
28 | * - scan_end_src TRIG_COUNT |
29 | * - stop_src TRIG_COUNT TRIG_EXT TRIG_NONE |
30 | * |
31 | * The channel list must contain at least one channel with no repeated |
32 | * channels. The scan end count must equal the number of channels in |
33 | * the channel list. |
34 | * |
35 | * There is only one external trigger source so only one of start_src, |
36 | * scan_begin_src or stop_src may use TRIG_EXT. |
37 | * |
38 | * Configuration options: |
39 | * none |
40 | * |
41 | * Manual configuration of PCI cards is not supported; they are configured |
42 | * automatically. |
43 | * |
44 | * Output range selection - PCI224: |
45 | * |
46 | * Output ranges on PCI224 are partly software-selectable and partly |
47 | * hardware-selectable according to jumper LK1. All channels are set |
48 | * to the same range: |
49 | * |
50 | * - LK1 position 1-2 (factory default) corresponds to the following |
51 | * comedi ranges: |
52 | * |
53 | * 0: [-10V,+10V]; 1: [-5V,+5V]; 2: [-2.5V,+2.5V], 3: [-1.25V,+1.25V], |
54 | * 4: [0,+10V], 5: [0,+5V], 6: [0,+2.5V], 7: [0,+1.25V] |
55 | * |
56 | * - LK1 position 2-3 corresponds to the following Comedi ranges, using |
57 | * an external voltage reference: |
58 | * |
59 | * 0: [-Vext,+Vext], |
60 | * 1: [0,+Vext] |
61 | * |
62 | * Output range selection - PCI234: |
63 | * |
64 | * Output ranges on PCI234 are hardware-selectable according to jumper |
65 | * LK1 which affects all channels, and jumpers LK2, LK3, LK4 and LK5 |
66 | * which affect channels 0, 1, 2 and 3 individually. LK1 chooses between |
67 | * an internal 5V reference and an external voltage reference (Vext). |
68 | * LK2/3/4/5 choose (per channel) to double the reference or not according |
69 | * to the following table: |
70 | * |
71 | * LK1 position LK2/3/4/5 pos Comedi range |
72 | * ------------- ------------- -------------- |
73 | * 2-3 (factory) 1-2 (factory) 0: [-10V,+10V] |
74 | * 2-3 (factory) 2-3 1: [-5V,+5V] |
75 | * 1-2 1-2 (factory) 2: [-2*Vext,+2*Vext] |
76 | * 1-2 2-3 3: [-Vext,+Vext] |
77 | * |
78 | * Caveats: |
79 | * |
80 | * 1) All channels on the PCI224 share the same range. Any change to the |
81 | * range as a result of insn_write or a streaming command will affect |
82 | * the output voltages of all channels, including those not specified |
83 | * by the instruction or command. |
84 | * |
85 | * 2) For the analog output command, the first scan may be triggered |
86 | * falsely at the start of acquisition. This occurs when the DAC scan |
87 | * trigger source is switched from 'none' to 'timer' (scan_begin_src = |
88 | * TRIG_TIMER) or 'external' (scan_begin_src == TRIG_EXT) at the start |
89 | * of acquisition and the trigger source is at logic level 1 at the |
90 | * time of the switch. This is very likely for TRIG_TIMER. For |
91 | * TRIG_EXT, it depends on the state of the external line and whether |
92 | * the CR_INVERT flag has been set. The remaining scans are triggered |
93 | * correctly. |
94 | */ |
95 | |
96 | #include <linux/module.h> |
97 | #include <linux/interrupt.h> |
98 | #include <linux/slab.h> |
99 | #include <linux/comedi/comedi_pci.h> |
100 | #include <linux/comedi/comedi_8254.h> |
101 | |
102 | /* |
103 | * PCI224/234 i/o space 1 (PCIBAR2) registers. |
104 | */ |
105 | #define PCI224_Z2_BASE 0x14 /* 82C54 counter/timer */ |
106 | #define PCI224_ZCLK_SCE 0x1A /* Group Z Clock Configuration Register */ |
107 | #define PCI224_ZGAT_SCE 0x1D /* Group Z Gate Configuration Register */ |
108 | #define PCI224_INT_SCE 0x1E /* ISR Interrupt source mask register */ |
109 | /* /Interrupt status */ |
110 | |
111 | /* |
112 | * PCI224/234 i/o space 2 (PCIBAR3) 16-bit registers. |
113 | */ |
114 | #define PCI224_DACDATA 0x00 /* (w-o) DAC FIFO data. */ |
115 | #define PCI224_SOFTTRIG 0x00 /* (r-o) DAC software scan trigger. */ |
116 | #define PCI224_DACCON 0x02 /* (r/w) DAC status/configuration. */ |
117 | #define PCI224_FIFOSIZ 0x04 /* (w-o) FIFO size for wraparound mode. */ |
118 | #define PCI224_DACCEN 0x06 /* (w-o) DAC channel enable register. */ |
119 | |
120 | /* |
121 | * DACCON values. |
122 | */ |
123 | /* (r/w) Scan trigger. */ |
124 | #define PCI224_DACCON_TRIG(x) (((x) & 0x7) << 0) |
125 | #define PCI224_DACCON_TRIG_MASK PCI224_DACCON_TRIG(7) |
126 | #define PCI224_DACCON_TRIG_NONE PCI224_DACCON_TRIG(0) /* none */ |
127 | #define PCI224_DACCON_TRIG_SW PCI224_DACCON_TRIG(1) /* soft trig */ |
128 | #define PCI224_DACCON_TRIG_EXTP PCI224_DACCON_TRIG(2) /* ext + edge */ |
129 | #define PCI224_DACCON_TRIG_EXTN PCI224_DACCON_TRIG(3) /* ext - edge */ |
130 | #define PCI224_DACCON_TRIG_Z2CT0 PCI224_DACCON_TRIG(4) /* Z2 CT0 out */ |
131 | #define PCI224_DACCON_TRIG_Z2CT1 PCI224_DACCON_TRIG(5) /* Z2 CT1 out */ |
132 | #define PCI224_DACCON_TRIG_Z2CT2 PCI224_DACCON_TRIG(6) /* Z2 CT2 out */ |
133 | /* (r/w) Polarity (PCI224 only, PCI234 always bipolar!). */ |
134 | #define PCI224_DACCON_POLAR(x) (((x) & 0x1) << 3) |
135 | #define PCI224_DACCON_POLAR_MASK PCI224_DACCON_POLAR(1) |
136 | #define PCI224_DACCON_POLAR_UNI PCI224_DACCON_POLAR(0) /* [0,+V] */ |
137 | #define PCI224_DACCON_POLAR_BI PCI224_DACCON_POLAR(1) /* [-V,+V] */ |
138 | /* (r/w) Internal Vref (PCI224 only, when LK1 in position 1-2). */ |
139 | #define PCI224_DACCON_VREF(x) (((x) & 0x3) << 4) |
140 | #define PCI224_DACCON_VREF_MASK PCI224_DACCON_VREF(3) |
141 | #define PCI224_DACCON_VREF_1_25 PCI224_DACCON_VREF(0) /* 1.25V */ |
142 | #define PCI224_DACCON_VREF_2_5 PCI224_DACCON_VREF(1) /* 2.5V */ |
143 | #define PCI224_DACCON_VREF_5 PCI224_DACCON_VREF(2) /* 5V */ |
144 | #define PCI224_DACCON_VREF_10 PCI224_DACCON_VREF(3) /* 10V */ |
145 | /* (r/w) Wraparound mode enable (to play back stored waveform). */ |
146 | #define PCI224_DACCON_FIFOWRAP BIT(7) |
147 | /* (r/w) FIFO enable. It MUST be set! */ |
148 | #define PCI224_DACCON_FIFOENAB BIT(8) |
149 | /* (r/w) FIFO interrupt trigger level (most values are not very useful). */ |
150 | #define PCI224_DACCON_FIFOINTR(x) (((x) & 0x7) << 9) |
151 | #define PCI224_DACCON_FIFOINTR_MASK PCI224_DACCON_FIFOINTR(7) |
152 | #define PCI224_DACCON_FIFOINTR_EMPTY PCI224_DACCON_FIFOINTR(0) /* empty */ |
153 | #define PCI224_DACCON_FIFOINTR_NEMPTY PCI224_DACCON_FIFOINTR(1) /* !empty */ |
154 | #define PCI224_DACCON_FIFOINTR_NHALF PCI224_DACCON_FIFOINTR(2) /* !half */ |
155 | #define PCI224_DACCON_FIFOINTR_HALF PCI224_DACCON_FIFOINTR(3) /* half */ |
156 | #define PCI224_DACCON_FIFOINTR_NFULL PCI224_DACCON_FIFOINTR(4) /* !full */ |
157 | #define PCI224_DACCON_FIFOINTR_FULL PCI224_DACCON_FIFOINTR(5) /* full */ |
158 | /* (r-o) FIFO fill level. */ |
159 | #define PCI224_DACCON_FIFOFL(x) (((x) & 0x7) << 12) |
160 | #define PCI224_DACCON_FIFOFL_MASK PCI224_DACCON_FIFOFL(7) |
161 | #define PCI224_DACCON_FIFOFL_EMPTY PCI224_DACCON_FIFOFL(1) /* 0 */ |
162 | #define PCI224_DACCON_FIFOFL_ONETOHALF PCI224_DACCON_FIFOFL(0) /* 1-2048 */ |
163 | #define PCI224_DACCON_FIFOFL_HALFTOFULL PCI224_DACCON_FIFOFL(4) /* 2049-4095 */ |
164 | #define PCI224_DACCON_FIFOFL_FULL PCI224_DACCON_FIFOFL(6) /* 4096 */ |
165 | /* (r-o) DAC busy flag. */ |
166 | #define PCI224_DACCON_BUSY BIT(15) |
167 | /* (w-o) FIFO reset. */ |
168 | #define PCI224_DACCON_FIFORESET BIT(12) |
169 | /* (w-o) Global reset (not sure what it does). */ |
170 | #define PCI224_DACCON_GLOBALRESET BIT(13) |
171 | |
172 | /* |
173 | * DAC FIFO size. |
174 | */ |
175 | #define PCI224_FIFO_SIZE 4096 |
176 | |
177 | /* |
178 | * DAC FIFO guaranteed minimum room available, depending on reported fill level. |
179 | * The maximum room available depends on the reported fill level and how much |
180 | * has been written! |
181 | */ |
182 | #define PCI224_FIFO_ROOM_EMPTY PCI224_FIFO_SIZE |
183 | #define PCI224_FIFO_ROOM_ONETOHALF (PCI224_FIFO_SIZE / 2) |
184 | #define PCI224_FIFO_ROOM_HALFTOFULL 1 |
185 | #define PCI224_FIFO_ROOM_FULL 0 |
186 | |
187 | /* |
188 | * Counter/timer clock input configuration sources. |
189 | */ |
190 | #define CLK_CLK 0 /* reserved (channel-specific clock) */ |
191 | #define CLK_10MHZ 1 /* internal 10 MHz clock */ |
192 | #define CLK_1MHZ 2 /* internal 1 MHz clock */ |
193 | #define CLK_100KHZ 3 /* internal 100 kHz clock */ |
194 | #define CLK_10KHZ 4 /* internal 10 kHz clock */ |
195 | #define CLK_1KHZ 5 /* internal 1 kHz clock */ |
196 | #define CLK_OUTNM1 6 /* output of channel-1 modulo total */ |
197 | #define CLK_EXT 7 /* external clock */ |
198 | |
199 | static unsigned int pci224_clk_config(unsigned int chan, unsigned int src) |
200 | { |
201 | return ((chan & 3) << 3) | (src & 7); |
202 | } |
203 | |
204 | /* |
205 | * Counter/timer gate input configuration sources. |
206 | */ |
207 | #define GAT_VCC 0 /* VCC (i.e. enabled) */ |
208 | #define GAT_GND 1 /* GND (i.e. disabled) */ |
209 | #define GAT_EXT 2 /* reserved (external gate input) */ |
210 | #define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */ |
211 | |
212 | static unsigned int pci224_gat_config(unsigned int chan, unsigned int src) |
213 | { |
214 | return ((chan & 3) << 3) | (src & 7); |
215 | } |
216 | |
217 | /* |
218 | * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI224 and PCI234: |
219 | * |
220 | * Channel's Channel's |
221 | * clock input gate input |
222 | * Channel CLK_OUTNM1 GAT_NOUTNM2 |
223 | * ------- ---------- ----------- |
224 | * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT |
225 | * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT |
226 | * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT |
227 | */ |
228 | |
229 | /* |
230 | * Interrupt enable/status bits |
231 | */ |
232 | #define PCI224_INTR_EXT 0x01 /* rising edge on external input */ |
233 | #define PCI224_INTR_DAC 0x04 /* DAC (FIFO) interrupt */ |
234 | #define PCI224_INTR_Z2CT1 0x20 /* rising edge on Z2-CT1 output */ |
235 | |
236 | #define PCI224_INTR_EDGE_BITS (PCI224_INTR_EXT | PCI224_INTR_Z2CT1) |
237 | #define PCI224_INTR_LEVEL_BITS PCI224_INTR_DACFIFO |
238 | |
239 | /* |
240 | * Handy macros. |
241 | */ |
242 | |
243 | /* Combine old and new bits. */ |
244 | #define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask))) |
245 | |
246 | /* Current CPU. XXX should this be hard_smp_processor_id()? */ |
247 | #define THISCPU smp_processor_id() |
248 | |
249 | /* State bits for use with atomic bit operations. */ |
250 | #define AO_CMD_STARTED 0 |
251 | |
252 | /* |
253 | * Range tables. |
254 | */ |
255 | |
256 | /* |
257 | * The ranges for PCI224. |
258 | * |
259 | * These are partly hardware-selectable by jumper LK1 and partly |
260 | * software-selectable. |
261 | * |
262 | * All channels share the same hardware range. |
263 | */ |
264 | static const struct comedi_lrange range_pci224 = { |
265 | 10, { |
266 | /* jumper LK1 in position 1-2 (factory default) */ |
267 | BIP_RANGE(10), |
268 | BIP_RANGE(5), |
269 | BIP_RANGE(2.5), |
270 | BIP_RANGE(1.25), |
271 | UNI_RANGE(10), |
272 | UNI_RANGE(5), |
273 | UNI_RANGE(2.5), |
274 | UNI_RANGE(1.25), |
275 | /* jumper LK1 in position 2-3 */ |
276 | RANGE_ext(-1, 1), /* bipolar [-Vext,+Vext] */ |
277 | RANGE_ext(0, 1), /* unipolar [0,+Vext] */ |
278 | } |
279 | }; |
280 | |
281 | static const unsigned short hwrange_pci224[10] = { |
282 | /* jumper LK1 in position 1-2 (factory default) */ |
283 | PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_10, |
284 | PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_5, |
285 | PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_2_5, |
286 | PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_1_25, |
287 | PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_10, |
288 | PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_5, |
289 | PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_2_5, |
290 | PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_1_25, |
291 | /* jumper LK1 in position 2-3 */ |
292 | PCI224_DACCON_POLAR_BI, |
293 | PCI224_DACCON_POLAR_UNI, |
294 | }; |
295 | |
296 | /* Used to check all channels set to the same range on PCI224. */ |
297 | static const unsigned char range_check_pci224[10] = { |
298 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, |
299 | }; |
300 | |
301 | /* |
302 | * The ranges for PCI234. |
303 | * |
304 | * These are all hardware-selectable by jumper LK1 affecting all channels, |
305 | * and jumpers LK2, LK3, LK4 and LK5 affecting channels 0, 1, 2 and 3 |
306 | * individually. |
307 | */ |
308 | static const struct comedi_lrange range_pci234 = { |
309 | 4, { |
310 | /* LK1: 1-2 (fact def), LK2/3/4/5: 2-3 (fac def) */ |
311 | BIP_RANGE(10), |
312 | /* LK1: 1-2 (fact def), LK2/3/4/5: 1-2 */ |
313 | BIP_RANGE(5), |
314 | /* LK1: 2-3, LK2/3/4/5: 2-3 (fac def) */ |
315 | RANGE_ext(-2, 2), /* bipolar [-2*Vext,+2*Vext] */ |
316 | /* LK1: 2-3, LK2/3/4/5: 1-2 */ |
317 | RANGE_ext(-1, 1), /* bipolar [-Vext,+Vext] */ |
318 | } |
319 | }; |
320 | |
321 | /* N.B. PCI234 ignores the polarity bit, but software uses it. */ |
322 | static const unsigned short hwrange_pci234[4] = { |
323 | PCI224_DACCON_POLAR_BI, |
324 | PCI224_DACCON_POLAR_BI, |
325 | PCI224_DACCON_POLAR_BI, |
326 | PCI224_DACCON_POLAR_BI, |
327 | }; |
328 | |
329 | /* Used to check all channels use same LK1 setting on PCI234. */ |
330 | static const unsigned char range_check_pci234[4] = { |
331 | 0, 0, 1, 1, |
332 | }; |
333 | |
334 | /* |
335 | * Board descriptions. |
336 | */ |
337 | |
338 | enum pci224_model { pci224_model, pci234_model }; |
339 | |
340 | struct pci224_board { |
341 | const char *name; |
342 | unsigned int ao_chans; |
343 | unsigned int ao_bits; |
344 | const struct comedi_lrange *ao_range; |
345 | const unsigned short *ao_hwrange; |
346 | const unsigned char *ao_range_check; |
347 | }; |
348 | |
349 | static const struct pci224_board pci224_boards[] = { |
350 | [pci224_model] = { |
351 | .name = "pci224" , |
352 | .ao_chans = 16, |
353 | .ao_bits = 12, |
354 | .ao_range = &range_pci224, |
355 | .ao_hwrange = &hwrange_pci224[0], |
356 | .ao_range_check = &range_check_pci224[0], |
357 | }, |
358 | [pci234_model] = { |
359 | .name = "pci234" , |
360 | .ao_chans = 4, |
361 | .ao_bits = 16, |
362 | .ao_range = &range_pci234, |
363 | .ao_hwrange = &hwrange_pci234[0], |
364 | .ao_range_check = &range_check_pci234[0], |
365 | }, |
366 | }; |
367 | |
368 | struct pci224_private { |
369 | unsigned long iobase1; |
370 | unsigned long state; |
371 | spinlock_t ao_spinlock; /* spinlock for AO command handling */ |
372 | unsigned short *ao_scan_vals; |
373 | unsigned char *ao_scan_order; |
374 | int intr_cpuid; |
375 | short intr_running; |
376 | unsigned short daccon; |
377 | unsigned short ao_enab; /* max 16 channels so 'short' will do */ |
378 | unsigned char intsce; |
379 | }; |
380 | |
381 | /* |
382 | * Called from the 'insn_write' function to perform a single write. |
383 | */ |
384 | static void |
385 | pci224_ao_set_data(struct comedi_device *dev, int chan, int range, |
386 | unsigned int data) |
387 | { |
388 | const struct pci224_board *board = dev->board_ptr; |
389 | struct pci224_private *devpriv = dev->private; |
390 | unsigned short mangled; |
391 | |
392 | /* Enable the channel. */ |
393 | outw(value: 1 << chan, port: dev->iobase + PCI224_DACCEN); |
394 | /* Set range and reset FIFO. */ |
395 | devpriv->daccon = COMBINE(devpriv->daccon, board->ao_hwrange[range], |
396 | PCI224_DACCON_POLAR_MASK | |
397 | PCI224_DACCON_VREF_MASK); |
398 | outw(value: devpriv->daccon | PCI224_DACCON_FIFORESET, |
399 | port: dev->iobase + PCI224_DACCON); |
400 | /* |
401 | * Mangle the data. The hardware expects: |
402 | * - bipolar: 16-bit 2's complement |
403 | * - unipolar: 16-bit unsigned |
404 | */ |
405 | mangled = (unsigned short)data << (16 - board->ao_bits); |
406 | if ((devpriv->daccon & PCI224_DACCON_POLAR_MASK) == |
407 | PCI224_DACCON_POLAR_BI) { |
408 | mangled ^= 0x8000; |
409 | } |
410 | /* Write mangled data to the FIFO. */ |
411 | outw(value: mangled, port: dev->iobase + PCI224_DACDATA); |
412 | /* Trigger the conversion. */ |
413 | inw(port: dev->iobase + PCI224_SOFTTRIG); |
414 | } |
415 | |
416 | static int pci224_ao_insn_write(struct comedi_device *dev, |
417 | struct comedi_subdevice *s, |
418 | struct comedi_insn *insn, |
419 | unsigned int *data) |
420 | { |
421 | unsigned int chan = CR_CHAN(insn->chanspec); |
422 | unsigned int range = CR_RANGE(insn->chanspec); |
423 | unsigned int val = s->readback[chan]; |
424 | int i; |
425 | |
426 | for (i = 0; i < insn->n; i++) { |
427 | val = data[i]; |
428 | pci224_ao_set_data(dev, chan, range, data: val); |
429 | } |
430 | s->readback[chan] = val; |
431 | |
432 | return insn->n; |
433 | } |
434 | |
435 | /* |
436 | * Kills a command running on the AO subdevice. |
437 | */ |
438 | static void pci224_ao_stop(struct comedi_device *dev, |
439 | struct comedi_subdevice *s) |
440 | { |
441 | struct pci224_private *devpriv = dev->private; |
442 | unsigned long flags; |
443 | |
444 | if (!test_and_clear_bit(AO_CMD_STARTED, addr: &devpriv->state)) |
445 | return; |
446 | |
447 | spin_lock_irqsave(&devpriv->ao_spinlock, flags); |
448 | /* Kill the interrupts. */ |
449 | devpriv->intsce = 0; |
450 | outb(value: 0, port: devpriv->iobase1 + PCI224_INT_SCE); |
451 | /* |
452 | * Interrupt routine may or may not be running. We may or may not |
453 | * have been called from the interrupt routine (directly or |
454 | * indirectly via a comedi_events() callback routine). It's highly |
455 | * unlikely that we've been called from some other interrupt routine |
456 | * but who knows what strange things coders get up to! |
457 | * |
458 | * If the interrupt routine is currently running, wait for it to |
459 | * finish, unless we appear to have been called via the interrupt |
460 | * routine. |
461 | */ |
462 | while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) { |
463 | spin_unlock_irqrestore(lock: &devpriv->ao_spinlock, flags); |
464 | spin_lock_irqsave(&devpriv->ao_spinlock, flags); |
465 | } |
466 | spin_unlock_irqrestore(lock: &devpriv->ao_spinlock, flags); |
467 | /* Reconfigure DAC for insn_write usage. */ |
468 | outw(value: 0, port: dev->iobase + PCI224_DACCEN); /* Disable channels. */ |
469 | devpriv->daccon = |
470 | COMBINE(devpriv->daccon, |
471 | PCI224_DACCON_TRIG_SW | PCI224_DACCON_FIFOINTR_EMPTY, |
472 | PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK); |
473 | outw(value: devpriv->daccon | PCI224_DACCON_FIFORESET, |
474 | port: dev->iobase + PCI224_DACCON); |
475 | } |
476 | |
477 | /* |
478 | * Handles start of acquisition for the AO subdevice. |
479 | */ |
480 | static void pci224_ao_start(struct comedi_device *dev, |
481 | struct comedi_subdevice *s) |
482 | { |
483 | struct pci224_private *devpriv = dev->private; |
484 | struct comedi_cmd *cmd = &s->async->cmd; |
485 | unsigned long flags; |
486 | |
487 | set_bit(AO_CMD_STARTED, addr: &devpriv->state); |
488 | |
489 | /* Enable interrupts. */ |
490 | spin_lock_irqsave(&devpriv->ao_spinlock, flags); |
491 | if (cmd->stop_src == TRIG_EXT) |
492 | devpriv->intsce = PCI224_INTR_EXT | PCI224_INTR_DAC; |
493 | else |
494 | devpriv->intsce = PCI224_INTR_DAC; |
495 | |
496 | outb(value: devpriv->intsce, port: devpriv->iobase1 + PCI224_INT_SCE); |
497 | spin_unlock_irqrestore(lock: &devpriv->ao_spinlock, flags); |
498 | } |
499 | |
500 | /* |
501 | * Handles interrupts from the DAC FIFO. |
502 | */ |
503 | static void pci224_ao_handle_fifo(struct comedi_device *dev, |
504 | struct comedi_subdevice *s) |
505 | { |
506 | struct pci224_private *devpriv = dev->private; |
507 | struct comedi_cmd *cmd = &s->async->cmd; |
508 | unsigned int num_scans = comedi_nscans_left(s, nscans: 0); |
509 | unsigned int room; |
510 | unsigned short dacstat; |
511 | unsigned int i, n; |
512 | |
513 | /* Determine how much room is in the FIFO (in samples). */ |
514 | dacstat = inw(port: dev->iobase + PCI224_DACCON); |
515 | switch (dacstat & PCI224_DACCON_FIFOFL_MASK) { |
516 | case PCI224_DACCON_FIFOFL_EMPTY: |
517 | room = PCI224_FIFO_ROOM_EMPTY; |
518 | if (cmd->stop_src == TRIG_COUNT && |
519 | s->async->scans_done >= cmd->stop_arg) { |
520 | /* FIFO empty at end of counted acquisition. */ |
521 | s->async->events |= COMEDI_CB_EOA; |
522 | comedi_handle_events(dev, s); |
523 | return; |
524 | } |
525 | break; |
526 | case PCI224_DACCON_FIFOFL_ONETOHALF: |
527 | room = PCI224_FIFO_ROOM_ONETOHALF; |
528 | break; |
529 | case PCI224_DACCON_FIFOFL_HALFTOFULL: |
530 | room = PCI224_FIFO_ROOM_HALFTOFULL; |
531 | break; |
532 | default: |
533 | room = PCI224_FIFO_ROOM_FULL; |
534 | break; |
535 | } |
536 | if (room >= PCI224_FIFO_ROOM_ONETOHALF) { |
537 | /* FIFO is less than half-full. */ |
538 | if (num_scans == 0) { |
539 | /* Nothing left to put in the FIFO. */ |
540 | dev_err(dev->class_dev, "AO buffer underrun\n" ); |
541 | s->async->events |= COMEDI_CB_OVERFLOW; |
542 | } |
543 | } |
544 | /* Determine how many new scans can be put in the FIFO. */ |
545 | room /= cmd->chanlist_len; |
546 | |
547 | /* Determine how many scans to process. */ |
548 | if (num_scans > room) |
549 | num_scans = room; |
550 | |
551 | /* Process scans. */ |
552 | for (n = 0; n < num_scans; n++) { |
553 | comedi_buf_read_samples(s, data: &devpriv->ao_scan_vals[0], |
554 | nsamples: cmd->chanlist_len); |
555 | for (i = 0; i < cmd->chanlist_len; i++) { |
556 | outw(value: devpriv->ao_scan_vals[devpriv->ao_scan_order[i]], |
557 | port: dev->iobase + PCI224_DACDATA); |
558 | } |
559 | } |
560 | if (cmd->stop_src == TRIG_COUNT && |
561 | s->async->scans_done >= cmd->stop_arg) { |
562 | /* |
563 | * Change FIFO interrupt trigger level to wait |
564 | * until FIFO is empty. |
565 | */ |
566 | devpriv->daccon = COMBINE(devpriv->daccon, |
567 | PCI224_DACCON_FIFOINTR_EMPTY, |
568 | PCI224_DACCON_FIFOINTR_MASK); |
569 | outw(value: devpriv->daccon, port: dev->iobase + PCI224_DACCON); |
570 | } |
571 | if ((devpriv->daccon & PCI224_DACCON_TRIG_MASK) == |
572 | PCI224_DACCON_TRIG_NONE) { |
573 | unsigned short trig; |
574 | |
575 | /* |
576 | * This is the initial DAC FIFO interrupt at the |
577 | * start of the acquisition. The DAC's scan trigger |
578 | * has been set to 'none' up until now. |
579 | * |
580 | * Now that data has been written to the FIFO, the |
581 | * DAC's scan trigger source can be set to the |
582 | * correct value. |
583 | * |
584 | * BUG: The first scan will be triggered immediately |
585 | * if the scan trigger source is at logic level 1. |
586 | */ |
587 | if (cmd->scan_begin_src == TRIG_TIMER) { |
588 | trig = PCI224_DACCON_TRIG_Z2CT0; |
589 | } else { |
590 | /* cmd->scan_begin_src == TRIG_EXT */ |
591 | if (cmd->scan_begin_arg & CR_INVERT) |
592 | trig = PCI224_DACCON_TRIG_EXTN; |
593 | else |
594 | trig = PCI224_DACCON_TRIG_EXTP; |
595 | } |
596 | devpriv->daccon = |
597 | COMBINE(devpriv->daccon, trig, PCI224_DACCON_TRIG_MASK); |
598 | outw(value: devpriv->daccon, port: dev->iobase + PCI224_DACCON); |
599 | } |
600 | |
601 | comedi_handle_events(dev, s); |
602 | } |
603 | |
604 | static int pci224_ao_inttrig_start(struct comedi_device *dev, |
605 | struct comedi_subdevice *s, |
606 | unsigned int trig_num) |
607 | { |
608 | struct comedi_cmd *cmd = &s->async->cmd; |
609 | |
610 | if (trig_num != cmd->start_arg) |
611 | return -EINVAL; |
612 | |
613 | s->async->inttrig = NULL; |
614 | pci224_ao_start(dev, s); |
615 | |
616 | return 1; |
617 | } |
618 | |
619 | static int pci224_ao_check_chanlist(struct comedi_device *dev, |
620 | struct comedi_subdevice *s, |
621 | struct comedi_cmd *cmd) |
622 | { |
623 | const struct pci224_board *board = dev->board_ptr; |
624 | unsigned int range_check_0; |
625 | unsigned int chan_mask = 0; |
626 | int i; |
627 | |
628 | range_check_0 = board->ao_range_check[CR_RANGE(cmd->chanlist[0])]; |
629 | for (i = 0; i < cmd->chanlist_len; i++) { |
630 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); |
631 | |
632 | if (chan_mask & (1 << chan)) { |
633 | dev_dbg(dev->class_dev, |
634 | "%s: entries in chanlist must contain no duplicate channels\n" , |
635 | __func__); |
636 | return -EINVAL; |
637 | } |
638 | chan_mask |= 1 << chan; |
639 | |
640 | if (board->ao_range_check[CR_RANGE(cmd->chanlist[i])] != |
641 | range_check_0) { |
642 | dev_dbg(dev->class_dev, |
643 | "%s: entries in chanlist have incompatible ranges\n" , |
644 | __func__); |
645 | return -EINVAL; |
646 | } |
647 | } |
648 | |
649 | return 0; |
650 | } |
651 | |
652 | #define MAX_SCAN_PERIOD 0xFFFFFFFFU |
653 | #define MIN_SCAN_PERIOD 2500 |
654 | #define CONVERT_PERIOD 625 |
655 | |
656 | /* |
657 | * 'do_cmdtest' function for AO subdevice. |
658 | */ |
659 | static int |
660 | pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, |
661 | struct comedi_cmd *cmd) |
662 | { |
663 | int err = 0; |
664 | unsigned int arg; |
665 | |
666 | /* Step 1 : check if triggers are trivially valid */ |
667 | |
668 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_INT | TRIG_EXT); |
669 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, |
670 | TRIG_EXT | TRIG_TIMER); |
671 | err |= comedi_check_trigger_src(src: &cmd->convert_src, TRIG_NOW); |
672 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
673 | err |= comedi_check_trigger_src(src: &cmd->stop_src, |
674 | TRIG_COUNT | TRIG_EXT | TRIG_NONE); |
675 | |
676 | if (err) |
677 | return 1; |
678 | |
679 | /* Step 2a : make sure trigger sources are unique */ |
680 | |
681 | err |= comedi_check_trigger_is_unique(src: cmd->start_src); |
682 | err |= comedi_check_trigger_is_unique(src: cmd->scan_begin_src); |
683 | err |= comedi_check_trigger_is_unique(src: cmd->stop_src); |
684 | |
685 | /* Step 2b : and mutually compatible */ |
686 | |
687 | /* |
688 | * There's only one external trigger signal (which makes these |
689 | * tests easier). Only one thing can use it. |
690 | */ |
691 | arg = 0; |
692 | if (cmd->start_src & TRIG_EXT) |
693 | arg++; |
694 | if (cmd->scan_begin_src & TRIG_EXT) |
695 | arg++; |
696 | if (cmd->stop_src & TRIG_EXT) |
697 | arg++; |
698 | if (arg > 1) |
699 | err |= -EINVAL; |
700 | |
701 | if (err) |
702 | return 2; |
703 | |
704 | /* Step 3: check if arguments are trivially valid */ |
705 | |
706 | switch (cmd->start_src) { |
707 | case TRIG_INT: |
708 | err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0); |
709 | break; |
710 | case TRIG_EXT: |
711 | /* Force to external trigger 0. */ |
712 | if (cmd->start_arg & ~CR_FLAGS_MASK) { |
713 | cmd->start_arg = |
714 | COMBINE(cmd->start_arg, 0, ~CR_FLAGS_MASK); |
715 | err |= -EINVAL; |
716 | } |
717 | /* The only flag allowed is CR_EDGE, which is ignored. */ |
718 | if (cmd->start_arg & CR_FLAGS_MASK & ~CR_EDGE) { |
719 | cmd->start_arg = COMBINE(cmd->start_arg, 0, |
720 | CR_FLAGS_MASK & ~CR_EDGE); |
721 | err |= -EINVAL; |
722 | } |
723 | break; |
724 | } |
725 | |
726 | switch (cmd->scan_begin_src) { |
727 | case TRIG_TIMER: |
728 | err |= comedi_check_trigger_arg_max(arg: &cmd->scan_begin_arg, |
729 | MAX_SCAN_PERIOD); |
730 | |
731 | arg = cmd->chanlist_len * CONVERT_PERIOD; |
732 | if (arg < MIN_SCAN_PERIOD) |
733 | arg = MIN_SCAN_PERIOD; |
734 | err |= comedi_check_trigger_arg_min(arg: &cmd->scan_begin_arg, val: arg); |
735 | break; |
736 | case TRIG_EXT: |
737 | /* Force to external trigger 0. */ |
738 | if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) { |
739 | cmd->scan_begin_arg = |
740 | COMBINE(cmd->scan_begin_arg, 0, ~CR_FLAGS_MASK); |
741 | err |= -EINVAL; |
742 | } |
743 | /* Only allow flags CR_EDGE and CR_INVERT. Ignore CR_EDGE. */ |
744 | if (cmd->scan_begin_arg & CR_FLAGS_MASK & |
745 | ~(CR_EDGE | CR_INVERT)) { |
746 | cmd->scan_begin_arg = |
747 | COMBINE(cmd->scan_begin_arg, 0, |
748 | CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT)); |
749 | err |= -EINVAL; |
750 | } |
751 | break; |
752 | } |
753 | |
754 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: 0); |
755 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
756 | val: cmd->chanlist_len); |
757 | |
758 | switch (cmd->stop_src) { |
759 | case TRIG_COUNT: |
760 | err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1); |
761 | break; |
762 | case TRIG_EXT: |
763 | /* Force to external trigger 0. */ |
764 | if (cmd->stop_arg & ~CR_FLAGS_MASK) { |
765 | cmd->stop_arg = |
766 | COMBINE(cmd->stop_arg, 0, ~CR_FLAGS_MASK); |
767 | err |= -EINVAL; |
768 | } |
769 | /* The only flag allowed is CR_EDGE, which is ignored. */ |
770 | if (cmd->stop_arg & CR_FLAGS_MASK & ~CR_EDGE) { |
771 | cmd->stop_arg = |
772 | COMBINE(cmd->stop_arg, 0, CR_FLAGS_MASK & ~CR_EDGE); |
773 | } |
774 | break; |
775 | case TRIG_NONE: |
776 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
777 | break; |
778 | } |
779 | |
780 | if (err) |
781 | return 3; |
782 | |
783 | /* Step 4: fix up any arguments. */ |
784 | |
785 | if (cmd->scan_begin_src == TRIG_TIMER) { |
786 | arg = cmd->scan_begin_arg; |
787 | /* Use two timers. */ |
788 | comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags); |
789 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: arg); |
790 | } |
791 | |
792 | if (err) |
793 | return 4; |
794 | |
795 | /* Step 5: check channel list if it exists */ |
796 | if (cmd->chanlist && cmd->chanlist_len > 0) |
797 | err |= pci224_ao_check_chanlist(dev, s, cmd); |
798 | |
799 | if (err) |
800 | return 5; |
801 | |
802 | return 0; |
803 | } |
804 | |
805 | static void pci224_ao_start_pacer(struct comedi_device *dev, |
806 | struct comedi_subdevice *s) |
807 | { |
808 | struct pci224_private *devpriv = dev->private; |
809 | |
810 | /* |
811 | * The output of timer Z2-0 will be used as the scan trigger |
812 | * source. |
813 | */ |
814 | /* Make sure Z2-0 is gated on. */ |
815 | outb(value: pci224_gat_config(chan: 0, GAT_VCC), port: devpriv->iobase1 + PCI224_ZGAT_SCE); |
816 | /* Cascading with Z2-2. */ |
817 | /* Make sure Z2-2 is gated on. */ |
818 | outb(value: pci224_gat_config(chan: 2, GAT_VCC), port: devpriv->iobase1 + PCI224_ZGAT_SCE); |
819 | /* Z2-2 needs 10 MHz clock. */ |
820 | outb(value: pci224_clk_config(chan: 2, CLK_10MHZ), |
821 | port: devpriv->iobase1 + PCI224_ZCLK_SCE); |
822 | /* Z2-0 is clocked from Z2-2's output. */ |
823 | outb(value: pci224_clk_config(chan: 0, CLK_OUTNM1), |
824 | port: devpriv->iobase1 + PCI224_ZCLK_SCE); |
825 | |
826 | comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 2, counter2: 0, enable: false); |
827 | } |
828 | |
829 | static int pci224_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
830 | { |
831 | const struct pci224_board *board = dev->board_ptr; |
832 | struct pci224_private *devpriv = dev->private; |
833 | struct comedi_cmd *cmd = &s->async->cmd; |
834 | int range; |
835 | unsigned int i, j; |
836 | unsigned int ch; |
837 | unsigned int rank; |
838 | unsigned long flags; |
839 | |
840 | /* Cannot handle null/empty chanlist. */ |
841 | if (!cmd->chanlist || cmd->chanlist_len == 0) |
842 | return -EINVAL; |
843 | |
844 | /* Determine which channels are enabled and their load order. */ |
845 | devpriv->ao_enab = 0; |
846 | |
847 | for (i = 0; i < cmd->chanlist_len; i++) { |
848 | ch = CR_CHAN(cmd->chanlist[i]); |
849 | devpriv->ao_enab |= 1U << ch; |
850 | rank = 0; |
851 | for (j = 0; j < cmd->chanlist_len; j++) { |
852 | if (CR_CHAN(cmd->chanlist[j]) < ch) |
853 | rank++; |
854 | } |
855 | devpriv->ao_scan_order[rank] = i; |
856 | } |
857 | |
858 | /* Set enabled channels. */ |
859 | outw(value: devpriv->ao_enab, port: dev->iobase + PCI224_DACCEN); |
860 | |
861 | /* Determine range and polarity. All channels the same. */ |
862 | range = CR_RANGE(cmd->chanlist[0]); |
863 | |
864 | /* |
865 | * Set DAC range and polarity. |
866 | * Set DAC scan trigger source to 'none'. |
867 | * Set DAC FIFO interrupt trigger level to 'not half full'. |
868 | * Reset DAC FIFO. |
869 | * |
870 | * N.B. DAC FIFO interrupts are currently disabled. |
871 | */ |
872 | devpriv->daccon = |
873 | COMBINE(devpriv->daccon, |
874 | board->ao_hwrange[range] | PCI224_DACCON_TRIG_NONE | |
875 | PCI224_DACCON_FIFOINTR_NHALF, |
876 | PCI224_DACCON_POLAR_MASK | PCI224_DACCON_VREF_MASK | |
877 | PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK); |
878 | outw(value: devpriv->daccon | PCI224_DACCON_FIFORESET, |
879 | port: dev->iobase + PCI224_DACCON); |
880 | |
881 | if (cmd->scan_begin_src == TRIG_TIMER) { |
882 | comedi_8254_update_divisors(i8254: dev->pacer); |
883 | pci224_ao_start_pacer(dev, s); |
884 | } |
885 | |
886 | spin_lock_irqsave(&devpriv->ao_spinlock, flags); |
887 | if (cmd->start_src == TRIG_INT) { |
888 | s->async->inttrig = pci224_ao_inttrig_start; |
889 | } else { /* TRIG_EXT */ |
890 | /* Enable external interrupt trigger to start acquisition. */ |
891 | devpriv->intsce |= PCI224_INTR_EXT; |
892 | outb(value: devpriv->intsce, port: devpriv->iobase1 + PCI224_INT_SCE); |
893 | } |
894 | spin_unlock_irqrestore(lock: &devpriv->ao_spinlock, flags); |
895 | |
896 | return 0; |
897 | } |
898 | |
899 | /* |
900 | * 'cancel' function for AO subdevice. |
901 | */ |
902 | static int pci224_ao_cancel(struct comedi_device *dev, |
903 | struct comedi_subdevice *s) |
904 | { |
905 | pci224_ao_stop(dev, s); |
906 | return 0; |
907 | } |
908 | |
909 | /* |
910 | * 'munge' data for AO command. |
911 | */ |
912 | static void |
913 | pci224_ao_munge(struct comedi_device *dev, struct comedi_subdevice *s, |
914 | void *data, unsigned int num_bytes, unsigned int chan_index) |
915 | { |
916 | const struct pci224_board *board = dev->board_ptr; |
917 | struct comedi_cmd *cmd = &s->async->cmd; |
918 | unsigned short *array = data; |
919 | unsigned int length = num_bytes / sizeof(*array); |
920 | unsigned int offset; |
921 | unsigned int shift; |
922 | unsigned int i; |
923 | |
924 | /* The hardware expects 16-bit numbers. */ |
925 | shift = 16 - board->ao_bits; |
926 | /* Channels will be all bipolar or all unipolar. */ |
927 | if ((board->ao_hwrange[CR_RANGE(cmd->chanlist[0])] & |
928 | PCI224_DACCON_POLAR_MASK) == PCI224_DACCON_POLAR_UNI) { |
929 | /* Unipolar */ |
930 | offset = 0; |
931 | } else { |
932 | /* Bipolar */ |
933 | offset = 32768; |
934 | } |
935 | /* Munge the data. */ |
936 | for (i = 0; i < length; i++) |
937 | array[i] = (array[i] << shift) - offset; |
938 | } |
939 | |
940 | /* |
941 | * Interrupt handler. |
942 | */ |
943 | static irqreturn_t pci224_interrupt(int irq, void *d) |
944 | { |
945 | struct comedi_device *dev = d; |
946 | struct pci224_private *devpriv = dev->private; |
947 | struct comedi_subdevice *s = dev->write_subdev; |
948 | struct comedi_cmd *cmd; |
949 | unsigned char intstat, valid_intstat; |
950 | unsigned char curenab; |
951 | int retval = 0; |
952 | unsigned long flags; |
953 | |
954 | intstat = inb(port: devpriv->iobase1 + PCI224_INT_SCE) & 0x3F; |
955 | if (intstat) { |
956 | retval = 1; |
957 | spin_lock_irqsave(&devpriv->ao_spinlock, flags); |
958 | valid_intstat = devpriv->intsce & intstat; |
959 | /* Temporarily disable interrupt sources. */ |
960 | curenab = devpriv->intsce & ~intstat; |
961 | outb(value: curenab, port: devpriv->iobase1 + PCI224_INT_SCE); |
962 | devpriv->intr_running = 1; |
963 | devpriv->intr_cpuid = THISCPU; |
964 | spin_unlock_irqrestore(lock: &devpriv->ao_spinlock, flags); |
965 | if (valid_intstat) { |
966 | cmd = &s->async->cmd; |
967 | if (valid_intstat & PCI224_INTR_EXT) { |
968 | devpriv->intsce &= ~PCI224_INTR_EXT; |
969 | if (cmd->start_src == TRIG_EXT) |
970 | pci224_ao_start(dev, s); |
971 | else if (cmd->stop_src == TRIG_EXT) |
972 | pci224_ao_stop(dev, s); |
973 | } |
974 | if (valid_intstat & PCI224_INTR_DAC) |
975 | pci224_ao_handle_fifo(dev, s); |
976 | } |
977 | /* Reenable interrupt sources. */ |
978 | spin_lock_irqsave(&devpriv->ao_spinlock, flags); |
979 | if (curenab != devpriv->intsce) { |
980 | outb(value: devpriv->intsce, |
981 | port: devpriv->iobase1 + PCI224_INT_SCE); |
982 | } |
983 | devpriv->intr_running = 0; |
984 | spin_unlock_irqrestore(lock: &devpriv->ao_spinlock, flags); |
985 | } |
986 | return IRQ_RETVAL(retval); |
987 | } |
988 | |
989 | static int |
990 | pci224_auto_attach(struct comedi_device *dev, unsigned long context_model) |
991 | { |
992 | struct pci_dev *pci_dev = comedi_to_pci_dev(dev); |
993 | const struct pci224_board *board = NULL; |
994 | struct pci224_private *devpriv; |
995 | struct comedi_subdevice *s; |
996 | unsigned int irq; |
997 | int ret; |
998 | |
999 | if (context_model < ARRAY_SIZE(pci224_boards)) |
1000 | board = &pci224_boards[context_model]; |
1001 | if (!board || !board->name) { |
1002 | dev_err(dev->class_dev, |
1003 | "amplc_pci224: BUG! cannot determine board type!\n" ); |
1004 | return -EINVAL; |
1005 | } |
1006 | dev->board_ptr = board; |
1007 | dev->board_name = board->name; |
1008 | |
1009 | dev_info(dev->class_dev, "amplc_pci224: attach pci %s - %s\n" , |
1010 | pci_name(pci_dev), dev->board_name); |
1011 | |
1012 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
1013 | if (!devpriv) |
1014 | return -ENOMEM; |
1015 | |
1016 | ret = comedi_pci_enable(dev); |
1017 | if (ret) |
1018 | return ret; |
1019 | |
1020 | spin_lock_init(&devpriv->ao_spinlock); |
1021 | |
1022 | devpriv->iobase1 = pci_resource_start(pci_dev, 2); |
1023 | dev->iobase = pci_resource_start(pci_dev, 3); |
1024 | irq = pci_dev->irq; |
1025 | |
1026 | /* Allocate buffer to hold values for AO channel scan. */ |
1027 | devpriv->ao_scan_vals = kmalloc_array(n: board->ao_chans, |
1028 | size: sizeof(devpriv->ao_scan_vals[0]), |
1029 | GFP_KERNEL); |
1030 | if (!devpriv->ao_scan_vals) |
1031 | return -ENOMEM; |
1032 | |
1033 | /* Allocate buffer to hold AO channel scan order. */ |
1034 | devpriv->ao_scan_order = |
1035 | kmalloc_array(n: board->ao_chans, |
1036 | size: sizeof(devpriv->ao_scan_order[0]), |
1037 | GFP_KERNEL); |
1038 | if (!devpriv->ao_scan_order) |
1039 | return -ENOMEM; |
1040 | |
1041 | /* Disable interrupt sources. */ |
1042 | devpriv->intsce = 0; |
1043 | outb(value: 0, port: devpriv->iobase1 + PCI224_INT_SCE); |
1044 | |
1045 | /* Initialize the DAC hardware. */ |
1046 | outw(PCI224_DACCON_GLOBALRESET, port: dev->iobase + PCI224_DACCON); |
1047 | outw(value: 0, port: dev->iobase + PCI224_DACCEN); |
1048 | outw(value: 0, port: dev->iobase + PCI224_FIFOSIZ); |
1049 | devpriv->daccon = PCI224_DACCON_TRIG_SW | PCI224_DACCON_POLAR_BI | |
1050 | PCI224_DACCON_FIFOENAB | PCI224_DACCON_FIFOINTR_EMPTY; |
1051 | outw(value: devpriv->daccon | PCI224_DACCON_FIFORESET, |
1052 | port: dev->iobase + PCI224_DACCON); |
1053 | |
1054 | dev->pacer = comedi_8254_io_alloc(iobase: devpriv->iobase1 + PCI224_Z2_BASE, |
1055 | I8254_OSC_BASE_10MHZ, I8254_IO8, regshift: 0); |
1056 | if (IS_ERR(ptr: dev->pacer)) |
1057 | return PTR_ERR(ptr: dev->pacer); |
1058 | |
1059 | ret = comedi_alloc_subdevices(dev, num_subdevices: 1); |
1060 | if (ret) |
1061 | return ret; |
1062 | |
1063 | s = &dev->subdevices[0]; |
1064 | /* Analog output subdevice. */ |
1065 | s->type = COMEDI_SUBD_AO; |
1066 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE; |
1067 | s->n_chan = board->ao_chans; |
1068 | s->maxdata = (1 << board->ao_bits) - 1; |
1069 | s->range_table = board->ao_range; |
1070 | s->insn_write = pci224_ao_insn_write; |
1071 | s->len_chanlist = s->n_chan; |
1072 | dev->write_subdev = s; |
1073 | s->do_cmd = pci224_ao_cmd; |
1074 | s->do_cmdtest = pci224_ao_cmdtest; |
1075 | s->cancel = pci224_ao_cancel; |
1076 | s->munge = pci224_ao_munge; |
1077 | |
1078 | ret = comedi_alloc_subdev_readback(s); |
1079 | if (ret) |
1080 | return ret; |
1081 | |
1082 | if (irq) { |
1083 | ret = request_irq(irq, handler: pci224_interrupt, IRQF_SHARED, |
1084 | name: dev->board_name, dev); |
1085 | if (ret < 0) { |
1086 | dev_err(dev->class_dev, |
1087 | "error! unable to allocate irq %u\n" , irq); |
1088 | return ret; |
1089 | } |
1090 | dev->irq = irq; |
1091 | } |
1092 | |
1093 | return 0; |
1094 | } |
1095 | |
1096 | static void pci224_detach(struct comedi_device *dev) |
1097 | { |
1098 | struct pci224_private *devpriv = dev->private; |
1099 | |
1100 | comedi_pci_detach(dev); |
1101 | if (devpriv) { |
1102 | kfree(objp: devpriv->ao_scan_vals); |
1103 | kfree(objp: devpriv->ao_scan_order); |
1104 | } |
1105 | } |
1106 | |
1107 | static struct comedi_driver amplc_pci224_driver = { |
1108 | .driver_name = "amplc_pci224" , |
1109 | .module = THIS_MODULE, |
1110 | .detach = pci224_detach, |
1111 | .auto_attach = pci224_auto_attach, |
1112 | .board_name = &pci224_boards[0].name, |
1113 | .offset = sizeof(struct pci224_board), |
1114 | .num_names = ARRAY_SIZE(pci224_boards), |
1115 | }; |
1116 | |
1117 | static int amplc_pci224_pci_probe(struct pci_dev *dev, |
1118 | const struct pci_device_id *id) |
1119 | { |
1120 | return comedi_pci_auto_config(pcidev: dev, driver: &lc_pci224_driver, |
1121 | context: id->driver_data); |
1122 | } |
1123 | |
1124 | static const struct pci_device_id amplc_pci224_pci_table[] = { |
1125 | { PCI_VDEVICE(AMPLICON, 0x0007), .driver_data: pci224_model }, |
1126 | { PCI_VDEVICE(AMPLICON, 0x0008), pci234_model }, |
1127 | { 0 } |
1128 | }; |
1129 | MODULE_DEVICE_TABLE(pci, amplc_pci224_pci_table); |
1130 | |
1131 | static struct pci_driver amplc_pci224_pci_driver = { |
1132 | .name = "amplc_pci224" , |
1133 | .id_table = amplc_pci224_pci_table, |
1134 | .probe = amplc_pci224_pci_probe, |
1135 | .remove = comedi_pci_auto_unconfig, |
1136 | }; |
1137 | module_comedi_pci_driver(amplc_pci224_driver, amplc_pci224_pci_driver); |
1138 | |
1139 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
1140 | MODULE_DESCRIPTION("Comedi driver for Amplicon PCI224 and PCI234 AO boards" ); |
1141 | MODULE_LICENSE("GPL" ); |
1142 | |