xref: /linux/drivers/comedi/drivers/dt3000.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman  * dt3000.c
48ffdff6aSGreg Kroah-Hartman  * Data Translation DT3000 series driver
58ffdff6aSGreg Kroah-Hartman  *
68ffdff6aSGreg Kroah-Hartman  * COMEDI - Linux Control and Measurement Device Interface
78ffdff6aSGreg Kroah-Hartman  * Copyright (C) 1999 David A. Schleef <ds@schleef.org>
88ffdff6aSGreg Kroah-Hartman  */
98ffdff6aSGreg Kroah-Hartman 
108ffdff6aSGreg Kroah-Hartman /*
118ffdff6aSGreg Kroah-Hartman  * Driver: dt3000
128ffdff6aSGreg Kroah-Hartman  * Description: Data Translation DT3000 series
138ffdff6aSGreg Kroah-Hartman  * Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
148ffdff6aSGreg Kroah-Hartman  *   DT3003-PGL, DT3004, DT3005, DT3004-200
158ffdff6aSGreg Kroah-Hartman  * Author: ds
168ffdff6aSGreg Kroah-Hartman  * Updated: Mon, 14 Apr 2008 15:41:24 +0100
178ffdff6aSGreg Kroah-Hartman  * Status: works
188ffdff6aSGreg Kroah-Hartman  *
198ffdff6aSGreg Kroah-Hartman  * Configuration Options: not applicable, uses PCI auto config
208ffdff6aSGreg Kroah-Hartman  *
218ffdff6aSGreg Kroah-Hartman  * There is code to support AI commands, but it may not work.
228ffdff6aSGreg Kroah-Hartman  *
238ffdff6aSGreg Kroah-Hartman  * AO commands are not supported.
248ffdff6aSGreg Kroah-Hartman  */
258ffdff6aSGreg Kroah-Hartman 
268ffdff6aSGreg Kroah-Hartman /*
278ffdff6aSGreg Kroah-Hartman  * The DT3000 series is Data Translation's attempt to make a PCI
288ffdff6aSGreg Kroah-Hartman  * data acquisition board.  The design of this series is very nice,
298ffdff6aSGreg Kroah-Hartman  * since each board has an on-board DSP (Texas Instruments TMS320C52).
308ffdff6aSGreg Kroah-Hartman  * However, a few details are a little annoying.  The boards lack
318ffdff6aSGreg Kroah-Hartman  * bus-mastering DMA, which eliminates them from serious work.
328ffdff6aSGreg Kroah-Hartman  * They also are not capable of autocalibration, which is a common
338ffdff6aSGreg Kroah-Hartman  * feature in modern hardware.  The default firmware is pretty bad,
348ffdff6aSGreg Kroah-Hartman  * making it nearly impossible to write an RT compatible driver.
358ffdff6aSGreg Kroah-Hartman  * It would make an interesting project to write a decent firmware
368ffdff6aSGreg Kroah-Hartman  * for these boards.
378ffdff6aSGreg Kroah-Hartman  *
388ffdff6aSGreg Kroah-Hartman  * Data Translation originally wanted an NDA for the documentation
398ffdff6aSGreg Kroah-Hartman  * for the 3k series.  However, if you ask nicely, they might send
408ffdff6aSGreg Kroah-Hartman  * you the docs without one, also.
418ffdff6aSGreg Kroah-Hartman  */
428ffdff6aSGreg Kroah-Hartman 
438ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
448ffdff6aSGreg Kroah-Hartman #include <linux/delay.h>
458ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
46*df0e68c1SIan Abbott #include <linux/comedi/comedi_pci.h>
478ffdff6aSGreg Kroah-Hartman 
488ffdff6aSGreg Kroah-Hartman /*
498ffdff6aSGreg Kroah-Hartman  * PCI BAR0 - dual-ported RAM location definitions (dev->mmio)
508ffdff6aSGreg Kroah-Hartman  */
518ffdff6aSGreg Kroah-Hartman #define DPR_DAC_BUFFER		(4 * 0x000)
528ffdff6aSGreg Kroah-Hartman #define DPR_ADC_BUFFER		(4 * 0x800)
538ffdff6aSGreg Kroah-Hartman #define DPR_COMMAND		(4 * 0xfd3)
548ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS		(4 * 0xfd3)
558ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_AI		0
568ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_AO		1
578ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_DIN		2
588ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_DOUT		3
598ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_MEM		4
608ffdff6aSGreg Kroah-Hartman #define DPR_SUBSYS_CT		5
618ffdff6aSGreg Kroah-Hartman #define DPR_ENCODE		(4 * 0xfd4)
628ffdff6aSGreg Kroah-Hartman #define DPR_PARAMS(x)		(4 * (0xfd5 + (x)))
638ffdff6aSGreg Kroah-Hartman #define DPR_TICK_REG_LO		(4 * 0xff5)
648ffdff6aSGreg Kroah-Hartman #define DPR_TICK_REG_HI		(4 * 0xff6)
658ffdff6aSGreg Kroah-Hartman #define DPR_DA_BUF_FRONT	(4 * 0xff7)
668ffdff6aSGreg Kroah-Hartman #define DPR_DA_BUF_REAR		(4 * 0xff8)
678ffdff6aSGreg Kroah-Hartman #define DPR_AD_BUF_FRONT	(4 * 0xff9)
688ffdff6aSGreg Kroah-Hartman #define DPR_AD_BUF_REAR		(4 * 0xffa)
698ffdff6aSGreg Kroah-Hartman #define DPR_INT_MASK		(4 * 0xffb)
708ffdff6aSGreg Kroah-Hartman #define DPR_INTR_FLAG		(4 * 0xffc)
718ffdff6aSGreg Kroah-Hartman #define DPR_INTR_CMDONE		BIT(7)
728ffdff6aSGreg Kroah-Hartman #define DPR_INTR_CTDONE		BIT(6)
738ffdff6aSGreg Kroah-Hartman #define DPR_INTR_DAHWERR	BIT(5)
748ffdff6aSGreg Kroah-Hartman #define DPR_INTR_DASWERR	BIT(4)
758ffdff6aSGreg Kroah-Hartman #define DPR_INTR_DAEMPTY	BIT(3)
768ffdff6aSGreg Kroah-Hartman #define DPR_INTR_ADHWERR	BIT(2)
778ffdff6aSGreg Kroah-Hartman #define DPR_INTR_ADSWERR	BIT(1)
788ffdff6aSGreg Kroah-Hartman #define DPR_INTR_ADFULL		BIT(0)
798ffdff6aSGreg Kroah-Hartman #define DPR_RESPONSE_MBX	(4 * 0xffe)
808ffdff6aSGreg Kroah-Hartman #define DPR_CMD_MBX		(4 * 0xfff)
818ffdff6aSGreg Kroah-Hartman #define DPR_CMD_COMPLETION(x)	((x) << 8)
828ffdff6aSGreg Kroah-Hartman #define DPR_CMD_NOTPROCESSED	DPR_CMD_COMPLETION(0x00)
838ffdff6aSGreg Kroah-Hartman #define DPR_CMD_NOERROR		DPR_CMD_COMPLETION(0x55)
848ffdff6aSGreg Kroah-Hartman #define DPR_CMD_ERROR		DPR_CMD_COMPLETION(0xaa)
858ffdff6aSGreg Kroah-Hartman #define DPR_CMD_NOTSUPPORTED	DPR_CMD_COMPLETION(0xff)
868ffdff6aSGreg Kroah-Hartman #define DPR_CMD_COMPLETION_MASK	DPR_CMD_COMPLETION(0xff)
878ffdff6aSGreg Kroah-Hartman #define DPR_CMD(x)		((x) << 0)
888ffdff6aSGreg Kroah-Hartman #define DPR_CMD_GETBRDINFO	DPR_CMD(0)
898ffdff6aSGreg Kroah-Hartman #define DPR_CMD_CONFIG		DPR_CMD(1)
908ffdff6aSGreg Kroah-Hartman #define DPR_CMD_GETCONFIG	DPR_CMD(2)
918ffdff6aSGreg Kroah-Hartman #define DPR_CMD_START		DPR_CMD(3)
928ffdff6aSGreg Kroah-Hartman #define DPR_CMD_STOP		DPR_CMD(4)
938ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READSINGLE	DPR_CMD(5)
948ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITESINGLE	DPR_CMD(6)
958ffdff6aSGreg Kroah-Hartman #define DPR_CMD_CALCCLOCK	DPR_CMD(7)
968ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READEVENTS	DPR_CMD(8)
978ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITECTCTRL	DPR_CMD(16)
988ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READCTCTRL	DPR_CMD(17)
998ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITECT		DPR_CMD(18)
1008ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READCT		DPR_CMD(19)
1018ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITEDATA	DPR_CMD(32)
1028ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READDATA	DPR_CMD(33)
1038ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITEIO		DPR_CMD(34)
1048ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READIO		DPR_CMD(35)
1058ffdff6aSGreg Kroah-Hartman #define DPR_CMD_WRITECODE	DPR_CMD(36)
1068ffdff6aSGreg Kroah-Hartman #define DPR_CMD_READCODE	DPR_CMD(37)
1078ffdff6aSGreg Kroah-Hartman #define DPR_CMD_EXECUTE		DPR_CMD(38)
1088ffdff6aSGreg Kroah-Hartman #define DPR_CMD_HALT		DPR_CMD(48)
1098ffdff6aSGreg Kroah-Hartman #define DPR_CMD_MASK		DPR_CMD(0xff)
1108ffdff6aSGreg Kroah-Hartman 
1118ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG(x)		(((x) & 0x7) << 2)
1128ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_INT		DPR_PARAM5_AD_TRIG(0)
1138ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_EXT		DPR_PARAM5_AD_TRIG(1)
1148ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_INT_RETRIG	DPR_PARAM5_AD_TRIG(2)
1158ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_EXT_RETRIG	DPR_PARAM5_AD_TRIG(3)
1168ffdff6aSGreg Kroah-Hartman #define DPR_PARAM5_AD_TRIG_INT_RETRIG2	DPR_PARAM5_AD_TRIG(4)
1178ffdff6aSGreg Kroah-Hartman 
1188ffdff6aSGreg Kroah-Hartman #define DPR_PARAM6_AD_DIFF		BIT(0)
1198ffdff6aSGreg Kroah-Hartman 
1208ffdff6aSGreg Kroah-Hartman #define DPR_AI_FIFO_DEPTH		2003
1218ffdff6aSGreg Kroah-Hartman #define DPR_AO_FIFO_DEPTH		2048
1228ffdff6aSGreg Kroah-Hartman 
1238ffdff6aSGreg Kroah-Hartman #define DPR_EXTERNAL_CLOCK		1
1248ffdff6aSGreg Kroah-Hartman #define DPR_RISING_EDGE			2
1258ffdff6aSGreg Kroah-Hartman 
1268ffdff6aSGreg Kroah-Hartman #define DPR_TMODE_MASK			0x1c
1278ffdff6aSGreg Kroah-Hartman 
1288ffdff6aSGreg Kroah-Hartman #define DPR_CMD_TIMEOUT			100
1298ffdff6aSGreg Kroah-Hartman 
1308ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_dt3000_ai = {
1318ffdff6aSGreg Kroah-Hartman 	4, {
1328ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),
1338ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(5),
1348ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(2.5),
1358ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(1.25)
1368ffdff6aSGreg Kroah-Hartman 	}
1378ffdff6aSGreg Kroah-Hartman };
1388ffdff6aSGreg Kroah-Hartman 
1398ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange range_dt3000_ai_pgl = {
1408ffdff6aSGreg Kroah-Hartman 	4, {
1418ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),
1428ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(1),
1438ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.1),
1448ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(0.02)
1458ffdff6aSGreg Kroah-Hartman 	}
1468ffdff6aSGreg Kroah-Hartman };
1478ffdff6aSGreg Kroah-Hartman 
1488ffdff6aSGreg Kroah-Hartman enum dt3k_boardid {
1498ffdff6aSGreg Kroah-Hartman 	BOARD_DT3001,
1508ffdff6aSGreg Kroah-Hartman 	BOARD_DT3001_PGL,
1518ffdff6aSGreg Kroah-Hartman 	BOARD_DT3002,
1528ffdff6aSGreg Kroah-Hartman 	BOARD_DT3003,
1538ffdff6aSGreg Kroah-Hartman 	BOARD_DT3003_PGL,
1548ffdff6aSGreg Kroah-Hartman 	BOARD_DT3004,
1558ffdff6aSGreg Kroah-Hartman 	BOARD_DT3005,
1568ffdff6aSGreg Kroah-Hartman };
1578ffdff6aSGreg Kroah-Hartman 
1588ffdff6aSGreg Kroah-Hartman struct dt3k_boardtype {
1598ffdff6aSGreg Kroah-Hartman 	const char *name;
1608ffdff6aSGreg Kroah-Hartman 	int adchan;
1618ffdff6aSGreg Kroah-Hartman 	int ai_speed;
1628ffdff6aSGreg Kroah-Hartman 	const struct comedi_lrange *adrange;
1638ffdff6aSGreg Kroah-Hartman 	unsigned int ai_is_16bit:1;
1648ffdff6aSGreg Kroah-Hartman 	unsigned int has_ao:1;
1658ffdff6aSGreg Kroah-Hartman };
1668ffdff6aSGreg Kroah-Hartman 
1678ffdff6aSGreg Kroah-Hartman static const struct dt3k_boardtype dt3k_boardtypes[] = {
1688ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3001] = {
1698ffdff6aSGreg Kroah-Hartman 		.name		= "dt3001",
1708ffdff6aSGreg Kroah-Hartman 		.adchan		= 16,
1718ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai,
1728ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 3000,
1738ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
1748ffdff6aSGreg Kroah-Hartman 	},
1758ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3001_PGL] = {
1768ffdff6aSGreg Kroah-Hartman 		.name		= "dt3001-pgl",
1778ffdff6aSGreg Kroah-Hartman 		.adchan		= 16,
1788ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai_pgl,
1798ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 3000,
1808ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
1818ffdff6aSGreg Kroah-Hartman 	},
1828ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3002] = {
1838ffdff6aSGreg Kroah-Hartman 		.name		= "dt3002",
1848ffdff6aSGreg Kroah-Hartman 		.adchan		= 32,
1858ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai,
1868ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 3000,
1878ffdff6aSGreg Kroah-Hartman 	},
1888ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3003] = {
1898ffdff6aSGreg Kroah-Hartman 		.name		= "dt3003",
1908ffdff6aSGreg Kroah-Hartman 		.adchan		= 64,
1918ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai,
1928ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 3000,
1938ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
1948ffdff6aSGreg Kroah-Hartman 	},
1958ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3003_PGL] = {
1968ffdff6aSGreg Kroah-Hartman 		.name		= "dt3003-pgl",
1978ffdff6aSGreg Kroah-Hartman 		.adchan		= 64,
1988ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai_pgl,
1998ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 3000,
2008ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
2018ffdff6aSGreg Kroah-Hartman 	},
2028ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3004] = {
2038ffdff6aSGreg Kroah-Hartman 		.name		= "dt3004",
2048ffdff6aSGreg Kroah-Hartman 		.adchan		= 16,
2058ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai,
2068ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 10000,
2078ffdff6aSGreg Kroah-Hartman 		.ai_is_16bit	= 1,
2088ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
2098ffdff6aSGreg Kroah-Hartman 	},
2108ffdff6aSGreg Kroah-Hartman 	[BOARD_DT3005] = {
2118ffdff6aSGreg Kroah-Hartman 		.name		= "dt3005",	/* a.k.a. 3004-200 */
2128ffdff6aSGreg Kroah-Hartman 		.adchan		= 16,
2138ffdff6aSGreg Kroah-Hartman 		.adrange	= &range_dt3000_ai,
2148ffdff6aSGreg Kroah-Hartman 		.ai_speed	= 5000,
2158ffdff6aSGreg Kroah-Hartman 		.ai_is_16bit	= 1,
2168ffdff6aSGreg Kroah-Hartman 		.has_ao		= 1,
2178ffdff6aSGreg Kroah-Hartman 	},
2188ffdff6aSGreg Kroah-Hartman };
2198ffdff6aSGreg Kroah-Hartman 
2208ffdff6aSGreg Kroah-Hartman struct dt3k_private {
2218ffdff6aSGreg Kroah-Hartman 	unsigned int lock;
2228ffdff6aSGreg Kroah-Hartman 	unsigned int ai_front;
2238ffdff6aSGreg Kroah-Hartman 	unsigned int ai_rear;
2248ffdff6aSGreg Kroah-Hartman };
2258ffdff6aSGreg Kroah-Hartman 
dt3k_send_cmd(struct comedi_device * dev,unsigned int cmd)2268ffdff6aSGreg Kroah-Hartman static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
2278ffdff6aSGreg Kroah-Hartman {
2288ffdff6aSGreg Kroah-Hartman 	int i;
2298ffdff6aSGreg Kroah-Hartman 	unsigned int status = 0;
2308ffdff6aSGreg Kroah-Hartman 
2318ffdff6aSGreg Kroah-Hartman 	writew(cmd, dev->mmio + DPR_CMD_MBX);
2328ffdff6aSGreg Kroah-Hartman 
2338ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < DPR_CMD_TIMEOUT; i++) {
2348ffdff6aSGreg Kroah-Hartman 		status = readw(dev->mmio + DPR_CMD_MBX);
2358ffdff6aSGreg Kroah-Hartman 		status &= DPR_CMD_COMPLETION_MASK;
2368ffdff6aSGreg Kroah-Hartman 		if (status != DPR_CMD_NOTPROCESSED)
2378ffdff6aSGreg Kroah-Hartman 			break;
2388ffdff6aSGreg Kroah-Hartman 		udelay(1);
2398ffdff6aSGreg Kroah-Hartman 	}
2408ffdff6aSGreg Kroah-Hartman 
2418ffdff6aSGreg Kroah-Hartman 	if (status != DPR_CMD_NOERROR)
2428ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
2438ffdff6aSGreg Kroah-Hartman 			__func__, status);
2448ffdff6aSGreg Kroah-Hartman }
2458ffdff6aSGreg Kroah-Hartman 
dt3k_readsingle(struct comedi_device * dev,unsigned int subsys,unsigned int chan,unsigned int gain)2468ffdff6aSGreg Kroah-Hartman static unsigned int dt3k_readsingle(struct comedi_device *dev,
2478ffdff6aSGreg Kroah-Hartman 				    unsigned int subsys, unsigned int chan,
2488ffdff6aSGreg Kroah-Hartman 				    unsigned int gain)
2498ffdff6aSGreg Kroah-Hartman {
2508ffdff6aSGreg Kroah-Hartman 	writew(subsys, dev->mmio + DPR_SUBSYS);
2518ffdff6aSGreg Kroah-Hartman 
2528ffdff6aSGreg Kroah-Hartman 	writew(chan, dev->mmio + DPR_PARAMS(0));
2538ffdff6aSGreg Kroah-Hartman 	writew(gain, dev->mmio + DPR_PARAMS(1));
2548ffdff6aSGreg Kroah-Hartman 
2558ffdff6aSGreg Kroah-Hartman 	dt3k_send_cmd(dev, DPR_CMD_READSINGLE);
2568ffdff6aSGreg Kroah-Hartman 
2578ffdff6aSGreg Kroah-Hartman 	return readw(dev->mmio + DPR_PARAMS(2));
2588ffdff6aSGreg Kroah-Hartman }
2598ffdff6aSGreg Kroah-Hartman 
dt3k_writesingle(struct comedi_device * dev,unsigned int subsys,unsigned int chan,unsigned int data)2608ffdff6aSGreg Kroah-Hartman static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
2618ffdff6aSGreg Kroah-Hartman 			     unsigned int chan, unsigned int data)
2628ffdff6aSGreg Kroah-Hartman {
2638ffdff6aSGreg Kroah-Hartman 	writew(subsys, dev->mmio + DPR_SUBSYS);
2648ffdff6aSGreg Kroah-Hartman 
2658ffdff6aSGreg Kroah-Hartman 	writew(chan, dev->mmio + DPR_PARAMS(0));
2668ffdff6aSGreg Kroah-Hartman 	writew(0, dev->mmio + DPR_PARAMS(1));
2678ffdff6aSGreg Kroah-Hartman 	writew(data, dev->mmio + DPR_PARAMS(2));
2688ffdff6aSGreg Kroah-Hartman 
2698ffdff6aSGreg Kroah-Hartman 	dt3k_send_cmd(dev, DPR_CMD_WRITESINGLE);
2708ffdff6aSGreg Kroah-Hartman }
2718ffdff6aSGreg Kroah-Hartman 
dt3k_ai_empty_fifo(struct comedi_device * dev,struct comedi_subdevice * s)2728ffdff6aSGreg Kroah-Hartman static void dt3k_ai_empty_fifo(struct comedi_device *dev,
2738ffdff6aSGreg Kroah-Hartman 			       struct comedi_subdevice *s)
2748ffdff6aSGreg Kroah-Hartman {
2758ffdff6aSGreg Kroah-Hartman 	struct dt3k_private *devpriv = dev->private;
2768ffdff6aSGreg Kroah-Hartman 	int front;
2778ffdff6aSGreg Kroah-Hartman 	int rear;
2788ffdff6aSGreg Kroah-Hartman 	int count;
2798ffdff6aSGreg Kroah-Hartman 	int i;
2808ffdff6aSGreg Kroah-Hartman 	unsigned short data;
2818ffdff6aSGreg Kroah-Hartman 
2828ffdff6aSGreg Kroah-Hartman 	front = readw(dev->mmio + DPR_AD_BUF_FRONT);
2838ffdff6aSGreg Kroah-Hartman 	count = front - devpriv->ai_front;
2848ffdff6aSGreg Kroah-Hartman 	if (count < 0)
2858ffdff6aSGreg Kroah-Hartman 		count += DPR_AI_FIFO_DEPTH;
2868ffdff6aSGreg Kroah-Hartman 
2878ffdff6aSGreg Kroah-Hartman 	rear = devpriv->ai_rear;
2888ffdff6aSGreg Kroah-Hartman 
2898ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < count; i++) {
2908ffdff6aSGreg Kroah-Hartman 		data = readw(dev->mmio + DPR_ADC_BUFFER + rear);
2918ffdff6aSGreg Kroah-Hartman 		comedi_buf_write_samples(s, &data, 1);
2928ffdff6aSGreg Kroah-Hartman 		rear++;
2938ffdff6aSGreg Kroah-Hartman 		if (rear >= DPR_AI_FIFO_DEPTH)
2948ffdff6aSGreg Kroah-Hartman 			rear = 0;
2958ffdff6aSGreg Kroah-Hartman 	}
2968ffdff6aSGreg Kroah-Hartman 
2978ffdff6aSGreg Kroah-Hartman 	devpriv->ai_rear = rear;
2988ffdff6aSGreg Kroah-Hartman 	writew(rear, dev->mmio + DPR_AD_BUF_REAR);
2998ffdff6aSGreg Kroah-Hartman }
3008ffdff6aSGreg Kroah-Hartman 
dt3k_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)3018ffdff6aSGreg Kroah-Hartman static int dt3k_ai_cancel(struct comedi_device *dev,
3028ffdff6aSGreg Kroah-Hartman 			  struct comedi_subdevice *s)
3038ffdff6aSGreg Kroah-Hartman {
3048ffdff6aSGreg Kroah-Hartman 	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
3058ffdff6aSGreg Kroah-Hartman 	dt3k_send_cmd(dev, DPR_CMD_STOP);
3068ffdff6aSGreg Kroah-Hartman 
3078ffdff6aSGreg Kroah-Hartman 	writew(0, dev->mmio + DPR_INT_MASK);
3088ffdff6aSGreg Kroah-Hartman 
3098ffdff6aSGreg Kroah-Hartman 	return 0;
3108ffdff6aSGreg Kroah-Hartman }
3118ffdff6aSGreg Kroah-Hartman 
3128ffdff6aSGreg Kroah-Hartman static int debug_n_ints;
3138ffdff6aSGreg Kroah-Hartman 
3148ffdff6aSGreg Kroah-Hartman /* FIXME! Assumes shared interrupt is for this card. */
3158ffdff6aSGreg Kroah-Hartman /* What's this debug_n_ints stuff? Obviously needs some work... */
dt3k_interrupt(int irq,void * d)3168ffdff6aSGreg Kroah-Hartman static irqreturn_t dt3k_interrupt(int irq, void *d)
3178ffdff6aSGreg Kroah-Hartman {
3188ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
3198ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s = dev->read_subdev;
3208ffdff6aSGreg Kroah-Hartman 	unsigned int status;
3218ffdff6aSGreg Kroah-Hartman 
3228ffdff6aSGreg Kroah-Hartman 	if (!dev->attached)
3238ffdff6aSGreg Kroah-Hartman 		return IRQ_NONE;
3248ffdff6aSGreg Kroah-Hartman 
3258ffdff6aSGreg Kroah-Hartman 	status = readw(dev->mmio + DPR_INTR_FLAG);
3268ffdff6aSGreg Kroah-Hartman 
3278ffdff6aSGreg Kroah-Hartman 	if (status & DPR_INTR_ADFULL)
3288ffdff6aSGreg Kroah-Hartman 		dt3k_ai_empty_fifo(dev, s);
3298ffdff6aSGreg Kroah-Hartman 
3308ffdff6aSGreg Kroah-Hartman 	if (status & (DPR_INTR_ADSWERR | DPR_INTR_ADHWERR))
3318ffdff6aSGreg Kroah-Hartman 		s->async->events |= COMEDI_CB_ERROR;
3328ffdff6aSGreg Kroah-Hartman 
3338ffdff6aSGreg Kroah-Hartman 	debug_n_ints++;
3348ffdff6aSGreg Kroah-Hartman 	if (debug_n_ints >= 10)
3358ffdff6aSGreg Kroah-Hartman 		s->async->events |= COMEDI_CB_EOA;
3368ffdff6aSGreg Kroah-Hartman 
3378ffdff6aSGreg Kroah-Hartman 	comedi_handle_events(dev, s);
3388ffdff6aSGreg Kroah-Hartman 	return IRQ_HANDLED;
3398ffdff6aSGreg Kroah-Hartman }
3408ffdff6aSGreg Kroah-Hartman 
dt3k_ns_to_timer(unsigned int timer_base,unsigned int * nanosec,unsigned int flags)3418ffdff6aSGreg Kroah-Hartman static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
3428ffdff6aSGreg Kroah-Hartman 			    unsigned int flags)
3438ffdff6aSGreg Kroah-Hartman {
3448ffdff6aSGreg Kroah-Hartman 	unsigned int divider, base, prescale;
3458ffdff6aSGreg Kroah-Hartman 
3468ffdff6aSGreg Kroah-Hartman 	/* This function needs improvement */
3478ffdff6aSGreg Kroah-Hartman 	/* Don't know if divider==0 works. */
3488ffdff6aSGreg Kroah-Hartman 
3498ffdff6aSGreg Kroah-Hartman 	for (prescale = 0; prescale < 16; prescale++) {
3508ffdff6aSGreg Kroah-Hartman 		base = timer_base * (prescale + 1);
3518ffdff6aSGreg Kroah-Hartman 		switch (flags & CMDF_ROUND_MASK) {
3528ffdff6aSGreg Kroah-Hartman 		case CMDF_ROUND_NEAREST:
3538ffdff6aSGreg Kroah-Hartman 		default:
3548ffdff6aSGreg Kroah-Hartman 			divider = DIV_ROUND_CLOSEST(*nanosec, base);
3558ffdff6aSGreg Kroah-Hartman 			break;
3568ffdff6aSGreg Kroah-Hartman 		case CMDF_ROUND_DOWN:
3578ffdff6aSGreg Kroah-Hartman 			divider = (*nanosec) / base;
3588ffdff6aSGreg Kroah-Hartman 			break;
3598ffdff6aSGreg Kroah-Hartman 		case CMDF_ROUND_UP:
3608ffdff6aSGreg Kroah-Hartman 			divider = DIV_ROUND_UP(*nanosec, base);
3618ffdff6aSGreg Kroah-Hartman 			break;
3628ffdff6aSGreg Kroah-Hartman 		}
3638ffdff6aSGreg Kroah-Hartman 		if (divider < 65536) {
3648ffdff6aSGreg Kroah-Hartman 			*nanosec = divider * base;
3658ffdff6aSGreg Kroah-Hartman 			return (prescale << 16) | (divider);
3668ffdff6aSGreg Kroah-Hartman 		}
3678ffdff6aSGreg Kroah-Hartman 	}
3688ffdff6aSGreg Kroah-Hartman 
3698ffdff6aSGreg Kroah-Hartman 	prescale = 15;
3708ffdff6aSGreg Kroah-Hartman 	base = timer_base * (prescale + 1);
3718ffdff6aSGreg Kroah-Hartman 	divider = 65535;
3728ffdff6aSGreg Kroah-Hartman 	*nanosec = divider * base;
3738ffdff6aSGreg Kroah-Hartman 	return (prescale << 16) | (divider);
3748ffdff6aSGreg Kroah-Hartman }
3758ffdff6aSGreg Kroah-Hartman 
dt3k_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)3768ffdff6aSGreg Kroah-Hartman static int dt3k_ai_cmdtest(struct comedi_device *dev,
3778ffdff6aSGreg Kroah-Hartman 			   struct comedi_subdevice *s, struct comedi_cmd *cmd)
3788ffdff6aSGreg Kroah-Hartman {
3798ffdff6aSGreg Kroah-Hartman 	const struct dt3k_boardtype *board = dev->board_ptr;
3808ffdff6aSGreg Kroah-Hartman 	int err = 0;
3818ffdff6aSGreg Kroah-Hartman 	unsigned int arg;
3828ffdff6aSGreg Kroah-Hartman 
3838ffdff6aSGreg Kroah-Hartman 	/* Step 1 : check if triggers are trivially valid */
3848ffdff6aSGreg Kroah-Hartman 
3858ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
3868ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
3878ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
3888ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
3898ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
3908ffdff6aSGreg Kroah-Hartman 
3918ffdff6aSGreg Kroah-Hartman 	if (err)
3928ffdff6aSGreg Kroah-Hartman 		return 1;
3938ffdff6aSGreg Kroah-Hartman 
3948ffdff6aSGreg Kroah-Hartman 	/* Step 2a : make sure trigger sources are unique */
3958ffdff6aSGreg Kroah-Hartman 	/* Step 2b : and mutually compatible */
3968ffdff6aSGreg Kroah-Hartman 
3978ffdff6aSGreg Kroah-Hartman 	/* Step 3: check if arguments are trivially valid */
3988ffdff6aSGreg Kroah-Hartman 
3998ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
4008ffdff6aSGreg Kroah-Hartman 
4018ffdff6aSGreg Kroah-Hartman 	if (cmd->scan_begin_src == TRIG_TIMER) {
4028ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
4038ffdff6aSGreg Kroah-Hartman 						    board->ai_speed);
4048ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
4058ffdff6aSGreg Kroah-Hartman 						    100 * 16 * 65535);
4068ffdff6aSGreg Kroah-Hartman 	}
4078ffdff6aSGreg Kroah-Hartman 
4088ffdff6aSGreg Kroah-Hartman 	if (cmd->convert_src == TRIG_TIMER) {
4098ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
4108ffdff6aSGreg Kroah-Hartman 						    board->ai_speed);
4118ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
4128ffdff6aSGreg Kroah-Hartman 						    50 * 16 * 65535);
4138ffdff6aSGreg Kroah-Hartman 	}
4148ffdff6aSGreg Kroah-Hartman 
4158ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
4168ffdff6aSGreg Kroah-Hartman 					   cmd->chanlist_len);
4178ffdff6aSGreg Kroah-Hartman 
4188ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT)
4198ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
4208ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NONE */
4218ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
4228ffdff6aSGreg Kroah-Hartman 
4238ffdff6aSGreg Kroah-Hartman 	if (err)
4248ffdff6aSGreg Kroah-Hartman 		return 3;
4258ffdff6aSGreg Kroah-Hartman 
4268ffdff6aSGreg Kroah-Hartman 	/* step 4: fix up any arguments */
4278ffdff6aSGreg Kroah-Hartman 
4288ffdff6aSGreg Kroah-Hartman 	if (cmd->scan_begin_src == TRIG_TIMER) {
4298ffdff6aSGreg Kroah-Hartman 		arg = cmd->scan_begin_arg;
4308ffdff6aSGreg Kroah-Hartman 		dt3k_ns_to_timer(100, &arg, cmd->flags);
4318ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
4328ffdff6aSGreg Kroah-Hartman 	}
4338ffdff6aSGreg Kroah-Hartman 
4348ffdff6aSGreg Kroah-Hartman 	if (cmd->convert_src == TRIG_TIMER) {
4358ffdff6aSGreg Kroah-Hartman 		arg = cmd->convert_arg;
4368ffdff6aSGreg Kroah-Hartman 		dt3k_ns_to_timer(50, &arg, cmd->flags);
4378ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
4388ffdff6aSGreg Kroah-Hartman 
4398ffdff6aSGreg Kroah-Hartman 		if (cmd->scan_begin_src == TRIG_TIMER) {
4408ffdff6aSGreg Kroah-Hartman 			arg = cmd->convert_arg * cmd->scan_end_arg;
4418ffdff6aSGreg Kroah-Hartman 			err |= comedi_check_trigger_arg_min(
4428ffdff6aSGreg Kroah-Hartman 				&cmd->scan_begin_arg, arg);
4438ffdff6aSGreg Kroah-Hartman 		}
4448ffdff6aSGreg Kroah-Hartman 	}
4458ffdff6aSGreg Kroah-Hartman 
4468ffdff6aSGreg Kroah-Hartman 	if (err)
4478ffdff6aSGreg Kroah-Hartman 		return 4;
4488ffdff6aSGreg Kroah-Hartman 
4498ffdff6aSGreg Kroah-Hartman 	return 0;
4508ffdff6aSGreg Kroah-Hartman }
4518ffdff6aSGreg Kroah-Hartman 
dt3k_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)4528ffdff6aSGreg Kroah-Hartman static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
4538ffdff6aSGreg Kroah-Hartman {
4548ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
4558ffdff6aSGreg Kroah-Hartman 	int i;
4568ffdff6aSGreg Kroah-Hartman 	unsigned int chan, range, aref;
4578ffdff6aSGreg Kroah-Hartman 	unsigned int divider;
4588ffdff6aSGreg Kroah-Hartman 	unsigned int tscandiv;
4598ffdff6aSGreg Kroah-Hartman 
4608ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < cmd->chanlist_len; i++) {
4618ffdff6aSGreg Kroah-Hartman 		chan = CR_CHAN(cmd->chanlist[i]);
4628ffdff6aSGreg Kroah-Hartman 		range = CR_RANGE(cmd->chanlist[i]);
4638ffdff6aSGreg Kroah-Hartman 
4648ffdff6aSGreg Kroah-Hartman 		writew((range << 6) | chan, dev->mmio + DPR_ADC_BUFFER + i);
4658ffdff6aSGreg Kroah-Hartman 	}
4668ffdff6aSGreg Kroah-Hartman 	aref = CR_AREF(cmd->chanlist[0]);
4678ffdff6aSGreg Kroah-Hartman 
4688ffdff6aSGreg Kroah-Hartman 	writew(cmd->scan_end_arg, dev->mmio + DPR_PARAMS(0));
4698ffdff6aSGreg Kroah-Hartman 
4708ffdff6aSGreg Kroah-Hartman 	if (cmd->convert_src == TRIG_TIMER) {
4718ffdff6aSGreg Kroah-Hartman 		divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags);
4728ffdff6aSGreg Kroah-Hartman 		writew((divider >> 16), dev->mmio + DPR_PARAMS(1));
4738ffdff6aSGreg Kroah-Hartman 		writew((divider & 0xffff), dev->mmio + DPR_PARAMS(2));
4748ffdff6aSGreg Kroah-Hartman 	}
4758ffdff6aSGreg Kroah-Hartman 
4768ffdff6aSGreg Kroah-Hartman 	if (cmd->scan_begin_src == TRIG_TIMER) {
4778ffdff6aSGreg Kroah-Hartman 		tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
4788ffdff6aSGreg Kroah-Hartman 					    cmd->flags);
4798ffdff6aSGreg Kroah-Hartman 		writew((tscandiv >> 16), dev->mmio + DPR_PARAMS(3));
4808ffdff6aSGreg Kroah-Hartman 		writew((tscandiv & 0xffff), dev->mmio + DPR_PARAMS(4));
4818ffdff6aSGreg Kroah-Hartman 	}
4828ffdff6aSGreg Kroah-Hartman 
4838ffdff6aSGreg Kroah-Hartman 	writew(DPR_PARAM5_AD_TRIG_INT_RETRIG, dev->mmio + DPR_PARAMS(5));
4848ffdff6aSGreg Kroah-Hartman 	writew((aref == AREF_DIFF) ? DPR_PARAM6_AD_DIFF : 0,
4858ffdff6aSGreg Kroah-Hartman 	       dev->mmio + DPR_PARAMS(6));
4868ffdff6aSGreg Kroah-Hartman 
4878ffdff6aSGreg Kroah-Hartman 	writew(DPR_AI_FIFO_DEPTH / 2, dev->mmio + DPR_PARAMS(7));
4888ffdff6aSGreg Kroah-Hartman 
4898ffdff6aSGreg Kroah-Hartman 	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
4908ffdff6aSGreg Kroah-Hartman 	dt3k_send_cmd(dev, DPR_CMD_CONFIG);
4918ffdff6aSGreg Kroah-Hartman 
4928ffdff6aSGreg Kroah-Hartman 	writew(DPR_INTR_ADFULL | DPR_INTR_ADSWERR | DPR_INTR_ADHWERR,
4938ffdff6aSGreg Kroah-Hartman 	       dev->mmio + DPR_INT_MASK);
4948ffdff6aSGreg Kroah-Hartman 
4958ffdff6aSGreg Kroah-Hartman 	debug_n_ints = 0;
4968ffdff6aSGreg Kroah-Hartman 
4978ffdff6aSGreg Kroah-Hartman 	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
4988ffdff6aSGreg Kroah-Hartman 	dt3k_send_cmd(dev, DPR_CMD_START);
4998ffdff6aSGreg Kroah-Hartman 
5008ffdff6aSGreg Kroah-Hartman 	return 0;
5018ffdff6aSGreg Kroah-Hartman }
5028ffdff6aSGreg Kroah-Hartman 
dt3k_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)5038ffdff6aSGreg Kroah-Hartman static int dt3k_ai_insn_read(struct comedi_device *dev,
5048ffdff6aSGreg Kroah-Hartman 			     struct comedi_subdevice *s,
5058ffdff6aSGreg Kroah-Hartman 			     struct comedi_insn *insn,
5068ffdff6aSGreg Kroah-Hartman 			     unsigned int *data)
5078ffdff6aSGreg Kroah-Hartman {
5088ffdff6aSGreg Kroah-Hartman 	int i;
5098ffdff6aSGreg Kroah-Hartman 	unsigned int chan, gain;
5108ffdff6aSGreg Kroah-Hartman 
5118ffdff6aSGreg Kroah-Hartman 	chan = CR_CHAN(insn->chanspec);
5128ffdff6aSGreg Kroah-Hartman 	gain = CR_RANGE(insn->chanspec);
5138ffdff6aSGreg Kroah-Hartman 	/* XXX docs don't explain how to select aref */
5148ffdff6aSGreg Kroah-Hartman 
5158ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++)
5168ffdff6aSGreg Kroah-Hartman 		data[i] = dt3k_readsingle(dev, DPR_SUBSYS_AI, chan, gain);
5178ffdff6aSGreg Kroah-Hartman 
5188ffdff6aSGreg Kroah-Hartman 	return i;
5198ffdff6aSGreg Kroah-Hartman }
5208ffdff6aSGreg Kroah-Hartman 
dt3k_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)5218ffdff6aSGreg Kroah-Hartman static int dt3k_ao_insn_write(struct comedi_device *dev,
5228ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s,
5238ffdff6aSGreg Kroah-Hartman 			      struct comedi_insn *insn,
5248ffdff6aSGreg Kroah-Hartman 			      unsigned int *data)
5258ffdff6aSGreg Kroah-Hartman {
5268ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
5278ffdff6aSGreg Kroah-Hartman 	unsigned int val = s->readback[chan];
5288ffdff6aSGreg Kroah-Hartman 	int i;
5298ffdff6aSGreg Kroah-Hartman 
5308ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++) {
5318ffdff6aSGreg Kroah-Hartman 		val = data[i];
5328ffdff6aSGreg Kroah-Hartman 		dt3k_writesingle(dev, DPR_SUBSYS_AO, chan, val);
5338ffdff6aSGreg Kroah-Hartman 	}
5348ffdff6aSGreg Kroah-Hartman 	s->readback[chan] = val;
5358ffdff6aSGreg Kroah-Hartman 
5368ffdff6aSGreg Kroah-Hartman 	return insn->n;
5378ffdff6aSGreg Kroah-Hartman }
5388ffdff6aSGreg Kroah-Hartman 
dt3k_dio_config(struct comedi_device * dev,int bits)5398ffdff6aSGreg Kroah-Hartman static void dt3k_dio_config(struct comedi_device *dev, int bits)
5408ffdff6aSGreg Kroah-Hartman {
5418ffdff6aSGreg Kroah-Hartman 	/* XXX */
5428ffdff6aSGreg Kroah-Hartman 	writew(DPR_SUBSYS_DOUT, dev->mmio + DPR_SUBSYS);
5438ffdff6aSGreg Kroah-Hartman 
5448ffdff6aSGreg Kroah-Hartman 	writew(bits, dev->mmio + DPR_PARAMS(0));
5458ffdff6aSGreg Kroah-Hartman 
5468ffdff6aSGreg Kroah-Hartman 	/* XXX write 0 to DPR_PARAMS(1) and DPR_PARAMS(2) ? */
5478ffdff6aSGreg Kroah-Hartman 
5488ffdff6aSGreg Kroah-Hartman 	dt3k_send_cmd(dev, DPR_CMD_CONFIG);
5498ffdff6aSGreg Kroah-Hartman }
5508ffdff6aSGreg Kroah-Hartman 
dt3k_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)5518ffdff6aSGreg Kroah-Hartman static int dt3k_dio_insn_config(struct comedi_device *dev,
5528ffdff6aSGreg Kroah-Hartman 				struct comedi_subdevice *s,
5538ffdff6aSGreg Kroah-Hartman 				struct comedi_insn *insn,
5548ffdff6aSGreg Kroah-Hartman 				unsigned int *data)
5558ffdff6aSGreg Kroah-Hartman {
5568ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
5578ffdff6aSGreg Kroah-Hartman 	unsigned int mask;
5588ffdff6aSGreg Kroah-Hartman 	int ret;
5598ffdff6aSGreg Kroah-Hartman 
5608ffdff6aSGreg Kroah-Hartman 	if (chan < 4)
5618ffdff6aSGreg Kroah-Hartman 		mask = 0x0f;
5628ffdff6aSGreg Kroah-Hartman 	else
5638ffdff6aSGreg Kroah-Hartman 		mask = 0xf0;
5648ffdff6aSGreg Kroah-Hartman 
5658ffdff6aSGreg Kroah-Hartman 	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
5668ffdff6aSGreg Kroah-Hartman 	if (ret)
5678ffdff6aSGreg Kroah-Hartman 		return ret;
5688ffdff6aSGreg Kroah-Hartman 
5698ffdff6aSGreg Kroah-Hartman 	dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3));
5708ffdff6aSGreg Kroah-Hartman 
5718ffdff6aSGreg Kroah-Hartman 	return insn->n;
5728ffdff6aSGreg Kroah-Hartman }
5738ffdff6aSGreg Kroah-Hartman 
dt3k_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)5748ffdff6aSGreg Kroah-Hartman static int dt3k_dio_insn_bits(struct comedi_device *dev,
5758ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s,
5768ffdff6aSGreg Kroah-Hartman 			      struct comedi_insn *insn,
5778ffdff6aSGreg Kroah-Hartman 			      unsigned int *data)
5788ffdff6aSGreg Kroah-Hartman {
5798ffdff6aSGreg Kroah-Hartman 	if (comedi_dio_update_state(s, data))
5808ffdff6aSGreg Kroah-Hartman 		dt3k_writesingle(dev, DPR_SUBSYS_DOUT, 0, s->state);
5818ffdff6aSGreg Kroah-Hartman 
5828ffdff6aSGreg Kroah-Hartman 	data[1] = dt3k_readsingle(dev, DPR_SUBSYS_DIN, 0, 0);
5838ffdff6aSGreg Kroah-Hartman 
5848ffdff6aSGreg Kroah-Hartman 	return insn->n;
5858ffdff6aSGreg Kroah-Hartman }
5868ffdff6aSGreg Kroah-Hartman 
dt3k_mem_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)5878ffdff6aSGreg Kroah-Hartman static int dt3k_mem_insn_read(struct comedi_device *dev,
5888ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s,
5898ffdff6aSGreg Kroah-Hartman 			      struct comedi_insn *insn,
5908ffdff6aSGreg Kroah-Hartman 			      unsigned int *data)
5918ffdff6aSGreg Kroah-Hartman {
5928ffdff6aSGreg Kroah-Hartman 	unsigned int addr = CR_CHAN(insn->chanspec);
5938ffdff6aSGreg Kroah-Hartman 	int i;
5948ffdff6aSGreg Kroah-Hartman 
5958ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++) {
5968ffdff6aSGreg Kroah-Hartman 		writew(DPR_SUBSYS_MEM, dev->mmio + DPR_SUBSYS);
5978ffdff6aSGreg Kroah-Hartman 		writew(addr, dev->mmio + DPR_PARAMS(0));
5988ffdff6aSGreg Kroah-Hartman 		writew(1, dev->mmio + DPR_PARAMS(1));
5998ffdff6aSGreg Kroah-Hartman 
6008ffdff6aSGreg Kroah-Hartman 		dt3k_send_cmd(dev, DPR_CMD_READCODE);
6018ffdff6aSGreg Kroah-Hartman 
6028ffdff6aSGreg Kroah-Hartman 		data[i] = readw(dev->mmio + DPR_PARAMS(2));
6038ffdff6aSGreg Kroah-Hartman 	}
6048ffdff6aSGreg Kroah-Hartman 
6058ffdff6aSGreg Kroah-Hartman 	return i;
6068ffdff6aSGreg Kroah-Hartman }
6078ffdff6aSGreg Kroah-Hartman 
dt3000_auto_attach(struct comedi_device * dev,unsigned long context)6088ffdff6aSGreg Kroah-Hartman static int dt3000_auto_attach(struct comedi_device *dev,
6098ffdff6aSGreg Kroah-Hartman 			      unsigned long context)
6108ffdff6aSGreg Kroah-Hartman {
6118ffdff6aSGreg Kroah-Hartman 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
6128ffdff6aSGreg Kroah-Hartman 	const struct dt3k_boardtype *board = NULL;
6138ffdff6aSGreg Kroah-Hartman 	struct dt3k_private *devpriv;
6148ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
6158ffdff6aSGreg Kroah-Hartman 	int ret = 0;
6168ffdff6aSGreg Kroah-Hartman 
6178ffdff6aSGreg Kroah-Hartman 	if (context < ARRAY_SIZE(dt3k_boardtypes))
6188ffdff6aSGreg Kroah-Hartman 		board = &dt3k_boardtypes[context];
6198ffdff6aSGreg Kroah-Hartman 	if (!board)
6208ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
6218ffdff6aSGreg Kroah-Hartman 	dev->board_ptr = board;
6228ffdff6aSGreg Kroah-Hartman 	dev->board_name = board->name;
6238ffdff6aSGreg Kroah-Hartman 
6248ffdff6aSGreg Kroah-Hartman 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
6258ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
6268ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
6278ffdff6aSGreg Kroah-Hartman 
6288ffdff6aSGreg Kroah-Hartman 	ret = comedi_pci_enable(dev);
6298ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
6308ffdff6aSGreg Kroah-Hartman 		return ret;
6318ffdff6aSGreg Kroah-Hartman 
6328ffdff6aSGreg Kroah-Hartman 	dev->mmio = pci_ioremap_bar(pcidev, 0);
6338ffdff6aSGreg Kroah-Hartman 	if (!dev->mmio)
6348ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
6358ffdff6aSGreg Kroah-Hartman 
6368ffdff6aSGreg Kroah-Hartman 	if (pcidev->irq) {
6378ffdff6aSGreg Kroah-Hartman 		ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
6388ffdff6aSGreg Kroah-Hartman 				  dev->board_name, dev);
6398ffdff6aSGreg Kroah-Hartman 		if (ret == 0)
6408ffdff6aSGreg Kroah-Hartman 			dev->irq = pcidev->irq;
6418ffdff6aSGreg Kroah-Hartman 	}
6428ffdff6aSGreg Kroah-Hartman 
6438ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, 4);
6448ffdff6aSGreg Kroah-Hartman 	if (ret)
6458ffdff6aSGreg Kroah-Hartman 		return ret;
6468ffdff6aSGreg Kroah-Hartman 
6478ffdff6aSGreg Kroah-Hartman 	/* Analog Input subdevice */
6488ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[0];
6498ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_AI;
6508ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
6518ffdff6aSGreg Kroah-Hartman 	s->n_chan	= board->adchan;
6528ffdff6aSGreg Kroah-Hartman 	s->maxdata	= board->ai_is_16bit ? 0xffff : 0x0fff;
6538ffdff6aSGreg Kroah-Hartman 	s->range_table	= &range_dt3000_ai;	/* XXX */
6548ffdff6aSGreg Kroah-Hartman 	s->insn_read	= dt3k_ai_insn_read;
6558ffdff6aSGreg Kroah-Hartman 	if (dev->irq) {
6568ffdff6aSGreg Kroah-Hartman 		dev->read_subdev = s;
6578ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	|= SDF_CMD_READ;
6588ffdff6aSGreg Kroah-Hartman 		s->len_chanlist	= 512;
6598ffdff6aSGreg Kroah-Hartman 		s->do_cmd	= dt3k_ai_cmd;
6608ffdff6aSGreg Kroah-Hartman 		s->do_cmdtest	= dt3k_ai_cmdtest;
6618ffdff6aSGreg Kroah-Hartman 		s->cancel	= dt3k_ai_cancel;
6628ffdff6aSGreg Kroah-Hartman 	}
6638ffdff6aSGreg Kroah-Hartman 
6648ffdff6aSGreg Kroah-Hartman 	/* Analog Output subdevice */
6658ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[1];
6668ffdff6aSGreg Kroah-Hartman 	if (board->has_ao) {
6678ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_AO;
6688ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_WRITABLE;
6698ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 2;
6708ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 0x0fff;
6718ffdff6aSGreg Kroah-Hartman 		s->range_table	= &range_bipolar10;
6728ffdff6aSGreg Kroah-Hartman 		s->insn_write	= dt3k_ao_insn_write;
6738ffdff6aSGreg Kroah-Hartman 
6748ffdff6aSGreg Kroah-Hartman 		ret = comedi_alloc_subdev_readback(s);
6758ffdff6aSGreg Kroah-Hartman 		if (ret)
6768ffdff6aSGreg Kroah-Hartman 			return ret;
6778ffdff6aSGreg Kroah-Hartman 
6788ffdff6aSGreg Kroah-Hartman 	} else {
6798ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_UNUSED;
6808ffdff6aSGreg Kroah-Hartman 	}
6818ffdff6aSGreg Kroah-Hartman 
6828ffdff6aSGreg Kroah-Hartman 	/* Digital I/O subdevice */
6838ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[2];
6848ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_DIO;
6858ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
6868ffdff6aSGreg Kroah-Hartman 	s->n_chan	= 8;
6878ffdff6aSGreg Kroah-Hartman 	s->maxdata	= 1;
6888ffdff6aSGreg Kroah-Hartman 	s->range_table	= &range_digital;
6898ffdff6aSGreg Kroah-Hartman 	s->insn_config	= dt3k_dio_insn_config;
6908ffdff6aSGreg Kroah-Hartman 	s->insn_bits	= dt3k_dio_insn_bits;
6918ffdff6aSGreg Kroah-Hartman 
6928ffdff6aSGreg Kroah-Hartman 	/* Memory subdevice */
6938ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[3];
6948ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_MEMORY;
6958ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE;
6968ffdff6aSGreg Kroah-Hartman 	s->n_chan	= 0x1000;
6978ffdff6aSGreg Kroah-Hartman 	s->maxdata	= 0xff;
6988ffdff6aSGreg Kroah-Hartman 	s->range_table	= &range_unknown;
6998ffdff6aSGreg Kroah-Hartman 	s->insn_read	= dt3k_mem_insn_read;
7008ffdff6aSGreg Kroah-Hartman 
7018ffdff6aSGreg Kroah-Hartman 	return 0;
7028ffdff6aSGreg Kroah-Hartman }
7038ffdff6aSGreg Kroah-Hartman 
7048ffdff6aSGreg Kroah-Hartman static struct comedi_driver dt3000_driver = {
7058ffdff6aSGreg Kroah-Hartman 	.driver_name	= "dt3000",
7068ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
7078ffdff6aSGreg Kroah-Hartman 	.auto_attach	= dt3000_auto_attach,
7088ffdff6aSGreg Kroah-Hartman 	.detach		= comedi_pci_detach,
7098ffdff6aSGreg Kroah-Hartman };
7108ffdff6aSGreg Kroah-Hartman 
dt3000_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)7118ffdff6aSGreg Kroah-Hartman static int dt3000_pci_probe(struct pci_dev *dev,
7128ffdff6aSGreg Kroah-Hartman 			    const struct pci_device_id *id)
7138ffdff6aSGreg Kroah-Hartman {
7148ffdff6aSGreg Kroah-Hartman 	return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
7158ffdff6aSGreg Kroah-Hartman }
7168ffdff6aSGreg Kroah-Hartman 
7178ffdff6aSGreg Kroah-Hartman static const struct pci_device_id dt3000_pci_table[] = {
7188ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
7198ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
7208ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
7218ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
7228ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
7238ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
7248ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
7258ffdff6aSGreg Kroah-Hartman 	{ 0 }
7268ffdff6aSGreg Kroah-Hartman };
7278ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
7288ffdff6aSGreg Kroah-Hartman 
7298ffdff6aSGreg Kroah-Hartman static struct pci_driver dt3000_pci_driver = {
7308ffdff6aSGreg Kroah-Hartman 	.name		= "dt3000",
7318ffdff6aSGreg Kroah-Hartman 	.id_table	= dt3000_pci_table,
7328ffdff6aSGreg Kroah-Hartman 	.probe		= dt3000_pci_probe,
7338ffdff6aSGreg Kroah-Hartman 	.remove		= comedi_pci_auto_unconfig,
7348ffdff6aSGreg Kroah-Hartman };
7358ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
7368ffdff6aSGreg Kroah-Hartman 
7378ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
7388ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi driver for Data Translation DT3000 series boards");
7398ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
740