Commit | Line | Data |
---|---|---|
7ff7e4c2 IA |
1 | /* |
2 | comedi/drivers/amplc_dio200_common.c | |
3 | ||
4 | Common support code for "amplc_dio200" and "amplc_dio200_pci". | |
5 | ||
6 | Copyright (C) 2005-2013 MEV Ltd. <http://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 | This program is free software; you can redistribute it and/or modify | |
12 | it under the terms of the GNU General Public License as published by | |
13 | the Free Software Foundation; either version 2 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | This program is distributed in the hope that it will be useful, | |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | GNU General Public License for more details. | |
7ff7e4c2 IA |
20 | */ |
21 | ||
ce157f80 | 22 | #include <linux/module.h> |
7ff7e4c2 | 23 | #include <linux/interrupt.h> |
7ff7e4c2 IA |
24 | |
25 | #include "../comedidev.h" | |
26 | ||
27 | #include "amplc_dio200.h" | |
28 | #include "comedi_fc.h" | |
29 | #include "8253.h" | |
30 | ||
31 | /* 8255 control register bits */ | |
32 | #define CR_C_LO_IO 0x01 | |
33 | #define CR_B_IO 0x02 | |
34 | #define CR_B_MODE 0x04 | |
35 | #define CR_C_HI_IO 0x08 | |
36 | #define CR_A_IO 0x10 | |
37 | #define CR_A_MODE(a) ((a)<<5) | |
38 | #define CR_CW 0x80 | |
39 | ||
40 | /* 200 series registers */ | |
41 | #define DIO200_IO_SIZE 0x20 | |
42 | #define DIO200_PCIE_IO_SIZE 0x4000 | |
43 | #define DIO200_XCLK_SCE 0x18 /* Group X clock selection register */ | |
44 | #define DIO200_YCLK_SCE 0x19 /* Group Y clock selection register */ | |
45 | #define DIO200_ZCLK_SCE 0x1a /* Group Z clock selection register */ | |
46 | #define DIO200_XGAT_SCE 0x1b /* Group X gate selection register */ | |
47 | #define DIO200_YGAT_SCE 0x1c /* Group Y gate selection register */ | |
48 | #define DIO200_ZGAT_SCE 0x1d /* Group Z gate selection register */ | |
49 | #define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */ | |
50 | /* Extra registers for new PCIe boards */ | |
51 | #define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */ | |
52 | #define DIO200_VERSION 0x24 /* Hardware version register */ | |
53 | #define DIO200_TS_CONFIG 0x600 /* Timestamp timer config register */ | |
54 | #define DIO200_TS_COUNT 0x602 /* Timestamp timer count register */ | |
55 | ||
56 | /* | |
57 | * Functions for constructing value for DIO_200_?CLK_SCE and | |
58 | * DIO_200_?GAT_SCE registers: | |
59 | * | |
60 | * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2. | |
61 | * 'chan' is the channel: 0, 1 or 2. | |
62 | * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards. | |
63 | */ | |
64 | static unsigned char clk_gat_sce(unsigned int which, unsigned int chan, | |
65 | unsigned int source) | |
66 | { | |
67 | return (which << 5) | (chan << 3) | | |
68 | ((source & 030) << 3) | (source & 007); | |
69 | } | |
70 | ||
71 | static unsigned char clk_sce(unsigned int which, unsigned int chan, | |
72 | unsigned int source) | |
73 | { | |
74 | return clk_gat_sce(which, chan, source); | |
75 | } | |
76 | ||
77 | static unsigned char gat_sce(unsigned int which, unsigned int chan, | |
78 | unsigned int source) | |
79 | { | |
80 | return clk_gat_sce(which, chan, source); | |
81 | } | |
82 | ||
83 | /* | |
84 | * Periods of the internal clock sources in nanoseconds. | |
85 | */ | |
86 | static const unsigned int clock_period[32] = { | |
87 | [1] = 100, /* 10 MHz */ | |
88 | [2] = 1000, /* 1 MHz */ | |
89 | [3] = 10000, /* 100 kHz */ | |
90 | [4] = 100000, /* 10 kHz */ | |
91 | [5] = 1000000, /* 1 kHz */ | |
92 | [11] = 50, /* 20 MHz (enhanced boards) */ | |
93 | /* clock sources 12 and later reserved for enhanced boards */ | |
94 | }; | |
95 | ||
96 | /* | |
97 | * Timestamp timer configuration register (for new PCIe boards). | |
98 | */ | |
99 | #define TS_CONFIG_RESET 0x100 /* Reset counter to zero. */ | |
100 | #define TS_CONFIG_CLK_SRC_MASK 0x0FF /* Clock source. */ | |
101 | #define TS_CONFIG_MAX_CLK_SRC 2 /* Maximum clock source value. */ | |
102 | ||
103 | /* | |
104 | * Periods of the timestamp timer clock sources in nanoseconds. | |
105 | */ | |
106 | static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = { | |
107 | 1, /* 1 nanosecond (but with 20 ns granularity). */ | |
108 | 1000, /* 1 microsecond. */ | |
109 | 1000000, /* 1 millisecond. */ | |
110 | }; | |
111 | ||
112 | struct dio200_subdev_8254 { | |
113 | unsigned int ofs; /* Counter base offset */ | |
114 | unsigned int clk_sce_ofs; /* CLK_SCE base address */ | |
115 | unsigned int gat_sce_ofs; /* GAT_SCE base address */ | |
116 | int which; /* Bit 5 of CLK_SCE or GAT_SCE */ | |
117 | unsigned int clock_src[3]; /* Current clock sources */ | |
118 | unsigned int gate_src[3]; /* Current gate sources */ | |
119 | spinlock_t spinlock; | |
120 | }; | |
121 | ||
122 | struct dio200_subdev_8255 { | |
123 | unsigned int ofs; /* DIO base offset */ | |
124 | }; | |
125 | ||
126 | struct dio200_subdev_intr { | |
127 | spinlock_t spinlock; | |
128 | unsigned int ofs; | |
129 | unsigned int valid_isns; | |
130 | unsigned int enabled_isns; | |
131 | unsigned int stopcount; | |
132 | bool active:1; | |
7ff7e4c2 IA |
133 | }; |
134 | ||
135 | static inline const struct dio200_layout * | |
136 | dio200_board_layout(const struct dio200_board *board) | |
137 | { | |
138 | return &board->layout; | |
139 | } | |
140 | ||
141 | static inline const struct dio200_layout * | |
142 | dio200_dev_layout(struct comedi_device *dev) | |
143 | { | |
144 | return dio200_board_layout(comedi_board(dev)); | |
145 | } | |
146 | ||
147 | /* | |
148 | * Read 8-bit register. | |
149 | */ | |
150 | static unsigned char dio200_read8(struct comedi_device *dev, | |
151 | unsigned int offset) | |
152 | { | |
153 | const struct dio200_board *thisboard = comedi_board(dev); | |
154 | struct dio200_private *devpriv = dev->private; | |
155 | ||
156 | offset <<= thisboard->mainshift; | |
157 | if (devpriv->io.regtype == io_regtype) | |
158 | return inb(devpriv->io.u.iobase + offset); | |
159 | else | |
160 | return readb(devpriv->io.u.membase + offset); | |
161 | } | |
162 | ||
163 | /* | |
164 | * Write 8-bit register. | |
165 | */ | |
166 | static void dio200_write8(struct comedi_device *dev, unsigned int offset, | |
167 | unsigned char val) | |
168 | { | |
169 | const struct dio200_board *thisboard = comedi_board(dev); | |
170 | struct dio200_private *devpriv = dev->private; | |
171 | ||
172 | offset <<= thisboard->mainshift; | |
173 | if (devpriv->io.regtype == io_regtype) | |
174 | outb(val, devpriv->io.u.iobase + offset); | |
175 | else | |
176 | writeb(val, devpriv->io.u.membase + offset); | |
177 | } | |
178 | ||
179 | /* | |
180 | * Read 32-bit register. | |
181 | */ | |
182 | static unsigned int dio200_read32(struct comedi_device *dev, | |
183 | unsigned int offset) | |
184 | { | |
185 | const struct dio200_board *thisboard = comedi_board(dev); | |
186 | struct dio200_private *devpriv = dev->private; | |
187 | ||
188 | offset <<= thisboard->mainshift; | |
189 | if (devpriv->io.regtype == io_regtype) | |
190 | return inl(devpriv->io.u.iobase + offset); | |
191 | else | |
192 | return readl(devpriv->io.u.membase + offset); | |
193 | } | |
194 | ||
195 | /* | |
196 | * Write 32-bit register. | |
197 | */ | |
198 | static void dio200_write32(struct comedi_device *dev, unsigned int offset, | |
199 | unsigned int val) | |
200 | { | |
201 | const struct dio200_board *thisboard = comedi_board(dev); | |
202 | struct dio200_private *devpriv = dev->private; | |
203 | ||
204 | offset <<= thisboard->mainshift; | |
205 | if (devpriv->io.regtype == io_regtype) | |
206 | outl(val, devpriv->io.u.iobase + offset); | |
207 | else | |
208 | writel(val, devpriv->io.u.membase + offset); | |
209 | } | |
210 | ||
211 | /* | |
212 | * 'insn_bits' function for an 'INTERRUPT' subdevice. | |
213 | */ | |
214 | static int | |
215 | dio200_subdev_intr_insn_bits(struct comedi_device *dev, | |
216 | struct comedi_subdevice *s, | |
217 | struct comedi_insn *insn, unsigned int *data) | |
218 | { | |
219 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
220 | struct dio200_subdev_intr *subpriv = s->private; | |
221 | ||
222 | if (layout->has_int_sce) { | |
223 | /* Just read the interrupt status register. */ | |
224 | data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns; | |
225 | } else { | |
226 | /* No interrupt status register. */ | |
227 | data[0] = 0; | |
228 | } | |
229 | ||
230 | return insn->n; | |
231 | } | |
232 | ||
233 | /* | |
234 | * Called to stop acquisition for an 'INTERRUPT' subdevice. | |
235 | */ | |
236 | static void dio200_stop_intr(struct comedi_device *dev, | |
237 | struct comedi_subdevice *s) | |
238 | { | |
239 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
240 | struct dio200_subdev_intr *subpriv = s->private; | |
241 | ||
242 | subpriv->active = false; | |
243 | subpriv->enabled_isns = 0; | |
244 | if (layout->has_int_sce) | |
245 | dio200_write8(dev, subpriv->ofs, 0); | |
246 | } | |
247 | ||
248 | /* | |
249 | * Called to start acquisition for an 'INTERRUPT' subdevice. | |
250 | */ | |
251 | static int dio200_start_intr(struct comedi_device *dev, | |
252 | struct comedi_subdevice *s) | |
253 | { | |
254 | unsigned int n; | |
255 | unsigned isn_bits; | |
256 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
257 | struct dio200_subdev_intr *subpriv = s->private; | |
258 | struct comedi_cmd *cmd = &s->async->cmd; | |
259 | int retval = 0; | |
260 | ||
22a27048 | 261 | if (cmd->stop_src == TRIG_COUNT && subpriv->stopcount == 0) { |
7ff7e4c2 IA |
262 | /* An empty acquisition! */ |
263 | s->async->events |= COMEDI_CB_EOA; | |
264 | subpriv->active = false; | |
265 | retval = 1; | |
266 | } else { | |
267 | /* Determine interrupt sources to enable. */ | |
268 | isn_bits = 0; | |
269 | if (cmd->chanlist) { | |
270 | for (n = 0; n < cmd->chanlist_len; n++) | |
271 | isn_bits |= (1U << CR_CHAN(cmd->chanlist[n])); | |
272 | } | |
273 | isn_bits &= subpriv->valid_isns; | |
274 | /* Enable interrupt sources. */ | |
275 | subpriv->enabled_isns = isn_bits; | |
276 | if (layout->has_int_sce) | |
277 | dio200_write8(dev, subpriv->ofs, isn_bits); | |
278 | } | |
279 | ||
280 | return retval; | |
281 | } | |
282 | ||
ebe0f68e HS |
283 | static int dio200_inttrig_start_intr(struct comedi_device *dev, |
284 | struct comedi_subdevice *s, | |
285 | unsigned int trig_num) | |
7ff7e4c2 | 286 | { |
ebe0f68e HS |
287 | struct dio200_subdev_intr *subpriv = s->private; |
288 | struct comedi_cmd *cmd = &s->async->cmd; | |
7ff7e4c2 IA |
289 | unsigned long flags; |
290 | int event = 0; | |
291 | ||
ebe0f68e | 292 | if (trig_num != cmd->start_arg) |
7ff7e4c2 IA |
293 | return -EINVAL; |
294 | ||
7ff7e4c2 IA |
295 | spin_lock_irqsave(&subpriv->spinlock, flags); |
296 | s->async->inttrig = NULL; | |
297 | if (subpriv->active) | |
298 | event = dio200_start_intr(dev, s); | |
299 | ||
300 | spin_unlock_irqrestore(&subpriv->spinlock, flags); | |
301 | ||
302 | if (event) | |
303 | comedi_event(dev, s); | |
304 | ||
305 | return 1; | |
306 | } | |
307 | ||
308 | static void dio200_read_scan_intr(struct comedi_device *dev, | |
309 | struct comedi_subdevice *s, | |
310 | unsigned int triggered) | |
311 | { | |
312 | struct dio200_subdev_intr *subpriv = s->private; | |
22a27048 | 313 | struct comedi_cmd *cmd = &s->async->cmd; |
7ff7e4c2 | 314 | unsigned short val; |
71ba7506 | 315 | unsigned int n, ch; |
7ff7e4c2 IA |
316 | |
317 | val = 0; | |
71ba7506 HS |
318 | for (n = 0; n < cmd->chanlist_len; n++) { |
319 | ch = CR_CHAN(cmd->chanlist[n]); | |
7ff7e4c2 IA |
320 | if (triggered & (1U << ch)) |
321 | val |= (1U << n); | |
322 | } | |
323 | /* Write the scan to the buffer. */ | |
3672effd | 324 | if (comedi_buf_put(s, val)) { |
7ff7e4c2 IA |
325 | s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS); |
326 | } else { | |
327 | /* Error! Stop acquisition. */ | |
328 | dio200_stop_intr(dev, s); | |
329 | s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW; | |
330 | comedi_error(dev, "buffer overflow"); | |
331 | } | |
332 | ||
333 | /* Check for end of acquisition. */ | |
22a27048 | 334 | if (cmd->stop_src == TRIG_COUNT) { |
7ff7e4c2 IA |
335 | if (subpriv->stopcount > 0) { |
336 | subpriv->stopcount--; | |
337 | if (subpriv->stopcount == 0) { | |
338 | s->async->events |= COMEDI_CB_EOA; | |
339 | dio200_stop_intr(dev, s); | |
340 | } | |
341 | } | |
342 | } | |
343 | } | |
344 | ||
345 | /* | |
346 | * This is called from the interrupt service routine to handle a read | |
347 | * scan on an 'INTERRUPT' subdevice. | |
348 | */ | |
349 | static int dio200_handle_read_intr(struct comedi_device *dev, | |
350 | struct comedi_subdevice *s) | |
351 | { | |
352 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
353 | struct dio200_subdev_intr *subpriv = s->private; | |
354 | unsigned triggered; | |
355 | unsigned intstat; | |
356 | unsigned cur_enabled; | |
357 | unsigned int oldevents; | |
358 | unsigned long flags; | |
359 | ||
360 | triggered = 0; | |
361 | ||
362 | spin_lock_irqsave(&subpriv->spinlock, flags); | |
363 | oldevents = s->async->events; | |
364 | if (layout->has_int_sce) { | |
365 | /* | |
366 | * Collect interrupt sources that have triggered and disable | |
367 | * them temporarily. Loop around until no extra interrupt | |
368 | * sources have triggered, at which point, the valid part of | |
369 | * the interrupt status register will read zero, clearing the | |
370 | * cause of the interrupt. | |
371 | * | |
372 | * Mask off interrupt sources already seen to avoid infinite | |
373 | * loop in case of misconfiguration. | |
374 | */ | |
375 | cur_enabled = subpriv->enabled_isns; | |
376 | while ((intstat = (dio200_read8(dev, subpriv->ofs) & | |
377 | subpriv->valid_isns & ~triggered)) != 0) { | |
378 | triggered |= intstat; | |
379 | cur_enabled &= ~triggered; | |
380 | dio200_write8(dev, subpriv->ofs, cur_enabled); | |
381 | } | |
382 | } else { | |
383 | /* | |
384 | * No interrupt status register. Assume the single interrupt | |
385 | * source has triggered. | |
386 | */ | |
387 | triggered = subpriv->enabled_isns; | |
388 | } | |
389 | ||
390 | if (triggered) { | |
391 | /* | |
392 | * Some interrupt sources have triggered and have been | |
393 | * temporarily disabled to clear the cause of the interrupt. | |
394 | * | |
395 | * Reenable them NOW to minimize the time they are disabled. | |
396 | */ | |
397 | cur_enabled = subpriv->enabled_isns; | |
398 | if (layout->has_int_sce) | |
399 | dio200_write8(dev, subpriv->ofs, cur_enabled); | |
400 | ||
401 | if (subpriv->active) { | |
402 | /* | |
403 | * The command is still active. | |
404 | * | |
405 | * Ignore interrupt sources that the command isn't | |
406 | * interested in (just in case there's a race | |
407 | * condition). | |
408 | */ | |
409 | if (triggered & subpriv->enabled_isns) | |
410 | /* Collect scan data. */ | |
411 | dio200_read_scan_intr(dev, s, triggered); | |
412 | } | |
413 | } | |
414 | spin_unlock_irqrestore(&subpriv->spinlock, flags); | |
415 | ||
416 | if (oldevents != s->async->events) | |
417 | comedi_event(dev, s); | |
418 | ||
419 | return (triggered != 0); | |
420 | } | |
421 | ||
422 | /* | |
423 | * 'cancel' function for an 'INTERRUPT' subdevice. | |
424 | */ | |
425 | static int dio200_subdev_intr_cancel(struct comedi_device *dev, | |
426 | struct comedi_subdevice *s) | |
427 | { | |
428 | struct dio200_subdev_intr *subpriv = s->private; | |
429 | unsigned long flags; | |
430 | ||
431 | spin_lock_irqsave(&subpriv->spinlock, flags); | |
432 | if (subpriv->active) | |
433 | dio200_stop_intr(dev, s); | |
434 | ||
435 | spin_unlock_irqrestore(&subpriv->spinlock, flags); | |
436 | ||
437 | return 0; | |
438 | } | |
439 | ||
440 | /* | |
441 | * 'do_cmdtest' function for an 'INTERRUPT' subdevice. | |
442 | */ | |
443 | static int | |
444 | dio200_subdev_intr_cmdtest(struct comedi_device *dev, | |
445 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
446 | { | |
447 | int err = 0; | |
448 | ||
449 | /* Step 1 : check if triggers are trivially valid */ | |
450 | ||
451 | err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT); | |
452 | err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); | |
453 | err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW); | |
454 | err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
455 | err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
456 | ||
457 | if (err) | |
458 | return 1; | |
459 | ||
460 | /* Step 2a : make sure trigger sources are unique */ | |
461 | ||
462 | err |= cfc_check_trigger_is_unique(cmd->start_src); | |
463 | err |= cfc_check_trigger_is_unique(cmd->stop_src); | |
464 | ||
465 | /* Step 2b : and mutually compatible */ | |
466 | ||
467 | if (err) | |
468 | return 2; | |
469 | ||
470 | /* Step 3: check if arguments are trivially valid */ | |
471 | ||
472 | err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); | |
473 | err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
474 | err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); | |
475 | err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); | |
476 | ||
477 | switch (cmd->stop_src) { | |
478 | case TRIG_COUNT: | |
479 | /* any count allowed */ | |
480 | break; | |
481 | case TRIG_NONE: | |
482 | err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); | |
483 | break; | |
484 | default: | |
485 | break; | |
486 | } | |
487 | ||
488 | if (err) | |
489 | return 3; | |
490 | ||
491 | /* step 4: fix up any arguments */ | |
492 | ||
493 | /* if (err) return 4; */ | |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
498 | /* | |
499 | * 'do_cmd' function for an 'INTERRUPT' subdevice. | |
500 | */ | |
501 | static int dio200_subdev_intr_cmd(struct comedi_device *dev, | |
502 | struct comedi_subdevice *s) | |
503 | { | |
504 | struct comedi_cmd *cmd = &s->async->cmd; | |
505 | struct dio200_subdev_intr *subpriv = s->private; | |
506 | unsigned long flags; | |
507 | int event = 0; | |
508 | ||
509 | spin_lock_irqsave(&subpriv->spinlock, flags); | |
42a5a418 | 510 | subpriv->active = true; |
7ff7e4c2 IA |
511 | |
512 | /* Set up end of acquisition. */ | |
22a27048 | 513 | if (cmd->stop_src == TRIG_COUNT) |
7ff7e4c2 | 514 | subpriv->stopcount = cmd->stop_arg; |
22a27048 | 515 | else /* TRIG_NONE */ |
7ff7e4c2 | 516 | subpriv->stopcount = 0; |
7ff7e4c2 | 517 | |
ebe0f68e | 518 | if (cmd->start_src == TRIG_INT) |
7ff7e4c2 | 519 | s->async->inttrig = dio200_inttrig_start_intr; |
ebe0f68e | 520 | else /* TRIG_NOW */ |
7ff7e4c2 | 521 | event = dio200_start_intr(dev, s); |
ebe0f68e | 522 | |
7ff7e4c2 IA |
523 | spin_unlock_irqrestore(&subpriv->spinlock, flags); |
524 | ||
525 | if (event) | |
526 | comedi_event(dev, s); | |
527 | ||
528 | return 0; | |
529 | } | |
530 | ||
531 | /* | |
532 | * This function initializes an 'INTERRUPT' subdevice. | |
533 | */ | |
534 | static int | |
535 | dio200_subdev_intr_init(struct comedi_device *dev, struct comedi_subdevice *s, | |
536 | unsigned int offset, unsigned valid_isns) | |
537 | { | |
538 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
539 | struct dio200_subdev_intr *subpriv; | |
540 | ||
0480bcb9 | 541 | subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); |
7ff7e4c2 IA |
542 | if (!subpriv) |
543 | return -ENOMEM; | |
544 | ||
545 | subpriv->ofs = offset; | |
546 | subpriv->valid_isns = valid_isns; | |
547 | spin_lock_init(&subpriv->spinlock); | |
548 | ||
549 | if (layout->has_int_sce) | |
550 | /* Disable interrupt sources. */ | |
551 | dio200_write8(dev, subpriv->ofs, 0); | |
552 | ||
7ff7e4c2 IA |
553 | s->type = COMEDI_SUBD_DI; |
554 | s->subdev_flags = SDF_READABLE | SDF_CMD_READ; | |
555 | if (layout->has_int_sce) { | |
556 | s->n_chan = DIO200_MAX_ISNS; | |
557 | s->len_chanlist = DIO200_MAX_ISNS; | |
558 | } else { | |
559 | /* No interrupt source register. Support single channel. */ | |
560 | s->n_chan = 1; | |
561 | s->len_chanlist = 1; | |
562 | } | |
563 | s->range_table = &range_digital; | |
564 | s->maxdata = 1; | |
565 | s->insn_bits = dio200_subdev_intr_insn_bits; | |
566 | s->do_cmdtest = dio200_subdev_intr_cmdtest; | |
567 | s->do_cmd = dio200_subdev_intr_cmd; | |
568 | s->cancel = dio200_subdev_intr_cancel; | |
569 | ||
570 | return 0; | |
571 | } | |
572 | ||
7ff7e4c2 IA |
573 | /* |
574 | * Interrupt service routine. | |
575 | */ | |
576 | static irqreturn_t dio200_interrupt(int irq, void *d) | |
577 | { | |
578 | struct comedi_device *dev = d; | |
579 | struct dio200_private *devpriv = dev->private; | |
580 | struct comedi_subdevice *s; | |
581 | int handled; | |
582 | ||
583 | if (!dev->attached) | |
584 | return IRQ_NONE; | |
585 | ||
586 | if (devpriv->intr_sd >= 0) { | |
587 | s = &dev->subdevices[devpriv->intr_sd]; | |
588 | handled = dio200_handle_read_intr(dev, s); | |
589 | } else { | |
590 | handled = 0; | |
591 | } | |
592 | ||
593 | return IRQ_RETVAL(handled); | |
594 | } | |
595 | ||
596 | /* | |
597 | * Read an '8254' counter subdevice channel. | |
598 | */ | |
599 | static unsigned int | |
600 | dio200_subdev_8254_read_chan(struct comedi_device *dev, | |
601 | struct comedi_subdevice *s, unsigned int chan) | |
602 | { | |
603 | struct dio200_subdev_8254 *subpriv = s->private; | |
604 | unsigned int val; | |
605 | ||
606 | /* latch counter */ | |
607 | val = chan << 6; | |
608 | dio200_write8(dev, subpriv->ofs + i8254_control_reg, val); | |
609 | /* read lsb, msb */ | |
610 | val = dio200_read8(dev, subpriv->ofs + chan); | |
611 | val += dio200_read8(dev, subpriv->ofs + chan) << 8; | |
612 | return val; | |
613 | } | |
614 | ||
615 | /* | |
616 | * Write an '8254' subdevice channel. | |
617 | */ | |
618 | static void | |
619 | dio200_subdev_8254_write_chan(struct comedi_device *dev, | |
620 | struct comedi_subdevice *s, unsigned int chan, | |
621 | unsigned int count) | |
622 | { | |
623 | struct dio200_subdev_8254 *subpriv = s->private; | |
624 | ||
625 | /* write lsb, msb */ | |
626 | dio200_write8(dev, subpriv->ofs + chan, count & 0xff); | |
627 | dio200_write8(dev, subpriv->ofs + chan, (count >> 8) & 0xff); | |
628 | } | |
629 | ||
630 | /* | |
631 | * Set mode of an '8254' subdevice channel. | |
632 | */ | |
633 | static void | |
634 | dio200_subdev_8254_set_mode(struct comedi_device *dev, | |
635 | struct comedi_subdevice *s, unsigned int chan, | |
636 | unsigned int mode) | |
637 | { | |
638 | struct dio200_subdev_8254 *subpriv = s->private; | |
639 | unsigned int byte; | |
640 | ||
641 | byte = chan << 6; | |
642 | byte |= 0x30; /* access order: lsb, msb */ | |
643 | byte |= (mode & 0xf); /* counter mode and BCD|binary */ | |
644 | dio200_write8(dev, subpriv->ofs + i8254_control_reg, byte); | |
645 | } | |
646 | ||
647 | /* | |
648 | * Read status byte of an '8254' counter subdevice channel. | |
649 | */ | |
650 | static unsigned int | |
651 | dio200_subdev_8254_status(struct comedi_device *dev, | |
652 | struct comedi_subdevice *s, unsigned int chan) | |
653 | { | |
654 | struct dio200_subdev_8254 *subpriv = s->private; | |
655 | ||
656 | /* latch status */ | |
657 | dio200_write8(dev, subpriv->ofs + i8254_control_reg, | |
658 | 0xe0 | (2 << chan)); | |
659 | /* read status */ | |
660 | return dio200_read8(dev, subpriv->ofs + chan); | |
661 | } | |
662 | ||
663 | /* | |
664 | * Handle 'insn_read' for an '8254' counter subdevice. | |
665 | */ | |
666 | static int | |
667 | dio200_subdev_8254_read(struct comedi_device *dev, struct comedi_subdevice *s, | |
668 | struct comedi_insn *insn, unsigned int *data) | |
669 | { | |
670 | struct dio200_subdev_8254 *subpriv = s->private; | |
671 | int chan = CR_CHAN(insn->chanspec); | |
672 | unsigned int n; | |
673 | unsigned long flags; | |
674 | ||
675 | for (n = 0; n < insn->n; n++) { | |
676 | spin_lock_irqsave(&subpriv->spinlock, flags); | |
677 | data[n] = dio200_subdev_8254_read_chan(dev, s, chan); | |
678 | spin_unlock_irqrestore(&subpriv->spinlock, flags); | |
679 | } | |
680 | return insn->n; | |
681 | } | |
682 | ||
683 | /* | |
684 | * Handle 'insn_write' for an '8254' counter subdevice. | |
685 | */ | |
686 | static int | |
687 | dio200_subdev_8254_write(struct comedi_device *dev, struct comedi_subdevice *s, | |
688 | struct comedi_insn *insn, unsigned int *data) | |
689 | { | |
690 | struct dio200_subdev_8254 *subpriv = s->private; | |
691 | int chan = CR_CHAN(insn->chanspec); | |
692 | unsigned int n; | |
693 | unsigned long flags; | |
694 | ||
695 | for (n = 0; n < insn->n; n++) { | |
696 | spin_lock_irqsave(&subpriv->spinlock, flags); | |
697 | dio200_subdev_8254_write_chan(dev, s, chan, data[n]); | |
698 | spin_unlock_irqrestore(&subpriv->spinlock, flags); | |
699 | } | |
700 | return insn->n; | |
701 | } | |
702 | ||
703 | /* | |
704 | * Set gate source for an '8254' counter subdevice channel. | |
705 | */ | |
706 | static int | |
707 | dio200_subdev_8254_set_gate_src(struct comedi_device *dev, | |
708 | struct comedi_subdevice *s, | |
709 | unsigned int counter_number, | |
710 | unsigned int gate_src) | |
711 | { | |
712 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
713 | struct dio200_subdev_8254 *subpriv = s->private; | |
714 | unsigned char byte; | |
715 | ||
716 | if (!layout->has_clk_gat_sce) | |
717 | return -1; | |
718 | if (counter_number > 2) | |
719 | return -1; | |
720 | if (gate_src > (layout->has_enhancements ? 31 : 7)) | |
721 | return -1; | |
722 | ||
723 | subpriv->gate_src[counter_number] = gate_src; | |
724 | byte = gat_sce(subpriv->which, counter_number, gate_src); | |
725 | dio200_write8(dev, subpriv->gat_sce_ofs, byte); | |
726 | ||
727 | return 0; | |
728 | } | |
729 | ||
730 | /* | |
731 | * Get gate source for an '8254' counter subdevice channel. | |
732 | */ | |
733 | static int | |
734 | dio200_subdev_8254_get_gate_src(struct comedi_device *dev, | |
735 | struct comedi_subdevice *s, | |
736 | unsigned int counter_number) | |
737 | { | |
738 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
739 | struct dio200_subdev_8254 *subpriv = s->private; | |
740 | ||
741 | if (!layout->has_clk_gat_sce) | |
742 | return -1; | |
743 | if (counter_number > 2) | |
744 | return -1; | |
745 | ||
746 | return subpriv->gate_src[counter_number]; | |
747 | } | |
748 | ||
749 | /* | |
750 | * Set clock source for an '8254' counter subdevice channel. | |
751 | */ | |
752 | static int | |
753 | dio200_subdev_8254_set_clock_src(struct comedi_device *dev, | |
754 | struct comedi_subdevice *s, | |
755 | unsigned int counter_number, | |
756 | unsigned int clock_src) | |
757 | { | |
758 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
759 | struct dio200_subdev_8254 *subpriv = s->private; | |
760 | unsigned char byte; | |
761 | ||
762 | if (!layout->has_clk_gat_sce) | |
763 | return -1; | |
764 | if (counter_number > 2) | |
765 | return -1; | |
766 | if (clock_src > (layout->has_enhancements ? 31 : 7)) | |
767 | return -1; | |
768 | ||
769 | subpriv->clock_src[counter_number] = clock_src; | |
770 | byte = clk_sce(subpriv->which, counter_number, clock_src); | |
771 | dio200_write8(dev, subpriv->clk_sce_ofs, byte); | |
772 | ||
773 | return 0; | |
774 | } | |
775 | ||
776 | /* | |
777 | * Get clock source for an '8254' counter subdevice channel. | |
778 | */ | |
779 | static int | |
780 | dio200_subdev_8254_get_clock_src(struct comedi_device *dev, | |
781 | struct comedi_subdevice *s, | |
782 | unsigned int counter_number, | |
783 | unsigned int *period_ns) | |
784 | { | |
785 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
786 | struct dio200_subdev_8254 *subpriv = s->private; | |
787 | unsigned clock_src; | |
788 | ||
789 | if (!layout->has_clk_gat_sce) | |
790 | return -1; | |
791 | if (counter_number > 2) | |
792 | return -1; | |
793 | ||
794 | clock_src = subpriv->clock_src[counter_number]; | |
795 | *period_ns = clock_period[clock_src]; | |
796 | return clock_src; | |
797 | } | |
798 | ||
799 | /* | |
800 | * Handle 'insn_config' for an '8254' counter subdevice. | |
801 | */ | |
802 | static int | |
803 | dio200_subdev_8254_config(struct comedi_device *dev, struct comedi_subdevice *s, | |
804 | struct comedi_insn *insn, unsigned int *data) | |
805 | { | |
806 | struct dio200_subdev_8254 *subpriv = s->private; | |
807 | int ret = 0; | |
808 | int chan = CR_CHAN(insn->chanspec); | |
809 | unsigned long flags; | |
810 | ||
811 | spin_lock_irqsave(&subpriv->spinlock, flags); | |
812 | switch (data[0]) { | |
813 | case INSN_CONFIG_SET_COUNTER_MODE: | |
6e2954e8 | 814 | if (data[1] > (I8254_MODE5 | I8254_BCD)) |
7ff7e4c2 IA |
815 | ret = -EINVAL; |
816 | else | |
817 | dio200_subdev_8254_set_mode(dev, s, chan, data[1]); | |
818 | break; | |
819 | case INSN_CONFIG_8254_READ_STATUS: | |
820 | data[1] = dio200_subdev_8254_status(dev, s, chan); | |
821 | break; | |
822 | case INSN_CONFIG_SET_GATE_SRC: | |
823 | ret = dio200_subdev_8254_set_gate_src(dev, s, chan, data[2]); | |
824 | if (ret < 0) | |
825 | ret = -EINVAL; | |
826 | break; | |
827 | case INSN_CONFIG_GET_GATE_SRC: | |
828 | ret = dio200_subdev_8254_get_gate_src(dev, s, chan); | |
829 | if (ret < 0) { | |
830 | ret = -EINVAL; | |
831 | break; | |
832 | } | |
833 | data[2] = ret; | |
834 | break; | |
835 | case INSN_CONFIG_SET_CLOCK_SRC: | |
836 | ret = dio200_subdev_8254_set_clock_src(dev, s, chan, data[1]); | |
837 | if (ret < 0) | |
838 | ret = -EINVAL; | |
839 | break; | |
840 | case INSN_CONFIG_GET_CLOCK_SRC: | |
841 | ret = dio200_subdev_8254_get_clock_src(dev, s, chan, &data[2]); | |
842 | if (ret < 0) { | |
843 | ret = -EINVAL; | |
844 | break; | |
845 | } | |
846 | data[1] = ret; | |
847 | break; | |
848 | default: | |
849 | ret = -EINVAL; | |
850 | break; | |
851 | } | |
852 | spin_unlock_irqrestore(&subpriv->spinlock, flags); | |
853 | return ret < 0 ? ret : insn->n; | |
854 | } | |
855 | ||
856 | /* | |
857 | * This function initializes an '8254' counter subdevice. | |
858 | */ | |
859 | static int | |
860 | dio200_subdev_8254_init(struct comedi_device *dev, struct comedi_subdevice *s, | |
861 | unsigned int offset) | |
862 | { | |
863 | const struct dio200_layout *layout = dio200_dev_layout(dev); | |
864 | struct dio200_subdev_8254 *subpriv; | |
865 | unsigned int chan; | |
866 | ||
0480bcb9 | 867 | subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); |
7ff7e4c2 IA |
868 | if (!subpriv) |
869 | return -ENOMEM; | |
870 | ||
7ff7e4c2 IA |
871 | s->type = COMEDI_SUBD_COUNTER; |
872 | s->subdev_flags = SDF_WRITABLE | SDF_READABLE; | |
873 | s->n_chan = 3; | |
874 | s->maxdata = 0xFFFF; | |
875 | s->insn_read = dio200_subdev_8254_read; | |
876 | s->insn_write = dio200_subdev_8254_write; | |
877 | s->insn_config = dio200_subdev_8254_config; | |
878 | ||
879 | spin_lock_init(&subpriv->spinlock); | |
880 | subpriv->ofs = offset; | |
881 | if (layout->has_clk_gat_sce) { | |
882 | /* Derive CLK_SCE and GAT_SCE register offsets from | |
883 | * 8254 offset. */ | |
884 | subpriv->clk_sce_ofs = DIO200_XCLK_SCE + (offset >> 3); | |
885 | subpriv->gat_sce_ofs = DIO200_XGAT_SCE + (offset >> 3); | |
886 | subpriv->which = (offset >> 2) & 1; | |
887 | } | |
888 | ||
889 | /* Initialize channels. */ | |
890 | for (chan = 0; chan < 3; chan++) { | |
891 | dio200_subdev_8254_set_mode(dev, s, chan, | |
892 | I8254_MODE0 | I8254_BINARY); | |
893 | if (layout->has_clk_gat_sce) { | |
894 | /* Gate source 0 is VCC (logic 1). */ | |
895 | dio200_subdev_8254_set_gate_src(dev, s, chan, 0); | |
896 | /* Clock source 0 is the dedicated clock input. */ | |
897 | dio200_subdev_8254_set_clock_src(dev, s, chan, 0); | |
898 | } | |
899 | } | |
900 | ||
901 | return 0; | |
902 | } | |
903 | ||
7ff7e4c2 IA |
904 | /* |
905 | * This function sets I/O directions for an '8255' DIO subdevice. | |
906 | */ | |
907 | static void dio200_subdev_8255_set_dir(struct comedi_device *dev, | |
908 | struct comedi_subdevice *s) | |
909 | { | |
910 | struct dio200_subdev_8255 *subpriv = s->private; | |
911 | int config; | |
912 | ||
913 | config = CR_CW; | |
914 | /* 1 in io_bits indicates output, 1 in config indicates input */ | |
915 | if (!(s->io_bits & 0x0000ff)) | |
916 | config |= CR_A_IO; | |
917 | if (!(s->io_bits & 0x00ff00)) | |
918 | config |= CR_B_IO; | |
919 | if (!(s->io_bits & 0x0f0000)) | |
920 | config |= CR_C_LO_IO; | |
921 | if (!(s->io_bits & 0xf00000)) | |
922 | config |= CR_C_HI_IO; | |
923 | dio200_write8(dev, subpriv->ofs + 3, config); | |
924 | } | |
925 | ||
7ff7e4c2 IA |
926 | static int dio200_subdev_8255_bits(struct comedi_device *dev, |
927 | struct comedi_subdevice *s, | |
b3ff824a HS |
928 | struct comedi_insn *insn, |
929 | unsigned int *data) | |
7ff7e4c2 IA |
930 | { |
931 | struct dio200_subdev_8255 *subpriv = s->private; | |
b3ff824a HS |
932 | unsigned int mask; |
933 | unsigned int val; | |
7ff7e4c2 | 934 | |
b3ff824a HS |
935 | mask = comedi_dio_update_state(s, data); |
936 | if (mask) { | |
937 | if (mask & 0xff) | |
7ff7e4c2 | 938 | dio200_write8(dev, subpriv->ofs, s->state & 0xff); |
b3ff824a | 939 | if (mask & 0xff00) |
7ff7e4c2 IA |
940 | dio200_write8(dev, subpriv->ofs + 1, |
941 | (s->state >> 8) & 0xff); | |
b3ff824a | 942 | if (mask & 0xff0000) |
7ff7e4c2 IA |
943 | dio200_write8(dev, subpriv->ofs + 2, |
944 | (s->state >> 16) & 0xff); | |
945 | } | |
b3ff824a HS |
946 | |
947 | val = dio200_read8(dev, subpriv->ofs); | |
948 | val |= dio200_read8(dev, subpriv->ofs + 1) << 8; | |
949 | val |= dio200_read8(dev, subpriv->ofs + 2) << 16; | |
950 | ||
951 | data[1] = val; | |
952 | ||
953 | return insn->n; | |
7ff7e4c2 IA |
954 | } |
955 | ||
956 | /* | |
957 | * Handle 'insn_config' for an '8255' DIO subdevice. | |
958 | */ | |
959 | static int dio200_subdev_8255_config(struct comedi_device *dev, | |
960 | struct comedi_subdevice *s, | |
961 | struct comedi_insn *insn, | |
962 | unsigned int *data) | |
963 | { | |
5dacadcc | 964 | unsigned int chan = CR_CHAN(insn->chanspec); |
7ff7e4c2 | 965 | unsigned int mask; |
5dacadcc HS |
966 | int ret; |
967 | ||
968 | if (chan < 8) | |
969 | mask = 0x0000ff; | |
970 | else if (chan < 16) | |
971 | mask = 0x00ff00; | |
972 | else if (chan < 20) | |
973 | mask = 0x0f0000; | |
7ff7e4c2 | 974 | else |
5dacadcc HS |
975 | mask = 0xf00000; |
976 | ||
977 | ret = comedi_dio_insn_config(dev, s, insn, data, mask); | |
978 | if (ret) | |
979 | return ret; | |
980 | ||
7ff7e4c2 | 981 | dio200_subdev_8255_set_dir(dev, s); |
5dacadcc HS |
982 | |
983 | return insn->n; | |
7ff7e4c2 IA |
984 | } |
985 | ||
986 | /* | |
987 | * This function initializes an '8255' DIO subdevice. | |
988 | * | |
989 | * offset is the offset to the 8255 chip. | |
990 | */ | |
991 | static int dio200_subdev_8255_init(struct comedi_device *dev, | |
992 | struct comedi_subdevice *s, | |
993 | unsigned int offset) | |
994 | { | |
995 | struct dio200_subdev_8255 *subpriv; | |
996 | ||
0480bcb9 | 997 | subpriv = comedi_alloc_spriv(s, sizeof(*subpriv)); |
7ff7e4c2 IA |
998 | if (!subpriv) |
999 | return -ENOMEM; | |
588ba6dc | 1000 | |
7ff7e4c2 | 1001 | subpriv->ofs = offset; |
588ba6dc | 1002 | |
7ff7e4c2 IA |
1003 | s->type = COMEDI_SUBD_DIO; |
1004 | s->subdev_flags = SDF_READABLE | SDF_WRITABLE; | |
1005 | s->n_chan = 24; | |
1006 | s->range_table = &range_digital; | |
1007 | s->maxdata = 1; | |
1008 | s->insn_bits = dio200_subdev_8255_bits; | |
1009 | s->insn_config = dio200_subdev_8255_config; | |
7ff7e4c2 IA |
1010 | dio200_subdev_8255_set_dir(dev, s); |
1011 | return 0; | |
1012 | } | |
1013 | ||
7ff7e4c2 IA |
1014 | /* |
1015 | * Handle 'insn_read' for a timer subdevice. | |
1016 | */ | |
1017 | static int dio200_subdev_timer_read(struct comedi_device *dev, | |
1018 | struct comedi_subdevice *s, | |
1019 | struct comedi_insn *insn, | |
1020 | unsigned int *data) | |
1021 | { | |
1022 | unsigned int n; | |
1023 | ||
1024 | for (n = 0; n < insn->n; n++) | |
1025 | data[n] = dio200_read32(dev, DIO200_TS_COUNT); | |
1026 | return n; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | * Reset timer subdevice. | |
1031 | */ | |
1032 | static void dio200_subdev_timer_reset(struct comedi_device *dev, | |
1033 | struct comedi_subdevice *s) | |
1034 | { | |
1035 | unsigned int clock; | |
1036 | ||
1037 | clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; | |
1038 | dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET); | |
1039 | dio200_write32(dev, DIO200_TS_CONFIG, clock); | |
1040 | } | |
1041 | ||
1042 | /* | |
1043 | * Get timer subdevice clock source and period. | |
1044 | */ | |
1045 | static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev, | |
1046 | struct comedi_subdevice *s, | |
1047 | unsigned int *src, | |
1048 | unsigned int *period) | |
1049 | { | |
1050 | unsigned int clk; | |
1051 | ||
1052 | clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK; | |
1053 | *src = clk; | |
1054 | *period = (clk < ARRAY_SIZE(ts_clock_period)) ? | |
1055 | ts_clock_period[clk] : 0; | |
1056 | } | |
1057 | ||
1058 | /* | |
1059 | * Set timer subdevice clock source. | |
1060 | */ | |
1061 | static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev, | |
1062 | struct comedi_subdevice *s, | |
1063 | unsigned int src) | |
1064 | { | |
1065 | if (src > TS_CONFIG_MAX_CLK_SRC) | |
1066 | return -EINVAL; | |
1067 | dio200_write32(dev, DIO200_TS_CONFIG, src); | |
1068 | return 0; | |
1069 | } | |
1070 | ||
1071 | /* | |
1072 | * Handle 'insn_config' for a timer subdevice. | |
1073 | */ | |
1074 | static int dio200_subdev_timer_config(struct comedi_device *dev, | |
1075 | struct comedi_subdevice *s, | |
1076 | struct comedi_insn *insn, | |
1077 | unsigned int *data) | |
1078 | { | |
1079 | int ret = 0; | |
1080 | ||
1081 | switch (data[0]) { | |
1082 | case INSN_CONFIG_RESET: | |
1083 | dio200_subdev_timer_reset(dev, s); | |
1084 | break; | |
1085 | case INSN_CONFIG_SET_CLOCK_SRC: | |
1086 | ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]); | |
1087 | if (ret < 0) | |
1088 | ret = -EINVAL; | |
1089 | break; | |
1090 | case INSN_CONFIG_GET_CLOCK_SRC: | |
1091 | dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]); | |
1092 | break; | |
1093 | default: | |
1094 | ret = -EINVAL; | |
1095 | break; | |
1096 | } | |
1097 | return ret < 0 ? ret : insn->n; | |
1098 | } | |
1099 | ||
1100 | /* | |
1101 | * This function initializes a timer subdevice. | |
1102 | * | |
1103 | * Uses the timestamp timer registers. There is only one timestamp timer. | |
1104 | */ | |
1105 | static int dio200_subdev_timer_init(struct comedi_device *dev, | |
1106 | struct comedi_subdevice *s) | |
1107 | { | |
1108 | s->type = COMEDI_SUBD_TIMER; | |
1109 | s->subdev_flags = SDF_READABLE | SDF_LSAMPL; | |
1110 | s->n_chan = 1; | |
1111 | s->maxdata = 0xFFFFFFFF; | |
1112 | s->insn_read = dio200_subdev_timer_read; | |
1113 | s->insn_config = dio200_subdev_timer_config; | |
1114 | return 0; | |
1115 | } | |
1116 | ||
7ff7e4c2 IA |
1117 | void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val) |
1118 | { | |
1119 | dio200_write8(dev, DIO200_ENHANCE, val); | |
1120 | } | |
1121 | EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance); | |
1122 | ||
1123 | int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq, | |
1124 | unsigned long req_irq_flags) | |
1125 | { | |
1126 | const struct dio200_board *thisboard = comedi_board(dev); | |
1127 | struct dio200_private *devpriv = dev->private; | |
1128 | const struct dio200_layout *layout = dio200_board_layout(thisboard); | |
1129 | struct comedi_subdevice *s; | |
1130 | int sdx; | |
1131 | unsigned int n; | |
1132 | int ret; | |
1133 | ||
1134 | devpriv->intr_sd = -1; | |
1135 | ||
1136 | ret = comedi_alloc_subdevices(dev, layout->n_subdevs); | |
1137 | if (ret) | |
1138 | return ret; | |
1139 | ||
1140 | for (n = 0; n < dev->n_subdevices; n++) { | |
1141 | s = &dev->subdevices[n]; | |
1142 | switch (layout->sdtype[n]) { | |
1143 | case sd_8254: | |
1144 | /* counter subdevice (8254) */ | |
1145 | ret = dio200_subdev_8254_init(dev, s, | |
1146 | layout->sdinfo[n]); | |
1147 | if (ret < 0) | |
1148 | return ret; | |
1149 | break; | |
1150 | case sd_8255: | |
1151 | /* digital i/o subdevice (8255) */ | |
1152 | ret = dio200_subdev_8255_init(dev, s, | |
1153 | layout->sdinfo[n]); | |
1154 | if (ret < 0) | |
1155 | return ret; | |
1156 | break; | |
1157 | case sd_intr: | |
1158 | /* 'INTERRUPT' subdevice */ | |
1159 | if (irq) { | |
1160 | ret = dio200_subdev_intr_init(dev, s, | |
1161 | DIO200_INT_SCE, | |
1162 | layout->sdinfo[n] | |
1163 | ); | |
1164 | if (ret < 0) | |
1165 | return ret; | |
1166 | devpriv->intr_sd = n; | |
1167 | } else { | |
1168 | s->type = COMEDI_SUBD_UNUSED; | |
1169 | } | |
1170 | break; | |
1171 | case sd_timer: | |
1172 | ret = dio200_subdev_timer_init(dev, s); | |
1173 | if (ret < 0) | |
1174 | return ret; | |
1175 | break; | |
1176 | default: | |
1177 | s->type = COMEDI_SUBD_UNUSED; | |
1178 | break; | |
1179 | } | |
1180 | } | |
1181 | sdx = devpriv->intr_sd; | |
1182 | if (sdx >= 0 && sdx < dev->n_subdevices) | |
1183 | dev->read_subdev = &dev->subdevices[sdx]; | |
1184 | if (irq) { | |
1185 | if (request_irq(irq, dio200_interrupt, req_irq_flags, | |
1186 | dev->board_name, dev) >= 0) { | |
1187 | dev->irq = irq; | |
1188 | } else { | |
1189 | dev_warn(dev->class_dev, | |
1190 | "warning! irq %u unavailable!\n", irq); | |
1191 | } | |
1192 | } | |
c93999c2 | 1193 | |
7ff7e4c2 IA |
1194 | return 0; |
1195 | } | |
1196 | EXPORT_SYMBOL_GPL(amplc_dio200_common_attach); | |
1197 | ||
1198 | void amplc_dio200_common_detach(struct comedi_device *dev) | |
1199 | { | |
1200 | const struct dio200_board *thisboard = comedi_board(dev); | |
1201 | struct dio200_private *devpriv = dev->private; | |
7ff7e4c2 IA |
1202 | |
1203 | if (!thisboard || !devpriv) | |
1204 | return; | |
1205 | if (dev->irq) | |
1206 | free_irq(dev->irq, dev); | |
7ff7e4c2 IA |
1207 | } |
1208 | EXPORT_SYMBOL_GPL(amplc_dio200_common_detach); | |
1209 | ||
1210 | static int __init amplc_dio200_common_init(void) | |
1211 | { | |
1212 | return 0; | |
1213 | } | |
1214 | module_init(amplc_dio200_common_init); | |
1215 | ||
1216 | static void __exit amplc_dio200_common_exit(void) | |
1217 | { | |
1218 | } | |
1219 | module_exit(amplc_dio200_common_exit); | |
1220 | ||
1221 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
1222 | MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci"); | |
1223 | MODULE_LICENSE("GPL"); |