xref: /openbmc/linux/drivers/comedi/drivers/amplc_dio200_common.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman  * comedi/drivers/amplc_dio200_common.c
48ffdff6aSGreg Kroah-Hartman  *
58ffdff6aSGreg Kroah-Hartman  * Common support code for "amplc_dio200" and "amplc_dio200_pci".
68ffdff6aSGreg Kroah-Hartman  *
78ffdff6aSGreg Kroah-Hartman  * Copyright (C) 2005-2013 MEV Ltd. <https://www.mev.co.uk/>
88ffdff6aSGreg Kroah-Hartman  *
98ffdff6aSGreg Kroah-Hartman  * COMEDI - Linux Control and Measurement Device Interface
108ffdff6aSGreg Kroah-Hartman  * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
118ffdff6aSGreg Kroah-Hartman  */
128ffdff6aSGreg Kroah-Hartman 
138ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
148ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
15df0e68c1SIan Abbott #include <linux/comedi/comedidev.h>
16631e272bSIan Abbott #include <linux/comedi/comedi_8255.h>	/* only for register defines */
17*44fb7affSIan Abbott #include <linux/comedi/comedi_8254.h>
188ffdff6aSGreg Kroah-Hartman 
198ffdff6aSGreg Kroah-Hartman #include "amplc_dio200.h"
208ffdff6aSGreg Kroah-Hartman 
218ffdff6aSGreg Kroah-Hartman /* 200 series registers */
228ffdff6aSGreg Kroah-Hartman #define DIO200_IO_SIZE		0x20
238ffdff6aSGreg Kroah-Hartman #define DIO200_PCIE_IO_SIZE	0x4000
248ffdff6aSGreg Kroah-Hartman #define DIO200_CLK_SCE(x)	(0x18 + (x))	/* Group X/Y/Z clock sel reg */
258ffdff6aSGreg Kroah-Hartman #define DIO200_GAT_SCE(x)	(0x1b + (x))	/* Group X/Y/Z gate sel reg */
268ffdff6aSGreg Kroah-Hartman #define DIO200_INT_SCE		0x1e	/* Interrupt enable/status register */
278ffdff6aSGreg Kroah-Hartman /* Extra registers for new PCIe boards */
288ffdff6aSGreg Kroah-Hartman #define DIO200_ENHANCE		0x20	/* 1 to enable enhanced features */
298ffdff6aSGreg Kroah-Hartman #define DIO200_VERSION		0x24	/* Hardware version register */
308ffdff6aSGreg Kroah-Hartman #define DIO200_TS_CONFIG	0x600	/* Timestamp timer config register */
318ffdff6aSGreg Kroah-Hartman #define DIO200_TS_COUNT		0x602	/* Timestamp timer count register */
328ffdff6aSGreg Kroah-Hartman 
338ffdff6aSGreg Kroah-Hartman /*
348ffdff6aSGreg Kroah-Hartman  * Functions for constructing value for DIO_200_?CLK_SCE and
358ffdff6aSGreg Kroah-Hartman  * DIO_200_?GAT_SCE registers:
368ffdff6aSGreg Kroah-Hartman  *
378ffdff6aSGreg Kroah-Hartman  * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
388ffdff6aSGreg Kroah-Hartman  * 'chan' is the channel: 0, 1 or 2.
398ffdff6aSGreg Kroah-Hartman  * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
408ffdff6aSGreg Kroah-Hartman  */
clk_gat_sce(unsigned int which,unsigned int chan,unsigned int source)418ffdff6aSGreg Kroah-Hartman static unsigned char clk_gat_sce(unsigned int which, unsigned int chan,
428ffdff6aSGreg Kroah-Hartman 				 unsigned int source)
438ffdff6aSGreg Kroah-Hartman {
448ffdff6aSGreg Kroah-Hartman 	return (which << 5) | (chan << 3) |
458ffdff6aSGreg Kroah-Hartman 	       ((source & 030) << 3) | (source & 007);
468ffdff6aSGreg Kroah-Hartman }
478ffdff6aSGreg Kroah-Hartman 
488ffdff6aSGreg Kroah-Hartman /*
498ffdff6aSGreg Kroah-Hartman  * Periods of the internal clock sources in nanoseconds.
508ffdff6aSGreg Kroah-Hartman  */
518ffdff6aSGreg Kroah-Hartman static const unsigned int clock_period[32] = {
528ffdff6aSGreg Kroah-Hartman 	[1] = 100,		/* 10 MHz */
538ffdff6aSGreg Kroah-Hartman 	[2] = 1000,		/* 1 MHz */
548ffdff6aSGreg Kroah-Hartman 	[3] = 10000,		/* 100 kHz */
558ffdff6aSGreg Kroah-Hartman 	[4] = 100000,		/* 10 kHz */
568ffdff6aSGreg Kroah-Hartman 	[5] = 1000000,		/* 1 kHz */
578ffdff6aSGreg Kroah-Hartman 	[11] = 50,		/* 20 MHz (enhanced boards) */
588ffdff6aSGreg Kroah-Hartman 	/* clock sources 12 and later reserved for enhanced boards */
598ffdff6aSGreg Kroah-Hartman };
608ffdff6aSGreg Kroah-Hartman 
618ffdff6aSGreg Kroah-Hartman /*
628ffdff6aSGreg Kroah-Hartman  * Timestamp timer configuration register (for new PCIe boards).
638ffdff6aSGreg Kroah-Hartman  */
648ffdff6aSGreg Kroah-Hartman #define TS_CONFIG_RESET		0x100	/* Reset counter to zero. */
658ffdff6aSGreg Kroah-Hartman #define TS_CONFIG_CLK_SRC_MASK	0x0FF	/* Clock source. */
668ffdff6aSGreg Kroah-Hartman #define TS_CONFIG_MAX_CLK_SRC	2	/* Maximum clock source value. */
678ffdff6aSGreg Kroah-Hartman 
688ffdff6aSGreg Kroah-Hartman /*
698ffdff6aSGreg Kroah-Hartman  * Periods of the timestamp timer clock sources in nanoseconds.
708ffdff6aSGreg Kroah-Hartman  */
718ffdff6aSGreg Kroah-Hartman static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
728ffdff6aSGreg Kroah-Hartman 	1,			/* 1 nanosecond (but with 20 ns granularity). */
738ffdff6aSGreg Kroah-Hartman 	1000,			/* 1 microsecond. */
748ffdff6aSGreg Kroah-Hartman 	1000000,		/* 1 millisecond. */
758ffdff6aSGreg Kroah-Hartman };
768ffdff6aSGreg Kroah-Hartman 
778ffdff6aSGreg Kroah-Hartman struct dio200_subdev_8255 {
788ffdff6aSGreg Kroah-Hartman 	unsigned int ofs;		/* DIO base offset */
798ffdff6aSGreg Kroah-Hartman };
808ffdff6aSGreg Kroah-Hartman 
818ffdff6aSGreg Kroah-Hartman struct dio200_subdev_intr {
828ffdff6aSGreg Kroah-Hartman 	spinlock_t spinlock;	/* protects the 'active' flag */
838ffdff6aSGreg Kroah-Hartman 	unsigned int ofs;
848ffdff6aSGreg Kroah-Hartman 	unsigned int valid_isns;
858ffdff6aSGreg Kroah-Hartman 	unsigned int enabled_isns;
868ffdff6aSGreg Kroah-Hartman 	unsigned int active:1;
878ffdff6aSGreg Kroah-Hartman };
888ffdff6aSGreg Kroah-Hartman 
dio200_read8(struct comedi_device * dev,unsigned int offset)898ffdff6aSGreg Kroah-Hartman static unsigned char dio200_read8(struct comedi_device *dev,
908ffdff6aSGreg Kroah-Hartman 				  unsigned int offset)
918ffdff6aSGreg Kroah-Hartman {
928ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
938ffdff6aSGreg Kroah-Hartman 
948ffdff6aSGreg Kroah-Hartman 	if (board->is_pcie)
958ffdff6aSGreg Kroah-Hartman 		offset <<= 3;
968ffdff6aSGreg Kroah-Hartman 
978ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
988ffdff6aSGreg Kroah-Hartman 		return readb(dev->mmio + offset);
998ffdff6aSGreg Kroah-Hartman 	return inb(dev->iobase + offset);
1008ffdff6aSGreg Kroah-Hartman }
1018ffdff6aSGreg Kroah-Hartman 
dio200_write8(struct comedi_device * dev,unsigned int offset,unsigned char val)1028ffdff6aSGreg Kroah-Hartman static void dio200_write8(struct comedi_device *dev,
1038ffdff6aSGreg Kroah-Hartman 			  unsigned int offset, unsigned char val)
1048ffdff6aSGreg Kroah-Hartman {
1058ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1068ffdff6aSGreg Kroah-Hartman 
1078ffdff6aSGreg Kroah-Hartman 	if (board->is_pcie)
1088ffdff6aSGreg Kroah-Hartman 		offset <<= 3;
1098ffdff6aSGreg Kroah-Hartman 
1108ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
1118ffdff6aSGreg Kroah-Hartman 		writeb(val, dev->mmio + offset);
1128ffdff6aSGreg Kroah-Hartman 	else
1138ffdff6aSGreg Kroah-Hartman 		outb(val, dev->iobase + offset);
1148ffdff6aSGreg Kroah-Hartman }
1158ffdff6aSGreg Kroah-Hartman 
dio200_read32(struct comedi_device * dev,unsigned int offset)1168ffdff6aSGreg Kroah-Hartman static unsigned int dio200_read32(struct comedi_device *dev,
1178ffdff6aSGreg Kroah-Hartman 				  unsigned int offset)
1188ffdff6aSGreg Kroah-Hartman {
1198ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1208ffdff6aSGreg Kroah-Hartman 
1218ffdff6aSGreg Kroah-Hartman 	if (board->is_pcie)
1228ffdff6aSGreg Kroah-Hartman 		offset <<= 3;
1238ffdff6aSGreg Kroah-Hartman 
1248ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
1258ffdff6aSGreg Kroah-Hartman 		return readl(dev->mmio + offset);
1268ffdff6aSGreg Kroah-Hartman 	return inl(dev->iobase + offset);
1278ffdff6aSGreg Kroah-Hartman }
1288ffdff6aSGreg Kroah-Hartman 
dio200_write32(struct comedi_device * dev,unsigned int offset,unsigned int val)1298ffdff6aSGreg Kroah-Hartman static void dio200_write32(struct comedi_device *dev,
1308ffdff6aSGreg Kroah-Hartman 			   unsigned int offset, unsigned int val)
1318ffdff6aSGreg Kroah-Hartman {
1328ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1338ffdff6aSGreg Kroah-Hartman 
1348ffdff6aSGreg Kroah-Hartman 	if (board->is_pcie)
1358ffdff6aSGreg Kroah-Hartman 		offset <<= 3;
1368ffdff6aSGreg Kroah-Hartman 
1378ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
1388ffdff6aSGreg Kroah-Hartman 		writel(val, dev->mmio + offset);
1398ffdff6aSGreg Kroah-Hartman 	else
1408ffdff6aSGreg Kroah-Hartman 		outl(val, dev->iobase + offset);
1418ffdff6aSGreg Kroah-Hartman }
1428ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8254_offset(struct comedi_device * dev,struct comedi_subdevice * s)1438ffdff6aSGreg Kroah-Hartman static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
1448ffdff6aSGreg Kroah-Hartman 					      struct comedi_subdevice *s)
1458ffdff6aSGreg Kroah-Hartman {
1468ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1478ffdff6aSGreg Kroah-Hartman 	struct comedi_8254 *i8254 = s->private;
1488ffdff6aSGreg Kroah-Hartman 	unsigned int offset;
1498ffdff6aSGreg Kroah-Hartman 
1508ffdff6aSGreg Kroah-Hartman 	/* get the offset that was passed to comedi_8254_*_init() */
1518ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
1528ffdff6aSGreg Kroah-Hartman 		offset = i8254->mmio - dev->mmio;
1538ffdff6aSGreg Kroah-Hartman 	else
1548ffdff6aSGreg Kroah-Hartman 		offset = i8254->iobase - dev->iobase;
1558ffdff6aSGreg Kroah-Hartman 
1568ffdff6aSGreg Kroah-Hartman 	/* remove the shift that was added for PCIe boards */
1578ffdff6aSGreg Kroah-Hartman 	if (board->is_pcie)
1588ffdff6aSGreg Kroah-Hartman 		offset >>= 3;
1598ffdff6aSGreg Kroah-Hartman 
1608ffdff6aSGreg Kroah-Hartman 	/* this offset now works for the dio200_{read,write} helpers */
1618ffdff6aSGreg Kroah-Hartman 	return offset;
1628ffdff6aSGreg Kroah-Hartman }
1638ffdff6aSGreg Kroah-Hartman 
dio200_subdev_intr_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)1648ffdff6aSGreg Kroah-Hartman static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
1658ffdff6aSGreg Kroah-Hartman 					struct comedi_subdevice *s,
1668ffdff6aSGreg Kroah-Hartman 					struct comedi_insn *insn,
1678ffdff6aSGreg Kroah-Hartman 					unsigned int *data)
1688ffdff6aSGreg Kroah-Hartman {
1698ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1708ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
1718ffdff6aSGreg Kroah-Hartman 
1728ffdff6aSGreg Kroah-Hartman 	if (board->has_int_sce) {
1738ffdff6aSGreg Kroah-Hartman 		/* Just read the interrupt status register.  */
1748ffdff6aSGreg Kroah-Hartman 		data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns;
1758ffdff6aSGreg Kroah-Hartman 	} else {
1768ffdff6aSGreg Kroah-Hartman 		/* No interrupt status register. */
1778ffdff6aSGreg Kroah-Hartman 		data[0] = 0;
1788ffdff6aSGreg Kroah-Hartman 	}
1798ffdff6aSGreg Kroah-Hartman 
1808ffdff6aSGreg Kroah-Hartman 	return insn->n;
1818ffdff6aSGreg Kroah-Hartman }
1828ffdff6aSGreg Kroah-Hartman 
dio200_stop_intr(struct comedi_device * dev,struct comedi_subdevice * s)1838ffdff6aSGreg Kroah-Hartman static void dio200_stop_intr(struct comedi_device *dev,
1848ffdff6aSGreg Kroah-Hartman 			     struct comedi_subdevice *s)
1858ffdff6aSGreg Kroah-Hartman {
1868ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1878ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
1888ffdff6aSGreg Kroah-Hartman 
1898ffdff6aSGreg Kroah-Hartman 	subpriv->active = false;
1908ffdff6aSGreg Kroah-Hartman 	subpriv->enabled_isns = 0;
1918ffdff6aSGreg Kroah-Hartman 	if (board->has_int_sce)
1928ffdff6aSGreg Kroah-Hartman 		dio200_write8(dev, subpriv->ofs, 0);
1938ffdff6aSGreg Kroah-Hartman }
1948ffdff6aSGreg Kroah-Hartman 
dio200_start_intr(struct comedi_device * dev,struct comedi_subdevice * s)1958ffdff6aSGreg Kroah-Hartman static void dio200_start_intr(struct comedi_device *dev,
1968ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s)
1978ffdff6aSGreg Kroah-Hartman {
1988ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
1998ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
2008ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
2018ffdff6aSGreg Kroah-Hartman 	unsigned int n;
2028ffdff6aSGreg Kroah-Hartman 	unsigned int isn_bits;
2038ffdff6aSGreg Kroah-Hartman 
2048ffdff6aSGreg Kroah-Hartman 	/* Determine interrupt sources to enable. */
2058ffdff6aSGreg Kroah-Hartman 	isn_bits = 0;
2068ffdff6aSGreg Kroah-Hartman 	if (cmd->chanlist) {
2078ffdff6aSGreg Kroah-Hartman 		for (n = 0; n < cmd->chanlist_len; n++)
2088ffdff6aSGreg Kroah-Hartman 			isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
2098ffdff6aSGreg Kroah-Hartman 	}
2108ffdff6aSGreg Kroah-Hartman 	isn_bits &= subpriv->valid_isns;
2118ffdff6aSGreg Kroah-Hartman 	/* Enable interrupt sources. */
2128ffdff6aSGreg Kroah-Hartman 	subpriv->enabled_isns = isn_bits;
2138ffdff6aSGreg Kroah-Hartman 	if (board->has_int_sce)
2148ffdff6aSGreg Kroah-Hartman 		dio200_write8(dev, subpriv->ofs, isn_bits);
2158ffdff6aSGreg Kroah-Hartman }
2168ffdff6aSGreg Kroah-Hartman 
dio200_inttrig_start_intr(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int trig_num)2178ffdff6aSGreg Kroah-Hartman static int dio200_inttrig_start_intr(struct comedi_device *dev,
2188ffdff6aSGreg Kroah-Hartman 				     struct comedi_subdevice *s,
2198ffdff6aSGreg Kroah-Hartman 				     unsigned int trig_num)
2208ffdff6aSGreg Kroah-Hartman {
2218ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
2228ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
2238ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
2248ffdff6aSGreg Kroah-Hartman 
2258ffdff6aSGreg Kroah-Hartman 	if (trig_num != cmd->start_arg)
2268ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
2278ffdff6aSGreg Kroah-Hartman 
2288ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&subpriv->spinlock, flags);
2298ffdff6aSGreg Kroah-Hartman 	s->async->inttrig = NULL;
2308ffdff6aSGreg Kroah-Hartman 	if (subpriv->active)
2318ffdff6aSGreg Kroah-Hartman 		dio200_start_intr(dev, s);
2328ffdff6aSGreg Kroah-Hartman 
2338ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&subpriv->spinlock, flags);
2348ffdff6aSGreg Kroah-Hartman 
2358ffdff6aSGreg Kroah-Hartman 	return 1;
2368ffdff6aSGreg Kroah-Hartman }
2378ffdff6aSGreg Kroah-Hartman 
dio200_read_scan_intr(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int triggered)2388ffdff6aSGreg Kroah-Hartman static void dio200_read_scan_intr(struct comedi_device *dev,
2398ffdff6aSGreg Kroah-Hartman 				  struct comedi_subdevice *s,
2408ffdff6aSGreg Kroah-Hartman 				  unsigned int triggered)
2418ffdff6aSGreg Kroah-Hartman {
2428ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
2438ffdff6aSGreg Kroah-Hartman 	unsigned short val;
2448ffdff6aSGreg Kroah-Hartman 	unsigned int n, ch;
2458ffdff6aSGreg Kroah-Hartman 
2468ffdff6aSGreg Kroah-Hartman 	val = 0;
2478ffdff6aSGreg Kroah-Hartman 	for (n = 0; n < cmd->chanlist_len; n++) {
2488ffdff6aSGreg Kroah-Hartman 		ch = CR_CHAN(cmd->chanlist[n]);
2498ffdff6aSGreg Kroah-Hartman 		if (triggered & (1U << ch))
2508ffdff6aSGreg Kroah-Hartman 			val |= (1U << n);
2518ffdff6aSGreg Kroah-Hartman 	}
2528ffdff6aSGreg Kroah-Hartman 
2538ffdff6aSGreg Kroah-Hartman 	comedi_buf_write_samples(s, &val, 1);
2548ffdff6aSGreg Kroah-Hartman 
2558ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT &&
2568ffdff6aSGreg Kroah-Hartman 	    s->async->scans_done >= cmd->stop_arg)
2578ffdff6aSGreg Kroah-Hartman 		s->async->events |= COMEDI_CB_EOA;
2588ffdff6aSGreg Kroah-Hartman }
2598ffdff6aSGreg Kroah-Hartman 
dio200_handle_read_intr(struct comedi_device * dev,struct comedi_subdevice * s)2608ffdff6aSGreg Kroah-Hartman static int dio200_handle_read_intr(struct comedi_device *dev,
2618ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s)
2628ffdff6aSGreg Kroah-Hartman {
2638ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
2648ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
2658ffdff6aSGreg Kroah-Hartman 	unsigned int triggered;
2668ffdff6aSGreg Kroah-Hartman 	unsigned int intstat;
2678ffdff6aSGreg Kroah-Hartman 	unsigned int cur_enabled;
2688ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
2698ffdff6aSGreg Kroah-Hartman 
2708ffdff6aSGreg Kroah-Hartman 	triggered = 0;
2718ffdff6aSGreg Kroah-Hartman 
2728ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&subpriv->spinlock, flags);
2738ffdff6aSGreg Kroah-Hartman 	if (board->has_int_sce) {
2748ffdff6aSGreg Kroah-Hartman 		/*
2758ffdff6aSGreg Kroah-Hartman 		 * Collect interrupt sources that have triggered and disable
2768ffdff6aSGreg Kroah-Hartman 		 * them temporarily.  Loop around until no extra interrupt
2778ffdff6aSGreg Kroah-Hartman 		 * sources have triggered, at which point, the valid part of
2788ffdff6aSGreg Kroah-Hartman 		 * the interrupt status register will read zero, clearing the
2798ffdff6aSGreg Kroah-Hartman 		 * cause of the interrupt.
2808ffdff6aSGreg Kroah-Hartman 		 *
2818ffdff6aSGreg Kroah-Hartman 		 * Mask off interrupt sources already seen to avoid infinite
2828ffdff6aSGreg Kroah-Hartman 		 * loop in case of misconfiguration.
2838ffdff6aSGreg Kroah-Hartman 		 */
2848ffdff6aSGreg Kroah-Hartman 		cur_enabled = subpriv->enabled_isns;
2858ffdff6aSGreg Kroah-Hartman 		while ((intstat = (dio200_read8(dev, subpriv->ofs) &
2868ffdff6aSGreg Kroah-Hartman 				   subpriv->valid_isns & ~triggered)) != 0) {
2878ffdff6aSGreg Kroah-Hartman 			triggered |= intstat;
2888ffdff6aSGreg Kroah-Hartman 			cur_enabled &= ~triggered;
2898ffdff6aSGreg Kroah-Hartman 			dio200_write8(dev, subpriv->ofs, cur_enabled);
2908ffdff6aSGreg Kroah-Hartman 		}
2918ffdff6aSGreg Kroah-Hartman 	} else {
2928ffdff6aSGreg Kroah-Hartman 		/*
2938ffdff6aSGreg Kroah-Hartman 		 * No interrupt status register.  Assume the single interrupt
2948ffdff6aSGreg Kroah-Hartman 		 * source has triggered.
2958ffdff6aSGreg Kroah-Hartman 		 */
2968ffdff6aSGreg Kroah-Hartman 		triggered = subpriv->enabled_isns;
2978ffdff6aSGreg Kroah-Hartman 	}
2988ffdff6aSGreg Kroah-Hartman 
2998ffdff6aSGreg Kroah-Hartman 	if (triggered) {
3008ffdff6aSGreg Kroah-Hartman 		/*
3018ffdff6aSGreg Kroah-Hartman 		 * Some interrupt sources have triggered and have been
3028ffdff6aSGreg Kroah-Hartman 		 * temporarily disabled to clear the cause of the interrupt.
3038ffdff6aSGreg Kroah-Hartman 		 *
3048ffdff6aSGreg Kroah-Hartman 		 * Reenable them NOW to minimize the time they are disabled.
3058ffdff6aSGreg Kroah-Hartman 		 */
3068ffdff6aSGreg Kroah-Hartman 		cur_enabled = subpriv->enabled_isns;
3078ffdff6aSGreg Kroah-Hartman 		if (board->has_int_sce)
3088ffdff6aSGreg Kroah-Hartman 			dio200_write8(dev, subpriv->ofs, cur_enabled);
3098ffdff6aSGreg Kroah-Hartman 
3108ffdff6aSGreg Kroah-Hartman 		if (subpriv->active) {
3118ffdff6aSGreg Kroah-Hartman 			/*
3128ffdff6aSGreg Kroah-Hartman 			 * The command is still active.
3138ffdff6aSGreg Kroah-Hartman 			 *
3148ffdff6aSGreg Kroah-Hartman 			 * Ignore interrupt sources that the command isn't
3158ffdff6aSGreg Kroah-Hartman 			 * interested in (just in case there's a race
3168ffdff6aSGreg Kroah-Hartman 			 * condition).
3178ffdff6aSGreg Kroah-Hartman 			 */
3188ffdff6aSGreg Kroah-Hartman 			if (triggered & subpriv->enabled_isns) {
3198ffdff6aSGreg Kroah-Hartman 				/* Collect scan data. */
3208ffdff6aSGreg Kroah-Hartman 				dio200_read_scan_intr(dev, s, triggered);
3218ffdff6aSGreg Kroah-Hartman 			}
3228ffdff6aSGreg Kroah-Hartman 		}
3238ffdff6aSGreg Kroah-Hartman 	}
3248ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&subpriv->spinlock, flags);
3258ffdff6aSGreg Kroah-Hartman 
3268ffdff6aSGreg Kroah-Hartman 	comedi_handle_events(dev, s);
3278ffdff6aSGreg Kroah-Hartman 
3288ffdff6aSGreg Kroah-Hartman 	return (triggered != 0);
3298ffdff6aSGreg Kroah-Hartman }
3308ffdff6aSGreg Kroah-Hartman 
dio200_subdev_intr_cancel(struct comedi_device * dev,struct comedi_subdevice * s)3318ffdff6aSGreg Kroah-Hartman static int dio200_subdev_intr_cancel(struct comedi_device *dev,
3328ffdff6aSGreg Kroah-Hartman 				     struct comedi_subdevice *s)
3338ffdff6aSGreg Kroah-Hartman {
3348ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
3358ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
3368ffdff6aSGreg Kroah-Hartman 
3378ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&subpriv->spinlock, flags);
3388ffdff6aSGreg Kroah-Hartman 	if (subpriv->active)
3398ffdff6aSGreg Kroah-Hartman 		dio200_stop_intr(dev, s);
3408ffdff6aSGreg Kroah-Hartman 
3418ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&subpriv->spinlock, flags);
3428ffdff6aSGreg Kroah-Hartman 
3438ffdff6aSGreg Kroah-Hartman 	return 0;
3448ffdff6aSGreg Kroah-Hartman }
3458ffdff6aSGreg Kroah-Hartman 
dio200_subdev_intr_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)3468ffdff6aSGreg Kroah-Hartman static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
3478ffdff6aSGreg Kroah-Hartman 				      struct comedi_subdevice *s,
3488ffdff6aSGreg Kroah-Hartman 				      struct comedi_cmd *cmd)
3498ffdff6aSGreg Kroah-Hartman {
3508ffdff6aSGreg Kroah-Hartman 	int err = 0;
3518ffdff6aSGreg Kroah-Hartman 
3528ffdff6aSGreg Kroah-Hartman 	/* Step 1 : check if triggers are trivially valid */
3538ffdff6aSGreg Kroah-Hartman 
3548ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
3558ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
3568ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
3578ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
3588ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
3598ffdff6aSGreg Kroah-Hartman 
3608ffdff6aSGreg Kroah-Hartman 	if (err)
3618ffdff6aSGreg Kroah-Hartman 		return 1;
3628ffdff6aSGreg Kroah-Hartman 
3638ffdff6aSGreg Kroah-Hartman 	/* Step 2a : make sure trigger sources are unique */
3648ffdff6aSGreg Kroah-Hartman 
3658ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->start_src);
3668ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
3678ffdff6aSGreg Kroah-Hartman 
3688ffdff6aSGreg Kroah-Hartman 	/* Step 2b : and mutually compatible */
3698ffdff6aSGreg Kroah-Hartman 
3708ffdff6aSGreg Kroah-Hartman 	if (err)
3718ffdff6aSGreg Kroah-Hartman 		return 2;
3728ffdff6aSGreg Kroah-Hartman 
3738ffdff6aSGreg Kroah-Hartman 	/* Step 3: check if arguments are trivially valid */
3748ffdff6aSGreg Kroah-Hartman 
3758ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
3768ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
3778ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
3788ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
3798ffdff6aSGreg Kroah-Hartman 					   cmd->chanlist_len);
3808ffdff6aSGreg Kroah-Hartman 
3818ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT)
3828ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
3838ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NONE */
3848ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
3858ffdff6aSGreg Kroah-Hartman 
3868ffdff6aSGreg Kroah-Hartman 	if (err)
3878ffdff6aSGreg Kroah-Hartman 		return 3;
3888ffdff6aSGreg Kroah-Hartman 
3898ffdff6aSGreg Kroah-Hartman 	/* step 4: fix up any arguments */
3908ffdff6aSGreg Kroah-Hartman 
3918ffdff6aSGreg Kroah-Hartman 	/* if (err) return 4; */
3928ffdff6aSGreg Kroah-Hartman 
3938ffdff6aSGreg Kroah-Hartman 	return 0;
3948ffdff6aSGreg Kroah-Hartman }
3958ffdff6aSGreg Kroah-Hartman 
dio200_subdev_intr_cmd(struct comedi_device * dev,struct comedi_subdevice * s)3968ffdff6aSGreg Kroah-Hartman static int dio200_subdev_intr_cmd(struct comedi_device *dev,
3978ffdff6aSGreg Kroah-Hartman 				  struct comedi_subdevice *s)
3988ffdff6aSGreg Kroah-Hartman {
3998ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
4008ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv = s->private;
4018ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
4028ffdff6aSGreg Kroah-Hartman 
4038ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&subpriv->spinlock, flags);
4048ffdff6aSGreg Kroah-Hartman 
4058ffdff6aSGreg Kroah-Hartman 	subpriv->active = true;
4068ffdff6aSGreg Kroah-Hartman 
4078ffdff6aSGreg Kroah-Hartman 	if (cmd->start_src == TRIG_INT)
4088ffdff6aSGreg Kroah-Hartman 		s->async->inttrig = dio200_inttrig_start_intr;
4098ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NOW */
4108ffdff6aSGreg Kroah-Hartman 		dio200_start_intr(dev, s);
4118ffdff6aSGreg Kroah-Hartman 
4128ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&subpriv->spinlock, flags);
4138ffdff6aSGreg Kroah-Hartman 
4148ffdff6aSGreg Kroah-Hartman 	return 0;
4158ffdff6aSGreg Kroah-Hartman }
4168ffdff6aSGreg Kroah-Hartman 
dio200_subdev_intr_init(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int offset,unsigned int valid_isns)4178ffdff6aSGreg Kroah-Hartman static int dio200_subdev_intr_init(struct comedi_device *dev,
4188ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
4198ffdff6aSGreg Kroah-Hartman 				   unsigned int offset,
4208ffdff6aSGreg Kroah-Hartman 				   unsigned int valid_isns)
4218ffdff6aSGreg Kroah-Hartman {
4228ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
4238ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_intr *subpriv;
4248ffdff6aSGreg Kroah-Hartman 
4258ffdff6aSGreg Kroah-Hartman 	subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
4268ffdff6aSGreg Kroah-Hartman 	if (!subpriv)
4278ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
4288ffdff6aSGreg Kroah-Hartman 
4298ffdff6aSGreg Kroah-Hartman 	subpriv->ofs = offset;
4308ffdff6aSGreg Kroah-Hartman 	subpriv->valid_isns = valid_isns;
4318ffdff6aSGreg Kroah-Hartman 	spin_lock_init(&subpriv->spinlock);
4328ffdff6aSGreg Kroah-Hartman 
4338ffdff6aSGreg Kroah-Hartman 	if (board->has_int_sce)
4348ffdff6aSGreg Kroah-Hartman 		/* Disable interrupt sources. */
4358ffdff6aSGreg Kroah-Hartman 		dio200_write8(dev, subpriv->ofs, 0);
4368ffdff6aSGreg Kroah-Hartman 
4378ffdff6aSGreg Kroah-Hartman 	s->type = COMEDI_SUBD_DI;
4388ffdff6aSGreg Kroah-Hartman 	s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED;
4398ffdff6aSGreg Kroah-Hartman 	if (board->has_int_sce) {
4408ffdff6aSGreg Kroah-Hartman 		s->n_chan = DIO200_MAX_ISNS;
4418ffdff6aSGreg Kroah-Hartman 		s->len_chanlist = DIO200_MAX_ISNS;
4428ffdff6aSGreg Kroah-Hartman 	} else {
4438ffdff6aSGreg Kroah-Hartman 		/* No interrupt source register.  Support single channel. */
4448ffdff6aSGreg Kroah-Hartman 		s->n_chan = 1;
4458ffdff6aSGreg Kroah-Hartman 		s->len_chanlist = 1;
4468ffdff6aSGreg Kroah-Hartman 	}
4478ffdff6aSGreg Kroah-Hartman 	s->range_table = &range_digital;
4488ffdff6aSGreg Kroah-Hartman 	s->maxdata = 1;
4498ffdff6aSGreg Kroah-Hartman 	s->insn_bits = dio200_subdev_intr_insn_bits;
4508ffdff6aSGreg Kroah-Hartman 	s->do_cmdtest = dio200_subdev_intr_cmdtest;
4518ffdff6aSGreg Kroah-Hartman 	s->do_cmd = dio200_subdev_intr_cmd;
4528ffdff6aSGreg Kroah-Hartman 	s->cancel = dio200_subdev_intr_cancel;
4538ffdff6aSGreg Kroah-Hartman 
4548ffdff6aSGreg Kroah-Hartman 	return 0;
4558ffdff6aSGreg Kroah-Hartman }
4568ffdff6aSGreg Kroah-Hartman 
dio200_interrupt(int irq,void * d)4578ffdff6aSGreg Kroah-Hartman static irqreturn_t dio200_interrupt(int irq, void *d)
4588ffdff6aSGreg Kroah-Hartman {
4598ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
4608ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s = dev->read_subdev;
4618ffdff6aSGreg Kroah-Hartman 	int handled;
4628ffdff6aSGreg Kroah-Hartman 
4638ffdff6aSGreg Kroah-Hartman 	if (!dev->attached)
4648ffdff6aSGreg Kroah-Hartman 		return IRQ_NONE;
4658ffdff6aSGreg Kroah-Hartman 
4668ffdff6aSGreg Kroah-Hartman 	handled = dio200_handle_read_intr(dev, s);
4678ffdff6aSGreg Kroah-Hartman 
4688ffdff6aSGreg Kroah-Hartman 	return IRQ_RETVAL(handled);
4698ffdff6aSGreg Kroah-Hartman }
4708ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8254_set_gate_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int chan,unsigned int src)4718ffdff6aSGreg Kroah-Hartman static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
4728ffdff6aSGreg Kroah-Hartman 					    struct comedi_subdevice *s,
4738ffdff6aSGreg Kroah-Hartman 					    unsigned int chan,
4748ffdff6aSGreg Kroah-Hartman 					    unsigned int src)
4758ffdff6aSGreg Kroah-Hartman {
4768ffdff6aSGreg Kroah-Hartman 	unsigned int offset = dio200_subdev_8254_offset(dev, s);
4778ffdff6aSGreg Kroah-Hartman 
4788ffdff6aSGreg Kroah-Hartman 	dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
4798ffdff6aSGreg Kroah-Hartman 		      clk_gat_sce((offset >> 2) & 1, chan, src));
4808ffdff6aSGreg Kroah-Hartman }
4818ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8254_set_clock_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int chan,unsigned int src)4828ffdff6aSGreg Kroah-Hartman static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
4838ffdff6aSGreg Kroah-Hartman 					     struct comedi_subdevice *s,
4848ffdff6aSGreg Kroah-Hartman 					     unsigned int chan,
4858ffdff6aSGreg Kroah-Hartman 					     unsigned int src)
4868ffdff6aSGreg Kroah-Hartman {
4878ffdff6aSGreg Kroah-Hartman 	unsigned int offset = dio200_subdev_8254_offset(dev, s);
4888ffdff6aSGreg Kroah-Hartman 
4898ffdff6aSGreg Kroah-Hartman 	dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
4908ffdff6aSGreg Kroah-Hartman 		      clk_gat_sce((offset >> 2) & 1, chan, src));
4918ffdff6aSGreg Kroah-Hartman }
4928ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8254_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)4938ffdff6aSGreg Kroah-Hartman static int dio200_subdev_8254_config(struct comedi_device *dev,
4948ffdff6aSGreg Kroah-Hartman 				     struct comedi_subdevice *s,
4958ffdff6aSGreg Kroah-Hartman 				     struct comedi_insn *insn,
4968ffdff6aSGreg Kroah-Hartman 				     unsigned int *data)
4978ffdff6aSGreg Kroah-Hartman {
4988ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
4998ffdff6aSGreg Kroah-Hartman 	struct comedi_8254 *i8254 = s->private;
5008ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
5018ffdff6aSGreg Kroah-Hartman 	unsigned int max_src = board->is_pcie ? 31 : 7;
5028ffdff6aSGreg Kroah-Hartman 	unsigned int src;
5038ffdff6aSGreg Kroah-Hartman 
5048ffdff6aSGreg Kroah-Hartman 	if (!board->has_clk_gat_sce)
5058ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
5068ffdff6aSGreg Kroah-Hartman 
5078ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
5088ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_SET_GATE_SRC:
5098ffdff6aSGreg Kroah-Hartman 		src = data[2];
5108ffdff6aSGreg Kroah-Hartman 		if (src > max_src)
5118ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
5128ffdff6aSGreg Kroah-Hartman 
5138ffdff6aSGreg Kroah-Hartman 		dio200_subdev_8254_set_gate_src(dev, s, chan, src);
5148ffdff6aSGreg Kroah-Hartman 		i8254->gate_src[chan] = src;
5158ffdff6aSGreg Kroah-Hartman 		break;
5168ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_GET_GATE_SRC:
5178ffdff6aSGreg Kroah-Hartman 		data[2] = i8254->gate_src[chan];
5188ffdff6aSGreg Kroah-Hartman 		break;
5198ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_SET_CLOCK_SRC:
5208ffdff6aSGreg Kroah-Hartman 		src = data[1];
5218ffdff6aSGreg Kroah-Hartman 		if (src > max_src)
5228ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
5238ffdff6aSGreg Kroah-Hartman 
5248ffdff6aSGreg Kroah-Hartman 		dio200_subdev_8254_set_clock_src(dev, s, chan, src);
5258ffdff6aSGreg Kroah-Hartman 		i8254->clock_src[chan] = src;
5268ffdff6aSGreg Kroah-Hartman 		break;
5278ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_GET_CLOCK_SRC:
5288ffdff6aSGreg Kroah-Hartman 		data[1] = i8254->clock_src[chan];
5298ffdff6aSGreg Kroah-Hartman 		data[2] = clock_period[i8254->clock_src[chan]];
5308ffdff6aSGreg Kroah-Hartman 		break;
5318ffdff6aSGreg Kroah-Hartman 	default:
5328ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
5338ffdff6aSGreg Kroah-Hartman 	}
5348ffdff6aSGreg Kroah-Hartman 
5358ffdff6aSGreg Kroah-Hartman 	return insn->n;
5368ffdff6aSGreg Kroah-Hartman }
5378ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8254_init(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int offset)5388ffdff6aSGreg Kroah-Hartman static int dio200_subdev_8254_init(struct comedi_device *dev,
5398ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
5408ffdff6aSGreg Kroah-Hartman 				   unsigned int offset)
5418ffdff6aSGreg Kroah-Hartman {
5428ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
5438ffdff6aSGreg Kroah-Hartman 	struct comedi_8254 *i8254;
5448ffdff6aSGreg Kroah-Hartman 	unsigned int regshift;
5458ffdff6aSGreg Kroah-Hartman 	int chan;
5468ffdff6aSGreg Kroah-Hartman 
5478ffdff6aSGreg Kroah-Hartman 	/*
5488ffdff6aSGreg Kroah-Hartman 	 * PCIe boards need the offset shifted in order to get the
5498ffdff6aSGreg Kroah-Hartman 	 * correct base address of the timer.
5508ffdff6aSGreg Kroah-Hartman 	 */
5518ffdff6aSGreg Kroah-Hartman 	if (board->is_pcie) {
5528ffdff6aSGreg Kroah-Hartman 		offset <<= 3;
5538ffdff6aSGreg Kroah-Hartman 		regshift = 3;
5548ffdff6aSGreg Kroah-Hartman 	} else {
5558ffdff6aSGreg Kroah-Hartman 		regshift = 0;
5568ffdff6aSGreg Kroah-Hartman 	}
5578ffdff6aSGreg Kroah-Hartman 
5588ffdff6aSGreg Kroah-Hartman 	if (dev->mmio) {
5598ffdff6aSGreg Kroah-Hartman 		i8254 = comedi_8254_mm_init(dev->mmio + offset,
5608ffdff6aSGreg Kroah-Hartman 					    0, I8254_IO8, regshift);
5618ffdff6aSGreg Kroah-Hartman 	} else {
5628ffdff6aSGreg Kroah-Hartman 		i8254 = comedi_8254_init(dev->iobase + offset,
5638ffdff6aSGreg Kroah-Hartman 					 0, I8254_IO8, regshift);
5648ffdff6aSGreg Kroah-Hartman 	}
5658ffdff6aSGreg Kroah-Hartman 	if (!i8254)
5668ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
5678ffdff6aSGreg Kroah-Hartman 
5688ffdff6aSGreg Kroah-Hartman 	comedi_8254_subdevice_init(s, i8254);
5698ffdff6aSGreg Kroah-Hartman 
5708ffdff6aSGreg Kroah-Hartman 	i8254->insn_config = dio200_subdev_8254_config;
5718ffdff6aSGreg Kroah-Hartman 
5728ffdff6aSGreg Kroah-Hartman 	/*
5738ffdff6aSGreg Kroah-Hartman 	 * There could be multiple timers so this driver does not
5748ffdff6aSGreg Kroah-Hartman 	 * use dev->pacer to save the i8254 pointer. Instead,
5758ffdff6aSGreg Kroah-Hartman 	 * comedi_8254_subdevice_init() saved the i8254 pointer in
5768ffdff6aSGreg Kroah-Hartman 	 * s->private.  Mark the subdevice as having private data
5778ffdff6aSGreg Kroah-Hartman 	 * to be automatically freed when the device is detached.
5788ffdff6aSGreg Kroah-Hartman 	 */
5798ffdff6aSGreg Kroah-Hartman 	comedi_set_spriv_auto_free(s);
5808ffdff6aSGreg Kroah-Hartman 
5818ffdff6aSGreg Kroah-Hartman 	/* Initialize channels. */
5828ffdff6aSGreg Kroah-Hartman 	if (board->has_clk_gat_sce) {
5838ffdff6aSGreg Kroah-Hartman 		for (chan = 0; chan < 3; chan++) {
5848ffdff6aSGreg Kroah-Hartman 			/* Gate source 0 is VCC (logic 1). */
5858ffdff6aSGreg Kroah-Hartman 			dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
5868ffdff6aSGreg Kroah-Hartman 			/* Clock source 0 is the dedicated clock input. */
5878ffdff6aSGreg Kroah-Hartman 			dio200_subdev_8254_set_clock_src(dev, s, chan, 0);
5888ffdff6aSGreg Kroah-Hartman 		}
5898ffdff6aSGreg Kroah-Hartman 	}
5908ffdff6aSGreg Kroah-Hartman 
5918ffdff6aSGreg Kroah-Hartman 	return 0;
5928ffdff6aSGreg Kroah-Hartman }
5938ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8255_set_dir(struct comedi_device * dev,struct comedi_subdevice * s)5948ffdff6aSGreg Kroah-Hartman static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
5958ffdff6aSGreg Kroah-Hartman 				       struct comedi_subdevice *s)
5968ffdff6aSGreg Kroah-Hartman {
5978ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_8255 *subpriv = s->private;
5988ffdff6aSGreg Kroah-Hartman 	int config;
5998ffdff6aSGreg Kroah-Hartman 
6008ffdff6aSGreg Kroah-Hartman 	config = I8255_CTRL_CW;
6018ffdff6aSGreg Kroah-Hartman 	/* 1 in io_bits indicates output, 1 in config indicates input */
6028ffdff6aSGreg Kroah-Hartman 	if (!(s->io_bits & 0x0000ff))
6038ffdff6aSGreg Kroah-Hartman 		config |= I8255_CTRL_A_IO;
6048ffdff6aSGreg Kroah-Hartman 	if (!(s->io_bits & 0x00ff00))
6058ffdff6aSGreg Kroah-Hartman 		config |= I8255_CTRL_B_IO;
6068ffdff6aSGreg Kroah-Hartman 	if (!(s->io_bits & 0x0f0000))
6078ffdff6aSGreg Kroah-Hartman 		config |= I8255_CTRL_C_LO_IO;
6088ffdff6aSGreg Kroah-Hartman 	if (!(s->io_bits & 0xf00000))
6098ffdff6aSGreg Kroah-Hartman 		config |= I8255_CTRL_C_HI_IO;
6108ffdff6aSGreg Kroah-Hartman 	dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config);
6118ffdff6aSGreg Kroah-Hartman }
6128ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8255_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6138ffdff6aSGreg Kroah-Hartman static int dio200_subdev_8255_bits(struct comedi_device *dev,
6148ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
6158ffdff6aSGreg Kroah-Hartman 				   struct comedi_insn *insn,
6168ffdff6aSGreg Kroah-Hartman 				   unsigned int *data)
6178ffdff6aSGreg Kroah-Hartman {
6188ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_8255 *subpriv = s->private;
6198ffdff6aSGreg Kroah-Hartman 	unsigned int mask;
6208ffdff6aSGreg Kroah-Hartman 	unsigned int val;
6218ffdff6aSGreg Kroah-Hartman 
6228ffdff6aSGreg Kroah-Hartman 	mask = comedi_dio_update_state(s, data);
6238ffdff6aSGreg Kroah-Hartman 	if (mask) {
6248ffdff6aSGreg Kroah-Hartman 		if (mask & 0xff) {
6258ffdff6aSGreg Kroah-Hartman 			dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG,
6268ffdff6aSGreg Kroah-Hartman 				      s->state & 0xff);
6278ffdff6aSGreg Kroah-Hartman 		}
6288ffdff6aSGreg Kroah-Hartman 		if (mask & 0xff00) {
6298ffdff6aSGreg Kroah-Hartman 			dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG,
6308ffdff6aSGreg Kroah-Hartman 				      (s->state >> 8) & 0xff);
6318ffdff6aSGreg Kroah-Hartman 		}
6328ffdff6aSGreg Kroah-Hartman 		if (mask & 0xff0000) {
6338ffdff6aSGreg Kroah-Hartman 			dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG,
6348ffdff6aSGreg Kroah-Hartman 				      (s->state >> 16) & 0xff);
6358ffdff6aSGreg Kroah-Hartman 		}
6368ffdff6aSGreg Kroah-Hartman 	}
6378ffdff6aSGreg Kroah-Hartman 
6388ffdff6aSGreg Kroah-Hartman 	val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG);
6398ffdff6aSGreg Kroah-Hartman 	val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8;
6408ffdff6aSGreg Kroah-Hartman 	val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16;
6418ffdff6aSGreg Kroah-Hartman 
6428ffdff6aSGreg Kroah-Hartman 	data[1] = val;
6438ffdff6aSGreg Kroah-Hartman 
6448ffdff6aSGreg Kroah-Hartman 	return insn->n;
6458ffdff6aSGreg Kroah-Hartman }
6468ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8255_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6478ffdff6aSGreg Kroah-Hartman static int dio200_subdev_8255_config(struct comedi_device *dev,
6488ffdff6aSGreg Kroah-Hartman 				     struct comedi_subdevice *s,
6498ffdff6aSGreg Kroah-Hartman 				     struct comedi_insn *insn,
6508ffdff6aSGreg Kroah-Hartman 				     unsigned int *data)
6518ffdff6aSGreg Kroah-Hartman {
6528ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
6538ffdff6aSGreg Kroah-Hartman 	unsigned int mask;
6548ffdff6aSGreg Kroah-Hartman 	int ret;
6558ffdff6aSGreg Kroah-Hartman 
6568ffdff6aSGreg Kroah-Hartman 	if (chan < 8)
6578ffdff6aSGreg Kroah-Hartman 		mask = 0x0000ff;
6588ffdff6aSGreg Kroah-Hartman 	else if (chan < 16)
6598ffdff6aSGreg Kroah-Hartman 		mask = 0x00ff00;
6608ffdff6aSGreg Kroah-Hartman 	else if (chan < 20)
6618ffdff6aSGreg Kroah-Hartman 		mask = 0x0f0000;
6628ffdff6aSGreg Kroah-Hartman 	else
6638ffdff6aSGreg Kroah-Hartman 		mask = 0xf00000;
6648ffdff6aSGreg Kroah-Hartman 
6658ffdff6aSGreg Kroah-Hartman 	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
6668ffdff6aSGreg Kroah-Hartman 	if (ret)
6678ffdff6aSGreg Kroah-Hartman 		return ret;
6688ffdff6aSGreg Kroah-Hartman 
6698ffdff6aSGreg Kroah-Hartman 	dio200_subdev_8255_set_dir(dev, s);
6708ffdff6aSGreg Kroah-Hartman 
6718ffdff6aSGreg Kroah-Hartman 	return insn->n;
6728ffdff6aSGreg Kroah-Hartman }
6738ffdff6aSGreg Kroah-Hartman 
dio200_subdev_8255_init(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int offset)6748ffdff6aSGreg Kroah-Hartman static int dio200_subdev_8255_init(struct comedi_device *dev,
6758ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
6768ffdff6aSGreg Kroah-Hartman 				   unsigned int offset)
6778ffdff6aSGreg Kroah-Hartman {
6788ffdff6aSGreg Kroah-Hartman 	struct dio200_subdev_8255 *subpriv;
6798ffdff6aSGreg Kroah-Hartman 
6808ffdff6aSGreg Kroah-Hartman 	subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
6818ffdff6aSGreg Kroah-Hartman 	if (!subpriv)
6828ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
6838ffdff6aSGreg Kroah-Hartman 
6848ffdff6aSGreg Kroah-Hartman 	subpriv->ofs = offset;
6858ffdff6aSGreg Kroah-Hartman 
6868ffdff6aSGreg Kroah-Hartman 	s->type = COMEDI_SUBD_DIO;
6878ffdff6aSGreg Kroah-Hartman 	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
6888ffdff6aSGreg Kroah-Hartman 	s->n_chan = 24;
6898ffdff6aSGreg Kroah-Hartman 	s->range_table = &range_digital;
6908ffdff6aSGreg Kroah-Hartman 	s->maxdata = 1;
6918ffdff6aSGreg Kroah-Hartman 	s->insn_bits = dio200_subdev_8255_bits;
6928ffdff6aSGreg Kroah-Hartman 	s->insn_config = dio200_subdev_8255_config;
6938ffdff6aSGreg Kroah-Hartman 	dio200_subdev_8255_set_dir(dev, s);
6948ffdff6aSGreg Kroah-Hartman 	return 0;
6958ffdff6aSGreg Kroah-Hartman }
6968ffdff6aSGreg Kroah-Hartman 
dio200_subdev_timer_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6978ffdff6aSGreg Kroah-Hartman static int dio200_subdev_timer_read(struct comedi_device *dev,
6988ffdff6aSGreg Kroah-Hartman 				    struct comedi_subdevice *s,
6998ffdff6aSGreg Kroah-Hartman 				    struct comedi_insn *insn,
7008ffdff6aSGreg Kroah-Hartman 				    unsigned int *data)
7018ffdff6aSGreg Kroah-Hartman {
7028ffdff6aSGreg Kroah-Hartman 	unsigned int n;
7038ffdff6aSGreg Kroah-Hartman 
7048ffdff6aSGreg Kroah-Hartman 	for (n = 0; n < insn->n; n++)
7058ffdff6aSGreg Kroah-Hartman 		data[n] = dio200_read32(dev, DIO200_TS_COUNT);
7068ffdff6aSGreg Kroah-Hartman 	return n;
7078ffdff6aSGreg Kroah-Hartman }
7088ffdff6aSGreg Kroah-Hartman 
dio200_subdev_timer_reset(struct comedi_device * dev,struct comedi_subdevice * s)7098ffdff6aSGreg Kroah-Hartman static void dio200_subdev_timer_reset(struct comedi_device *dev,
7108ffdff6aSGreg Kroah-Hartman 				      struct comedi_subdevice *s)
7118ffdff6aSGreg Kroah-Hartman {
7128ffdff6aSGreg Kroah-Hartman 	unsigned int clock;
7138ffdff6aSGreg Kroah-Hartman 
7148ffdff6aSGreg Kroah-Hartman 	clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
7158ffdff6aSGreg Kroah-Hartman 	dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET);
7168ffdff6aSGreg Kroah-Hartman 	dio200_write32(dev, DIO200_TS_CONFIG, clock);
7178ffdff6aSGreg Kroah-Hartman }
7188ffdff6aSGreg Kroah-Hartman 
dio200_subdev_timer_get_clock_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int * src,unsigned int * period)7198ffdff6aSGreg Kroah-Hartman static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
7208ffdff6aSGreg Kroah-Hartman 					      struct comedi_subdevice *s,
7218ffdff6aSGreg Kroah-Hartman 					      unsigned int *src,
7228ffdff6aSGreg Kroah-Hartman 					      unsigned int *period)
7238ffdff6aSGreg Kroah-Hartman {
7248ffdff6aSGreg Kroah-Hartman 	unsigned int clk;
7258ffdff6aSGreg Kroah-Hartman 
7268ffdff6aSGreg Kroah-Hartman 	clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
7278ffdff6aSGreg Kroah-Hartman 	*src = clk;
7288ffdff6aSGreg Kroah-Hartman 	*period = (clk < ARRAY_SIZE(ts_clock_period)) ?
7298ffdff6aSGreg Kroah-Hartman 		  ts_clock_period[clk] : 0;
7308ffdff6aSGreg Kroah-Hartman }
7318ffdff6aSGreg Kroah-Hartman 
dio200_subdev_timer_set_clock_src(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int src)7328ffdff6aSGreg Kroah-Hartman static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
7338ffdff6aSGreg Kroah-Hartman 					     struct comedi_subdevice *s,
7348ffdff6aSGreg Kroah-Hartman 					     unsigned int src)
7358ffdff6aSGreg Kroah-Hartman {
7368ffdff6aSGreg Kroah-Hartman 	if (src > TS_CONFIG_MAX_CLK_SRC)
7378ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
7388ffdff6aSGreg Kroah-Hartman 	dio200_write32(dev, DIO200_TS_CONFIG, src);
7398ffdff6aSGreg Kroah-Hartman 	return 0;
7408ffdff6aSGreg Kroah-Hartman }
7418ffdff6aSGreg Kroah-Hartman 
dio200_subdev_timer_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)7428ffdff6aSGreg Kroah-Hartman static int dio200_subdev_timer_config(struct comedi_device *dev,
7438ffdff6aSGreg Kroah-Hartman 				      struct comedi_subdevice *s,
7448ffdff6aSGreg Kroah-Hartman 				      struct comedi_insn *insn,
7458ffdff6aSGreg Kroah-Hartman 				      unsigned int *data)
7468ffdff6aSGreg Kroah-Hartman {
7478ffdff6aSGreg Kroah-Hartman 	int ret = 0;
7488ffdff6aSGreg Kroah-Hartman 
7498ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
7508ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_RESET:
7518ffdff6aSGreg Kroah-Hartman 		dio200_subdev_timer_reset(dev, s);
7528ffdff6aSGreg Kroah-Hartman 		break;
7538ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_SET_CLOCK_SRC:
7548ffdff6aSGreg Kroah-Hartman 		ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]);
7558ffdff6aSGreg Kroah-Hartman 		if (ret < 0)
7568ffdff6aSGreg Kroah-Hartman 			ret = -EINVAL;
7578ffdff6aSGreg Kroah-Hartman 		break;
7588ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_GET_CLOCK_SRC:
7598ffdff6aSGreg Kroah-Hartman 		dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]);
7608ffdff6aSGreg Kroah-Hartman 		break;
7618ffdff6aSGreg Kroah-Hartman 	default:
7628ffdff6aSGreg Kroah-Hartman 		ret = -EINVAL;
7638ffdff6aSGreg Kroah-Hartman 		break;
7648ffdff6aSGreg Kroah-Hartman 	}
7658ffdff6aSGreg Kroah-Hartman 	return ret < 0 ? ret : insn->n;
7668ffdff6aSGreg Kroah-Hartman }
7678ffdff6aSGreg Kroah-Hartman 
amplc_dio200_set_enhance(struct comedi_device * dev,unsigned char val)7688ffdff6aSGreg Kroah-Hartman void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val)
7698ffdff6aSGreg Kroah-Hartman {
7708ffdff6aSGreg Kroah-Hartman 	dio200_write8(dev, DIO200_ENHANCE, val);
7718ffdff6aSGreg Kroah-Hartman }
7728ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance);
7738ffdff6aSGreg Kroah-Hartman 
amplc_dio200_common_attach(struct comedi_device * dev,unsigned int irq,unsigned long req_irq_flags)7748ffdff6aSGreg Kroah-Hartman int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
7758ffdff6aSGreg Kroah-Hartman 			       unsigned long req_irq_flags)
7768ffdff6aSGreg Kroah-Hartman {
7778ffdff6aSGreg Kroah-Hartman 	const struct dio200_board *board = dev->board_ptr;
7788ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
7798ffdff6aSGreg Kroah-Hartman 	unsigned int n;
7808ffdff6aSGreg Kroah-Hartman 	int ret;
7818ffdff6aSGreg Kroah-Hartman 
7828ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, board->n_subdevs);
7838ffdff6aSGreg Kroah-Hartman 	if (ret)
7848ffdff6aSGreg Kroah-Hartman 		return ret;
7858ffdff6aSGreg Kroah-Hartman 
7868ffdff6aSGreg Kroah-Hartman 	for (n = 0; n < dev->n_subdevices; n++) {
7878ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[n];
7888ffdff6aSGreg Kroah-Hartman 		switch (board->sdtype[n]) {
7898ffdff6aSGreg Kroah-Hartman 		case sd_8254:
7908ffdff6aSGreg Kroah-Hartman 			/* counter subdevice (8254) */
7918ffdff6aSGreg Kroah-Hartman 			ret = dio200_subdev_8254_init(dev, s,
7928ffdff6aSGreg Kroah-Hartman 						      board->sdinfo[n]);
7938ffdff6aSGreg Kroah-Hartman 			if (ret < 0)
7948ffdff6aSGreg Kroah-Hartman 				return ret;
7958ffdff6aSGreg Kroah-Hartman 			break;
7968ffdff6aSGreg Kroah-Hartman 		case sd_8255:
7978ffdff6aSGreg Kroah-Hartman 			/* digital i/o subdevice (8255) */
7988ffdff6aSGreg Kroah-Hartman 			ret = dio200_subdev_8255_init(dev, s,
7998ffdff6aSGreg Kroah-Hartman 						      board->sdinfo[n]);
8008ffdff6aSGreg Kroah-Hartman 			if (ret < 0)
8018ffdff6aSGreg Kroah-Hartman 				return ret;
8028ffdff6aSGreg Kroah-Hartman 			break;
8038ffdff6aSGreg Kroah-Hartman 		case sd_intr:
8048ffdff6aSGreg Kroah-Hartman 			/* 'INTERRUPT' subdevice */
8058ffdff6aSGreg Kroah-Hartman 			if (irq && !dev->read_subdev) {
8068ffdff6aSGreg Kroah-Hartman 				ret = dio200_subdev_intr_init(dev, s,
8078ffdff6aSGreg Kroah-Hartman 							      DIO200_INT_SCE,
8088ffdff6aSGreg Kroah-Hartman 							      board->sdinfo[n]);
8098ffdff6aSGreg Kroah-Hartman 				if (ret < 0)
8108ffdff6aSGreg Kroah-Hartman 					return ret;
8118ffdff6aSGreg Kroah-Hartman 				dev->read_subdev = s;
8128ffdff6aSGreg Kroah-Hartman 			} else {
8138ffdff6aSGreg Kroah-Hartman 				s->type = COMEDI_SUBD_UNUSED;
8148ffdff6aSGreg Kroah-Hartman 			}
8158ffdff6aSGreg Kroah-Hartman 			break;
8168ffdff6aSGreg Kroah-Hartman 		case sd_timer:
8178ffdff6aSGreg Kroah-Hartman 			s->type		= COMEDI_SUBD_TIMER;
8188ffdff6aSGreg Kroah-Hartman 			s->subdev_flags	= SDF_READABLE | SDF_LSAMPL;
8198ffdff6aSGreg Kroah-Hartman 			s->n_chan	= 1;
8208ffdff6aSGreg Kroah-Hartman 			s->maxdata	= 0xffffffff;
8218ffdff6aSGreg Kroah-Hartman 			s->insn_read	= dio200_subdev_timer_read;
8228ffdff6aSGreg Kroah-Hartman 			s->insn_config	= dio200_subdev_timer_config;
8238ffdff6aSGreg Kroah-Hartman 			break;
8248ffdff6aSGreg Kroah-Hartman 		default:
8258ffdff6aSGreg Kroah-Hartman 			s->type = COMEDI_SUBD_UNUSED;
8268ffdff6aSGreg Kroah-Hartman 			break;
8278ffdff6aSGreg Kroah-Hartman 		}
8288ffdff6aSGreg Kroah-Hartman 	}
8298ffdff6aSGreg Kroah-Hartman 
8308ffdff6aSGreg Kroah-Hartman 	if (irq && dev->read_subdev) {
8318ffdff6aSGreg Kroah-Hartman 		if (request_irq(irq, dio200_interrupt, req_irq_flags,
8328ffdff6aSGreg Kroah-Hartman 				dev->board_name, dev) >= 0) {
8338ffdff6aSGreg Kroah-Hartman 			dev->irq = irq;
8348ffdff6aSGreg Kroah-Hartman 		} else {
8358ffdff6aSGreg Kroah-Hartman 			dev_warn(dev->class_dev,
8368ffdff6aSGreg Kroah-Hartman 				 "warning! irq %u unavailable!\n", irq);
8378ffdff6aSGreg Kroah-Hartman 		}
8388ffdff6aSGreg Kroah-Hartman 	}
8398ffdff6aSGreg Kroah-Hartman 
8408ffdff6aSGreg Kroah-Hartman 	return 0;
8418ffdff6aSGreg Kroah-Hartman }
8428ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(amplc_dio200_common_attach);
8438ffdff6aSGreg Kroah-Hartman 
amplc_dio200_common_init(void)8448ffdff6aSGreg Kroah-Hartman static int __init amplc_dio200_common_init(void)
8458ffdff6aSGreg Kroah-Hartman {
8468ffdff6aSGreg Kroah-Hartman 	return 0;
8478ffdff6aSGreg Kroah-Hartman }
8488ffdff6aSGreg Kroah-Hartman module_init(amplc_dio200_common_init);
8498ffdff6aSGreg Kroah-Hartman 
amplc_dio200_common_exit(void)8508ffdff6aSGreg Kroah-Hartman static void __exit amplc_dio200_common_exit(void)
8518ffdff6aSGreg Kroah-Hartman {
8528ffdff6aSGreg Kroah-Hartman }
8538ffdff6aSGreg Kroah-Hartman module_exit(amplc_dio200_common_exit);
8548ffdff6aSGreg Kroah-Hartman 
8558ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
8568ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci");
8578ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
858