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> 15*df0e68c1SIan Abbott #include <linux/comedi/comedidev.h> 168ffdff6aSGreg Kroah-Hartman 178ffdff6aSGreg Kroah-Hartman #include "amplc_dio200.h" 188ffdff6aSGreg Kroah-Hartman #include "comedi_8254.h" 198ffdff6aSGreg Kroah-Hartman #include "8255.h" /* only for register defines */ 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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