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