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