1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * addi_apci_1500.c |
4 | * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. |
5 | * |
6 | * ADDI-DATA GmbH |
7 | * Dieselstrasse 3 |
8 | * D-77833 Ottersweier |
9 | * Tel: +19(0)7223/9493-0 |
10 | * Fax: +49(0)7223/9493-92 |
11 | * http://www.addi-data.com |
12 | * info@addi-data.com |
13 | */ |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/comedi/comedi_pci.h> |
18 | |
19 | #include "amcc_s5933.h" |
20 | #include "z8536.h" |
21 | |
22 | /* |
23 | * PCI Bar 0 Register map (devpriv->amcc) |
24 | * see amcc_s5933.h for register and bit defines |
25 | */ |
26 | |
27 | /* |
28 | * PCI Bar 1 Register map (dev->iobase) |
29 | * see z8536.h for Z8536 internal registers and bit defines |
30 | */ |
31 | #define APCI1500_Z8536_PORTC_REG 0x00 |
32 | #define APCI1500_Z8536_PORTB_REG 0x01 |
33 | #define APCI1500_Z8536_PORTA_REG 0x02 |
34 | #define APCI1500_Z8536_CTRL_REG 0x03 |
35 | |
36 | /* |
37 | * PCI Bar 2 Register map (devpriv->addon) |
38 | */ |
39 | #define APCI1500_CLK_SEL_REG 0x00 |
40 | #define APCI1500_DI_REG 0x00 |
41 | #define APCI1500_DO_REG 0x02 |
42 | |
43 | struct apci1500_private { |
44 | unsigned long amcc; |
45 | unsigned long addon; |
46 | |
47 | unsigned int clk_src; |
48 | |
49 | /* Digital trigger configuration [0]=AND [1]=OR */ |
50 | unsigned int pm[2]; /* Pattern Mask */ |
51 | unsigned int pt[2]; /* Pattern Transition */ |
52 | unsigned int pp[2]; /* Pattern Polarity */ |
53 | }; |
54 | |
55 | static unsigned int z8536_read(struct comedi_device *dev, unsigned int reg) |
56 | { |
57 | unsigned long flags; |
58 | unsigned int val; |
59 | |
60 | spin_lock_irqsave(&dev->spinlock, flags); |
61 | outb(value: reg, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
62 | val = inb(port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
63 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
64 | |
65 | return val; |
66 | } |
67 | |
68 | static void z8536_write(struct comedi_device *dev, |
69 | unsigned int val, unsigned int reg) |
70 | { |
71 | unsigned long flags; |
72 | |
73 | spin_lock_irqsave(&dev->spinlock, flags); |
74 | outb(value: reg, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
75 | outb(value: val, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
76 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
77 | } |
78 | |
79 | static void z8536_reset(struct comedi_device *dev) |
80 | { |
81 | unsigned long flags; |
82 | |
83 | /* |
84 | * Even if the state of the Z8536 is not known, the following |
85 | * sequence will reset it and put it in State 0. |
86 | */ |
87 | spin_lock_irqsave(&dev->spinlock, flags); |
88 | inb(port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
89 | outb(value: 0, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
90 | inb(port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
91 | outb(value: 0, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
92 | outb(value: 1, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
93 | outb(value: 0, port: dev->iobase + APCI1500_Z8536_CTRL_REG); |
94 | spin_unlock_irqrestore(lock: &dev->spinlock, flags); |
95 | |
96 | /* Disable all Ports and Counter/Timers */ |
97 | z8536_write(dev, val: 0x00, Z8536_CFG_CTRL_REG); |
98 | |
99 | /* |
100 | * Port A is connected to Ditial Input channels 0-7. |
101 | * Configure the port to allow interrupt detection. |
102 | */ |
103 | z8536_write(dev, Z8536_PAB_MODE_PTS_BIT | |
104 | Z8536_PAB_MODE_SB | |
105 | Z8536_PAB_MODE_PMS_DISABLE, |
106 | Z8536_PA_MODE_REG); |
107 | z8536_write(dev, val: 0xff, Z8536_PB_DPP_REG); |
108 | z8536_write(dev, val: 0xff, Z8536_PA_DD_REG); |
109 | |
110 | /* |
111 | * Port B is connected to Ditial Input channels 8-13. |
112 | * Configure the port to allow interrupt detection. |
113 | * |
114 | * NOTE: Bits 7 and 6 of Port B are connected to internal |
115 | * diagnostic signals and bit 7 is inverted. |
116 | */ |
117 | z8536_write(dev, Z8536_PAB_MODE_PTS_BIT | |
118 | Z8536_PAB_MODE_SB | |
119 | Z8536_PAB_MODE_PMS_DISABLE, |
120 | Z8536_PB_MODE_REG); |
121 | z8536_write(dev, val: 0x7f, Z8536_PB_DPP_REG); |
122 | z8536_write(dev, val: 0xff, Z8536_PB_DD_REG); |
123 | |
124 | /* |
125 | * Not sure what Port C is connected to... |
126 | */ |
127 | z8536_write(dev, val: 0x09, Z8536_PC_DPP_REG); |
128 | z8536_write(dev, val: 0x0e, Z8536_PC_DD_REG); |
129 | |
130 | /* |
131 | * Clear and disable all interrupt sources. |
132 | * |
133 | * Just in case, the reset of the Z8536 should have already |
134 | * done this. |
135 | */ |
136 | z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_PA_CMDSTAT_REG); |
137 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PA_CMDSTAT_REG); |
138 | |
139 | z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_PB_CMDSTAT_REG); |
140 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PB_CMDSTAT_REG); |
141 | |
142 | z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(0)); |
143 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(0)); |
144 | |
145 | z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(1)); |
146 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(1)); |
147 | |
148 | z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(2)); |
149 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(2)); |
150 | |
151 | /* Disable all interrupts */ |
152 | z8536_write(dev, val: 0x00, Z8536_INT_CTRL_REG); |
153 | } |
154 | |
155 | static void apci1500_port_enable(struct comedi_device *dev, bool enable) |
156 | { |
157 | unsigned int cfg; |
158 | |
159 | cfg = z8536_read(dev, Z8536_CFG_CTRL_REG); |
160 | if (enable) |
161 | cfg |= (Z8536_CFG_CTRL_PAE | Z8536_CFG_CTRL_PBE); |
162 | else |
163 | cfg &= ~(Z8536_CFG_CTRL_PAE | Z8536_CFG_CTRL_PBE); |
164 | z8536_write(dev, val: cfg, Z8536_CFG_CTRL_REG); |
165 | } |
166 | |
167 | static void apci1500_timer_enable(struct comedi_device *dev, |
168 | unsigned int chan, bool enable) |
169 | { |
170 | unsigned int bit; |
171 | unsigned int cfg; |
172 | |
173 | if (chan == 0) |
174 | bit = Z8536_CFG_CTRL_CT1E; |
175 | else if (chan == 1) |
176 | bit = Z8536_CFG_CTRL_CT2E; |
177 | else |
178 | bit = Z8536_CFG_CTRL_PCE_CT3E; |
179 | |
180 | cfg = z8536_read(dev, Z8536_CFG_CTRL_REG); |
181 | if (enable) { |
182 | cfg |= bit; |
183 | } else { |
184 | cfg &= ~bit; |
185 | z8536_write(dev, val: 0x00, Z8536_CT_CMDSTAT_REG(chan)); |
186 | } |
187 | z8536_write(dev, val: cfg, Z8536_CFG_CTRL_REG); |
188 | } |
189 | |
190 | static bool apci1500_ack_irq(struct comedi_device *dev, |
191 | unsigned int reg) |
192 | { |
193 | unsigned int val; |
194 | |
195 | val = z8536_read(dev, reg); |
196 | if ((val & Z8536_STAT_IE_IP) == Z8536_STAT_IE_IP) { |
197 | val &= 0x0f; /* preserve any write bits */ |
198 | val |= Z8536_CMD_CLR_IP_IUS; |
199 | z8536_write(dev, val, reg); |
200 | |
201 | return true; |
202 | } |
203 | return false; |
204 | } |
205 | |
206 | static irqreturn_t apci1500_interrupt(int irq, void *d) |
207 | { |
208 | struct comedi_device *dev = d; |
209 | struct apci1500_private *devpriv = dev->private; |
210 | struct comedi_subdevice *s = dev->read_subdev; |
211 | unsigned short status = 0; |
212 | unsigned int val; |
213 | |
214 | val = inl(port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
215 | if (!(val & INTCSR_INTR_ASSERTED)) |
216 | return IRQ_NONE; |
217 | |
218 | if (apci1500_ack_irq(dev, Z8536_PA_CMDSTAT_REG)) |
219 | status |= 0x01; /* port a event (inputs 0-7) */ |
220 | |
221 | if (apci1500_ack_irq(dev, Z8536_PB_CMDSTAT_REG)) { |
222 | /* Tests if this is an external error */ |
223 | val = inb(port: dev->iobase + APCI1500_Z8536_PORTB_REG); |
224 | val &= 0xc0; |
225 | if (val) { |
226 | if (val & 0x80) /* voltage error */ |
227 | status |= 0x40; |
228 | if (val & 0x40) /* short circuit error */ |
229 | status |= 0x80; |
230 | } else { |
231 | status |= 0x02; /* port b event (inputs 8-13) */ |
232 | } |
233 | } |
234 | |
235 | /* |
236 | * NOTE: The 'status' returned by the sample matches the |
237 | * interrupt mask information from the APCI-1500 Users Manual. |
238 | * |
239 | * Mask Meaning |
240 | * ---------- ------------------------------------------ |
241 | * 0b00000001 Event 1 has occurred |
242 | * 0b00000010 Event 2 has occurred |
243 | * 0b00000100 Counter/timer 1 has run down (not implemented) |
244 | * 0b00001000 Counter/timer 2 has run down (not implemented) |
245 | * 0b00010000 Counter 3 has run down (not implemented) |
246 | * 0b00100000 Watchdog has run down (not implemented) |
247 | * 0b01000000 Voltage error |
248 | * 0b10000000 Short-circuit error |
249 | */ |
250 | comedi_buf_write_samples(s, data: &status, nsamples: 1); |
251 | comedi_handle_events(dev, s); |
252 | |
253 | return IRQ_HANDLED; |
254 | } |
255 | |
256 | static int apci1500_di_cancel(struct comedi_device *dev, |
257 | struct comedi_subdevice *s) |
258 | { |
259 | /* Disables the main interrupt on the board */ |
260 | z8536_write(dev, val: 0x00, Z8536_INT_CTRL_REG); |
261 | |
262 | /* Disable Ports A & B */ |
263 | apci1500_port_enable(dev, enable: false); |
264 | |
265 | /* Ack any pending interrupts */ |
266 | apci1500_ack_irq(dev, Z8536_PA_CMDSTAT_REG); |
267 | apci1500_ack_irq(dev, Z8536_PB_CMDSTAT_REG); |
268 | |
269 | /* Disable pattern interrupts */ |
270 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PA_CMDSTAT_REG); |
271 | z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PB_CMDSTAT_REG); |
272 | |
273 | /* Enable Ports A & B */ |
274 | apci1500_port_enable(dev, enable: true); |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | static int apci1500_di_inttrig_start(struct comedi_device *dev, |
280 | struct comedi_subdevice *s, |
281 | unsigned int trig_num) |
282 | { |
283 | struct apci1500_private *devpriv = dev->private; |
284 | struct comedi_cmd *cmd = &s->async->cmd; |
285 | unsigned int pa_mode = Z8536_PAB_MODE_PMS_DISABLE; |
286 | unsigned int pb_mode = Z8536_PAB_MODE_PMS_DISABLE; |
287 | unsigned int pa_trig = trig_num & 0x01; |
288 | unsigned int pb_trig = (trig_num >> 1) & 0x01; |
289 | bool valid_trig = false; |
290 | unsigned int val; |
291 | |
292 | if (trig_num != cmd->start_arg) |
293 | return -EINVAL; |
294 | |
295 | /* Disable Ports A & B */ |
296 | apci1500_port_enable(dev, enable: false); |
297 | |
298 | /* Set Port A for selected trigger pattern */ |
299 | z8536_write(dev, val: devpriv->pm[pa_trig] & 0xff, Z8536_PA_PM_REG); |
300 | z8536_write(dev, val: devpriv->pt[pa_trig] & 0xff, Z8536_PA_PT_REG); |
301 | z8536_write(dev, val: devpriv->pp[pa_trig] & 0xff, Z8536_PA_PP_REG); |
302 | |
303 | /* Set Port B for selected trigger pattern */ |
304 | z8536_write(dev, val: (devpriv->pm[pb_trig] >> 8) & 0xff, Z8536_PB_PM_REG); |
305 | z8536_write(dev, val: (devpriv->pt[pb_trig] >> 8) & 0xff, Z8536_PB_PT_REG); |
306 | z8536_write(dev, val: (devpriv->pp[pb_trig] >> 8) & 0xff, Z8536_PB_PP_REG); |
307 | |
308 | /* Set Port A trigger mode (if enabled) and enable interrupt */ |
309 | if (devpriv->pm[pa_trig] & 0xff) { |
310 | pa_mode = pa_trig ? Z8536_PAB_MODE_PMS_AND |
311 | : Z8536_PAB_MODE_PMS_OR; |
312 | |
313 | val = z8536_read(dev, Z8536_PA_MODE_REG); |
314 | val &= ~Z8536_PAB_MODE_PMS_MASK; |
315 | val |= (pa_mode | Z8536_PAB_MODE_IMO); |
316 | z8536_write(dev, val, Z8536_PA_MODE_REG); |
317 | |
318 | z8536_write(dev, Z8536_CMD_SET_IE, Z8536_PA_CMDSTAT_REG); |
319 | |
320 | valid_trig = true; |
321 | |
322 | dev_dbg(dev->class_dev, |
323 | "Port A configured for %s mode pattern detection\n" , |
324 | pa_trig ? "AND" : "OR" ); |
325 | } |
326 | |
327 | /* Set Port B trigger mode (if enabled) and enable interrupt */ |
328 | if (devpriv->pm[pb_trig] & 0xff00) { |
329 | pb_mode = pb_trig ? Z8536_PAB_MODE_PMS_AND |
330 | : Z8536_PAB_MODE_PMS_OR; |
331 | |
332 | val = z8536_read(dev, Z8536_PB_MODE_REG); |
333 | val &= ~Z8536_PAB_MODE_PMS_MASK; |
334 | val |= (pb_mode | Z8536_PAB_MODE_IMO); |
335 | z8536_write(dev, val, Z8536_PB_MODE_REG); |
336 | |
337 | z8536_write(dev, Z8536_CMD_SET_IE, Z8536_PB_CMDSTAT_REG); |
338 | |
339 | valid_trig = true; |
340 | |
341 | dev_dbg(dev->class_dev, |
342 | "Port B configured for %s mode pattern detection\n" , |
343 | pb_trig ? "AND" : "OR" ); |
344 | } |
345 | |
346 | /* Enable Ports A & B */ |
347 | apci1500_port_enable(dev, enable: true); |
348 | |
349 | if (!valid_trig) { |
350 | dev_dbg(dev->class_dev, |
351 | "digital trigger %d is not configured\n" , trig_num); |
352 | return -EINVAL; |
353 | } |
354 | |
355 | /* Authorizes the main interrupt on the board */ |
356 | z8536_write(dev, Z8536_INT_CTRL_MIE | Z8536_INT_CTRL_DLC, |
357 | Z8536_INT_CTRL_REG); |
358 | |
359 | return 0; |
360 | } |
361 | |
362 | static int apci1500_di_cmd(struct comedi_device *dev, |
363 | struct comedi_subdevice *s) |
364 | { |
365 | s->async->inttrig = apci1500_di_inttrig_start; |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | static int apci1500_di_cmdtest(struct comedi_device *dev, |
371 | struct comedi_subdevice *s, |
372 | struct comedi_cmd *cmd) |
373 | { |
374 | int err = 0; |
375 | |
376 | /* Step 1 : check if triggers are trivially valid */ |
377 | |
378 | err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_INT); |
379 | err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, TRIG_EXT); |
380 | err |= comedi_check_trigger_src(src: &cmd->convert_src, TRIG_FOLLOW); |
381 | err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT); |
382 | err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_NONE); |
383 | |
384 | if (err) |
385 | return 1; |
386 | |
387 | /* Step 2a : make sure trigger sources are unique */ |
388 | /* Step 2b : and mutually compatible */ |
389 | |
390 | /* Step 3: check if arguments are trivially valid */ |
391 | |
392 | /* |
393 | * Internal start source triggers: |
394 | * |
395 | * 0 AND mode for Port A (digital inputs 0-7) |
396 | * AND mode for Port B (digital inputs 8-13 and internal signals) |
397 | * |
398 | * 1 OR mode for Port A (digital inputs 0-7) |
399 | * AND mode for Port B (digital inputs 8-13 and internal signals) |
400 | * |
401 | * 2 AND mode for Port A (digital inputs 0-7) |
402 | * OR mode for Port B (digital inputs 8-13 and internal signals) |
403 | * |
404 | * 3 OR mode for Port A (digital inputs 0-7) |
405 | * OR mode for Port B (digital inputs 8-13 and internal signals) |
406 | */ |
407 | err |= comedi_check_trigger_arg_max(arg: &cmd->start_arg, val: 3); |
408 | |
409 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: 0); |
410 | err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: 0); |
411 | err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg, |
412 | val: cmd->chanlist_len); |
413 | err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0); |
414 | |
415 | if (err) |
416 | return 3; |
417 | |
418 | /* Step 4: fix up any arguments */ |
419 | |
420 | /* Step 5: check channel list if it exists */ |
421 | |
422 | return 0; |
423 | } |
424 | |
425 | /* |
426 | * The pattern-recognition logic must be configured before the digital |
427 | * input async command is started. |
428 | * |
429 | * Digital input channels 0 to 13 can generate interrupts. Channels 14 |
430 | * and 15 are connected to internal board status/diagnostic signals. |
431 | * |
432 | * Channel 14 - Voltage error (the external supply is < 5V) |
433 | * Channel 15 - Short-circuit/overtemperature error |
434 | * |
435 | * data[0] : INSN_CONFIG_DIGITAL_TRIG |
436 | * data[1] : trigger number |
437 | * 0 = AND mode |
438 | * 1 = OR mode |
439 | * data[2] : configuration operation: |
440 | * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts |
441 | * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = edge interrupts |
442 | * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = level interrupts |
443 | * data[3] : left-shift for data[4] and data[5] |
444 | * data[4] : rising-edge/high level channels |
445 | * data[5] : falling-edge/low level channels |
446 | */ |
447 | static int apci1500_di_cfg_trig(struct comedi_device *dev, |
448 | struct comedi_subdevice *s, |
449 | struct comedi_insn *insn, |
450 | unsigned int *data) |
451 | { |
452 | struct apci1500_private *devpriv = dev->private; |
453 | unsigned int trig = data[1]; |
454 | unsigned int shift = data[3]; |
455 | unsigned int hi_mask; |
456 | unsigned int lo_mask; |
457 | unsigned int chan_mask; |
458 | unsigned int old_mask; |
459 | unsigned int pm; |
460 | unsigned int pt; |
461 | unsigned int pp; |
462 | unsigned int invalid_chan; |
463 | |
464 | if (trig > 1) { |
465 | dev_dbg(dev->class_dev, |
466 | "invalid digital trigger number (0=AND, 1=OR)\n" ); |
467 | return -EINVAL; |
468 | } |
469 | |
470 | if (shift <= 16) { |
471 | hi_mask = data[4] << shift; |
472 | lo_mask = data[5] << shift; |
473 | old_mask = (1U << shift) - 1; |
474 | invalid_chan = (data[4] | data[5]) >> (16 - shift); |
475 | } else { |
476 | hi_mask = 0; |
477 | lo_mask = 0; |
478 | old_mask = 0xffff; |
479 | invalid_chan = data[4] | data[5]; |
480 | } |
481 | chan_mask = hi_mask | lo_mask; |
482 | |
483 | if (invalid_chan) { |
484 | dev_dbg(dev->class_dev, "invalid digital trigger channel\n" ); |
485 | return -EINVAL; |
486 | } |
487 | |
488 | pm = devpriv->pm[trig] & old_mask; |
489 | pt = devpriv->pt[trig] & old_mask; |
490 | pp = devpriv->pp[trig] & old_mask; |
491 | |
492 | switch (data[2]) { |
493 | case COMEDI_DIGITAL_TRIG_DISABLE: |
494 | /* clear trigger configuration */ |
495 | pm = 0; |
496 | pt = 0; |
497 | pp = 0; |
498 | break; |
499 | case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: |
500 | pm |= chan_mask; /* enable channels */ |
501 | pt |= chan_mask; /* enable edge detection */ |
502 | pp |= hi_mask; /* rising-edge channels */ |
503 | pp &= ~lo_mask; /* falling-edge channels */ |
504 | break; |
505 | case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: |
506 | pm |= chan_mask; /* enable channels */ |
507 | pt &= ~chan_mask; /* enable level detection */ |
508 | pp |= hi_mask; /* high level channels */ |
509 | pp &= ~lo_mask; /* low level channels */ |
510 | break; |
511 | default: |
512 | return -EINVAL; |
513 | } |
514 | |
515 | /* |
516 | * The AND mode trigger can only have one channel (max) enabled |
517 | * for edge detection. |
518 | */ |
519 | if (trig == 0) { |
520 | int ret = 0; |
521 | unsigned int src; |
522 | |
523 | src = pt & 0xff; |
524 | if (src) |
525 | ret |= comedi_check_trigger_is_unique(src); |
526 | |
527 | src = (pt >> 8) & 0xff; |
528 | if (src) |
529 | ret |= comedi_check_trigger_is_unique(src); |
530 | |
531 | if (ret) { |
532 | dev_dbg(dev->class_dev, |
533 | "invalid AND trigger configuration\n" ); |
534 | return ret; |
535 | } |
536 | } |
537 | |
538 | /* save the trigger configuration */ |
539 | devpriv->pm[trig] = pm; |
540 | devpriv->pt[trig] = pt; |
541 | devpriv->pp[trig] = pp; |
542 | |
543 | return insn->n; |
544 | } |
545 | |
546 | static int apci1500_di_insn_config(struct comedi_device *dev, |
547 | struct comedi_subdevice *s, |
548 | struct comedi_insn *insn, |
549 | unsigned int *data) |
550 | { |
551 | switch (data[0]) { |
552 | case INSN_CONFIG_DIGITAL_TRIG: |
553 | return apci1500_di_cfg_trig(dev, s, insn, data); |
554 | default: |
555 | return -EINVAL; |
556 | } |
557 | } |
558 | |
559 | static int apci1500_di_insn_bits(struct comedi_device *dev, |
560 | struct comedi_subdevice *s, |
561 | struct comedi_insn *insn, |
562 | unsigned int *data) |
563 | { |
564 | struct apci1500_private *devpriv = dev->private; |
565 | |
566 | data[1] = inw(port: devpriv->addon + APCI1500_DI_REG); |
567 | |
568 | return insn->n; |
569 | } |
570 | |
571 | static int apci1500_do_insn_bits(struct comedi_device *dev, |
572 | struct comedi_subdevice *s, |
573 | struct comedi_insn *insn, |
574 | unsigned int *data) |
575 | { |
576 | struct apci1500_private *devpriv = dev->private; |
577 | |
578 | if (comedi_dio_update_state(s, data)) |
579 | outw(value: s->state, port: devpriv->addon + APCI1500_DO_REG); |
580 | |
581 | data[1] = s->state; |
582 | |
583 | return insn->n; |
584 | } |
585 | |
586 | static int apci1500_timer_insn_config(struct comedi_device *dev, |
587 | struct comedi_subdevice *s, |
588 | struct comedi_insn *insn, |
589 | unsigned int *data) |
590 | { |
591 | struct apci1500_private *devpriv = dev->private; |
592 | unsigned int chan = CR_CHAN(insn->chanspec); |
593 | unsigned int val; |
594 | |
595 | switch (data[0]) { |
596 | case INSN_CONFIG_ARM: |
597 | val = data[1] & s->maxdata; |
598 | z8536_write(dev, val: val & 0xff, Z8536_CT_RELOAD_LSB_REG(chan)); |
599 | z8536_write(dev, val: (val >> 8) & 0xff, |
600 | Z8536_CT_RELOAD_MSB_REG(chan)); |
601 | |
602 | apci1500_timer_enable(dev, chan, enable: true); |
603 | z8536_write(dev, Z8536_CT_CMDSTAT_GCB, |
604 | Z8536_CT_CMDSTAT_REG(chan)); |
605 | break; |
606 | case INSN_CONFIG_DISARM: |
607 | apci1500_timer_enable(dev, chan, enable: false); |
608 | break; |
609 | |
610 | case INSN_CONFIG_GET_COUNTER_STATUS: |
611 | data[1] = 0; |
612 | val = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan)); |
613 | if (val & Z8536_CT_STAT_CIP) |
614 | data[1] |= COMEDI_COUNTER_COUNTING; |
615 | if (val & Z8536_CT_CMDSTAT_GCB) |
616 | data[1] |= COMEDI_COUNTER_ARMED; |
617 | if (val & Z8536_STAT_IP) { |
618 | data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; |
619 | apci1500_ack_irq(dev, Z8536_CT_CMDSTAT_REG(chan)); |
620 | } |
621 | data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING | |
622 | COMEDI_COUNTER_TERMINAL_COUNT; |
623 | break; |
624 | |
625 | case INSN_CONFIG_SET_COUNTER_MODE: |
626 | /* Simulate the 8254 timer modes */ |
627 | switch (data[1]) { |
628 | case I8254_MODE0: |
629 | /* Interrupt on Terminal Count */ |
630 | val = Z8536_CT_MODE_ECE | |
631 | Z8536_CT_MODE_DCS_ONESHOT; |
632 | break; |
633 | case I8254_MODE1: |
634 | /* Hardware Retriggerable One-Shot */ |
635 | val = Z8536_CT_MODE_ETE | |
636 | Z8536_CT_MODE_DCS_ONESHOT; |
637 | break; |
638 | case I8254_MODE2: |
639 | /* Rate Generator */ |
640 | val = Z8536_CT_MODE_CSC | |
641 | Z8536_CT_MODE_DCS_PULSE; |
642 | break; |
643 | case I8254_MODE3: |
644 | /* Square Wave Mode */ |
645 | val = Z8536_CT_MODE_CSC | |
646 | Z8536_CT_MODE_DCS_SQRWAVE; |
647 | break; |
648 | case I8254_MODE4: |
649 | /* Software Triggered Strobe */ |
650 | val = Z8536_CT_MODE_REB | |
651 | Z8536_CT_MODE_DCS_PULSE; |
652 | break; |
653 | case I8254_MODE5: |
654 | /* Hardware Triggered Strobe (watchdog) */ |
655 | val = Z8536_CT_MODE_EOE | |
656 | Z8536_CT_MODE_ETE | |
657 | Z8536_CT_MODE_REB | |
658 | Z8536_CT_MODE_DCS_PULSE; |
659 | break; |
660 | default: |
661 | return -EINVAL; |
662 | } |
663 | apci1500_timer_enable(dev, chan, enable: false); |
664 | z8536_write(dev, val, Z8536_CT_MODE_REG(chan)); |
665 | break; |
666 | |
667 | case INSN_CONFIG_SET_CLOCK_SRC: |
668 | if (data[1] > 2) |
669 | return -EINVAL; |
670 | devpriv->clk_src = data[1]; |
671 | if (devpriv->clk_src == 2) |
672 | devpriv->clk_src = 3; |
673 | outw(value: devpriv->clk_src, port: devpriv->addon + APCI1500_CLK_SEL_REG); |
674 | break; |
675 | case INSN_CONFIG_GET_CLOCK_SRC: |
676 | switch (devpriv->clk_src) { |
677 | case 0: |
678 | data[1] = 0; /* 111.86 kHz / 2 */ |
679 | data[2] = 17879; /* 17879 ns (approx) */ |
680 | break; |
681 | case 1: |
682 | data[1] = 1; /* 3.49 kHz / 2 */ |
683 | data[2] = 573066; /* 573066 ns (approx) */ |
684 | break; |
685 | case 3: |
686 | data[1] = 2; /* 1.747 kHz / 2 */ |
687 | data[2] = 1164822; /* 1164822 ns (approx) */ |
688 | break; |
689 | default: |
690 | return -EINVAL; |
691 | } |
692 | break; |
693 | |
694 | case INSN_CONFIG_SET_GATE_SRC: |
695 | if (chan == 0) |
696 | return -EINVAL; |
697 | |
698 | val = z8536_read(dev, Z8536_CT_MODE_REG(chan)); |
699 | val &= Z8536_CT_MODE_EGE; |
700 | if (data[1] == 1) |
701 | val |= Z8536_CT_MODE_EGE; |
702 | else if (data[1] > 1) |
703 | return -EINVAL; |
704 | z8536_write(dev, val, Z8536_CT_MODE_REG(chan)); |
705 | break; |
706 | case INSN_CONFIG_GET_GATE_SRC: |
707 | if (chan == 0) |
708 | return -EINVAL; |
709 | break; |
710 | |
711 | default: |
712 | return -EINVAL; |
713 | } |
714 | return insn->n; |
715 | } |
716 | |
717 | static int apci1500_timer_insn_write(struct comedi_device *dev, |
718 | struct comedi_subdevice *s, |
719 | struct comedi_insn *insn, |
720 | unsigned int *data) |
721 | { |
722 | unsigned int chan = CR_CHAN(insn->chanspec); |
723 | unsigned int cmd; |
724 | |
725 | cmd = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan)); |
726 | cmd &= Z8536_CT_CMDSTAT_GCB; /* preserve gate */ |
727 | cmd |= Z8536_CT_CMD_TCB; /* set trigger */ |
728 | |
729 | /* software trigger a timer, it only makes sense to do one write */ |
730 | if (insn->n) |
731 | z8536_write(dev, val: cmd, Z8536_CT_CMDSTAT_REG(chan)); |
732 | |
733 | return insn->n; |
734 | } |
735 | |
736 | static int apci1500_timer_insn_read(struct comedi_device *dev, |
737 | struct comedi_subdevice *s, |
738 | struct comedi_insn *insn, |
739 | unsigned int *data) |
740 | { |
741 | unsigned int chan = CR_CHAN(insn->chanspec); |
742 | unsigned int cmd; |
743 | unsigned int val; |
744 | int i; |
745 | |
746 | cmd = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan)); |
747 | cmd &= Z8536_CT_CMDSTAT_GCB; /* preserve gate */ |
748 | cmd |= Z8536_CT_CMD_RCC; /* set RCC */ |
749 | |
750 | for (i = 0; i < insn->n; i++) { |
751 | z8536_write(dev, val: cmd, Z8536_CT_CMDSTAT_REG(chan)); |
752 | |
753 | val = z8536_read(dev, Z8536_CT_VAL_MSB_REG(chan)) << 8; |
754 | val |= z8536_read(dev, Z8536_CT_VAL_LSB_REG(chan)); |
755 | |
756 | data[i] = val; |
757 | } |
758 | |
759 | return insn->n; |
760 | } |
761 | |
762 | static int apci1500_auto_attach(struct comedi_device *dev, |
763 | unsigned long context) |
764 | { |
765 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
766 | struct apci1500_private *devpriv; |
767 | struct comedi_subdevice *s; |
768 | int ret; |
769 | |
770 | devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv)); |
771 | if (!devpriv) |
772 | return -ENOMEM; |
773 | |
774 | ret = comedi_pci_enable(dev); |
775 | if (ret) |
776 | return ret; |
777 | |
778 | dev->iobase = pci_resource_start(pcidev, 1); |
779 | devpriv->amcc = pci_resource_start(pcidev, 0); |
780 | devpriv->addon = pci_resource_start(pcidev, 2); |
781 | |
782 | z8536_reset(dev); |
783 | |
784 | if (pcidev->irq > 0) { |
785 | ret = request_irq(irq: pcidev->irq, handler: apci1500_interrupt, IRQF_SHARED, |
786 | name: dev->board_name, dev); |
787 | if (ret == 0) |
788 | dev->irq = pcidev->irq; |
789 | } |
790 | |
791 | ret = comedi_alloc_subdevices(dev, num_subdevices: 3); |
792 | if (ret) |
793 | return ret; |
794 | |
795 | /* Digital Input subdevice */ |
796 | s = &dev->subdevices[0]; |
797 | s->type = COMEDI_SUBD_DI; |
798 | s->subdev_flags = SDF_READABLE; |
799 | s->n_chan = 16; |
800 | s->maxdata = 1; |
801 | s->range_table = &range_digital; |
802 | s->insn_bits = apci1500_di_insn_bits; |
803 | if (dev->irq) { |
804 | dev->read_subdev = s; |
805 | s->subdev_flags |= SDF_CMD_READ; |
806 | s->len_chanlist = 1; |
807 | s->insn_config = apci1500_di_insn_config; |
808 | s->do_cmdtest = apci1500_di_cmdtest; |
809 | s->do_cmd = apci1500_di_cmd; |
810 | s->cancel = apci1500_di_cancel; |
811 | } |
812 | |
813 | /* Digital Output subdevice */ |
814 | s = &dev->subdevices[1]; |
815 | s->type = COMEDI_SUBD_DO; |
816 | s->subdev_flags = SDF_WRITABLE; |
817 | s->n_chan = 16; |
818 | s->maxdata = 1; |
819 | s->range_table = &range_digital; |
820 | s->insn_bits = apci1500_do_insn_bits; |
821 | |
822 | /* reset all the digital outputs */ |
823 | outw(value: 0x0, port: devpriv->addon + APCI1500_DO_REG); |
824 | |
825 | /* Counter/Timer(Watchdog) subdevice */ |
826 | s = &dev->subdevices[2]; |
827 | s->type = COMEDI_SUBD_TIMER; |
828 | s->subdev_flags = SDF_WRITABLE | SDF_READABLE; |
829 | s->n_chan = 3; |
830 | s->maxdata = 0xffff; |
831 | s->range_table = &range_unknown; |
832 | s->insn_config = apci1500_timer_insn_config; |
833 | s->insn_write = apci1500_timer_insn_write; |
834 | s->insn_read = apci1500_timer_insn_read; |
835 | |
836 | /* Enable the PCI interrupt */ |
837 | if (dev->irq) { |
838 | outl(value: 0x2000 | INTCSR_INBOX_FULL_INT, |
839 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
840 | inl(port: devpriv->amcc + AMCC_OP_REG_IMB1); |
841 | inl(port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
842 | outl(INTCSR_INBOX_INTR_STATUS | 0x2000 | INTCSR_INBOX_FULL_INT, |
843 | port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
844 | } |
845 | |
846 | return 0; |
847 | } |
848 | |
849 | static void apci1500_detach(struct comedi_device *dev) |
850 | { |
851 | struct apci1500_private *devpriv = dev->private; |
852 | |
853 | if (devpriv->amcc) |
854 | outl(value: 0x0, port: devpriv->amcc + AMCC_OP_REG_INTCSR); |
855 | comedi_pci_detach(dev); |
856 | } |
857 | |
858 | static struct comedi_driver apci1500_driver = { |
859 | .driver_name = "addi_apci_1500" , |
860 | .module = THIS_MODULE, |
861 | .auto_attach = apci1500_auto_attach, |
862 | .detach = apci1500_detach, |
863 | }; |
864 | |
865 | static int apci1500_pci_probe(struct pci_dev *dev, |
866 | const struct pci_device_id *id) |
867 | { |
868 | return comedi_pci_auto_config(pcidev: dev, driver: &apci1500_driver, context: id->driver_data); |
869 | } |
870 | |
871 | static const struct pci_device_id apci1500_pci_table[] = { |
872 | { PCI_DEVICE(PCI_VENDOR_ID_AMCC, 0x80fc) }, |
873 | { 0 } |
874 | }; |
875 | MODULE_DEVICE_TABLE(pci, apci1500_pci_table); |
876 | |
877 | static struct pci_driver apci1500_pci_driver = { |
878 | .name = "addi_apci_1500" , |
879 | .id_table = apci1500_pci_table, |
880 | .probe = apci1500_pci_probe, |
881 | .remove = comedi_pci_auto_unconfig, |
882 | }; |
883 | module_comedi_pci_driver(apci1500_driver, apci1500_pci_driver); |
884 | |
885 | MODULE_AUTHOR("Comedi https://www.comedi.org" ); |
886 | MODULE_DESCRIPTION("ADDI-DATA APCI-1500, 16 channel DI / 16 channel DO boards" ); |
887 | MODULE_LICENSE("GPL" ); |
888 | |