18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman * addi_apci_3120.c
48ffdff6aSGreg Kroah-Hartman * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
58ffdff6aSGreg Kroah-Hartman *
68ffdff6aSGreg Kroah-Hartman * ADDI-DATA GmbH
78ffdff6aSGreg Kroah-Hartman * Dieselstrasse 3
88ffdff6aSGreg Kroah-Hartman * D-77833 Ottersweier
98ffdff6aSGreg Kroah-Hartman * Tel: +19(0)7223/9493-0
108ffdff6aSGreg Kroah-Hartman * Fax: +49(0)7223/9493-92
118ffdff6aSGreg Kroah-Hartman * http://www.addi-data.com
128ffdff6aSGreg Kroah-Hartman * info@addi-data.com
138ffdff6aSGreg Kroah-Hartman */
148ffdff6aSGreg Kroah-Hartman
158ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
168ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
17*df0e68c1SIan Abbott #include <linux/comedi/comedi_pci.h>
188ffdff6aSGreg Kroah-Hartman
198ffdff6aSGreg Kroah-Hartman #include "amcc_s5933.h"
208ffdff6aSGreg Kroah-Hartman
218ffdff6aSGreg Kroah-Hartman /*
228ffdff6aSGreg Kroah-Hartman * PCI BAR 0 register map (devpriv->amcc)
238ffdff6aSGreg Kroah-Hartman * see amcc_s5933.h for register and bit defines
248ffdff6aSGreg Kroah-Hartman */
258ffdff6aSGreg Kroah-Hartman #define APCI3120_FIFO_ADVANCE_ON_BYTE_2 BIT(29)
268ffdff6aSGreg Kroah-Hartman
278ffdff6aSGreg Kroah-Hartman /*
288ffdff6aSGreg Kroah-Hartman * PCI BAR 1 register map (dev->iobase)
298ffdff6aSGreg Kroah-Hartman */
308ffdff6aSGreg Kroah-Hartman #define APCI3120_AI_FIFO_REG 0x00
318ffdff6aSGreg Kroah-Hartman #define APCI3120_CTRL_REG 0x00
328ffdff6aSGreg Kroah-Hartman #define APCI3120_CTRL_EXT_TRIG BIT(15)
338ffdff6aSGreg Kroah-Hartman #define APCI3120_CTRL_GATE(x) BIT(12 + (x))
348ffdff6aSGreg Kroah-Hartman #define APCI3120_CTRL_PR(x) (((x) & 0xf) << 8)
358ffdff6aSGreg Kroah-Hartman #define APCI3120_CTRL_PA(x) (((x) & 0xf) << 0)
368ffdff6aSGreg Kroah-Hartman #define APCI3120_AI_SOFTTRIG_REG 0x02
378ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_REG 0x02
388ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_EOC_INT BIT(15)
398ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_AMCC_INT BIT(14)
408ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_EOS_INT BIT(13)
418ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_TIMER2_INT BIT(12)
428ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_INT_MASK (0xf << 12)
438ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_TO_DI_BITS(x) (((x) >> 8) & 0xf)
448ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_TO_VERSION(x) (((x) >> 4) & 0xf)
458ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_FIFO_FULL BIT(2)
468ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_FIFO_EMPTY BIT(1)
478ffdff6aSGreg Kroah-Hartman #define APCI3120_STATUS_DA_READY BIT(0)
488ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_REG 0x04
498ffdff6aSGreg Kroah-Hartman #define APCI3120_CHANLIST_REG 0x06
508ffdff6aSGreg Kroah-Hartman #define APCI3120_CHANLIST_INDEX(x) (((x) & 0xf) << 8)
518ffdff6aSGreg Kroah-Hartman #define APCI3120_CHANLIST_UNIPOLAR BIT(7)
528ffdff6aSGreg Kroah-Hartman #define APCI3120_CHANLIST_GAIN(x) (((x) & 0x3) << 4)
538ffdff6aSGreg Kroah-Hartman #define APCI3120_CHANLIST_MUX(x) (((x) & 0xf) << 0)
548ffdff6aSGreg Kroah-Hartman #define APCI3120_AO_REG(x) (0x08 + (((x) / 4) * 2))
558ffdff6aSGreg Kroah-Hartman #define APCI3120_AO_MUX(x) (((x) & 0x3) << 14)
568ffdff6aSGreg Kroah-Hartman #define APCI3120_AO_DATA(x) ((x) << 0)
578ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE_REG 0x0c
588ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE(_t, _m) ((_m) << ((_t) * 2))
598ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE0 0 /* I8254_MODE0 */
608ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE2 1 /* I8254_MODE2 */
618ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE4 2 /* I8254_MODE4 */
628ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE5 3 /* I8254_MODE5 */
638ffdff6aSGreg Kroah-Hartman #define APCI3120_TIMER_MODE_MASK(_t) (3 << ((_t) * 2))
648ffdff6aSGreg Kroah-Hartman #define APCI3120_CTR0_REG 0x0d
658ffdff6aSGreg Kroah-Hartman #define APCI3120_CTR0_DO_BITS(x) ((x) << 4)
668ffdff6aSGreg Kroah-Hartman #define APCI3120_CTR0_TIMER_SEL(x) ((x) << 0)
678ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_REG 0x0e
688ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_CLK(x) (((x) & 0x3) << 6)
698ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_CLK_OSC APCI3120_MODE_TIMER2_CLK(0)
708ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_CLK_OUT1 APCI3120_MODE_TIMER2_CLK(1)
718ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_CLK_EOC APCI3120_MODE_TIMER2_CLK(2)
728ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_CLK_EOS APCI3120_MODE_TIMER2_CLK(3)
738ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_CLK_MASK APCI3120_MODE_TIMER2_CLK(3)
748ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_AS(x) (((x) & 0x3) << 4)
758ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_AS_TIMER APCI3120_MODE_TIMER2_AS(0)
768ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_AS_COUNTER APCI3120_MODE_TIMER2_AS(1)
778ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_AS_WDOG APCI3120_MODE_TIMER2_AS(2)
788ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_AS_MASK APCI3120_MODE_TIMER2_AS(3)
798ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_SCAN_ENA BIT(3)
808ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_TIMER2_IRQ_ENA BIT(2)
818ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_EOS_IRQ_ENA BIT(1)
828ffdff6aSGreg Kroah-Hartman #define APCI3120_MODE_EOC_IRQ_ENA BIT(0)
838ffdff6aSGreg Kroah-Hartman
848ffdff6aSGreg Kroah-Hartman /*
858ffdff6aSGreg Kroah-Hartman * PCI BAR 2 register map (devpriv->addon)
868ffdff6aSGreg Kroah-Hartman */
878ffdff6aSGreg Kroah-Hartman #define APCI3120_ADDON_ADDR_REG 0x00
888ffdff6aSGreg Kroah-Hartman #define APCI3120_ADDON_DATA_REG 0x02
898ffdff6aSGreg Kroah-Hartman #define APCI3120_ADDON_CTRL_REG 0x04
908ffdff6aSGreg Kroah-Hartman #define APCI3120_ADDON_CTRL_AMWEN_ENA BIT(1)
918ffdff6aSGreg Kroah-Hartman #define APCI3120_ADDON_CTRL_A2P_FIFO_ENA BIT(0)
928ffdff6aSGreg Kroah-Hartman
938ffdff6aSGreg Kroah-Hartman /*
948ffdff6aSGreg Kroah-Hartman * Board revisions
958ffdff6aSGreg Kroah-Hartman */
968ffdff6aSGreg Kroah-Hartman #define APCI3120_REVA 0xa
978ffdff6aSGreg Kroah-Hartman #define APCI3120_REVB 0xb
988ffdff6aSGreg Kroah-Hartman #define APCI3120_REVA_OSC_BASE 70 /* 70ns = 14.29MHz */
998ffdff6aSGreg Kroah-Hartman #define APCI3120_REVB_OSC_BASE 50 /* 50ns = 20MHz */
1008ffdff6aSGreg Kroah-Hartman
1018ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange apci3120_ai_range = {
1028ffdff6aSGreg Kroah-Hartman 8, {
1038ffdff6aSGreg Kroah-Hartman BIP_RANGE(10),
1048ffdff6aSGreg Kroah-Hartman BIP_RANGE(5),
1058ffdff6aSGreg Kroah-Hartman BIP_RANGE(2),
1068ffdff6aSGreg Kroah-Hartman BIP_RANGE(1),
1078ffdff6aSGreg Kroah-Hartman UNI_RANGE(10),
1088ffdff6aSGreg Kroah-Hartman UNI_RANGE(5),
1098ffdff6aSGreg Kroah-Hartman UNI_RANGE(2),
1108ffdff6aSGreg Kroah-Hartman UNI_RANGE(1)
1118ffdff6aSGreg Kroah-Hartman }
1128ffdff6aSGreg Kroah-Hartman };
1138ffdff6aSGreg Kroah-Hartman
1148ffdff6aSGreg Kroah-Hartman enum apci3120_boardid {
1158ffdff6aSGreg Kroah-Hartman BOARD_APCI3120,
1168ffdff6aSGreg Kroah-Hartman BOARD_APCI3001,
1178ffdff6aSGreg Kroah-Hartman };
1188ffdff6aSGreg Kroah-Hartman
1198ffdff6aSGreg Kroah-Hartman struct apci3120_board {
1208ffdff6aSGreg Kroah-Hartman const char *name;
1218ffdff6aSGreg Kroah-Hartman unsigned int ai_is_16bit:1;
1228ffdff6aSGreg Kroah-Hartman unsigned int has_ao:1;
1238ffdff6aSGreg Kroah-Hartman };
1248ffdff6aSGreg Kroah-Hartman
1258ffdff6aSGreg Kroah-Hartman static const struct apci3120_board apci3120_boardtypes[] = {
1268ffdff6aSGreg Kroah-Hartman [BOARD_APCI3120] = {
1278ffdff6aSGreg Kroah-Hartman .name = "apci3120",
1288ffdff6aSGreg Kroah-Hartman .ai_is_16bit = 1,
1298ffdff6aSGreg Kroah-Hartman .has_ao = 1,
1308ffdff6aSGreg Kroah-Hartman },
1318ffdff6aSGreg Kroah-Hartman [BOARD_APCI3001] = {
1328ffdff6aSGreg Kroah-Hartman .name = "apci3001",
1338ffdff6aSGreg Kroah-Hartman },
1348ffdff6aSGreg Kroah-Hartman };
1358ffdff6aSGreg Kroah-Hartman
1368ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf {
1378ffdff6aSGreg Kroah-Hartman unsigned short *virt;
1388ffdff6aSGreg Kroah-Hartman dma_addr_t hw;
1398ffdff6aSGreg Kroah-Hartman unsigned int size;
1408ffdff6aSGreg Kroah-Hartman unsigned int use_size;
1418ffdff6aSGreg Kroah-Hartman };
1428ffdff6aSGreg Kroah-Hartman
1438ffdff6aSGreg Kroah-Hartman struct apci3120_private {
1448ffdff6aSGreg Kroah-Hartman unsigned long amcc;
1458ffdff6aSGreg Kroah-Hartman unsigned long addon;
1468ffdff6aSGreg Kroah-Hartman unsigned int osc_base;
1478ffdff6aSGreg Kroah-Hartman unsigned int use_dma:1;
1488ffdff6aSGreg Kroah-Hartman unsigned int use_double_buffer:1;
1498ffdff6aSGreg Kroah-Hartman unsigned int cur_dmabuf:1;
1508ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf dmabuf[2];
1518ffdff6aSGreg Kroah-Hartman unsigned char do_bits;
1528ffdff6aSGreg Kroah-Hartman unsigned char timer_mode;
1538ffdff6aSGreg Kroah-Hartman unsigned char mode;
1548ffdff6aSGreg Kroah-Hartman unsigned short ctrl;
1558ffdff6aSGreg Kroah-Hartman };
1568ffdff6aSGreg Kroah-Hartman
apci3120_addon_write(struct comedi_device * dev,unsigned int val,unsigned int reg)1578ffdff6aSGreg Kroah-Hartman static void apci3120_addon_write(struct comedi_device *dev,
1588ffdff6aSGreg Kroah-Hartman unsigned int val, unsigned int reg)
1598ffdff6aSGreg Kroah-Hartman {
1608ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
1618ffdff6aSGreg Kroah-Hartman
1628ffdff6aSGreg Kroah-Hartman /* 16-bit interface for AMCC add-on registers */
1638ffdff6aSGreg Kroah-Hartman
1648ffdff6aSGreg Kroah-Hartman outw(reg, devpriv->addon + APCI3120_ADDON_ADDR_REG);
1658ffdff6aSGreg Kroah-Hartman outw(val & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG);
1668ffdff6aSGreg Kroah-Hartman
1678ffdff6aSGreg Kroah-Hartman outw(reg + 2, devpriv->addon + APCI3120_ADDON_ADDR_REG);
1688ffdff6aSGreg Kroah-Hartman outw((val >> 16) & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG);
1698ffdff6aSGreg Kroah-Hartman }
1708ffdff6aSGreg Kroah-Hartman
apci3120_init_dma(struct comedi_device * dev,struct apci3120_dmabuf * dmabuf)1718ffdff6aSGreg Kroah-Hartman static void apci3120_init_dma(struct comedi_device *dev,
1728ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf *dmabuf)
1738ffdff6aSGreg Kroah-Hartman {
1748ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
1758ffdff6aSGreg Kroah-Hartman
1768ffdff6aSGreg Kroah-Hartman /* AMCC - enable transfer count and reset A2P FIFO */
1778ffdff6aSGreg Kroah-Hartman outl(AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO,
1788ffdff6aSGreg Kroah-Hartman devpriv->amcc + AMCC_OP_REG_AGCSTS);
1798ffdff6aSGreg Kroah-Hartman
1808ffdff6aSGreg Kroah-Hartman /* Add-On - enable transfer count and reset A2P FIFO */
1818ffdff6aSGreg Kroah-Hartman apci3120_addon_write(dev, AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO,
1828ffdff6aSGreg Kroah-Hartman AMCC_OP_REG_AGCSTS);
1838ffdff6aSGreg Kroah-Hartman
1848ffdff6aSGreg Kroah-Hartman /* AMCC - enable transfers and reset A2P flags */
1858ffdff6aSGreg Kroah-Hartman outl(RESET_A2P_FLAGS | EN_A2P_TRANSFERS,
1868ffdff6aSGreg Kroah-Hartman devpriv->amcc + AMCC_OP_REG_MCSR);
1878ffdff6aSGreg Kroah-Hartman
1888ffdff6aSGreg Kroah-Hartman /* Add-On - DMA start address */
1898ffdff6aSGreg Kroah-Hartman apci3120_addon_write(dev, dmabuf->hw, AMCC_OP_REG_AMWAR);
1908ffdff6aSGreg Kroah-Hartman
1918ffdff6aSGreg Kroah-Hartman /* Add-On - Number of acquisitions */
1928ffdff6aSGreg Kroah-Hartman apci3120_addon_write(dev, dmabuf->use_size, AMCC_OP_REG_AMWTC);
1938ffdff6aSGreg Kroah-Hartman
1948ffdff6aSGreg Kroah-Hartman /* AMCC - enable write complete (DMA) and set FIFO advance */
1958ffdff6aSGreg Kroah-Hartman outl(APCI3120_FIFO_ADVANCE_ON_BYTE_2 | AINT_WRITE_COMPL,
1968ffdff6aSGreg Kroah-Hartman devpriv->amcc + AMCC_OP_REG_INTCSR);
1978ffdff6aSGreg Kroah-Hartman
1988ffdff6aSGreg Kroah-Hartman /* Add-On - enable DMA */
1998ffdff6aSGreg Kroah-Hartman outw(APCI3120_ADDON_CTRL_AMWEN_ENA | APCI3120_ADDON_CTRL_A2P_FIFO_ENA,
2008ffdff6aSGreg Kroah-Hartman devpriv->addon + APCI3120_ADDON_CTRL_REG);
2018ffdff6aSGreg Kroah-Hartman }
2028ffdff6aSGreg Kroah-Hartman
apci3120_setup_dma(struct comedi_device * dev,struct comedi_subdevice * s)2038ffdff6aSGreg Kroah-Hartman static void apci3120_setup_dma(struct comedi_device *dev,
2048ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s)
2058ffdff6aSGreg Kroah-Hartman {
2068ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
2078ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd;
2088ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf *dmabuf0 = &devpriv->dmabuf[0];
2098ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf *dmabuf1 = &devpriv->dmabuf[1];
2108ffdff6aSGreg Kroah-Hartman unsigned int dmalen0 = dmabuf0->size;
2118ffdff6aSGreg Kroah-Hartman unsigned int dmalen1 = dmabuf1->size;
2128ffdff6aSGreg Kroah-Hartman unsigned int scan_bytes;
2138ffdff6aSGreg Kroah-Hartman
2148ffdff6aSGreg Kroah-Hartman scan_bytes = comedi_samples_to_bytes(s, cmd->scan_end_arg);
2158ffdff6aSGreg Kroah-Hartman
2168ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT) {
2178ffdff6aSGreg Kroah-Hartman /*
2188ffdff6aSGreg Kroah-Hartman * Must we fill full first buffer? And must we fill
2198ffdff6aSGreg Kroah-Hartman * full second buffer when first is once filled?
2208ffdff6aSGreg Kroah-Hartman */
2218ffdff6aSGreg Kroah-Hartman if (dmalen0 > (cmd->stop_arg * scan_bytes))
2228ffdff6aSGreg Kroah-Hartman dmalen0 = cmd->stop_arg * scan_bytes;
2238ffdff6aSGreg Kroah-Hartman else if (dmalen1 > (cmd->stop_arg * scan_bytes - dmalen0))
2248ffdff6aSGreg Kroah-Hartman dmalen1 = cmd->stop_arg * scan_bytes - dmalen0;
2258ffdff6aSGreg Kroah-Hartman }
2268ffdff6aSGreg Kroah-Hartman
2278ffdff6aSGreg Kroah-Hartman if (cmd->flags & CMDF_WAKE_EOS) {
2288ffdff6aSGreg Kroah-Hartman /* don't we want wake up every scan? */
2298ffdff6aSGreg Kroah-Hartman if (dmalen0 > scan_bytes) {
2308ffdff6aSGreg Kroah-Hartman dmalen0 = scan_bytes;
2318ffdff6aSGreg Kroah-Hartman if (cmd->scan_end_arg & 1)
2328ffdff6aSGreg Kroah-Hartman dmalen0 += 2;
2338ffdff6aSGreg Kroah-Hartman }
2348ffdff6aSGreg Kroah-Hartman if (dmalen1 > scan_bytes) {
2358ffdff6aSGreg Kroah-Hartman dmalen1 = scan_bytes;
2368ffdff6aSGreg Kroah-Hartman if (cmd->scan_end_arg & 1)
2378ffdff6aSGreg Kroah-Hartman dmalen1 -= 2;
2388ffdff6aSGreg Kroah-Hartman if (dmalen1 < 4)
2398ffdff6aSGreg Kroah-Hartman dmalen1 = 4;
2408ffdff6aSGreg Kroah-Hartman }
2418ffdff6aSGreg Kroah-Hartman } else {
2428ffdff6aSGreg Kroah-Hartman /* isn't output buff smaller that our DMA buff? */
2438ffdff6aSGreg Kroah-Hartman if (dmalen0 > s->async->prealloc_bufsz)
2448ffdff6aSGreg Kroah-Hartman dmalen0 = s->async->prealloc_bufsz;
2458ffdff6aSGreg Kroah-Hartman if (dmalen1 > s->async->prealloc_bufsz)
2468ffdff6aSGreg Kroah-Hartman dmalen1 = s->async->prealloc_bufsz;
2478ffdff6aSGreg Kroah-Hartman }
2488ffdff6aSGreg Kroah-Hartman dmabuf0->use_size = dmalen0;
2498ffdff6aSGreg Kroah-Hartman dmabuf1->use_size = dmalen1;
2508ffdff6aSGreg Kroah-Hartman
2518ffdff6aSGreg Kroah-Hartman apci3120_init_dma(dev, dmabuf0);
2528ffdff6aSGreg Kroah-Hartman }
2538ffdff6aSGreg Kroah-Hartman
2548ffdff6aSGreg Kroah-Hartman /*
2558ffdff6aSGreg Kroah-Hartman * There are three timers on the board. They all use the same base
2568ffdff6aSGreg Kroah-Hartman * clock with a fixed prescaler for each timer. The base clock used
2578ffdff6aSGreg Kroah-Hartman * depends on the board version and type.
2588ffdff6aSGreg Kroah-Hartman *
2598ffdff6aSGreg Kroah-Hartman * APCI-3120 Rev A boards OSC = 14.29MHz base clock (~70ns)
2608ffdff6aSGreg Kroah-Hartman * APCI-3120 Rev B boards OSC = 20MHz base clock (50ns)
2618ffdff6aSGreg Kroah-Hartman * APCI-3001 boards OSC = 20MHz base clock (50ns)
2628ffdff6aSGreg Kroah-Hartman *
2638ffdff6aSGreg Kroah-Hartman * The prescalers for each timer are:
2648ffdff6aSGreg Kroah-Hartman * Timer 0 CLK = OSC/10
2658ffdff6aSGreg Kroah-Hartman * Timer 1 CLK = OSC/1000
2668ffdff6aSGreg Kroah-Hartman * Timer 2 CLK = OSC/1000
2678ffdff6aSGreg Kroah-Hartman */
apci3120_ns_to_timer(struct comedi_device * dev,unsigned int timer,unsigned int ns,unsigned int flags)2688ffdff6aSGreg Kroah-Hartman static unsigned int apci3120_ns_to_timer(struct comedi_device *dev,
2698ffdff6aSGreg Kroah-Hartman unsigned int timer,
2708ffdff6aSGreg Kroah-Hartman unsigned int ns,
2718ffdff6aSGreg Kroah-Hartman unsigned int flags)
2728ffdff6aSGreg Kroah-Hartman {
2738ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
2748ffdff6aSGreg Kroah-Hartman unsigned int prescale = (timer == 0) ? 10 : 1000;
2758ffdff6aSGreg Kroah-Hartman unsigned int timer_base = devpriv->osc_base * prescale;
2768ffdff6aSGreg Kroah-Hartman unsigned int divisor;
2778ffdff6aSGreg Kroah-Hartman
2788ffdff6aSGreg Kroah-Hartman switch (flags & CMDF_ROUND_MASK) {
2798ffdff6aSGreg Kroah-Hartman case CMDF_ROUND_UP:
2808ffdff6aSGreg Kroah-Hartman divisor = DIV_ROUND_UP(ns, timer_base);
2818ffdff6aSGreg Kroah-Hartman break;
2828ffdff6aSGreg Kroah-Hartman case CMDF_ROUND_DOWN:
2838ffdff6aSGreg Kroah-Hartman divisor = ns / timer_base;
2848ffdff6aSGreg Kroah-Hartman break;
2858ffdff6aSGreg Kroah-Hartman case CMDF_ROUND_NEAREST:
2868ffdff6aSGreg Kroah-Hartman default:
2878ffdff6aSGreg Kroah-Hartman divisor = DIV_ROUND_CLOSEST(ns, timer_base);
2888ffdff6aSGreg Kroah-Hartman break;
2898ffdff6aSGreg Kroah-Hartman }
2908ffdff6aSGreg Kroah-Hartman
2918ffdff6aSGreg Kroah-Hartman if (timer == 2) {
2928ffdff6aSGreg Kroah-Hartman /* timer 2 is 24-bits */
2938ffdff6aSGreg Kroah-Hartman if (divisor > 0x00ffffff)
2948ffdff6aSGreg Kroah-Hartman divisor = 0x00ffffff;
2958ffdff6aSGreg Kroah-Hartman } else {
2968ffdff6aSGreg Kroah-Hartman /* timers 0 and 1 are 16-bits */
2978ffdff6aSGreg Kroah-Hartman if (divisor > 0xffff)
2988ffdff6aSGreg Kroah-Hartman divisor = 0xffff;
2998ffdff6aSGreg Kroah-Hartman }
3008ffdff6aSGreg Kroah-Hartman /* the timers require a minimum divisor of 2 */
3018ffdff6aSGreg Kroah-Hartman if (divisor < 2)
3028ffdff6aSGreg Kroah-Hartman divisor = 2;
3038ffdff6aSGreg Kroah-Hartman
3048ffdff6aSGreg Kroah-Hartman return divisor;
3058ffdff6aSGreg Kroah-Hartman }
3068ffdff6aSGreg Kroah-Hartman
apci3120_clr_timer2_interrupt(struct comedi_device * dev)3078ffdff6aSGreg Kroah-Hartman static void apci3120_clr_timer2_interrupt(struct comedi_device *dev)
3088ffdff6aSGreg Kroah-Hartman {
3098ffdff6aSGreg Kroah-Hartman /* a dummy read of APCI3120_CTR0_REG clears the timer 2 interrupt */
3108ffdff6aSGreg Kroah-Hartman inb(dev->iobase + APCI3120_CTR0_REG);
3118ffdff6aSGreg Kroah-Hartman }
3128ffdff6aSGreg Kroah-Hartman
apci3120_timer_write(struct comedi_device * dev,unsigned int timer,unsigned int val)3138ffdff6aSGreg Kroah-Hartman static void apci3120_timer_write(struct comedi_device *dev,
3148ffdff6aSGreg Kroah-Hartman unsigned int timer, unsigned int val)
3158ffdff6aSGreg Kroah-Hartman {
3168ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
3178ffdff6aSGreg Kroah-Hartman
3188ffdff6aSGreg Kroah-Hartman /* write 16-bit value to timer (lower 16-bits of timer 2) */
3198ffdff6aSGreg Kroah-Hartman outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
3208ffdff6aSGreg Kroah-Hartman APCI3120_CTR0_TIMER_SEL(timer),
3218ffdff6aSGreg Kroah-Hartman dev->iobase + APCI3120_CTR0_REG);
3228ffdff6aSGreg Kroah-Hartman outw(val & 0xffff, dev->iobase + APCI3120_TIMER_REG);
3238ffdff6aSGreg Kroah-Hartman
3248ffdff6aSGreg Kroah-Hartman if (timer == 2) {
3258ffdff6aSGreg Kroah-Hartman /* write upper 16-bits to timer 2 */
3268ffdff6aSGreg Kroah-Hartman outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
3278ffdff6aSGreg Kroah-Hartman APCI3120_CTR0_TIMER_SEL(timer + 1),
3288ffdff6aSGreg Kroah-Hartman dev->iobase + APCI3120_CTR0_REG);
3298ffdff6aSGreg Kroah-Hartman outw((val >> 16) & 0xffff, dev->iobase + APCI3120_TIMER_REG);
3308ffdff6aSGreg Kroah-Hartman }
3318ffdff6aSGreg Kroah-Hartman }
3328ffdff6aSGreg Kroah-Hartman
apci3120_timer_read(struct comedi_device * dev,unsigned int timer)3338ffdff6aSGreg Kroah-Hartman static unsigned int apci3120_timer_read(struct comedi_device *dev,
3348ffdff6aSGreg Kroah-Hartman unsigned int timer)
3358ffdff6aSGreg Kroah-Hartman {
3368ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
3378ffdff6aSGreg Kroah-Hartman unsigned int val;
3388ffdff6aSGreg Kroah-Hartman
3398ffdff6aSGreg Kroah-Hartman /* read 16-bit value from timer (lower 16-bits of timer 2) */
3408ffdff6aSGreg Kroah-Hartman outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
3418ffdff6aSGreg Kroah-Hartman APCI3120_CTR0_TIMER_SEL(timer),
3428ffdff6aSGreg Kroah-Hartman dev->iobase + APCI3120_CTR0_REG);
3438ffdff6aSGreg Kroah-Hartman val = inw(dev->iobase + APCI3120_TIMER_REG);
3448ffdff6aSGreg Kroah-Hartman
3458ffdff6aSGreg Kroah-Hartman if (timer == 2) {
3468ffdff6aSGreg Kroah-Hartman /* read upper 16-bits from timer 2 */
3478ffdff6aSGreg Kroah-Hartman outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
3488ffdff6aSGreg Kroah-Hartman APCI3120_CTR0_TIMER_SEL(timer + 1),
3498ffdff6aSGreg Kroah-Hartman dev->iobase + APCI3120_CTR0_REG);
3508ffdff6aSGreg Kroah-Hartman val |= (inw(dev->iobase + APCI3120_TIMER_REG) << 16);
3518ffdff6aSGreg Kroah-Hartman }
3528ffdff6aSGreg Kroah-Hartman
3538ffdff6aSGreg Kroah-Hartman return val;
3548ffdff6aSGreg Kroah-Hartman }
3558ffdff6aSGreg Kroah-Hartman
apci3120_timer_set_mode(struct comedi_device * dev,unsigned int timer,unsigned int mode)3568ffdff6aSGreg Kroah-Hartman static void apci3120_timer_set_mode(struct comedi_device *dev,
3578ffdff6aSGreg Kroah-Hartman unsigned int timer, unsigned int mode)
3588ffdff6aSGreg Kroah-Hartman {
3598ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
3608ffdff6aSGreg Kroah-Hartman
3618ffdff6aSGreg Kroah-Hartman devpriv->timer_mode &= ~APCI3120_TIMER_MODE_MASK(timer);
3628ffdff6aSGreg Kroah-Hartman devpriv->timer_mode |= APCI3120_TIMER_MODE(timer, mode);
3638ffdff6aSGreg Kroah-Hartman outb(devpriv->timer_mode, dev->iobase + APCI3120_TIMER_MODE_REG);
3648ffdff6aSGreg Kroah-Hartman }
3658ffdff6aSGreg Kroah-Hartman
apci3120_timer_enable(struct comedi_device * dev,unsigned int timer,bool enable)3668ffdff6aSGreg Kroah-Hartman static void apci3120_timer_enable(struct comedi_device *dev,
3678ffdff6aSGreg Kroah-Hartman unsigned int timer, bool enable)
3688ffdff6aSGreg Kroah-Hartman {
3698ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
3708ffdff6aSGreg Kroah-Hartman
3718ffdff6aSGreg Kroah-Hartman if (enable)
3728ffdff6aSGreg Kroah-Hartman devpriv->ctrl |= APCI3120_CTRL_GATE(timer);
3738ffdff6aSGreg Kroah-Hartman else
3748ffdff6aSGreg Kroah-Hartman devpriv->ctrl &= ~APCI3120_CTRL_GATE(timer);
3758ffdff6aSGreg Kroah-Hartman outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
3768ffdff6aSGreg Kroah-Hartman }
3778ffdff6aSGreg Kroah-Hartman
apci3120_exttrig_enable(struct comedi_device * dev,bool enable)3788ffdff6aSGreg Kroah-Hartman static void apci3120_exttrig_enable(struct comedi_device *dev, bool enable)
3798ffdff6aSGreg Kroah-Hartman {
3808ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
3818ffdff6aSGreg Kroah-Hartman
3828ffdff6aSGreg Kroah-Hartman if (enable)
3838ffdff6aSGreg Kroah-Hartman devpriv->ctrl |= APCI3120_CTRL_EXT_TRIG;
3848ffdff6aSGreg Kroah-Hartman else
3858ffdff6aSGreg Kroah-Hartman devpriv->ctrl &= ~APCI3120_CTRL_EXT_TRIG;
3868ffdff6aSGreg Kroah-Hartman outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
3878ffdff6aSGreg Kroah-Hartman }
3888ffdff6aSGreg Kroah-Hartman
apci3120_set_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,int n_chan,unsigned int * chanlist)3898ffdff6aSGreg Kroah-Hartman static void apci3120_set_chanlist(struct comedi_device *dev,
3908ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
3918ffdff6aSGreg Kroah-Hartman int n_chan, unsigned int *chanlist)
3928ffdff6aSGreg Kroah-Hartman {
3938ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
3948ffdff6aSGreg Kroah-Hartman int i;
3958ffdff6aSGreg Kroah-Hartman
3968ffdff6aSGreg Kroah-Hartman /* set chanlist for scan */
3978ffdff6aSGreg Kroah-Hartman for (i = 0; i < n_chan; i++) {
3988ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(chanlist[i]);
3998ffdff6aSGreg Kroah-Hartman unsigned int range = CR_RANGE(chanlist[i]);
4008ffdff6aSGreg Kroah-Hartman unsigned int val;
4018ffdff6aSGreg Kroah-Hartman
4028ffdff6aSGreg Kroah-Hartman val = APCI3120_CHANLIST_MUX(chan) |
4038ffdff6aSGreg Kroah-Hartman APCI3120_CHANLIST_GAIN(range) |
4048ffdff6aSGreg Kroah-Hartman APCI3120_CHANLIST_INDEX(i);
4058ffdff6aSGreg Kroah-Hartman
4068ffdff6aSGreg Kroah-Hartman if (comedi_range_is_unipolar(s, range))
4078ffdff6aSGreg Kroah-Hartman val |= APCI3120_CHANLIST_UNIPOLAR;
4088ffdff6aSGreg Kroah-Hartman
4098ffdff6aSGreg Kroah-Hartman outw(val, dev->iobase + APCI3120_CHANLIST_REG);
4108ffdff6aSGreg Kroah-Hartman }
4118ffdff6aSGreg Kroah-Hartman
4128ffdff6aSGreg Kroah-Hartman /* a dummy read of APCI3120_TIMER_MODE_REG resets the ai FIFO */
4138ffdff6aSGreg Kroah-Hartman inw(dev->iobase + APCI3120_TIMER_MODE_REG);
4148ffdff6aSGreg Kroah-Hartman
4158ffdff6aSGreg Kroah-Hartman /* set scan length (PR) and scan start (PA) */
4168ffdff6aSGreg Kroah-Hartman devpriv->ctrl = APCI3120_CTRL_PR(n_chan - 1) | APCI3120_CTRL_PA(0);
4178ffdff6aSGreg Kroah-Hartman outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
4188ffdff6aSGreg Kroah-Hartman
4198ffdff6aSGreg Kroah-Hartman /* enable chanlist scanning if necessary */
4208ffdff6aSGreg Kroah-Hartman if (n_chan > 1)
4218ffdff6aSGreg Kroah-Hartman devpriv->mode |= APCI3120_MODE_SCAN_ENA;
4228ffdff6aSGreg Kroah-Hartman }
4238ffdff6aSGreg Kroah-Hartman
apci3120_interrupt_dma(struct comedi_device * dev,struct comedi_subdevice * s)4248ffdff6aSGreg Kroah-Hartman static void apci3120_interrupt_dma(struct comedi_device *dev,
4258ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s)
4268ffdff6aSGreg Kroah-Hartman {
4278ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
4288ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async;
4298ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd;
4308ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf *dmabuf;
4318ffdff6aSGreg Kroah-Hartman unsigned int nbytes;
4328ffdff6aSGreg Kroah-Hartman unsigned int nsamples;
4338ffdff6aSGreg Kroah-Hartman
4348ffdff6aSGreg Kroah-Hartman dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf];
4358ffdff6aSGreg Kroah-Hartman
4368ffdff6aSGreg Kroah-Hartman nbytes = dmabuf->use_size - inl(devpriv->amcc + AMCC_OP_REG_MWTC);
4378ffdff6aSGreg Kroah-Hartman
4388ffdff6aSGreg Kroah-Hartman if (nbytes < dmabuf->use_size)
4398ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "Interrupted DMA transfer!\n");
4408ffdff6aSGreg Kroah-Hartman if (nbytes & 1) {
4418ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "Odd count of bytes in DMA ring!\n");
4428ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_ERROR;
4438ffdff6aSGreg Kroah-Hartman return;
4448ffdff6aSGreg Kroah-Hartman }
4458ffdff6aSGreg Kroah-Hartman
4468ffdff6aSGreg Kroah-Hartman nsamples = comedi_bytes_to_samples(s, nbytes);
4478ffdff6aSGreg Kroah-Hartman if (nsamples) {
4488ffdff6aSGreg Kroah-Hartman comedi_buf_write_samples(s, dmabuf->virt, nsamples);
4498ffdff6aSGreg Kroah-Hartman
4508ffdff6aSGreg Kroah-Hartman if (!(cmd->flags & CMDF_WAKE_EOS))
4518ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_EOS;
4528ffdff6aSGreg Kroah-Hartman }
4538ffdff6aSGreg Kroah-Hartman
4548ffdff6aSGreg Kroah-Hartman if ((async->events & COMEDI_CB_CANCEL_MASK) ||
4558ffdff6aSGreg Kroah-Hartman (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg))
4568ffdff6aSGreg Kroah-Hartman return;
4578ffdff6aSGreg Kroah-Hartman
4588ffdff6aSGreg Kroah-Hartman if (devpriv->use_double_buffer) {
4598ffdff6aSGreg Kroah-Hartman /* switch DMA buffers for next interrupt */
4608ffdff6aSGreg Kroah-Hartman devpriv->cur_dmabuf = !devpriv->cur_dmabuf;
4618ffdff6aSGreg Kroah-Hartman dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf];
4628ffdff6aSGreg Kroah-Hartman apci3120_init_dma(dev, dmabuf);
4638ffdff6aSGreg Kroah-Hartman } else {
4648ffdff6aSGreg Kroah-Hartman /* restart DMA if not using double buffering */
4658ffdff6aSGreg Kroah-Hartman apci3120_init_dma(dev, dmabuf);
4668ffdff6aSGreg Kroah-Hartman }
4678ffdff6aSGreg Kroah-Hartman }
4688ffdff6aSGreg Kroah-Hartman
apci3120_interrupt(int irq,void * d)4698ffdff6aSGreg Kroah-Hartman static irqreturn_t apci3120_interrupt(int irq, void *d)
4708ffdff6aSGreg Kroah-Hartman {
4718ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = d;
4728ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
4738ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s = dev->read_subdev;
4748ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async;
4758ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd;
4768ffdff6aSGreg Kroah-Hartman unsigned int status;
4778ffdff6aSGreg Kroah-Hartman unsigned int int_amcc;
4788ffdff6aSGreg Kroah-Hartman
4798ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + APCI3120_STATUS_REG);
4808ffdff6aSGreg Kroah-Hartman int_amcc = inl(devpriv->amcc + AMCC_OP_REG_INTCSR);
4818ffdff6aSGreg Kroah-Hartman
4828ffdff6aSGreg Kroah-Hartman if (!(status & APCI3120_STATUS_INT_MASK) &&
4838ffdff6aSGreg Kroah-Hartman !(int_amcc & ANY_S593X_INT)) {
4848ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "IRQ from unknown source\n");
4858ffdff6aSGreg Kroah-Hartman return IRQ_NONE;
4868ffdff6aSGreg Kroah-Hartman }
4878ffdff6aSGreg Kroah-Hartman
4888ffdff6aSGreg Kroah-Hartman outl(int_amcc | AINT_INT_MASK, devpriv->amcc + AMCC_OP_REG_INTCSR);
4898ffdff6aSGreg Kroah-Hartman
4908ffdff6aSGreg Kroah-Hartman if (devpriv->ctrl & APCI3120_CTRL_EXT_TRIG)
4918ffdff6aSGreg Kroah-Hartman apci3120_exttrig_enable(dev, false);
4928ffdff6aSGreg Kroah-Hartman
4938ffdff6aSGreg Kroah-Hartman if (int_amcc & MASTER_ABORT_INT)
4948ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n");
4958ffdff6aSGreg Kroah-Hartman if (int_amcc & TARGET_ABORT_INT)
4968ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n");
4978ffdff6aSGreg Kroah-Hartman
4988ffdff6aSGreg Kroah-Hartman if ((status & APCI3120_STATUS_EOS_INT) &&
4998ffdff6aSGreg Kroah-Hartman (devpriv->mode & APCI3120_MODE_EOS_IRQ_ENA)) {
5008ffdff6aSGreg Kroah-Hartman unsigned short val;
5018ffdff6aSGreg Kroah-Hartman int i;
5028ffdff6aSGreg Kroah-Hartman
5038ffdff6aSGreg Kroah-Hartman for (i = 0; i < cmd->chanlist_len; i++) {
5048ffdff6aSGreg Kroah-Hartman val = inw(dev->iobase + APCI3120_AI_FIFO_REG);
5058ffdff6aSGreg Kroah-Hartman comedi_buf_write_samples(s, &val, 1);
5068ffdff6aSGreg Kroah-Hartman }
5078ffdff6aSGreg Kroah-Hartman
5088ffdff6aSGreg Kroah-Hartman devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA;
5098ffdff6aSGreg Kroah-Hartman outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
5108ffdff6aSGreg Kroah-Hartman }
5118ffdff6aSGreg Kroah-Hartman
5128ffdff6aSGreg Kroah-Hartman if (status & APCI3120_STATUS_TIMER2_INT) {
5138ffdff6aSGreg Kroah-Hartman /*
5148ffdff6aSGreg Kroah-Hartman * for safety...
5158ffdff6aSGreg Kroah-Hartman * timer2 interrupts are not enabled in the driver
5168ffdff6aSGreg Kroah-Hartman */
5178ffdff6aSGreg Kroah-Hartman apci3120_clr_timer2_interrupt(dev);
5188ffdff6aSGreg Kroah-Hartman }
5198ffdff6aSGreg Kroah-Hartman
5208ffdff6aSGreg Kroah-Hartman if (status & APCI3120_STATUS_AMCC_INT) {
5218ffdff6aSGreg Kroah-Hartman /* AMCC- Clear write complete interrupt (DMA) */
5228ffdff6aSGreg Kroah-Hartman outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR);
5238ffdff6aSGreg Kroah-Hartman
5248ffdff6aSGreg Kroah-Hartman /* do some data transfer */
5258ffdff6aSGreg Kroah-Hartman apci3120_interrupt_dma(dev, s);
5268ffdff6aSGreg Kroah-Hartman }
5278ffdff6aSGreg Kroah-Hartman
5288ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
5298ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_EOA;
5308ffdff6aSGreg Kroah-Hartman
5318ffdff6aSGreg Kroah-Hartman comedi_handle_events(dev, s);
5328ffdff6aSGreg Kroah-Hartman
5338ffdff6aSGreg Kroah-Hartman return IRQ_HANDLED;
5348ffdff6aSGreg Kroah-Hartman }
5358ffdff6aSGreg Kroah-Hartman
apci3120_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)5368ffdff6aSGreg Kroah-Hartman static int apci3120_ai_cmd(struct comedi_device *dev,
5378ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s)
5388ffdff6aSGreg Kroah-Hartman {
5398ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
5408ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd;
5418ffdff6aSGreg Kroah-Hartman unsigned int divisor;
5428ffdff6aSGreg Kroah-Hartman
5438ffdff6aSGreg Kroah-Hartman /* set default mode bits */
5448ffdff6aSGreg Kroah-Hartman devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC |
5458ffdff6aSGreg Kroah-Hartman APCI3120_MODE_TIMER2_AS_TIMER;
5468ffdff6aSGreg Kroah-Hartman
5478ffdff6aSGreg Kroah-Hartman /* AMCC- Clear write complete interrupt (DMA) */
5488ffdff6aSGreg Kroah-Hartman outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR);
5498ffdff6aSGreg Kroah-Hartman
5508ffdff6aSGreg Kroah-Hartman devpriv->cur_dmabuf = 0;
5518ffdff6aSGreg Kroah-Hartman
5528ffdff6aSGreg Kroah-Hartman /* load chanlist for command scan */
5538ffdff6aSGreg Kroah-Hartman apci3120_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist);
5548ffdff6aSGreg Kroah-Hartman
5558ffdff6aSGreg Kroah-Hartman if (cmd->start_src == TRIG_EXT)
5568ffdff6aSGreg Kroah-Hartman apci3120_exttrig_enable(dev, true);
5578ffdff6aSGreg Kroah-Hartman
5588ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) {
5598ffdff6aSGreg Kroah-Hartman /*
5608ffdff6aSGreg Kroah-Hartman * Timer 1 is used in MODE2 (rate generator) to set the
5618ffdff6aSGreg Kroah-Hartman * start time for each scan.
5628ffdff6aSGreg Kroah-Hartman */
5638ffdff6aSGreg Kroah-Hartman divisor = apci3120_ns_to_timer(dev, 1, cmd->scan_begin_arg,
5648ffdff6aSGreg Kroah-Hartman cmd->flags);
5658ffdff6aSGreg Kroah-Hartman apci3120_timer_set_mode(dev, 1, APCI3120_TIMER_MODE2);
5668ffdff6aSGreg Kroah-Hartman apci3120_timer_write(dev, 1, divisor);
5678ffdff6aSGreg Kroah-Hartman }
5688ffdff6aSGreg Kroah-Hartman
5698ffdff6aSGreg Kroah-Hartman /*
5708ffdff6aSGreg Kroah-Hartman * Timer 0 is used in MODE2 (rate generator) to set the conversion
5718ffdff6aSGreg Kroah-Hartman * time for each acquisition.
5728ffdff6aSGreg Kroah-Hartman */
5738ffdff6aSGreg Kroah-Hartman divisor = apci3120_ns_to_timer(dev, 0, cmd->convert_arg, cmd->flags);
5748ffdff6aSGreg Kroah-Hartman apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE2);
5758ffdff6aSGreg Kroah-Hartman apci3120_timer_write(dev, 0, divisor);
5768ffdff6aSGreg Kroah-Hartman
5778ffdff6aSGreg Kroah-Hartman if (devpriv->use_dma)
5788ffdff6aSGreg Kroah-Hartman apci3120_setup_dma(dev, s);
5798ffdff6aSGreg Kroah-Hartman else
5808ffdff6aSGreg Kroah-Hartman devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA;
5818ffdff6aSGreg Kroah-Hartman
5828ffdff6aSGreg Kroah-Hartman /* set mode to enable acquisition */
5838ffdff6aSGreg Kroah-Hartman outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
5848ffdff6aSGreg Kroah-Hartman
5858ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER)
5868ffdff6aSGreg Kroah-Hartman apci3120_timer_enable(dev, 1, true);
5878ffdff6aSGreg Kroah-Hartman apci3120_timer_enable(dev, 0, true);
5888ffdff6aSGreg Kroah-Hartman
5898ffdff6aSGreg Kroah-Hartman return 0;
5908ffdff6aSGreg Kroah-Hartman }
5918ffdff6aSGreg Kroah-Hartman
apci3120_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)5928ffdff6aSGreg Kroah-Hartman static int apci3120_ai_cmdtest(struct comedi_device *dev,
5938ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
5948ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd)
5958ffdff6aSGreg Kroah-Hartman {
5968ffdff6aSGreg Kroah-Hartman unsigned int arg;
5978ffdff6aSGreg Kroah-Hartman int err = 0;
5988ffdff6aSGreg Kroah-Hartman
5998ffdff6aSGreg Kroah-Hartman /* Step 1 : check if triggers are trivially valid */
6008ffdff6aSGreg Kroah-Hartman
6018ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
6028ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_begin_src,
6038ffdff6aSGreg Kroah-Hartman TRIG_TIMER | TRIG_FOLLOW);
6048ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
6058ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
6068ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
6078ffdff6aSGreg Kroah-Hartman
6088ffdff6aSGreg Kroah-Hartman if (err)
6098ffdff6aSGreg Kroah-Hartman return 1;
6108ffdff6aSGreg Kroah-Hartman
6118ffdff6aSGreg Kroah-Hartman /* Step 2a : make sure trigger sources are unique */
6128ffdff6aSGreg Kroah-Hartman
6138ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->start_src);
6148ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
6158ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_is_unique(cmd->stop_src);
6168ffdff6aSGreg Kroah-Hartman
6178ffdff6aSGreg Kroah-Hartman /* Step 2b : and mutually compatible */
6188ffdff6aSGreg Kroah-Hartman
6198ffdff6aSGreg Kroah-Hartman if (err)
6208ffdff6aSGreg Kroah-Hartman return 2;
6218ffdff6aSGreg Kroah-Hartman
6228ffdff6aSGreg Kroah-Hartman /* Step 3: check if arguments are trivially valid */
6238ffdff6aSGreg Kroah-Hartman
6248ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
6258ffdff6aSGreg Kroah-Hartman
6268ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) { /* Test Delay timing */
6278ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
6288ffdff6aSGreg Kroah-Hartman 100000);
6298ffdff6aSGreg Kroah-Hartman }
6308ffdff6aSGreg Kroah-Hartman
6318ffdff6aSGreg Kroah-Hartman /* minimum conversion time per sample is 10us */
6328ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
6338ffdff6aSGreg Kroah-Hartman
6348ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
6358ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
6368ffdff6aSGreg Kroah-Hartman cmd->chanlist_len);
6378ffdff6aSGreg Kroah-Hartman
6388ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT)
6398ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
6408ffdff6aSGreg Kroah-Hartman else /* TRIG_NONE */
6418ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
6428ffdff6aSGreg Kroah-Hartman
6438ffdff6aSGreg Kroah-Hartman if (err)
6448ffdff6aSGreg Kroah-Hartman return 3;
6458ffdff6aSGreg Kroah-Hartman
6468ffdff6aSGreg Kroah-Hartman /* Step 4: fix up any arguments */
6478ffdff6aSGreg Kroah-Hartman
6488ffdff6aSGreg Kroah-Hartman if (cmd->scan_begin_src == TRIG_TIMER) {
6498ffdff6aSGreg Kroah-Hartman /* scan begin must be larger than the scan time */
6508ffdff6aSGreg Kroah-Hartman arg = cmd->convert_arg * cmd->scan_end_arg;
6518ffdff6aSGreg Kroah-Hartman err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
6528ffdff6aSGreg Kroah-Hartman }
6538ffdff6aSGreg Kroah-Hartman
6548ffdff6aSGreg Kroah-Hartman if (err)
6558ffdff6aSGreg Kroah-Hartman return 4;
6568ffdff6aSGreg Kroah-Hartman
6578ffdff6aSGreg Kroah-Hartman /* Step 5: check channel list if it exists */
6588ffdff6aSGreg Kroah-Hartman
6598ffdff6aSGreg Kroah-Hartman return 0;
6608ffdff6aSGreg Kroah-Hartman }
6618ffdff6aSGreg Kroah-Hartman
apci3120_cancel(struct comedi_device * dev,struct comedi_subdevice * s)6628ffdff6aSGreg Kroah-Hartman static int apci3120_cancel(struct comedi_device *dev,
6638ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s)
6648ffdff6aSGreg Kroah-Hartman {
6658ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
6668ffdff6aSGreg Kroah-Hartman
6678ffdff6aSGreg Kroah-Hartman /* Add-On - disable DMA */
6688ffdff6aSGreg Kroah-Hartman outw(0, devpriv->addon + 4);
6698ffdff6aSGreg Kroah-Hartman
6708ffdff6aSGreg Kroah-Hartman /* Add-On - disable bus master */
6718ffdff6aSGreg Kroah-Hartman apci3120_addon_write(dev, 0, AMCC_OP_REG_AGCSTS);
6728ffdff6aSGreg Kroah-Hartman
6738ffdff6aSGreg Kroah-Hartman /* AMCC - disable bus master */
6748ffdff6aSGreg Kroah-Hartman outl(0, devpriv->amcc + AMCC_OP_REG_MCSR);
6758ffdff6aSGreg Kroah-Hartman
6768ffdff6aSGreg Kroah-Hartman /* disable all counters, ext trigger, and reset scan */
6778ffdff6aSGreg Kroah-Hartman devpriv->ctrl = 0;
6788ffdff6aSGreg Kroah-Hartman outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
6798ffdff6aSGreg Kroah-Hartman
6808ffdff6aSGreg Kroah-Hartman /* DISABLE_ALL_INTERRUPT */
6818ffdff6aSGreg Kroah-Hartman devpriv->mode = 0;
6828ffdff6aSGreg Kroah-Hartman outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
6838ffdff6aSGreg Kroah-Hartman
6848ffdff6aSGreg Kroah-Hartman inw(dev->iobase + APCI3120_STATUS_REG);
6858ffdff6aSGreg Kroah-Hartman devpriv->cur_dmabuf = 0;
6868ffdff6aSGreg Kroah-Hartman
6878ffdff6aSGreg Kroah-Hartman return 0;
6888ffdff6aSGreg Kroah-Hartman }
6898ffdff6aSGreg Kroah-Hartman
apci3120_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)6908ffdff6aSGreg Kroah-Hartman static int apci3120_ai_eoc(struct comedi_device *dev,
6918ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
6928ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
6938ffdff6aSGreg Kroah-Hartman unsigned long context)
6948ffdff6aSGreg Kroah-Hartman {
6958ffdff6aSGreg Kroah-Hartman unsigned int status;
6968ffdff6aSGreg Kroah-Hartman
6978ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + APCI3120_STATUS_REG);
6988ffdff6aSGreg Kroah-Hartman if ((status & APCI3120_STATUS_EOC_INT) == 0)
6998ffdff6aSGreg Kroah-Hartman return 0;
7008ffdff6aSGreg Kroah-Hartman return -EBUSY;
7018ffdff6aSGreg Kroah-Hartman }
7028ffdff6aSGreg Kroah-Hartman
apci3120_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)7038ffdff6aSGreg Kroah-Hartman static int apci3120_ai_insn_read(struct comedi_device *dev,
7048ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
7058ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
7068ffdff6aSGreg Kroah-Hartman unsigned int *data)
7078ffdff6aSGreg Kroah-Hartman {
7088ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
7098ffdff6aSGreg Kroah-Hartman unsigned int divisor;
7108ffdff6aSGreg Kroah-Hartman int ret;
7118ffdff6aSGreg Kroah-Hartman int i;
7128ffdff6aSGreg Kroah-Hartman
7138ffdff6aSGreg Kroah-Hartman /* set mode for A/D conversions by software trigger with timer 0 */
7148ffdff6aSGreg Kroah-Hartman devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC |
7158ffdff6aSGreg Kroah-Hartman APCI3120_MODE_TIMER2_AS_TIMER;
7168ffdff6aSGreg Kroah-Hartman outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
7178ffdff6aSGreg Kroah-Hartman
7188ffdff6aSGreg Kroah-Hartman /* load chanlist for single channel scan */
7198ffdff6aSGreg Kroah-Hartman apci3120_set_chanlist(dev, s, 1, &insn->chanspec);
7208ffdff6aSGreg Kroah-Hartman
7218ffdff6aSGreg Kroah-Hartman /*
7228ffdff6aSGreg Kroah-Hartman * Timer 0 is used in MODE4 (software triggered strobe) to set the
7238ffdff6aSGreg Kroah-Hartman * conversion time for each acquisition. Each conversion is triggered
7248ffdff6aSGreg Kroah-Hartman * when the divisor is written to the timer, The conversion is done
7258ffdff6aSGreg Kroah-Hartman * when the EOC bit in the status register is '0'.
7268ffdff6aSGreg Kroah-Hartman */
7278ffdff6aSGreg Kroah-Hartman apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4);
7288ffdff6aSGreg Kroah-Hartman apci3120_timer_enable(dev, 0, true);
7298ffdff6aSGreg Kroah-Hartman
7308ffdff6aSGreg Kroah-Hartman /* fixed conversion time of 10 us */
7318ffdff6aSGreg Kroah-Hartman divisor = apci3120_ns_to_timer(dev, 0, 10000, CMDF_ROUND_NEAREST);
7328ffdff6aSGreg Kroah-Hartman
7338ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) {
7348ffdff6aSGreg Kroah-Hartman /* trigger conversion */
7358ffdff6aSGreg Kroah-Hartman apci3120_timer_write(dev, 0, divisor);
7368ffdff6aSGreg Kroah-Hartman
7378ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, apci3120_ai_eoc, 0);
7388ffdff6aSGreg Kroah-Hartman if (ret)
7398ffdff6aSGreg Kroah-Hartman return ret;
7408ffdff6aSGreg Kroah-Hartman
7418ffdff6aSGreg Kroah-Hartman data[i] = inw(dev->iobase + APCI3120_AI_FIFO_REG);
7428ffdff6aSGreg Kroah-Hartman }
7438ffdff6aSGreg Kroah-Hartman
7448ffdff6aSGreg Kroah-Hartman return insn->n;
7458ffdff6aSGreg Kroah-Hartman }
7468ffdff6aSGreg Kroah-Hartman
apci3120_ao_ready(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)7478ffdff6aSGreg Kroah-Hartman static int apci3120_ao_ready(struct comedi_device *dev,
7488ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
7498ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
7508ffdff6aSGreg Kroah-Hartman unsigned long context)
7518ffdff6aSGreg Kroah-Hartman {
7528ffdff6aSGreg Kroah-Hartman unsigned int status;
7538ffdff6aSGreg Kroah-Hartman
7548ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + APCI3120_STATUS_REG);
7558ffdff6aSGreg Kroah-Hartman if (status & APCI3120_STATUS_DA_READY)
7568ffdff6aSGreg Kroah-Hartman return 0;
7578ffdff6aSGreg Kroah-Hartman return -EBUSY;
7588ffdff6aSGreg Kroah-Hartman }
7598ffdff6aSGreg Kroah-Hartman
apci3120_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)7608ffdff6aSGreg Kroah-Hartman static int apci3120_ao_insn_write(struct comedi_device *dev,
7618ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
7628ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
7638ffdff6aSGreg Kroah-Hartman unsigned int *data)
7648ffdff6aSGreg Kroah-Hartman {
7658ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec);
7668ffdff6aSGreg Kroah-Hartman int i;
7678ffdff6aSGreg Kroah-Hartman
7688ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) {
7698ffdff6aSGreg Kroah-Hartman unsigned int val = data[i];
7708ffdff6aSGreg Kroah-Hartman int ret;
7718ffdff6aSGreg Kroah-Hartman
7728ffdff6aSGreg Kroah-Hartman ret = comedi_timeout(dev, s, insn, apci3120_ao_ready, 0);
7738ffdff6aSGreg Kroah-Hartman if (ret)
7748ffdff6aSGreg Kroah-Hartman return ret;
7758ffdff6aSGreg Kroah-Hartman
7768ffdff6aSGreg Kroah-Hartman outw(APCI3120_AO_MUX(chan) | APCI3120_AO_DATA(val),
7778ffdff6aSGreg Kroah-Hartman dev->iobase + APCI3120_AO_REG(chan));
7788ffdff6aSGreg Kroah-Hartman
7798ffdff6aSGreg Kroah-Hartman s->readback[chan] = val;
7808ffdff6aSGreg Kroah-Hartman }
7818ffdff6aSGreg Kroah-Hartman
7828ffdff6aSGreg Kroah-Hartman return insn->n;
7838ffdff6aSGreg Kroah-Hartman }
7848ffdff6aSGreg Kroah-Hartman
apci3120_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)7858ffdff6aSGreg Kroah-Hartman static int apci3120_di_insn_bits(struct comedi_device *dev,
7868ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
7878ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
7888ffdff6aSGreg Kroah-Hartman unsigned int *data)
7898ffdff6aSGreg Kroah-Hartman {
7908ffdff6aSGreg Kroah-Hartman unsigned int status;
7918ffdff6aSGreg Kroah-Hartman
7928ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + APCI3120_STATUS_REG);
7938ffdff6aSGreg Kroah-Hartman data[1] = APCI3120_STATUS_TO_DI_BITS(status);
7948ffdff6aSGreg Kroah-Hartman
7958ffdff6aSGreg Kroah-Hartman return insn->n;
7968ffdff6aSGreg Kroah-Hartman }
7978ffdff6aSGreg Kroah-Hartman
apci3120_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)7988ffdff6aSGreg Kroah-Hartman static int apci3120_do_insn_bits(struct comedi_device *dev,
7998ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
8008ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
8018ffdff6aSGreg Kroah-Hartman unsigned int *data)
8028ffdff6aSGreg Kroah-Hartman {
8038ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
8048ffdff6aSGreg Kroah-Hartman
8058ffdff6aSGreg Kroah-Hartman if (comedi_dio_update_state(s, data)) {
8068ffdff6aSGreg Kroah-Hartman devpriv->do_bits = s->state;
8078ffdff6aSGreg Kroah-Hartman outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits),
8088ffdff6aSGreg Kroah-Hartman dev->iobase + APCI3120_CTR0_REG);
8098ffdff6aSGreg Kroah-Hartman }
8108ffdff6aSGreg Kroah-Hartman
8118ffdff6aSGreg Kroah-Hartman data[1] = s->state;
8128ffdff6aSGreg Kroah-Hartman
8138ffdff6aSGreg Kroah-Hartman return insn->n;
8148ffdff6aSGreg Kroah-Hartman }
8158ffdff6aSGreg Kroah-Hartman
apci3120_timer_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)8168ffdff6aSGreg Kroah-Hartman static int apci3120_timer_insn_config(struct comedi_device *dev,
8178ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
8188ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
8198ffdff6aSGreg Kroah-Hartman unsigned int *data)
8208ffdff6aSGreg Kroah-Hartman {
8218ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
8228ffdff6aSGreg Kroah-Hartman unsigned int divisor;
8238ffdff6aSGreg Kroah-Hartman unsigned int status;
8248ffdff6aSGreg Kroah-Hartman unsigned int mode;
8258ffdff6aSGreg Kroah-Hartman unsigned int timer_mode;
8268ffdff6aSGreg Kroah-Hartman
8278ffdff6aSGreg Kroah-Hartman switch (data[0]) {
8288ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_ARM:
8298ffdff6aSGreg Kroah-Hartman apci3120_clr_timer2_interrupt(dev);
8308ffdff6aSGreg Kroah-Hartman divisor = apci3120_ns_to_timer(dev, 2, data[1],
8318ffdff6aSGreg Kroah-Hartman CMDF_ROUND_DOWN);
8328ffdff6aSGreg Kroah-Hartman apci3120_timer_write(dev, 2, divisor);
8338ffdff6aSGreg Kroah-Hartman apci3120_timer_enable(dev, 2, true);
8348ffdff6aSGreg Kroah-Hartman break;
8358ffdff6aSGreg Kroah-Hartman
8368ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DISARM:
8378ffdff6aSGreg Kroah-Hartman apci3120_timer_enable(dev, 2, false);
8388ffdff6aSGreg Kroah-Hartman apci3120_clr_timer2_interrupt(dev);
8398ffdff6aSGreg Kroah-Hartman break;
8408ffdff6aSGreg Kroah-Hartman
8418ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_GET_COUNTER_STATUS:
8428ffdff6aSGreg Kroah-Hartman data[1] = 0;
8438ffdff6aSGreg Kroah-Hartman data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
8448ffdff6aSGreg Kroah-Hartman COMEDI_COUNTER_TERMINAL_COUNT;
8458ffdff6aSGreg Kroah-Hartman
8468ffdff6aSGreg Kroah-Hartman if (devpriv->ctrl & APCI3120_CTRL_GATE(2)) {
8478ffdff6aSGreg Kroah-Hartman data[1] |= COMEDI_COUNTER_ARMED;
8488ffdff6aSGreg Kroah-Hartman data[1] |= COMEDI_COUNTER_COUNTING;
8498ffdff6aSGreg Kroah-Hartman }
8508ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + APCI3120_STATUS_REG);
8518ffdff6aSGreg Kroah-Hartman if (status & APCI3120_STATUS_TIMER2_INT) {
8528ffdff6aSGreg Kroah-Hartman data[1] &= ~COMEDI_COUNTER_COUNTING;
8538ffdff6aSGreg Kroah-Hartman data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
8548ffdff6aSGreg Kroah-Hartman }
8558ffdff6aSGreg Kroah-Hartman break;
8568ffdff6aSGreg Kroah-Hartman
8578ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_SET_COUNTER_MODE:
8588ffdff6aSGreg Kroah-Hartman switch (data[1]) {
8598ffdff6aSGreg Kroah-Hartman case I8254_MODE0:
8608ffdff6aSGreg Kroah-Hartman mode = APCI3120_MODE_TIMER2_AS_COUNTER;
8618ffdff6aSGreg Kroah-Hartman timer_mode = APCI3120_TIMER_MODE0;
8628ffdff6aSGreg Kroah-Hartman break;
8638ffdff6aSGreg Kroah-Hartman case I8254_MODE2:
8648ffdff6aSGreg Kroah-Hartman mode = APCI3120_MODE_TIMER2_AS_TIMER;
8658ffdff6aSGreg Kroah-Hartman timer_mode = APCI3120_TIMER_MODE2;
8668ffdff6aSGreg Kroah-Hartman break;
8678ffdff6aSGreg Kroah-Hartman case I8254_MODE4:
8688ffdff6aSGreg Kroah-Hartman mode = APCI3120_MODE_TIMER2_AS_TIMER;
8698ffdff6aSGreg Kroah-Hartman timer_mode = APCI3120_TIMER_MODE4;
8708ffdff6aSGreg Kroah-Hartman break;
8718ffdff6aSGreg Kroah-Hartman case I8254_MODE5:
8728ffdff6aSGreg Kroah-Hartman mode = APCI3120_MODE_TIMER2_AS_WDOG;
8738ffdff6aSGreg Kroah-Hartman timer_mode = APCI3120_TIMER_MODE5;
8748ffdff6aSGreg Kroah-Hartman break;
8758ffdff6aSGreg Kroah-Hartman default:
8768ffdff6aSGreg Kroah-Hartman return -EINVAL;
8778ffdff6aSGreg Kroah-Hartman }
8788ffdff6aSGreg Kroah-Hartman apci3120_timer_enable(dev, 2, false);
8798ffdff6aSGreg Kroah-Hartman apci3120_clr_timer2_interrupt(dev);
8808ffdff6aSGreg Kroah-Hartman apci3120_timer_set_mode(dev, 2, timer_mode);
8818ffdff6aSGreg Kroah-Hartman devpriv->mode &= ~APCI3120_MODE_TIMER2_AS_MASK;
8828ffdff6aSGreg Kroah-Hartman devpriv->mode |= mode;
8838ffdff6aSGreg Kroah-Hartman outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
8848ffdff6aSGreg Kroah-Hartman break;
8858ffdff6aSGreg Kroah-Hartman
8868ffdff6aSGreg Kroah-Hartman default:
8878ffdff6aSGreg Kroah-Hartman return -EINVAL;
8888ffdff6aSGreg Kroah-Hartman }
8898ffdff6aSGreg Kroah-Hartman
8908ffdff6aSGreg Kroah-Hartman return insn->n;
8918ffdff6aSGreg Kroah-Hartman }
8928ffdff6aSGreg Kroah-Hartman
apci3120_timer_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)8938ffdff6aSGreg Kroah-Hartman static int apci3120_timer_insn_read(struct comedi_device *dev,
8948ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s,
8958ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
8968ffdff6aSGreg Kroah-Hartman unsigned int *data)
8978ffdff6aSGreg Kroah-Hartman {
8988ffdff6aSGreg Kroah-Hartman int i;
8998ffdff6aSGreg Kroah-Hartman
9008ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++)
9018ffdff6aSGreg Kroah-Hartman data[i] = apci3120_timer_read(dev, 2);
9028ffdff6aSGreg Kroah-Hartman
9038ffdff6aSGreg Kroah-Hartman return insn->n;
9048ffdff6aSGreg Kroah-Hartman }
9058ffdff6aSGreg Kroah-Hartman
apci3120_dma_alloc(struct comedi_device * dev)9068ffdff6aSGreg Kroah-Hartman static void apci3120_dma_alloc(struct comedi_device *dev)
9078ffdff6aSGreg Kroah-Hartman {
9088ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
9098ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf *dmabuf;
9108ffdff6aSGreg Kroah-Hartman int order;
9118ffdff6aSGreg Kroah-Hartman int i;
9128ffdff6aSGreg Kroah-Hartman
9138ffdff6aSGreg Kroah-Hartman for (i = 0; i < 2; i++) {
9148ffdff6aSGreg Kroah-Hartman dmabuf = &devpriv->dmabuf[i];
9158ffdff6aSGreg Kroah-Hartman for (order = 2; order >= 0; order--) {
9168ffdff6aSGreg Kroah-Hartman dmabuf->virt = dma_alloc_coherent(dev->hw_dev,
9178ffdff6aSGreg Kroah-Hartman PAGE_SIZE << order,
9188ffdff6aSGreg Kroah-Hartman &dmabuf->hw,
9198ffdff6aSGreg Kroah-Hartman GFP_KERNEL);
9208ffdff6aSGreg Kroah-Hartman if (dmabuf->virt)
9218ffdff6aSGreg Kroah-Hartman break;
9228ffdff6aSGreg Kroah-Hartman }
9238ffdff6aSGreg Kroah-Hartman if (!dmabuf->virt)
9248ffdff6aSGreg Kroah-Hartman break;
9258ffdff6aSGreg Kroah-Hartman dmabuf->size = PAGE_SIZE << order;
9268ffdff6aSGreg Kroah-Hartman
9278ffdff6aSGreg Kroah-Hartman if (i == 0)
9288ffdff6aSGreg Kroah-Hartman devpriv->use_dma = 1;
9298ffdff6aSGreg Kroah-Hartman if (i == 1)
9308ffdff6aSGreg Kroah-Hartman devpriv->use_double_buffer = 1;
9318ffdff6aSGreg Kroah-Hartman }
9328ffdff6aSGreg Kroah-Hartman }
9338ffdff6aSGreg Kroah-Hartman
apci3120_dma_free(struct comedi_device * dev)9348ffdff6aSGreg Kroah-Hartman static void apci3120_dma_free(struct comedi_device *dev)
9358ffdff6aSGreg Kroah-Hartman {
9368ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv = dev->private;
9378ffdff6aSGreg Kroah-Hartman struct apci3120_dmabuf *dmabuf;
9388ffdff6aSGreg Kroah-Hartman int i;
9398ffdff6aSGreg Kroah-Hartman
9408ffdff6aSGreg Kroah-Hartman if (!devpriv)
9418ffdff6aSGreg Kroah-Hartman return;
9428ffdff6aSGreg Kroah-Hartman
9438ffdff6aSGreg Kroah-Hartman for (i = 0; i < 2; i++) {
9448ffdff6aSGreg Kroah-Hartman dmabuf = &devpriv->dmabuf[i];
9458ffdff6aSGreg Kroah-Hartman if (dmabuf->virt) {
9468ffdff6aSGreg Kroah-Hartman dma_free_coherent(dev->hw_dev, dmabuf->size,
9478ffdff6aSGreg Kroah-Hartman dmabuf->virt, dmabuf->hw);
9488ffdff6aSGreg Kroah-Hartman }
9498ffdff6aSGreg Kroah-Hartman }
9508ffdff6aSGreg Kroah-Hartman }
9518ffdff6aSGreg Kroah-Hartman
apci3120_reset(struct comedi_device * dev)9528ffdff6aSGreg Kroah-Hartman static void apci3120_reset(struct comedi_device *dev)
9538ffdff6aSGreg Kroah-Hartman {
9548ffdff6aSGreg Kroah-Hartman /* disable all interrupt sources */
9558ffdff6aSGreg Kroah-Hartman outb(0, dev->iobase + APCI3120_MODE_REG);
9568ffdff6aSGreg Kroah-Hartman
9578ffdff6aSGreg Kroah-Hartman /* disable all counters, ext trigger, and reset scan */
9588ffdff6aSGreg Kroah-Hartman outw(0, dev->iobase + APCI3120_CTRL_REG);
9598ffdff6aSGreg Kroah-Hartman
9608ffdff6aSGreg Kroah-Hartman /* clear interrupt status */
9618ffdff6aSGreg Kroah-Hartman inw(dev->iobase + APCI3120_STATUS_REG);
9628ffdff6aSGreg Kroah-Hartman }
9638ffdff6aSGreg Kroah-Hartman
apci3120_auto_attach(struct comedi_device * dev,unsigned long context)9648ffdff6aSGreg Kroah-Hartman static int apci3120_auto_attach(struct comedi_device *dev,
9658ffdff6aSGreg Kroah-Hartman unsigned long context)
9668ffdff6aSGreg Kroah-Hartman {
9678ffdff6aSGreg Kroah-Hartman struct pci_dev *pcidev = comedi_to_pci_dev(dev);
9688ffdff6aSGreg Kroah-Hartman const struct apci3120_board *board = NULL;
9698ffdff6aSGreg Kroah-Hartman struct apci3120_private *devpriv;
9708ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s;
9718ffdff6aSGreg Kroah-Hartman unsigned int status;
9728ffdff6aSGreg Kroah-Hartman int ret;
9738ffdff6aSGreg Kroah-Hartman
9748ffdff6aSGreg Kroah-Hartman if (context < ARRAY_SIZE(apci3120_boardtypes))
9758ffdff6aSGreg Kroah-Hartman board = &apci3120_boardtypes[context];
9768ffdff6aSGreg Kroah-Hartman if (!board)
9778ffdff6aSGreg Kroah-Hartman return -ENODEV;
9788ffdff6aSGreg Kroah-Hartman dev->board_ptr = board;
9798ffdff6aSGreg Kroah-Hartman dev->board_name = board->name;
9808ffdff6aSGreg Kroah-Hartman
9818ffdff6aSGreg Kroah-Hartman devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
9828ffdff6aSGreg Kroah-Hartman if (!devpriv)
9838ffdff6aSGreg Kroah-Hartman return -ENOMEM;
9848ffdff6aSGreg Kroah-Hartman
9858ffdff6aSGreg Kroah-Hartman ret = comedi_pci_enable(dev);
9868ffdff6aSGreg Kroah-Hartman if (ret)
9878ffdff6aSGreg Kroah-Hartman return ret;
9888ffdff6aSGreg Kroah-Hartman pci_set_master(pcidev);
9898ffdff6aSGreg Kroah-Hartman
9908ffdff6aSGreg Kroah-Hartman dev->iobase = pci_resource_start(pcidev, 1);
9918ffdff6aSGreg Kroah-Hartman devpriv->amcc = pci_resource_start(pcidev, 0);
9928ffdff6aSGreg Kroah-Hartman devpriv->addon = pci_resource_start(pcidev, 2);
9938ffdff6aSGreg Kroah-Hartman
9948ffdff6aSGreg Kroah-Hartman apci3120_reset(dev);
9958ffdff6aSGreg Kroah-Hartman
9968ffdff6aSGreg Kroah-Hartman if (pcidev->irq > 0) {
9978ffdff6aSGreg Kroah-Hartman ret = request_irq(pcidev->irq, apci3120_interrupt, IRQF_SHARED,
9988ffdff6aSGreg Kroah-Hartman dev->board_name, dev);
9998ffdff6aSGreg Kroah-Hartman if (ret == 0) {
10008ffdff6aSGreg Kroah-Hartman dev->irq = pcidev->irq;
10018ffdff6aSGreg Kroah-Hartman
10028ffdff6aSGreg Kroah-Hartman apci3120_dma_alloc(dev);
10038ffdff6aSGreg Kroah-Hartman }
10048ffdff6aSGreg Kroah-Hartman }
10058ffdff6aSGreg Kroah-Hartman
10068ffdff6aSGreg Kroah-Hartman status = inw(dev->iobase + APCI3120_STATUS_REG);
10078ffdff6aSGreg Kroah-Hartman if (APCI3120_STATUS_TO_VERSION(status) == APCI3120_REVB ||
10088ffdff6aSGreg Kroah-Hartman context == BOARD_APCI3001)
10098ffdff6aSGreg Kroah-Hartman devpriv->osc_base = APCI3120_REVB_OSC_BASE;
10108ffdff6aSGreg Kroah-Hartman else
10118ffdff6aSGreg Kroah-Hartman devpriv->osc_base = APCI3120_REVA_OSC_BASE;
10128ffdff6aSGreg Kroah-Hartman
10138ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdevices(dev, 5);
10148ffdff6aSGreg Kroah-Hartman if (ret)
10158ffdff6aSGreg Kroah-Hartman return ret;
10168ffdff6aSGreg Kroah-Hartman
10178ffdff6aSGreg Kroah-Hartman /* Analog Input subdevice */
10188ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[0];
10198ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AI;
10208ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
10218ffdff6aSGreg Kroah-Hartman s->n_chan = 16;
10228ffdff6aSGreg Kroah-Hartman s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff;
10238ffdff6aSGreg Kroah-Hartman s->range_table = &apci3120_ai_range;
10248ffdff6aSGreg Kroah-Hartman s->insn_read = apci3120_ai_insn_read;
10258ffdff6aSGreg Kroah-Hartman if (dev->irq) {
10268ffdff6aSGreg Kroah-Hartman dev->read_subdev = s;
10278ffdff6aSGreg Kroah-Hartman s->subdev_flags |= SDF_CMD_READ;
10288ffdff6aSGreg Kroah-Hartman s->len_chanlist = s->n_chan;
10298ffdff6aSGreg Kroah-Hartman s->do_cmdtest = apci3120_ai_cmdtest;
10308ffdff6aSGreg Kroah-Hartman s->do_cmd = apci3120_ai_cmd;
10318ffdff6aSGreg Kroah-Hartman s->cancel = apci3120_cancel;
10328ffdff6aSGreg Kroah-Hartman }
10338ffdff6aSGreg Kroah-Hartman
10348ffdff6aSGreg Kroah-Hartman /* Analog Output subdevice */
10358ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[1];
10368ffdff6aSGreg Kroah-Hartman if (board->has_ao) {
10378ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_AO;
10388ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
10398ffdff6aSGreg Kroah-Hartman s->n_chan = 8;
10408ffdff6aSGreg Kroah-Hartman s->maxdata = 0x3fff;
10418ffdff6aSGreg Kroah-Hartman s->range_table = &range_bipolar10;
10428ffdff6aSGreg Kroah-Hartman s->insn_write = apci3120_ao_insn_write;
10438ffdff6aSGreg Kroah-Hartman
10448ffdff6aSGreg Kroah-Hartman ret = comedi_alloc_subdev_readback(s);
10458ffdff6aSGreg Kroah-Hartman if (ret)
10468ffdff6aSGreg Kroah-Hartman return ret;
10478ffdff6aSGreg Kroah-Hartman } else {
10488ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_UNUSED;
10498ffdff6aSGreg Kroah-Hartman }
10508ffdff6aSGreg Kroah-Hartman
10518ffdff6aSGreg Kroah-Hartman /* Digital Input subdevice */
10528ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[2];
10538ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DI;
10548ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE;
10558ffdff6aSGreg Kroah-Hartman s->n_chan = 4;
10568ffdff6aSGreg Kroah-Hartman s->maxdata = 1;
10578ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital;
10588ffdff6aSGreg Kroah-Hartman s->insn_bits = apci3120_di_insn_bits;
10598ffdff6aSGreg Kroah-Hartman
10608ffdff6aSGreg Kroah-Hartman /* Digital Output subdevice */
10618ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[3];
10628ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_DO;
10638ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_WRITABLE;
10648ffdff6aSGreg Kroah-Hartman s->n_chan = 4;
10658ffdff6aSGreg Kroah-Hartman s->maxdata = 1;
10668ffdff6aSGreg Kroah-Hartman s->range_table = &range_digital;
10678ffdff6aSGreg Kroah-Hartman s->insn_bits = apci3120_do_insn_bits;
10688ffdff6aSGreg Kroah-Hartman
10698ffdff6aSGreg Kroah-Hartman /* Timer subdevice */
10708ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[4];
10718ffdff6aSGreg Kroah-Hartman s->type = COMEDI_SUBD_TIMER;
10728ffdff6aSGreg Kroah-Hartman s->subdev_flags = SDF_READABLE;
10738ffdff6aSGreg Kroah-Hartman s->n_chan = 1;
10748ffdff6aSGreg Kroah-Hartman s->maxdata = 0x00ffffff;
10758ffdff6aSGreg Kroah-Hartman s->insn_config = apci3120_timer_insn_config;
10768ffdff6aSGreg Kroah-Hartman s->insn_read = apci3120_timer_insn_read;
10778ffdff6aSGreg Kroah-Hartman
10788ffdff6aSGreg Kroah-Hartman return 0;
10798ffdff6aSGreg Kroah-Hartman }
10808ffdff6aSGreg Kroah-Hartman
apci3120_detach(struct comedi_device * dev)10818ffdff6aSGreg Kroah-Hartman static void apci3120_detach(struct comedi_device *dev)
10828ffdff6aSGreg Kroah-Hartman {
10838ffdff6aSGreg Kroah-Hartman comedi_pci_detach(dev);
10848ffdff6aSGreg Kroah-Hartman apci3120_dma_free(dev);
10858ffdff6aSGreg Kroah-Hartman }
10868ffdff6aSGreg Kroah-Hartman
10878ffdff6aSGreg Kroah-Hartman static struct comedi_driver apci3120_driver = {
10888ffdff6aSGreg Kroah-Hartman .driver_name = "addi_apci_3120",
10898ffdff6aSGreg Kroah-Hartman .module = THIS_MODULE,
10908ffdff6aSGreg Kroah-Hartman .auto_attach = apci3120_auto_attach,
10918ffdff6aSGreg Kroah-Hartman .detach = apci3120_detach,
10928ffdff6aSGreg Kroah-Hartman };
10938ffdff6aSGreg Kroah-Hartman
apci3120_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)10948ffdff6aSGreg Kroah-Hartman static int apci3120_pci_probe(struct pci_dev *dev,
10958ffdff6aSGreg Kroah-Hartman const struct pci_device_id *id)
10968ffdff6aSGreg Kroah-Hartman {
10978ffdff6aSGreg Kroah-Hartman return comedi_pci_auto_config(dev, &apci3120_driver, id->driver_data);
10988ffdff6aSGreg Kroah-Hartman }
10998ffdff6aSGreg Kroah-Hartman
11008ffdff6aSGreg Kroah-Hartman static const struct pci_device_id apci3120_pci_table[] = {
11018ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 },
11028ffdff6aSGreg Kroah-Hartman { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 },
11038ffdff6aSGreg Kroah-Hartman { 0 }
11048ffdff6aSGreg Kroah-Hartman };
11058ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, apci3120_pci_table);
11068ffdff6aSGreg Kroah-Hartman
11078ffdff6aSGreg Kroah-Hartman static struct pci_driver apci3120_pci_driver = {
11088ffdff6aSGreg Kroah-Hartman .name = "addi_apci_3120",
11098ffdff6aSGreg Kroah-Hartman .id_table = apci3120_pci_table,
11108ffdff6aSGreg Kroah-Hartman .probe = apci3120_pci_probe,
11118ffdff6aSGreg Kroah-Hartman .remove = comedi_pci_auto_unconfig,
11128ffdff6aSGreg Kroah-Hartman };
11138ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver);
11148ffdff6aSGreg Kroah-Hartman
11158ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
11168ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board");
11178ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
1118