1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * comedi/drivers/amplc_dio200_common.c
4 *
5 * Common support code for "amplc_dio200" and "amplc_dio200_pci".
6 *
7 * Copyright (C) 2005-2013 MEV Ltd. <https://www.mev.co.uk/>
8 *
9 * COMEDI - Linux Control and Measurement Device Interface
10 * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
11 */
12
13 #include <linux/module.h>
14 #include <linux/interrupt.h>
15 #include <linux/comedi/comedidev.h>
16 #include <linux/comedi/comedi_8255.h> /* only for register defines */
17 #include <linux/comedi/comedi_8254.h>
18
19 #include "amplc_dio200.h"
20
21 /* 200 series registers */
22 #define DIO200_IO_SIZE 0x20
23 #define DIO200_PCIE_IO_SIZE 0x4000
24 #define DIO200_CLK_SCE(x) (0x18 + (x)) /* Group X/Y/Z clock sel reg */
25 #define DIO200_GAT_SCE(x) (0x1b + (x)) /* Group X/Y/Z gate sel reg */
26 #define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */
27 /* Extra registers for new PCIe boards */
28 #define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */
29 #define DIO200_VERSION 0x24 /* Hardware version register */
30 #define DIO200_TS_CONFIG 0x600 /* Timestamp timer config register */
31 #define DIO200_TS_COUNT 0x602 /* Timestamp timer count register */
32
33 /*
34 * Functions for constructing value for DIO_200_?CLK_SCE and
35 * DIO_200_?GAT_SCE registers:
36 *
37 * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
38 * 'chan' is the channel: 0, 1 or 2.
39 * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
40 */
clk_gat_sce(unsigned int which,unsigned int chan,unsigned int source)41 static unsigned char clk_gat_sce(unsigned int which, unsigned int chan,
42 unsigned int source)
43 {
44 return (which << 5) | (chan << 3) |
45 ((source & 030) << 3) | (source & 007);
46 }
47
48 /*
49 * Periods of the internal clock sources in nanoseconds.
50 */
51 static const unsigned int clock_period[32] = {
52 [1] = 100, /* 10 MHz */
53 [2] = 1000, /* 1 MHz */
54 [3] = 10000, /* 100 kHz */
55 [4] = 100000, /* 10 kHz */
56 [5] = 1000000, /* 1 kHz */
57 [11] = 50, /* 20 MHz (enhanced boards) */
58 /* clock sources 12 and later reserved for enhanced boards */
59 };
60
61 /*
62 * Timestamp timer configuration register (for new PCIe boards).
63 */
64 #define TS_CONFIG_RESET 0x100 /* Reset counter to zero. */
65 #define TS_CONFIG_CLK_SRC_MASK 0x0FF /* Clock source. */
66 #define TS_CONFIG_MAX_CLK_SRC 2 /* Maximum clock source value. */
67
68 /*
69 * Periods of the timestamp timer clock sources in nanoseconds.
70 */
71 static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
72 1, /* 1 nanosecond (but with 20 ns granularity). */
73 1000, /* 1 microsecond. */
74 1000000, /* 1 millisecond. */
75 };
76
77 struct dio200_subdev_8255 {
78 unsigned int ofs; /* DIO base offset */
79 };
80
81 struct dio200_subdev_intr {
82 spinlock_t spinlock; /* protects the 'active' flag */
83 unsigned int ofs;
84 unsigned int valid_isns;
85 unsigned int enabled_isns;
86 unsigned int active:1;
87 };
88
dio200_read8(struct comedi_device * dev,unsigned int offset)89 static unsigned char dio200_read8(struct comedi_device *dev,
90 unsigned int offset)
91 {
92 const struct dio200_board *board = dev->board_ptr;
93
94 if (board->is_pcie)
95 offset <<= 3;
96
97 if (dev->mmio)
98 return readb(dev->mmio + offset);
99 return inb(dev->iobase + offset);
100 }
101
dio200_write8(struct comedi_device * dev,unsigned int offset,unsigned char val)102 static void dio200_write8(struct comedi_device *dev,
103 unsigned int offset, unsigned char val)
104 {
105 const struct dio200_board *board = dev->board_ptr;
106
107 if (board->is_pcie)
108 offset <<= 3;
109
110 if (dev->mmio)
111 writeb(val, dev->mmio + offset);
112 else
113 outb(val, dev->iobase + offset);
114 }
115
dio200_read32(struct comedi_device * dev,unsigned int offset)116 static unsigned int dio200_read32(struct comedi_device *dev,
117 unsigned int offset)
118 {
119 const struct dio200_board *board = dev->board_ptr;
120
121 if (board->is_pcie)
122 offset <<= 3;
123
124 if (dev->mmio)
125 return readl(dev->mmio + offset);
126 return inl(dev->iobase + offset);
127 }
128
dio200_write32(struct comedi_device * dev,unsigned int offset,unsigned int val)129 static void dio200_write32(struct comedi_device *dev,
130 unsigned int offset, unsigned int val)
131 {
132 const struct dio200_board *board = dev->board_ptr;
133
134 if (board->is_pcie)
135 offset <<= 3;
136
137 if (dev->mmio)
138 writel(val, dev->mmio + offset);
139 else
140 outl(val, dev->iobase + offset);
141 }
142
dio200_subdev_8254_offset(struct comedi_device * dev,struct comedi_subdevice * s)143 static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
144 struct comedi_subdevice *s)
145 {
146 const struct dio200_board *board = dev->board_ptr;
147 struct comedi_8254 *i8254 = s->private;
148 unsigned int offset;
149
150 /* get the offset that was passed to comedi_8254_*_init() */
151 if (dev->mmio)
152 offset = i8254->mmio - dev->mmio;
153 else
154 offset = i8254->iobase - dev->iobase;
155
156 /* remove the shift that was added for PCIe boards */
157 if (board->is_pcie)
158 offset >>= 3;
159
160 /* this offset now works for the dio200_{read,write} helpers */
161 return offset;
162 }
163
dio200_subdev_intr_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)164 static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
165 struct comedi_subdevice *s,
166 struct comedi_insn *insn,
167 unsigned int *data)
168 {
169 const struct dio200_board *board = dev->board_ptr;
170 struct dio200_subdev_intr *subpriv = s->private;
171
172 if (board->has_int_sce) {
173 /* Just read the interrupt status register. */
174 data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns;
175 } else {
176 /* No interrupt status register. */
177 data[0] = 0;
178 }
179
180 return insn->n;
181 }
182
dio200_stop_intr(struct comedi_device * dev,struct comedi_subdevice * s)183 static void dio200_stop_intr(struct comedi_device *dev,
184 struct comedi_subdevice *s)
185 {
186 const struct dio200_board *board = dev->board_ptr;
187 struct dio200_subdev_intr *subpriv = s->private;
188
189 subpriv->active = false;
190 subpriv->enabled_isns = 0;
191 if (board->has_int_sce)
192 dio200_write8(dev, subpriv->ofs, 0);
193 }
194
dio200_start_intr(struct comedi_device * dev,struct comedi_subdevice * s)195 static void dio200_start_intr(struct comedi_device *dev,
196 struct comedi_subdevice *s)
197 {
198 const struct dio200_board *board = dev->board_ptr;
199 struct dio200_subdev_intr *subpriv = s->private;
200 struct comedi_cmd *cmd = &s->async->cmd;
201 unsigned int n;
202 unsigned int isn_bits;
203
204 /* Determine interrupt sources to enable. */
205 isn_bits = 0;
206 if (cmd->chanlist) {
207 for (n = 0; n < cmd->chanlist_len; n++)
208 isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
209 }
210 isn_bits &= subpriv->valid_isns;
211 /* Enable interrupt sources. */
212 subpriv->enabled_isns = isn_bits;
213 if (board->has_int_sce)
214 dio200_write8(dev, subpriv->ofs, isn_bits);
215 }
216
dio200_inttrig_start_intr(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int trig_num)217 static int dio200_inttrig_start_intr(struct comedi_device *dev,
218 struct comedi_subdevice *s,
219 unsigned int trig_num)
220 {
221 struct dio200_subdev_intr *subpriv = s->private;
222 struct comedi_cmd *cmd = &s->async->cmd;
223 unsigned long flags;
224
225 if (trig_num != cmd->start_arg)
226 return -EINVAL;
227
228 spin_lock_irqsave(&subpriv->spinlock, flags);
229 s->async->inttrig = NULL;
230 if (subpriv->active)
231 dio200_start_intr(dev, s);
232
233 spin_unlock_irqrestore(&subpriv->spinlock, flags);
234
235 return 1;
236 }
237
dio200_read_scan_intr(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int triggered)238 static void dio200_read_scan_intr(struct comedi_device *dev,
239 struct comedi_subdevice *s,
240 unsigned int triggered)
241 {
242 struct comedi_cmd *cmd = &s->async->cmd;
243 unsigned short val;
244 unsigned int n, ch;
245
246 val = 0;
247 for (n = 0; n < cmd->chanlist_len; n++) {
248 ch = CR_CHAN(cmd->chanlist[n]);
249 if (triggered & (1U << ch))
250 val |= (1U << n);
251 }
252
253 comedi_buf_write_samples(s, &val, 1);
254
255 if (cmd->stop_src == TRIG_COUNT &&
256 s->async->scans_done >= cmd->stop_arg)
257 s->async->events |= COMEDI_CB_EOA;
258 }
259
dio200_handle_read_intr(struct comedi_device * dev,struct comedi_subdevice * s)260 static int dio200_handle_read_intr(struct comedi_device *dev,
261 struct comedi_subdevice *s)
262 {
263 const struct dio200_board *board = dev->board_ptr;
264 struct dio200_subdev_intr *subpriv = s->private;
265 unsigned int triggered;
266 unsigned int intstat;
267 unsigned int cur_enabled;
268 unsigned long flags;
269
270 triggered = 0;
271
272 spin_lock_irqsave(&subpriv->spinlock, flags);
273 if (board->has_int_sce) {
274 /*
275 * Collect interrupt sources that have triggered and disable
276 * them temporarily. Loop around until no extra interrupt
277 * sources have triggered, at which point, the valid part of
278 * the interrupt status register will read zero, clearing the
279 * cause of the interrupt.
280 *
281 * Mask off interrupt sources already seen to avoid infinite
282 * loop in case of misconfiguration.
283 */
284 cur_enabled = subpriv->enabled_isns;
285 while ((intstat = (dio200_read8(dev, subpriv->ofs) &
286 subpriv->valid_isns & ~triggered)) != 0) {
287 triggered |= intstat;
288 cur_enabled &= ~triggered;
289 dio200_write8(dev, subpriv->ofs, cur_enabled);
290 }
291 } else {
292 /*
293 * No interrupt status register. Assume the single interrupt
294 * source has triggered.
295 */
296 triggered = subpriv->enabled_isns;
297 }
298
299 if (triggered) {
300 /*
301 * Some interrupt sources have triggered and have been
302 * temporarily disabled to clear the cause of the interrupt.
303 *
304 * Reenable them NOW to minimize the time they are disabled.
305 */
306 cur_enabled = subpriv->enabled_isns;
307 if (board->has_int_sce)
308 dio200_write8(dev, subpriv->ofs, cur_enabled);
309
310 if (subpriv->active) {
311 /*
312 * The command is still active.
313 *
314 * Ignore interrupt sources that the command isn't
315 * interested in (just in case there's a race
316 * condition).
317 */
318 if (triggered & subpriv->enabled_isns) {
319 /* Collect scan data. */
320 dio200_read_scan_intr(dev, s, triggered);
321 }
322 }
323 }
324 spin_unlock_irqrestore(&subpriv->spinlock, flags);
325
326 comedi_handle_events(dev, s);
327
328 return (triggered != 0);
329 }
330
dio200_subdev_intr_cancel(struct comedi_device * dev,struct comedi_subdevice * s)331 static int dio200_subdev_intr_cancel(struct comedi_device *dev,
332 struct comedi_subdevice *s)
333 {
334 struct dio200_subdev_intr *subpriv = s->private;
335 unsigned long flags;
336
337 spin_lock_irqsave(&subpriv->spinlock, flags);
338 if (subpriv->active)
339 dio200_stop_intr(dev, s);
340
341 spin_unlock_irqrestore(&subpriv->spinlock, flags);
342
343 return 0;
344 }
345
dio200_subdev_intr_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)346 static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
347 struct comedi_subdevice *s,
348 struct comedi_cmd *cmd)
349 {
350 int err = 0;
351
352 /* Step 1 : check if triggers are trivially valid */
353
354 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
355 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
356 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
357 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
358 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
359
360 if (err)
361 return 1;
362
363 /* Step 2a : make sure trigger sources are unique */
364
365 err |= comedi_check_trigger_is_unique(cmd->start_src);
366 err |= comedi_check_trigger_is_unique(cmd->stop_src);
367
368 /* Step 2b : and mutually compatible */
369
370 if (err)
371 return 2;
372
373 /* Step 3: check if arguments are trivially valid */
374
375 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
376 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
377 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
378 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
379 cmd->chanlist_len);
380
381 if (cmd->stop_src == TRIG_COUNT)
382 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
383 else /* TRIG_NONE */
384 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
385
386 if (err)
387 return 3;
388
389 /* step 4: fix up any arguments */
390
391 /* if (err) return 4; */
392
393 return 0;
394 }
395
dio200_subdev_intr_cmd(struct comedi_device * dev,struct comedi_subdevice * s)396 static int dio200_subdev_intr_cmd(struct comedi_device *dev,
397 struct comedi_subdevice *s)
398 {
399 struct comedi_cmd *cmd = &s->async->cmd;
400 struct dio200_subdev_intr *subpriv = s->private;
401 unsigned long flags;
402
403 spin_lock_irqsave(&subpriv->spinlock, flags);
404
405 subpriv->active = true;
406
407 if (cmd->start_src == TRIG_INT)
408 s->async->inttrig = dio200_inttrig_start_intr;
409 else /* TRIG_NOW */
410 dio200_start_intr(dev, s);
411
412 spin_unlock_irqrestore(&subpriv->spinlock, flags);
413
414 return 0;
415 }
416
dio200_subdev_intr_init(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int offset,unsigned int valid_isns)417 static int dio200_subdev_intr_init(struct comedi_device *dev,
418 struct comedi_subdevice *s,
419 unsigned int offset,
420 unsigned int valid_isns)
421 {
422 const struct dio200_board *board = dev->board_ptr;
423 struct dio200_subdev_intr *subpriv;
424
425 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
426 if (!subpriv)
427 return -ENOMEM;
428
429 subpriv->ofs = offset;
430 subpriv->valid_isns = valid_isns;
431 spin_lock_init(&subpriv->spinlock);
432
433 if (board->has_int_sce)
434 /* Disable interrupt sources. */
435 dio200_write8(dev, subpriv->ofs, 0);
436
437 s->type = COMEDI_SUBD_DI;
438 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED;
439 if (board->has_int_sce) {
440 s->n_chan = DIO200_MAX_ISNS;
441 s->len_chanlist = DIO200_MAX_ISNS;
442 } else {
443 /* No interrupt source register. Support single channel. */
444 s->n_chan = 1;
445 s->len_chanlist = 1;
446 }
447 s->range_table = &range_digital;
448 s->maxdata = 1;
449 s->insn_bits = dio200_subdev_intr_insn_bits;
450 s->do_cmdtest = dio200_subdev_intr_cmdtest;
451 s->do_cmd = dio200_subdev_intr_cmd;
452 s->cancel = dio200_subdev_intr_cancel;
453
454 return 0;
455 }
456
dio200_interrupt(int irq,void * d)457 static irqreturn_t dio200_interrupt(int irq, void *d)
458 {
459 struct comedi_device *dev = d;
460 struct comedi_subdevice *s = dev->read_subdev;
461 int handled;
462
463 if (!dev->attached)
464 return IRQ_NONE;
465
466 handled = dio200_handle_read_intr(dev, s);
467
468 return IRQ_RETVAL(handled);
469 }
470
dio200_subdev_8254_set_gate_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int chan,unsigned int src)471 static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
472 struct comedi_subdevice *s,
473 unsigned int chan,
474 unsigned int src)
475 {
476 unsigned int offset = dio200_subdev_8254_offset(dev, s);
477
478 dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
479 clk_gat_sce((offset >> 2) & 1, chan, src));
480 }
481
dio200_subdev_8254_set_clock_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int chan,unsigned int src)482 static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
483 struct comedi_subdevice *s,
484 unsigned int chan,
485 unsigned int src)
486 {
487 unsigned int offset = dio200_subdev_8254_offset(dev, s);
488
489 dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
490 clk_gat_sce((offset >> 2) & 1, chan, src));
491 }
492
dio200_subdev_8254_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)493 static int dio200_subdev_8254_config(struct comedi_device *dev,
494 struct comedi_subdevice *s,
495 struct comedi_insn *insn,
496 unsigned int *data)
497 {
498 const struct dio200_board *board = dev->board_ptr;
499 struct comedi_8254 *i8254 = s->private;
500 unsigned int chan = CR_CHAN(insn->chanspec);
501 unsigned int max_src = board->is_pcie ? 31 : 7;
502 unsigned int src;
503
504 if (!board->has_clk_gat_sce)
505 return -EINVAL;
506
507 switch (data[0]) {
508 case INSN_CONFIG_SET_GATE_SRC:
509 src = data[2];
510 if (src > max_src)
511 return -EINVAL;
512
513 dio200_subdev_8254_set_gate_src(dev, s, chan, src);
514 i8254->gate_src[chan] = src;
515 break;
516 case INSN_CONFIG_GET_GATE_SRC:
517 data[2] = i8254->gate_src[chan];
518 break;
519 case INSN_CONFIG_SET_CLOCK_SRC:
520 src = data[1];
521 if (src > max_src)
522 return -EINVAL;
523
524 dio200_subdev_8254_set_clock_src(dev, s, chan, src);
525 i8254->clock_src[chan] = src;
526 break;
527 case INSN_CONFIG_GET_CLOCK_SRC:
528 data[1] = i8254->clock_src[chan];
529 data[2] = clock_period[i8254->clock_src[chan]];
530 break;
531 default:
532 return -EINVAL;
533 }
534
535 return insn->n;
536 }
537
dio200_subdev_8254_init(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int offset)538 static int dio200_subdev_8254_init(struct comedi_device *dev,
539 struct comedi_subdevice *s,
540 unsigned int offset)
541 {
542 const struct dio200_board *board = dev->board_ptr;
543 struct comedi_8254 *i8254;
544 unsigned int regshift;
545 int chan;
546
547 /*
548 * PCIe boards need the offset shifted in order to get the
549 * correct base address of the timer.
550 */
551 if (board->is_pcie) {
552 offset <<= 3;
553 regshift = 3;
554 } else {
555 regshift = 0;
556 }
557
558 if (dev->mmio) {
559 i8254 = comedi_8254_mm_init(dev->mmio + offset,
560 0, I8254_IO8, regshift);
561 } else {
562 i8254 = comedi_8254_init(dev->iobase + offset,
563 0, I8254_IO8, regshift);
564 }
565 if (!i8254)
566 return -ENOMEM;
567
568 comedi_8254_subdevice_init(s, i8254);
569
570 i8254->insn_config = dio200_subdev_8254_config;
571
572 /*
573 * There could be multiple timers so this driver does not
574 * use dev->pacer to save the i8254 pointer. Instead,
575 * comedi_8254_subdevice_init() saved the i8254 pointer in
576 * s->private. Mark the subdevice as having private data
577 * to be automatically freed when the device is detached.
578 */
579 comedi_set_spriv_auto_free(s);
580
581 /* Initialize channels. */
582 if (board->has_clk_gat_sce) {
583 for (chan = 0; chan < 3; chan++) {
584 /* Gate source 0 is VCC (logic 1). */
585 dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
586 /* Clock source 0 is the dedicated clock input. */
587 dio200_subdev_8254_set_clock_src(dev, s, chan, 0);
588 }
589 }
590
591 return 0;
592 }
593
dio200_subdev_8255_set_dir(struct comedi_device * dev,struct comedi_subdevice * s)594 static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
595 struct comedi_subdevice *s)
596 {
597 struct dio200_subdev_8255 *subpriv = s->private;
598 int config;
599
600 config = I8255_CTRL_CW;
601 /* 1 in io_bits indicates output, 1 in config indicates input */
602 if (!(s->io_bits & 0x0000ff))
603 config |= I8255_CTRL_A_IO;
604 if (!(s->io_bits & 0x00ff00))
605 config |= I8255_CTRL_B_IO;
606 if (!(s->io_bits & 0x0f0000))
607 config |= I8255_CTRL_C_LO_IO;
608 if (!(s->io_bits & 0xf00000))
609 config |= I8255_CTRL_C_HI_IO;
610 dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config);
611 }
612
dio200_subdev_8255_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)613 static int dio200_subdev_8255_bits(struct comedi_device *dev,
614 struct comedi_subdevice *s,
615 struct comedi_insn *insn,
616 unsigned int *data)
617 {
618 struct dio200_subdev_8255 *subpriv = s->private;
619 unsigned int mask;
620 unsigned int val;
621
622 mask = comedi_dio_update_state(s, data);
623 if (mask) {
624 if (mask & 0xff) {
625 dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG,
626 s->state & 0xff);
627 }
628 if (mask & 0xff00) {
629 dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG,
630 (s->state >> 8) & 0xff);
631 }
632 if (mask & 0xff0000) {
633 dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG,
634 (s->state >> 16) & 0xff);
635 }
636 }
637
638 val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG);
639 val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8;
640 val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16;
641
642 data[1] = val;
643
644 return insn->n;
645 }
646
dio200_subdev_8255_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)647 static int dio200_subdev_8255_config(struct comedi_device *dev,
648 struct comedi_subdevice *s,
649 struct comedi_insn *insn,
650 unsigned int *data)
651 {
652 unsigned int chan = CR_CHAN(insn->chanspec);
653 unsigned int mask;
654 int ret;
655
656 if (chan < 8)
657 mask = 0x0000ff;
658 else if (chan < 16)
659 mask = 0x00ff00;
660 else if (chan < 20)
661 mask = 0x0f0000;
662 else
663 mask = 0xf00000;
664
665 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
666 if (ret)
667 return ret;
668
669 dio200_subdev_8255_set_dir(dev, s);
670
671 return insn->n;
672 }
673
dio200_subdev_8255_init(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int offset)674 static int dio200_subdev_8255_init(struct comedi_device *dev,
675 struct comedi_subdevice *s,
676 unsigned int offset)
677 {
678 struct dio200_subdev_8255 *subpriv;
679
680 subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
681 if (!subpriv)
682 return -ENOMEM;
683
684 subpriv->ofs = offset;
685
686 s->type = COMEDI_SUBD_DIO;
687 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
688 s->n_chan = 24;
689 s->range_table = &range_digital;
690 s->maxdata = 1;
691 s->insn_bits = dio200_subdev_8255_bits;
692 s->insn_config = dio200_subdev_8255_config;
693 dio200_subdev_8255_set_dir(dev, s);
694 return 0;
695 }
696
dio200_subdev_timer_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)697 static int dio200_subdev_timer_read(struct comedi_device *dev,
698 struct comedi_subdevice *s,
699 struct comedi_insn *insn,
700 unsigned int *data)
701 {
702 unsigned int n;
703
704 for (n = 0; n < insn->n; n++)
705 data[n] = dio200_read32(dev, DIO200_TS_COUNT);
706 return n;
707 }
708
dio200_subdev_timer_reset(struct comedi_device * dev,struct comedi_subdevice * s)709 static void dio200_subdev_timer_reset(struct comedi_device *dev,
710 struct comedi_subdevice *s)
711 {
712 unsigned int clock;
713
714 clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
715 dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET);
716 dio200_write32(dev, DIO200_TS_CONFIG, clock);
717 }
718
dio200_subdev_timer_get_clock_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int * src,unsigned int * period)719 static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
720 struct comedi_subdevice *s,
721 unsigned int *src,
722 unsigned int *period)
723 {
724 unsigned int clk;
725
726 clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
727 *src = clk;
728 *period = (clk < ARRAY_SIZE(ts_clock_period)) ?
729 ts_clock_period[clk] : 0;
730 }
731
dio200_subdev_timer_set_clock_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int src)732 static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
733 struct comedi_subdevice *s,
734 unsigned int src)
735 {
736 if (src > TS_CONFIG_MAX_CLK_SRC)
737 return -EINVAL;
738 dio200_write32(dev, DIO200_TS_CONFIG, src);
739 return 0;
740 }
741
dio200_subdev_timer_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)742 static int dio200_subdev_timer_config(struct comedi_device *dev,
743 struct comedi_subdevice *s,
744 struct comedi_insn *insn,
745 unsigned int *data)
746 {
747 int ret = 0;
748
749 switch (data[0]) {
750 case INSN_CONFIG_RESET:
751 dio200_subdev_timer_reset(dev, s);
752 break;
753 case INSN_CONFIG_SET_CLOCK_SRC:
754 ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]);
755 if (ret < 0)
756 ret = -EINVAL;
757 break;
758 case INSN_CONFIG_GET_CLOCK_SRC:
759 dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]);
760 break;
761 default:
762 ret = -EINVAL;
763 break;
764 }
765 return ret < 0 ? ret : insn->n;
766 }
767
amplc_dio200_set_enhance(struct comedi_device * dev,unsigned char val)768 void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val)
769 {
770 dio200_write8(dev, DIO200_ENHANCE, val);
771 }
772 EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance);
773
amplc_dio200_common_attach(struct comedi_device * dev,unsigned int irq,unsigned long req_irq_flags)774 int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
775 unsigned long req_irq_flags)
776 {
777 const struct dio200_board *board = dev->board_ptr;
778 struct comedi_subdevice *s;
779 unsigned int n;
780 int ret;
781
782 ret = comedi_alloc_subdevices(dev, board->n_subdevs);
783 if (ret)
784 return ret;
785
786 for (n = 0; n < dev->n_subdevices; n++) {
787 s = &dev->subdevices[n];
788 switch (board->sdtype[n]) {
789 case sd_8254:
790 /* counter subdevice (8254) */
791 ret = dio200_subdev_8254_init(dev, s,
792 board->sdinfo[n]);
793 if (ret < 0)
794 return ret;
795 break;
796 case sd_8255:
797 /* digital i/o subdevice (8255) */
798 ret = dio200_subdev_8255_init(dev, s,
799 board->sdinfo[n]);
800 if (ret < 0)
801 return ret;
802 break;
803 case sd_intr:
804 /* 'INTERRUPT' subdevice */
805 if (irq && !dev->read_subdev) {
806 ret = dio200_subdev_intr_init(dev, s,
807 DIO200_INT_SCE,
808 board->sdinfo[n]);
809 if (ret < 0)
810 return ret;
811 dev->read_subdev = s;
812 } else {
813 s->type = COMEDI_SUBD_UNUSED;
814 }
815 break;
816 case sd_timer:
817 s->type = COMEDI_SUBD_TIMER;
818 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
819 s->n_chan = 1;
820 s->maxdata = 0xffffffff;
821 s->insn_read = dio200_subdev_timer_read;
822 s->insn_config = dio200_subdev_timer_config;
823 break;
824 default:
825 s->type = COMEDI_SUBD_UNUSED;
826 break;
827 }
828 }
829
830 if (irq && dev->read_subdev) {
831 if (request_irq(irq, dio200_interrupt, req_irq_flags,
832 dev->board_name, dev) >= 0) {
833 dev->irq = irq;
834 } else {
835 dev_warn(dev->class_dev,
836 "warning! irq %u unavailable!\n", irq);
837 }
838 }
839
840 return 0;
841 }
842 EXPORT_SYMBOL_GPL(amplc_dio200_common_attach);
843
amplc_dio200_common_init(void)844 static int __init amplc_dio200_common_init(void)
845 {
846 return 0;
847 }
848 module_init(amplc_dio200_common_init);
849
amplc_dio200_common_exit(void)850 static void __exit amplc_dio200_common_exit(void)
851 {
852 }
853 module_exit(amplc_dio200_common_exit);
854
855 MODULE_AUTHOR("Comedi https://www.comedi.org");
856 MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci");
857 MODULE_LICENSE("GPL");
858