xref: /openbmc/linux/drivers/comedi/drivers/addi_apci_3120.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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