18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman  * addi_apci_3xxx.c
48ffdff6aSGreg Kroah-Hartman  * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
58ffdff6aSGreg Kroah-Hartman  * Project manager: S. Weber
68ffdff6aSGreg Kroah-Hartman  *
78ffdff6aSGreg Kroah-Hartman  *	ADDI-DATA GmbH
88ffdff6aSGreg Kroah-Hartman  *	Dieselstrasse 3
98ffdff6aSGreg Kroah-Hartman  *	D-77833 Ottersweier
108ffdff6aSGreg Kroah-Hartman  *	Tel: +19(0)7223/9493-0
118ffdff6aSGreg Kroah-Hartman  *	Fax: +49(0)7223/9493-92
128ffdff6aSGreg Kroah-Hartman  *	http://www.addi-data.com
138ffdff6aSGreg Kroah-Hartman  *	info@addi-data.com
148ffdff6aSGreg Kroah-Hartman  */
158ffdff6aSGreg Kroah-Hartman 
168ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
178ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
18*df0e68c1SIan Abbott #include <linux/comedi/comedi_pci.h>
198ffdff6aSGreg Kroah-Hartman 
208ffdff6aSGreg Kroah-Hartman #define CONV_UNIT_NS		BIT(0)
218ffdff6aSGreg Kroah-Hartman #define CONV_UNIT_US		BIT(1)
228ffdff6aSGreg Kroah-Hartman #define CONV_UNIT_MS		BIT(2)
238ffdff6aSGreg Kroah-Hartman 
248ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange apci3xxx_ai_range = {
258ffdff6aSGreg Kroah-Hartman 	8, {
268ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),
278ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(5),
288ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(2),
298ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(1),
308ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(10),
318ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(5),
328ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(2),
338ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(1)
348ffdff6aSGreg Kroah-Hartman 	}
358ffdff6aSGreg Kroah-Hartman };
368ffdff6aSGreg Kroah-Hartman 
378ffdff6aSGreg Kroah-Hartman static const struct comedi_lrange apci3xxx_ao_range = {
388ffdff6aSGreg Kroah-Hartman 	2, {
398ffdff6aSGreg Kroah-Hartman 		BIP_RANGE(10),
408ffdff6aSGreg Kroah-Hartman 		UNI_RANGE(10)
418ffdff6aSGreg Kroah-Hartman 	}
428ffdff6aSGreg Kroah-Hartman };
438ffdff6aSGreg Kroah-Hartman 
448ffdff6aSGreg Kroah-Hartman enum apci3xxx_boardid {
458ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3000_16,
468ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3000_8,
478ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3000_4,
488ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3006_16,
498ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3006_8,
508ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3006_4,
518ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3010_16,
528ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3010_8,
538ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3010_4,
548ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3016_16,
558ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3016_8,
568ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3016_4,
578ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3100_16_4,
588ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3100_8_4,
598ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3106_16_4,
608ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3106_8_4,
618ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3110_16_4,
628ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3110_8_4,
638ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3116_16_4,
648ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3116_8_4,
658ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3003,
668ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3002_16,
678ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3002_8,
688ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3002_4,
698ffdff6aSGreg Kroah-Hartman 	BOARD_APCI3500,
708ffdff6aSGreg Kroah-Hartman };
718ffdff6aSGreg Kroah-Hartman 
728ffdff6aSGreg Kroah-Hartman struct apci3xxx_boardinfo {
738ffdff6aSGreg Kroah-Hartman 	const char *name;
748ffdff6aSGreg Kroah-Hartman 	int ai_subdev_flags;
758ffdff6aSGreg Kroah-Hartman 	int ai_n_chan;
768ffdff6aSGreg Kroah-Hartman 	unsigned int ai_maxdata;
778ffdff6aSGreg Kroah-Hartman 	unsigned char ai_conv_units;
788ffdff6aSGreg Kroah-Hartman 	unsigned int ai_min_acq_ns;
798ffdff6aSGreg Kroah-Hartman 	unsigned int has_ao:1;
808ffdff6aSGreg Kroah-Hartman 	unsigned int has_dig_in:1;
818ffdff6aSGreg Kroah-Hartman 	unsigned int has_dig_out:1;
828ffdff6aSGreg Kroah-Hartman 	unsigned int has_ttl_io:1;
838ffdff6aSGreg Kroah-Hartman };
848ffdff6aSGreg Kroah-Hartman 
858ffdff6aSGreg Kroah-Hartman static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
868ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3000_16] = {
878ffdff6aSGreg Kroah-Hartman 		.name			= "apci3000-16",
888ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
898ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
908ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
918ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
928ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
938ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
948ffdff6aSGreg Kroah-Hartman 	},
958ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3000_8] = {
968ffdff6aSGreg Kroah-Hartman 		.name			= "apci3000-8",
978ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
988ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
998ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
1008ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1018ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
1028ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1038ffdff6aSGreg Kroah-Hartman 	},
1048ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3000_4] = {
1058ffdff6aSGreg Kroah-Hartman 		.name			= "apci3000-4",
1068ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1078ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 4,
1088ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
1098ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1108ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
1118ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1128ffdff6aSGreg Kroah-Hartman 	},
1138ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3006_16] = {
1148ffdff6aSGreg Kroah-Hartman 		.name			= "apci3006-16",
1158ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1168ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
1178ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
1188ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1198ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
1208ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1218ffdff6aSGreg Kroah-Hartman 	},
1228ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3006_8] = {
1238ffdff6aSGreg Kroah-Hartman 		.name			= "apci3006-8",
1248ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1258ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
1268ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
1278ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1288ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
1298ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1308ffdff6aSGreg Kroah-Hartman 	},
1318ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3006_4] = {
1328ffdff6aSGreg Kroah-Hartman 		.name			= "apci3006-4",
1338ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1348ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 4,
1358ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
1368ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1378ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
1388ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1398ffdff6aSGreg Kroah-Hartman 	},
1408ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3010_16] = {
1418ffdff6aSGreg Kroah-Hartman 		.name			= "apci3010-16",
1428ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1438ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
1448ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
1458ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1468ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
1478ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
1488ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
1498ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1508ffdff6aSGreg Kroah-Hartman 	},
1518ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3010_8] = {
1528ffdff6aSGreg Kroah-Hartman 		.name			= "apci3010-8",
1538ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1548ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
1558ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
1568ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1578ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
1588ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
1598ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
1608ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1618ffdff6aSGreg Kroah-Hartman 	},
1628ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3010_4] = {
1638ffdff6aSGreg Kroah-Hartman 		.name			= "apci3010-4",
1648ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1658ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 4,
1668ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
1678ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1688ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
1698ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
1708ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
1718ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1728ffdff6aSGreg Kroah-Hartman 	},
1738ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3016_16] = {
1748ffdff6aSGreg Kroah-Hartman 		.name			= "apci3016-16",
1758ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1768ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
1778ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
1788ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1798ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
1808ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
1818ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
1828ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1838ffdff6aSGreg Kroah-Hartman 	},
1848ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3016_8] = {
1858ffdff6aSGreg Kroah-Hartman 		.name			= "apci3016-8",
1868ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1878ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
1888ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
1898ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
1908ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
1918ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
1928ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
1938ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
1948ffdff6aSGreg Kroah-Hartman 	},
1958ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3016_4] = {
1968ffdff6aSGreg Kroah-Hartman 		.name			= "apci3016-4",
1978ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
1988ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 4,
1998ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
2008ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2018ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
2028ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
2038ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
2048ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2058ffdff6aSGreg Kroah-Hartman 	},
2068ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3100_16_4] = {
2078ffdff6aSGreg Kroah-Hartman 		.name			= "apci3100-16-4",
2088ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2098ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
2108ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
2118ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2128ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
2138ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2148ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2158ffdff6aSGreg Kroah-Hartman 	},
2168ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3100_8_4] = {
2178ffdff6aSGreg Kroah-Hartman 		.name			= "apci3100-8-4",
2188ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2198ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
2208ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
2218ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2228ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
2238ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2248ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2258ffdff6aSGreg Kroah-Hartman 	},
2268ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3106_16_4] = {
2278ffdff6aSGreg Kroah-Hartman 		.name			= "apci3106-16-4",
2288ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2298ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
2308ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
2318ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2328ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
2338ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2348ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2358ffdff6aSGreg Kroah-Hartman 	},
2368ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3106_8_4] = {
2378ffdff6aSGreg Kroah-Hartman 		.name			= "apci3106-8-4",
2388ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2398ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
2408ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
2418ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2428ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 10000,
2438ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2448ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2458ffdff6aSGreg Kroah-Hartman 	},
2468ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3110_16_4] = {
2478ffdff6aSGreg Kroah-Hartman 		.name			= "apci3110-16-4",
2488ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2498ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
2508ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
2518ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2528ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
2538ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2548ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
2558ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
2568ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2578ffdff6aSGreg Kroah-Hartman 	},
2588ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3110_8_4] = {
2598ffdff6aSGreg Kroah-Hartman 		.name			= "apci3110-8-4",
2608ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2618ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
2628ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0x0fff,
2638ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2648ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
2658ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2668ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
2678ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
2688ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2698ffdff6aSGreg Kroah-Hartman 	},
2708ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3116_16_4] = {
2718ffdff6aSGreg Kroah-Hartman 		.name			= "apci3116-16-4",
2728ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2738ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
2748ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
2758ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2768ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
2778ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2788ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
2798ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
2808ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2818ffdff6aSGreg Kroah-Hartman 	},
2828ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3116_8_4] = {
2838ffdff6aSGreg Kroah-Hartman 		.name			= "apci3116-8-4",
2848ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_COMMON | SDF_GROUND | SDF_DIFF,
2858ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
2868ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
2878ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
2888ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
2898ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
2908ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
2918ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
2928ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
2938ffdff6aSGreg Kroah-Hartman 	},
2948ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3003] = {
2958ffdff6aSGreg Kroah-Hartman 		.name			= "apci3003",
2968ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_DIFF,
2978ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 4,
2988ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
2998ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US |
3008ffdff6aSGreg Kroah-Hartman 					  CONV_UNIT_NS,
3018ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 2500,
3028ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
3038ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
3048ffdff6aSGreg Kroah-Hartman 	},
3058ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3002_16] = {
3068ffdff6aSGreg Kroah-Hartman 		.name			= "apci3002-16",
3078ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_DIFF,
3088ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 16,
3098ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
3108ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
3118ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
3128ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
3138ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
3148ffdff6aSGreg Kroah-Hartman 	},
3158ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3002_8] = {
3168ffdff6aSGreg Kroah-Hartman 		.name			= "apci3002-8",
3178ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_DIFF,
3188ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 8,
3198ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
3208ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
3218ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
3228ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
3238ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
3248ffdff6aSGreg Kroah-Hartman 	},
3258ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3002_4] = {
3268ffdff6aSGreg Kroah-Hartman 		.name			= "apci3002-4",
3278ffdff6aSGreg Kroah-Hartman 		.ai_subdev_flags	= SDF_DIFF,
3288ffdff6aSGreg Kroah-Hartman 		.ai_n_chan		= 4,
3298ffdff6aSGreg Kroah-Hartman 		.ai_maxdata		= 0xffff,
3308ffdff6aSGreg Kroah-Hartman 		.ai_conv_units		= CONV_UNIT_MS | CONV_UNIT_US,
3318ffdff6aSGreg Kroah-Hartman 		.ai_min_acq_ns		= 5000,
3328ffdff6aSGreg Kroah-Hartman 		.has_dig_in		= 1,
3338ffdff6aSGreg Kroah-Hartman 		.has_dig_out		= 1,
3348ffdff6aSGreg Kroah-Hartman 	},
3358ffdff6aSGreg Kroah-Hartman 	[BOARD_APCI3500] = {
3368ffdff6aSGreg Kroah-Hartman 		.name			= "apci3500",
3378ffdff6aSGreg Kroah-Hartman 		.has_ao			= 1,
3388ffdff6aSGreg Kroah-Hartman 		.has_ttl_io		= 1,
3398ffdff6aSGreg Kroah-Hartman 	},
3408ffdff6aSGreg Kroah-Hartman };
3418ffdff6aSGreg Kroah-Hartman 
3428ffdff6aSGreg Kroah-Hartman struct apci3xxx_private {
3438ffdff6aSGreg Kroah-Hartman 	unsigned int ai_timer;
3448ffdff6aSGreg Kroah-Hartman 	unsigned char ai_time_base;
3458ffdff6aSGreg Kroah-Hartman };
3468ffdff6aSGreg Kroah-Hartman 
apci3xxx_irq_handler(int irq,void * d)3478ffdff6aSGreg Kroah-Hartman static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
3488ffdff6aSGreg Kroah-Hartman {
3498ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
3508ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s = dev->read_subdev;
3518ffdff6aSGreg Kroah-Hartman 	unsigned int status;
3528ffdff6aSGreg Kroah-Hartman 	unsigned int val;
3538ffdff6aSGreg Kroah-Hartman 
3548ffdff6aSGreg Kroah-Hartman 	/* Test if interrupt occur */
3558ffdff6aSGreg Kroah-Hartman 	status = readl(dev->mmio + 16);
3568ffdff6aSGreg Kroah-Hartman 	if ((status & 0x2) == 0x2) {
3578ffdff6aSGreg Kroah-Hartman 		/* Reset the interrupt */
3588ffdff6aSGreg Kroah-Hartman 		writel(status, dev->mmio + 16);
3598ffdff6aSGreg Kroah-Hartman 
3608ffdff6aSGreg Kroah-Hartman 		val = readl(dev->mmio + 28);
3618ffdff6aSGreg Kroah-Hartman 		comedi_buf_write_samples(s, &val, 1);
3628ffdff6aSGreg Kroah-Hartman 
3638ffdff6aSGreg Kroah-Hartman 		s->async->events |= COMEDI_CB_EOA;
3648ffdff6aSGreg Kroah-Hartman 		comedi_handle_events(dev, s);
3658ffdff6aSGreg Kroah-Hartman 
3668ffdff6aSGreg Kroah-Hartman 		return IRQ_HANDLED;
3678ffdff6aSGreg Kroah-Hartman 	}
3688ffdff6aSGreg Kroah-Hartman 	return IRQ_NONE;
3698ffdff6aSGreg Kroah-Hartman }
3708ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_started(struct comedi_device * dev)3718ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_started(struct comedi_device *dev)
3728ffdff6aSGreg Kroah-Hartman {
3738ffdff6aSGreg Kroah-Hartman 	if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
3748ffdff6aSGreg Kroah-Hartman 		return 1;
3758ffdff6aSGreg Kroah-Hartman 
3768ffdff6aSGreg Kroah-Hartman 	return 0;
3778ffdff6aSGreg Kroah-Hartman }
3788ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_setup(struct comedi_device * dev,unsigned int chanspec)3798ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
3808ffdff6aSGreg Kroah-Hartman {
3818ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(chanspec);
3828ffdff6aSGreg Kroah-Hartman 	unsigned int range = CR_RANGE(chanspec);
3838ffdff6aSGreg Kroah-Hartman 	unsigned int aref = CR_AREF(chanspec);
3848ffdff6aSGreg Kroah-Hartman 	unsigned int delay_mode;
3858ffdff6aSGreg Kroah-Hartman 	unsigned int val;
3868ffdff6aSGreg Kroah-Hartman 
3878ffdff6aSGreg Kroah-Hartman 	if (apci3xxx_ai_started(dev))
3888ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
3898ffdff6aSGreg Kroah-Hartman 
3908ffdff6aSGreg Kroah-Hartman 	/* Clear the FIFO */
3918ffdff6aSGreg Kroah-Hartman 	writel(0x10000, dev->mmio + 12);
3928ffdff6aSGreg Kroah-Hartman 
3938ffdff6aSGreg Kroah-Hartman 	/* Get and save the delay mode */
3948ffdff6aSGreg Kroah-Hartman 	delay_mode = readl(dev->mmio + 4);
3958ffdff6aSGreg Kroah-Hartman 	delay_mode &= 0xfffffef0;
3968ffdff6aSGreg Kroah-Hartman 
3978ffdff6aSGreg Kroah-Hartman 	/* Channel configuration selection */
3988ffdff6aSGreg Kroah-Hartman 	writel(delay_mode, dev->mmio + 4);
3998ffdff6aSGreg Kroah-Hartman 
4008ffdff6aSGreg Kroah-Hartman 	/* Make the configuration */
4018ffdff6aSGreg Kroah-Hartman 	val = (range & 3) | ((range >> 2) << 6) |
4028ffdff6aSGreg Kroah-Hartman 	      ((aref == AREF_DIFF) << 7);
4038ffdff6aSGreg Kroah-Hartman 	writel(val, dev->mmio + 0);
4048ffdff6aSGreg Kroah-Hartman 
4058ffdff6aSGreg Kroah-Hartman 	/* Channel selection */
4068ffdff6aSGreg Kroah-Hartman 	writel(delay_mode | 0x100, dev->mmio + 4);
4078ffdff6aSGreg Kroah-Hartman 	writel(chan, dev->mmio + 0);
4088ffdff6aSGreg Kroah-Hartman 
4098ffdff6aSGreg Kroah-Hartman 	/* Restore delay mode */
4108ffdff6aSGreg Kroah-Hartman 	writel(delay_mode, dev->mmio + 4);
4118ffdff6aSGreg Kroah-Hartman 
4128ffdff6aSGreg Kroah-Hartman 	/* Set the number of sequence to 1 */
4138ffdff6aSGreg Kroah-Hartman 	writel(1, dev->mmio + 48);
4148ffdff6aSGreg Kroah-Hartman 
4158ffdff6aSGreg Kroah-Hartman 	return 0;
4168ffdff6aSGreg Kroah-Hartman }
4178ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)4188ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_eoc(struct comedi_device *dev,
4198ffdff6aSGreg Kroah-Hartman 			   struct comedi_subdevice *s,
4208ffdff6aSGreg Kroah-Hartman 			   struct comedi_insn *insn,
4218ffdff6aSGreg Kroah-Hartman 			   unsigned long context)
4228ffdff6aSGreg Kroah-Hartman {
4238ffdff6aSGreg Kroah-Hartman 	unsigned int status;
4248ffdff6aSGreg Kroah-Hartman 
4258ffdff6aSGreg Kroah-Hartman 	status = readl(dev->mmio + 20);
4268ffdff6aSGreg Kroah-Hartman 	if (status & 0x1)
4278ffdff6aSGreg Kroah-Hartman 		return 0;
4288ffdff6aSGreg Kroah-Hartman 	return -EBUSY;
4298ffdff6aSGreg Kroah-Hartman }
4308ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)4318ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_insn_read(struct comedi_device *dev,
4328ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
4338ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
4348ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
4358ffdff6aSGreg Kroah-Hartman {
4368ffdff6aSGreg Kroah-Hartman 	int ret;
4378ffdff6aSGreg Kroah-Hartman 	int i;
4388ffdff6aSGreg Kroah-Hartman 
4398ffdff6aSGreg Kroah-Hartman 	ret = apci3xxx_ai_setup(dev, insn->chanspec);
4408ffdff6aSGreg Kroah-Hartman 	if (ret)
4418ffdff6aSGreg Kroah-Hartman 		return ret;
4428ffdff6aSGreg Kroah-Hartman 
4438ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++) {
4448ffdff6aSGreg Kroah-Hartman 		/* Start the conversion */
4458ffdff6aSGreg Kroah-Hartman 		writel(0x80000, dev->mmio + 8);
4468ffdff6aSGreg Kroah-Hartman 
4478ffdff6aSGreg Kroah-Hartman 		/* Wait the EOS */
4488ffdff6aSGreg Kroah-Hartman 		ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
4498ffdff6aSGreg Kroah-Hartman 		if (ret)
4508ffdff6aSGreg Kroah-Hartman 			return ret;
4518ffdff6aSGreg Kroah-Hartman 
4528ffdff6aSGreg Kroah-Hartman 		/* Read the analog value */
4538ffdff6aSGreg Kroah-Hartman 		data[i] = readl(dev->mmio + 28);
4548ffdff6aSGreg Kroah-Hartman 	}
4558ffdff6aSGreg Kroah-Hartman 
4568ffdff6aSGreg Kroah-Hartman 	return insn->n;
4578ffdff6aSGreg Kroah-Hartman }
4588ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_ns_to_timer(struct comedi_device * dev,unsigned int * ns,unsigned int flags)4598ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
4608ffdff6aSGreg Kroah-Hartman 				   unsigned int *ns, unsigned int flags)
4618ffdff6aSGreg Kroah-Hartman {
4628ffdff6aSGreg Kroah-Hartman 	const struct apci3xxx_boardinfo *board = dev->board_ptr;
4638ffdff6aSGreg Kroah-Hartman 	struct apci3xxx_private *devpriv = dev->private;
4648ffdff6aSGreg Kroah-Hartman 	unsigned int base;
4658ffdff6aSGreg Kroah-Hartman 	unsigned int timer;
4668ffdff6aSGreg Kroah-Hartman 	int time_base;
4678ffdff6aSGreg Kroah-Hartman 
4688ffdff6aSGreg Kroah-Hartman 	/* time_base: 0 = ns, 1 = us, 2 = ms */
4698ffdff6aSGreg Kroah-Hartman 	for (time_base = 0; time_base < 3; time_base++) {
4708ffdff6aSGreg Kroah-Hartman 		/* skip unsupported time bases */
4718ffdff6aSGreg Kroah-Hartman 		if (!(board->ai_conv_units & (1 << time_base)))
4728ffdff6aSGreg Kroah-Hartman 			continue;
4738ffdff6aSGreg Kroah-Hartman 
4748ffdff6aSGreg Kroah-Hartman 		switch (time_base) {
4758ffdff6aSGreg Kroah-Hartman 		case 0:
4768ffdff6aSGreg Kroah-Hartman 			base = 1;
4778ffdff6aSGreg Kroah-Hartman 			break;
4788ffdff6aSGreg Kroah-Hartman 		case 1:
4798ffdff6aSGreg Kroah-Hartman 			base = 1000;
4808ffdff6aSGreg Kroah-Hartman 			break;
4818ffdff6aSGreg Kroah-Hartman 		case 2:
4828ffdff6aSGreg Kroah-Hartman 			base = 1000000;
4838ffdff6aSGreg Kroah-Hartman 			break;
4848ffdff6aSGreg Kroah-Hartman 		}
4858ffdff6aSGreg Kroah-Hartman 
4868ffdff6aSGreg Kroah-Hartman 		switch (flags & CMDF_ROUND_MASK) {
4878ffdff6aSGreg Kroah-Hartman 		case CMDF_ROUND_NEAREST:
4888ffdff6aSGreg Kroah-Hartman 		default:
4898ffdff6aSGreg Kroah-Hartman 			timer = DIV_ROUND_CLOSEST(*ns, base);
4908ffdff6aSGreg Kroah-Hartman 			break;
4918ffdff6aSGreg Kroah-Hartman 		case CMDF_ROUND_DOWN:
4928ffdff6aSGreg Kroah-Hartman 			timer = *ns / base;
4938ffdff6aSGreg Kroah-Hartman 			break;
4948ffdff6aSGreg Kroah-Hartman 		case CMDF_ROUND_UP:
4958ffdff6aSGreg Kroah-Hartman 			timer = DIV_ROUND_UP(*ns, base);
4968ffdff6aSGreg Kroah-Hartman 			break;
4978ffdff6aSGreg Kroah-Hartman 		}
4988ffdff6aSGreg Kroah-Hartman 
4998ffdff6aSGreg Kroah-Hartman 		if (timer < 0x10000) {
5008ffdff6aSGreg Kroah-Hartman 			devpriv->ai_time_base = time_base;
5018ffdff6aSGreg Kroah-Hartman 			devpriv->ai_timer = timer;
5028ffdff6aSGreg Kroah-Hartman 			*ns = timer * time_base;
5038ffdff6aSGreg Kroah-Hartman 			return 0;
5048ffdff6aSGreg Kroah-Hartman 		}
5058ffdff6aSGreg Kroah-Hartman 	}
5068ffdff6aSGreg Kroah-Hartman 	return -EINVAL;
5078ffdff6aSGreg Kroah-Hartman }
5088ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)5098ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
5108ffdff6aSGreg Kroah-Hartman 			       struct comedi_subdevice *s,
5118ffdff6aSGreg Kroah-Hartman 			       struct comedi_cmd *cmd)
5128ffdff6aSGreg Kroah-Hartman {
5138ffdff6aSGreg Kroah-Hartman 	const struct apci3xxx_boardinfo *board = dev->board_ptr;
5148ffdff6aSGreg Kroah-Hartman 	int err = 0;
5158ffdff6aSGreg Kroah-Hartman 	unsigned int arg;
5168ffdff6aSGreg Kroah-Hartman 
5178ffdff6aSGreg Kroah-Hartman 	/* Step 1 : check if triggers are trivially valid */
5188ffdff6aSGreg Kroah-Hartman 
5198ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
5208ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
5218ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
5228ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
5238ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
5248ffdff6aSGreg Kroah-Hartman 
5258ffdff6aSGreg Kroah-Hartman 	if (err)
5268ffdff6aSGreg Kroah-Hartman 		return 1;
5278ffdff6aSGreg Kroah-Hartman 
5288ffdff6aSGreg Kroah-Hartman 	/* Step 2a : make sure trigger sources are unique */
5298ffdff6aSGreg Kroah-Hartman 
5308ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_is_unique(cmd->stop_src);
5318ffdff6aSGreg Kroah-Hartman 
5328ffdff6aSGreg Kroah-Hartman 	/* Step 2b : and mutually compatible */
5338ffdff6aSGreg Kroah-Hartman 
5348ffdff6aSGreg Kroah-Hartman 	if (err)
5358ffdff6aSGreg Kroah-Hartman 		return 2;
5368ffdff6aSGreg Kroah-Hartman 
5378ffdff6aSGreg Kroah-Hartman 	/* Step 3: check if arguments are trivially valid */
5388ffdff6aSGreg Kroah-Hartman 
5398ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
5408ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
5418ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
5428ffdff6aSGreg Kroah-Hartman 					    board->ai_min_acq_ns);
5438ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
5448ffdff6aSGreg Kroah-Hartman 					   cmd->chanlist_len);
5458ffdff6aSGreg Kroah-Hartman 
5468ffdff6aSGreg Kroah-Hartman 	if (cmd->stop_src == TRIG_COUNT)
5478ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
5488ffdff6aSGreg Kroah-Hartman 	else	/* TRIG_NONE */
5498ffdff6aSGreg Kroah-Hartman 		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
5508ffdff6aSGreg Kroah-Hartman 
5518ffdff6aSGreg Kroah-Hartman 	if (err)
5528ffdff6aSGreg Kroah-Hartman 		return 3;
5538ffdff6aSGreg Kroah-Hartman 
5548ffdff6aSGreg Kroah-Hartman 	/* step 4: fix up any arguments */
5558ffdff6aSGreg Kroah-Hartman 
5568ffdff6aSGreg Kroah-Hartman 	arg = cmd->convert_arg;
5578ffdff6aSGreg Kroah-Hartman 	err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
5588ffdff6aSGreg Kroah-Hartman 	err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
5598ffdff6aSGreg Kroah-Hartman 
5608ffdff6aSGreg Kroah-Hartman 	if (err)
5618ffdff6aSGreg Kroah-Hartman 		return 4;
5628ffdff6aSGreg Kroah-Hartman 
5638ffdff6aSGreg Kroah-Hartman 	return 0;
5648ffdff6aSGreg Kroah-Hartman }
5658ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)5668ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_cmd(struct comedi_device *dev,
5678ffdff6aSGreg Kroah-Hartman 			   struct comedi_subdevice *s)
5688ffdff6aSGreg Kroah-Hartman {
5698ffdff6aSGreg Kroah-Hartman 	struct apci3xxx_private *devpriv = dev->private;
5708ffdff6aSGreg Kroah-Hartman 	struct comedi_cmd *cmd = &s->async->cmd;
5718ffdff6aSGreg Kroah-Hartman 	int ret;
5728ffdff6aSGreg Kroah-Hartman 
5738ffdff6aSGreg Kroah-Hartman 	ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
5748ffdff6aSGreg Kroah-Hartman 	if (ret)
5758ffdff6aSGreg Kroah-Hartman 		return ret;
5768ffdff6aSGreg Kroah-Hartman 
5778ffdff6aSGreg Kroah-Hartman 	/* Set the convert timing unit */
5788ffdff6aSGreg Kroah-Hartman 	writel(devpriv->ai_time_base, dev->mmio + 36);
5798ffdff6aSGreg Kroah-Hartman 
5808ffdff6aSGreg Kroah-Hartman 	/* Set the convert timing */
5818ffdff6aSGreg Kroah-Hartman 	writel(devpriv->ai_timer, dev->mmio + 32);
5828ffdff6aSGreg Kroah-Hartman 
5838ffdff6aSGreg Kroah-Hartman 	/* Start the conversion */
5848ffdff6aSGreg Kroah-Hartman 	writel(0x180000, dev->mmio + 8);
5858ffdff6aSGreg Kroah-Hartman 
5868ffdff6aSGreg Kroah-Hartman 	return 0;
5878ffdff6aSGreg Kroah-Hartman }
5888ffdff6aSGreg Kroah-Hartman 
apci3xxx_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)5898ffdff6aSGreg Kroah-Hartman static int apci3xxx_ai_cancel(struct comedi_device *dev,
5908ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s)
5918ffdff6aSGreg Kroah-Hartman {
5928ffdff6aSGreg Kroah-Hartman 	return 0;
5938ffdff6aSGreg Kroah-Hartman }
5948ffdff6aSGreg Kroah-Hartman 
apci3xxx_ao_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)5958ffdff6aSGreg Kroah-Hartman static int apci3xxx_ao_eoc(struct comedi_device *dev,
5968ffdff6aSGreg Kroah-Hartman 			   struct comedi_subdevice *s,
5978ffdff6aSGreg Kroah-Hartman 			   struct comedi_insn *insn,
5988ffdff6aSGreg Kroah-Hartman 			   unsigned long context)
5998ffdff6aSGreg Kroah-Hartman {
6008ffdff6aSGreg Kroah-Hartman 	unsigned int status;
6018ffdff6aSGreg Kroah-Hartman 
6028ffdff6aSGreg Kroah-Hartman 	status = readl(dev->mmio + 96);
6038ffdff6aSGreg Kroah-Hartman 	if (status & 0x100)
6048ffdff6aSGreg Kroah-Hartman 		return 0;
6058ffdff6aSGreg Kroah-Hartman 	return -EBUSY;
6068ffdff6aSGreg Kroah-Hartman }
6078ffdff6aSGreg Kroah-Hartman 
apci3xxx_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6088ffdff6aSGreg Kroah-Hartman static int apci3xxx_ao_insn_write(struct comedi_device *dev,
6098ffdff6aSGreg Kroah-Hartman 				  struct comedi_subdevice *s,
6108ffdff6aSGreg Kroah-Hartman 				  struct comedi_insn *insn,
6118ffdff6aSGreg Kroah-Hartman 				  unsigned int *data)
6128ffdff6aSGreg Kroah-Hartman {
6138ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
6148ffdff6aSGreg Kroah-Hartman 	unsigned int range = CR_RANGE(insn->chanspec);
6158ffdff6aSGreg Kroah-Hartman 	int ret;
6168ffdff6aSGreg Kroah-Hartman 	int i;
6178ffdff6aSGreg Kroah-Hartman 
6188ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < insn->n; i++) {
6198ffdff6aSGreg Kroah-Hartman 		unsigned int val = data[i];
6208ffdff6aSGreg Kroah-Hartman 
6218ffdff6aSGreg Kroah-Hartman 		/* Set the range selection */
6228ffdff6aSGreg Kroah-Hartman 		writel(range, dev->mmio + 96);
6238ffdff6aSGreg Kroah-Hartman 
6248ffdff6aSGreg Kroah-Hartman 		/* Write the analog value to the selected channel */
6258ffdff6aSGreg Kroah-Hartman 		writel((val << 8) | chan, dev->mmio + 100);
6268ffdff6aSGreg Kroah-Hartman 
6278ffdff6aSGreg Kroah-Hartman 		/* Wait the end of transfer */
6288ffdff6aSGreg Kroah-Hartman 		ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
6298ffdff6aSGreg Kroah-Hartman 		if (ret)
6308ffdff6aSGreg Kroah-Hartman 			return ret;
6318ffdff6aSGreg Kroah-Hartman 
6328ffdff6aSGreg Kroah-Hartman 		s->readback[chan] = val;
6338ffdff6aSGreg Kroah-Hartman 	}
6348ffdff6aSGreg Kroah-Hartman 
6358ffdff6aSGreg Kroah-Hartman 	return insn->n;
6368ffdff6aSGreg Kroah-Hartman }
6378ffdff6aSGreg Kroah-Hartman 
apci3xxx_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6388ffdff6aSGreg Kroah-Hartman static int apci3xxx_di_insn_bits(struct comedi_device *dev,
6398ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
6408ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
6418ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
6428ffdff6aSGreg Kroah-Hartman {
6438ffdff6aSGreg Kroah-Hartman 	data[1] = inl(dev->iobase + 32) & 0xf;
6448ffdff6aSGreg Kroah-Hartman 
6458ffdff6aSGreg Kroah-Hartman 	return insn->n;
6468ffdff6aSGreg Kroah-Hartman }
6478ffdff6aSGreg Kroah-Hartman 
apci3xxx_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6488ffdff6aSGreg Kroah-Hartman static int apci3xxx_do_insn_bits(struct comedi_device *dev,
6498ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
6508ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
6518ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
6528ffdff6aSGreg Kroah-Hartman {
6538ffdff6aSGreg Kroah-Hartman 	s->state = inl(dev->iobase + 48) & 0xf;
6548ffdff6aSGreg Kroah-Hartman 
6558ffdff6aSGreg Kroah-Hartman 	if (comedi_dio_update_state(s, data))
6568ffdff6aSGreg Kroah-Hartman 		outl(s->state, dev->iobase + 48);
6578ffdff6aSGreg Kroah-Hartman 
6588ffdff6aSGreg Kroah-Hartman 	data[1] = s->state;
6598ffdff6aSGreg Kroah-Hartman 
6608ffdff6aSGreg Kroah-Hartman 	return insn->n;
6618ffdff6aSGreg Kroah-Hartman }
6628ffdff6aSGreg Kroah-Hartman 
apci3xxx_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6638ffdff6aSGreg Kroah-Hartman static int apci3xxx_dio_insn_config(struct comedi_device *dev,
6648ffdff6aSGreg Kroah-Hartman 				    struct comedi_subdevice *s,
6658ffdff6aSGreg Kroah-Hartman 				    struct comedi_insn *insn,
6668ffdff6aSGreg Kroah-Hartman 				    unsigned int *data)
6678ffdff6aSGreg Kroah-Hartman {
6688ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
6698ffdff6aSGreg Kroah-Hartman 	unsigned int mask = 0;
6708ffdff6aSGreg Kroah-Hartman 	int ret;
6718ffdff6aSGreg Kroah-Hartman 
6728ffdff6aSGreg Kroah-Hartman 	/*
6738ffdff6aSGreg Kroah-Hartman 	 * Port 0 (channels 0-7) are always inputs
6748ffdff6aSGreg Kroah-Hartman 	 * Port 1 (channels 8-15) are always outputs
6758ffdff6aSGreg Kroah-Hartman 	 * Port 2 (channels 16-23) are programmable i/o
6768ffdff6aSGreg Kroah-Hartman 	 */
6778ffdff6aSGreg Kroah-Hartman 	if (data[0] != INSN_CONFIG_DIO_QUERY) {
6788ffdff6aSGreg Kroah-Hartman 		/* ignore all other instructions for ports 0 and 1 */
6798ffdff6aSGreg Kroah-Hartman 		if (chan < 16)
6808ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
6818ffdff6aSGreg Kroah-Hartman 
6828ffdff6aSGreg Kroah-Hartman 		/* changing any channel in port 2 changes the entire port */
6838ffdff6aSGreg Kroah-Hartman 		mask = 0xff0000;
6848ffdff6aSGreg Kroah-Hartman 	}
6858ffdff6aSGreg Kroah-Hartman 
6868ffdff6aSGreg Kroah-Hartman 	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
6878ffdff6aSGreg Kroah-Hartman 	if (ret)
6888ffdff6aSGreg Kroah-Hartman 		return ret;
6898ffdff6aSGreg Kroah-Hartman 
6908ffdff6aSGreg Kroah-Hartman 	/* update port 2 configuration */
6918ffdff6aSGreg Kroah-Hartman 	outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
6928ffdff6aSGreg Kroah-Hartman 
6938ffdff6aSGreg Kroah-Hartman 	return insn->n;
6948ffdff6aSGreg Kroah-Hartman }
6958ffdff6aSGreg Kroah-Hartman 
apci3xxx_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6968ffdff6aSGreg Kroah-Hartman static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
6978ffdff6aSGreg Kroah-Hartman 				  struct comedi_subdevice *s,
6988ffdff6aSGreg Kroah-Hartman 				  struct comedi_insn *insn,
6998ffdff6aSGreg Kroah-Hartman 				  unsigned int *data)
7008ffdff6aSGreg Kroah-Hartman {
7018ffdff6aSGreg Kroah-Hartman 	unsigned int mask;
7028ffdff6aSGreg Kroah-Hartman 	unsigned int val;
7038ffdff6aSGreg Kroah-Hartman 
7048ffdff6aSGreg Kroah-Hartman 	mask = comedi_dio_update_state(s, data);
7058ffdff6aSGreg Kroah-Hartman 	if (mask) {
7068ffdff6aSGreg Kroah-Hartman 		if (mask & 0xff)
7078ffdff6aSGreg Kroah-Hartman 			outl(s->state & 0xff, dev->iobase + 80);
7088ffdff6aSGreg Kroah-Hartman 		if (mask & 0xff0000)
7098ffdff6aSGreg Kroah-Hartman 			outl((s->state >> 16) & 0xff, dev->iobase + 112);
7108ffdff6aSGreg Kroah-Hartman 	}
7118ffdff6aSGreg Kroah-Hartman 
7128ffdff6aSGreg Kroah-Hartman 	val = inl(dev->iobase + 80);
7138ffdff6aSGreg Kroah-Hartman 	val |= (inl(dev->iobase + 64) << 8);
7148ffdff6aSGreg Kroah-Hartman 	if (s->io_bits & 0xff0000)
7158ffdff6aSGreg Kroah-Hartman 		val |= (inl(dev->iobase + 112) << 16);
7168ffdff6aSGreg Kroah-Hartman 	else
7178ffdff6aSGreg Kroah-Hartman 		val |= (inl(dev->iobase + 96) << 16);
7188ffdff6aSGreg Kroah-Hartman 
7198ffdff6aSGreg Kroah-Hartman 	data[1] = val;
7208ffdff6aSGreg Kroah-Hartman 
7218ffdff6aSGreg Kroah-Hartman 	return insn->n;
7228ffdff6aSGreg Kroah-Hartman }
7238ffdff6aSGreg Kroah-Hartman 
apci3xxx_reset(struct comedi_device * dev)7248ffdff6aSGreg Kroah-Hartman static int apci3xxx_reset(struct comedi_device *dev)
7258ffdff6aSGreg Kroah-Hartman {
7268ffdff6aSGreg Kroah-Hartman 	unsigned int val;
7278ffdff6aSGreg Kroah-Hartman 	int i;
7288ffdff6aSGreg Kroah-Hartman 
7298ffdff6aSGreg Kroah-Hartman 	/* Disable the interrupt */
7308ffdff6aSGreg Kroah-Hartman 	disable_irq(dev->irq);
7318ffdff6aSGreg Kroah-Hartman 
7328ffdff6aSGreg Kroah-Hartman 	/* Clear the start command */
7338ffdff6aSGreg Kroah-Hartman 	writel(0, dev->mmio + 8);
7348ffdff6aSGreg Kroah-Hartman 
7358ffdff6aSGreg Kroah-Hartman 	/* Reset the interrupt flags */
7368ffdff6aSGreg Kroah-Hartman 	val = readl(dev->mmio + 16);
7378ffdff6aSGreg Kroah-Hartman 	writel(val, dev->mmio + 16);
7388ffdff6aSGreg Kroah-Hartman 
7398ffdff6aSGreg Kroah-Hartman 	/* clear the EOS */
7408ffdff6aSGreg Kroah-Hartman 	readl(dev->mmio + 20);
7418ffdff6aSGreg Kroah-Hartman 
7428ffdff6aSGreg Kroah-Hartman 	/* Clear the FIFO */
7438ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < 16; i++)
7448ffdff6aSGreg Kroah-Hartman 		val = readl(dev->mmio + 28);
7458ffdff6aSGreg Kroah-Hartman 
7468ffdff6aSGreg Kroah-Hartman 	/* Enable the interrupt */
7478ffdff6aSGreg Kroah-Hartman 	enable_irq(dev->irq);
7488ffdff6aSGreg Kroah-Hartman 
7498ffdff6aSGreg Kroah-Hartman 	return 0;
7508ffdff6aSGreg Kroah-Hartman }
7518ffdff6aSGreg Kroah-Hartman 
apci3xxx_auto_attach(struct comedi_device * dev,unsigned long context)7528ffdff6aSGreg Kroah-Hartman static int apci3xxx_auto_attach(struct comedi_device *dev,
7538ffdff6aSGreg Kroah-Hartman 				unsigned long context)
7548ffdff6aSGreg Kroah-Hartman {
7558ffdff6aSGreg Kroah-Hartman 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
7568ffdff6aSGreg Kroah-Hartman 	const struct apci3xxx_boardinfo *board = NULL;
7578ffdff6aSGreg Kroah-Hartman 	struct apci3xxx_private *devpriv;
7588ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
7598ffdff6aSGreg Kroah-Hartman 	int n_subdevices;
7608ffdff6aSGreg Kroah-Hartman 	int subdev;
7618ffdff6aSGreg Kroah-Hartman 	int ret;
7628ffdff6aSGreg Kroah-Hartman 
7638ffdff6aSGreg Kroah-Hartman 	if (context < ARRAY_SIZE(apci3xxx_boardtypes))
7648ffdff6aSGreg Kroah-Hartman 		board = &apci3xxx_boardtypes[context];
7658ffdff6aSGreg Kroah-Hartman 	if (!board)
7668ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
7678ffdff6aSGreg Kroah-Hartman 	dev->board_ptr = board;
7688ffdff6aSGreg Kroah-Hartman 	dev->board_name = board->name;
7698ffdff6aSGreg Kroah-Hartman 
7708ffdff6aSGreg Kroah-Hartman 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
7718ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
7728ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
7738ffdff6aSGreg Kroah-Hartman 
7748ffdff6aSGreg Kroah-Hartman 	ret = comedi_pci_enable(dev);
7758ffdff6aSGreg Kroah-Hartman 	if (ret)
7768ffdff6aSGreg Kroah-Hartman 		return ret;
7778ffdff6aSGreg Kroah-Hartman 
7788ffdff6aSGreg Kroah-Hartman 	dev->iobase = pci_resource_start(pcidev, 2);
7798ffdff6aSGreg Kroah-Hartman 	dev->mmio = pci_ioremap_bar(pcidev, 3);
7808ffdff6aSGreg Kroah-Hartman 	if (!dev->mmio)
7818ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
7828ffdff6aSGreg Kroah-Hartman 
7838ffdff6aSGreg Kroah-Hartman 	if (pcidev->irq > 0) {
7848ffdff6aSGreg Kroah-Hartman 		ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
7858ffdff6aSGreg Kroah-Hartman 				  IRQF_SHARED, dev->board_name, dev);
7868ffdff6aSGreg Kroah-Hartman 		if (ret == 0)
7878ffdff6aSGreg Kroah-Hartman 			dev->irq = pcidev->irq;
7888ffdff6aSGreg Kroah-Hartman 	}
7898ffdff6aSGreg Kroah-Hartman 
7908ffdff6aSGreg Kroah-Hartman 	n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
7918ffdff6aSGreg Kroah-Hartman 		       board->has_dig_in + board->has_dig_out +
7928ffdff6aSGreg Kroah-Hartman 		       board->has_ttl_io;
7938ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, n_subdevices);
7948ffdff6aSGreg Kroah-Hartman 	if (ret)
7958ffdff6aSGreg Kroah-Hartman 		return ret;
7968ffdff6aSGreg Kroah-Hartman 
7978ffdff6aSGreg Kroah-Hartman 	subdev = 0;
7988ffdff6aSGreg Kroah-Hartman 
7998ffdff6aSGreg Kroah-Hartman 	/* Analog Input subdevice */
8008ffdff6aSGreg Kroah-Hartman 	if (board->ai_n_chan) {
8018ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev];
8028ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_AI;
8038ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_READABLE | board->ai_subdev_flags;
8048ffdff6aSGreg Kroah-Hartman 		s->n_chan	= board->ai_n_chan;
8058ffdff6aSGreg Kroah-Hartman 		s->maxdata	= board->ai_maxdata;
8068ffdff6aSGreg Kroah-Hartman 		s->range_table	= &apci3xxx_ai_range;
8078ffdff6aSGreg Kroah-Hartman 		s->insn_read	= apci3xxx_ai_insn_read;
8088ffdff6aSGreg Kroah-Hartman 		if (dev->irq) {
8098ffdff6aSGreg Kroah-Hartman 			/*
8108ffdff6aSGreg Kroah-Hartman 			 * FIXME: The hardware supports multiple scan modes
8118ffdff6aSGreg Kroah-Hartman 			 * but the original addi-data driver only supported
8128ffdff6aSGreg Kroah-Hartman 			 * reading a single channel with interrupts. Need a
8138ffdff6aSGreg Kroah-Hartman 			 * proper datasheet to fix this.
8148ffdff6aSGreg Kroah-Hartman 			 *
8158ffdff6aSGreg Kroah-Hartman 			 * The following scan modes are supported by the
8168ffdff6aSGreg Kroah-Hartman 			 * hardware:
8178ffdff6aSGreg Kroah-Hartman 			 *   1) Single software scan
8188ffdff6aSGreg Kroah-Hartman 			 *   2) Single hardware triggered scan
8198ffdff6aSGreg Kroah-Hartman 			 *   3) Continuous software scan
8208ffdff6aSGreg Kroah-Hartman 			 *   4) Continuous software scan with timer delay
8218ffdff6aSGreg Kroah-Hartman 			 *   5) Continuous hardware triggered scan
8228ffdff6aSGreg Kroah-Hartman 			 *   6) Continuous hardware triggered scan with timer
8238ffdff6aSGreg Kroah-Hartman 			 *      delay
8248ffdff6aSGreg Kroah-Hartman 			 *
8258ffdff6aSGreg Kroah-Hartman 			 * For now, limit the chanlist to a single channel.
8268ffdff6aSGreg Kroah-Hartman 			 */
8278ffdff6aSGreg Kroah-Hartman 			dev->read_subdev = s;
8288ffdff6aSGreg Kroah-Hartman 			s->subdev_flags	|= SDF_CMD_READ;
8298ffdff6aSGreg Kroah-Hartman 			s->len_chanlist	= 1;
8308ffdff6aSGreg Kroah-Hartman 			s->do_cmdtest	= apci3xxx_ai_cmdtest;
8318ffdff6aSGreg Kroah-Hartman 			s->do_cmd	= apci3xxx_ai_cmd;
8328ffdff6aSGreg Kroah-Hartman 			s->cancel	= apci3xxx_ai_cancel;
8338ffdff6aSGreg Kroah-Hartman 		}
8348ffdff6aSGreg Kroah-Hartman 
8358ffdff6aSGreg Kroah-Hartman 		subdev++;
8368ffdff6aSGreg Kroah-Hartman 	}
8378ffdff6aSGreg Kroah-Hartman 
8388ffdff6aSGreg Kroah-Hartman 	/* Analog Output subdevice */
8398ffdff6aSGreg Kroah-Hartman 	if (board->has_ao) {
8408ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev];
8418ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_AO;
8428ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
8438ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 4;
8448ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 0x0fff;
8458ffdff6aSGreg Kroah-Hartman 		s->range_table	= &apci3xxx_ao_range;
8468ffdff6aSGreg Kroah-Hartman 		s->insn_write	= apci3xxx_ao_insn_write;
8478ffdff6aSGreg Kroah-Hartman 
8488ffdff6aSGreg Kroah-Hartman 		ret = comedi_alloc_subdev_readback(s);
8498ffdff6aSGreg Kroah-Hartman 		if (ret)
8508ffdff6aSGreg Kroah-Hartman 			return ret;
8518ffdff6aSGreg Kroah-Hartman 
8528ffdff6aSGreg Kroah-Hartman 		subdev++;
8538ffdff6aSGreg Kroah-Hartman 	}
8548ffdff6aSGreg Kroah-Hartman 
8558ffdff6aSGreg Kroah-Hartman 	/* Digital Input subdevice */
8568ffdff6aSGreg Kroah-Hartman 	if (board->has_dig_in) {
8578ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev];
8588ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_DI;
8598ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_READABLE;
8608ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 4;
8618ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 1;
8628ffdff6aSGreg Kroah-Hartman 		s->range_table	= &range_digital;
8638ffdff6aSGreg Kroah-Hartman 		s->insn_bits	= apci3xxx_di_insn_bits;
8648ffdff6aSGreg Kroah-Hartman 
8658ffdff6aSGreg Kroah-Hartman 		subdev++;
8668ffdff6aSGreg Kroah-Hartman 	}
8678ffdff6aSGreg Kroah-Hartman 
8688ffdff6aSGreg Kroah-Hartman 	/* Digital Output subdevice */
8698ffdff6aSGreg Kroah-Hartman 	if (board->has_dig_out) {
8708ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev];
8718ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_DO;
8728ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_WRITABLE;
8738ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 4;
8748ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 1;
8758ffdff6aSGreg Kroah-Hartman 		s->range_table	= &range_digital;
8768ffdff6aSGreg Kroah-Hartman 		s->insn_bits	= apci3xxx_do_insn_bits;
8778ffdff6aSGreg Kroah-Hartman 
8788ffdff6aSGreg Kroah-Hartman 		subdev++;
8798ffdff6aSGreg Kroah-Hartman 	}
8808ffdff6aSGreg Kroah-Hartman 
8818ffdff6aSGreg Kroah-Hartman 	/* TTL Digital I/O subdevice */
8828ffdff6aSGreg Kroah-Hartman 	if (board->has_ttl_io) {
8838ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev];
8848ffdff6aSGreg Kroah-Hartman 		s->type		= COMEDI_SUBD_DIO;
8858ffdff6aSGreg Kroah-Hartman 		s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
8868ffdff6aSGreg Kroah-Hartman 		s->n_chan	= 24;
8878ffdff6aSGreg Kroah-Hartman 		s->maxdata	= 1;
8888ffdff6aSGreg Kroah-Hartman 		s->io_bits	= 0xff;	/* channels 0-7 are always outputs */
8898ffdff6aSGreg Kroah-Hartman 		s->range_table	= &range_digital;
8908ffdff6aSGreg Kroah-Hartman 		s->insn_config	= apci3xxx_dio_insn_config;
8918ffdff6aSGreg Kroah-Hartman 		s->insn_bits	= apci3xxx_dio_insn_bits;
8928ffdff6aSGreg Kroah-Hartman 
8938ffdff6aSGreg Kroah-Hartman 		subdev++;
8948ffdff6aSGreg Kroah-Hartman 	}
8958ffdff6aSGreg Kroah-Hartman 
8968ffdff6aSGreg Kroah-Hartman 	apci3xxx_reset(dev);
8978ffdff6aSGreg Kroah-Hartman 	return 0;
8988ffdff6aSGreg Kroah-Hartman }
8998ffdff6aSGreg Kroah-Hartman 
apci3xxx_detach(struct comedi_device * dev)9008ffdff6aSGreg Kroah-Hartman static void apci3xxx_detach(struct comedi_device *dev)
9018ffdff6aSGreg Kroah-Hartman {
9028ffdff6aSGreg Kroah-Hartman 	if (dev->iobase)
9038ffdff6aSGreg Kroah-Hartman 		apci3xxx_reset(dev);
9048ffdff6aSGreg Kroah-Hartman 	comedi_pci_detach(dev);
9058ffdff6aSGreg Kroah-Hartman }
9068ffdff6aSGreg Kroah-Hartman 
9078ffdff6aSGreg Kroah-Hartman static struct comedi_driver apci3xxx_driver = {
9088ffdff6aSGreg Kroah-Hartman 	.driver_name	= "addi_apci_3xxx",
9098ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
9108ffdff6aSGreg Kroah-Hartman 	.auto_attach	= apci3xxx_auto_attach,
9118ffdff6aSGreg Kroah-Hartman 	.detach		= apci3xxx_detach,
9128ffdff6aSGreg Kroah-Hartman };
9138ffdff6aSGreg Kroah-Hartman 
apci3xxx_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)9148ffdff6aSGreg Kroah-Hartman static int apci3xxx_pci_probe(struct pci_dev *dev,
9158ffdff6aSGreg Kroah-Hartman 			      const struct pci_device_id *id)
9168ffdff6aSGreg Kroah-Hartman {
9178ffdff6aSGreg Kroah-Hartman 	return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
9188ffdff6aSGreg Kroah-Hartman }
9198ffdff6aSGreg Kroah-Hartman 
9208ffdff6aSGreg Kroah-Hartman static const struct pci_device_id apci3xxx_pci_table[] = {
9218ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
9228ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
9238ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
9248ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
9258ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
9268ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
9278ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
9288ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
9298ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
9308ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
9318ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
9328ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
9338ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
9348ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
9358ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
9368ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
9378ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
9388ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
9398ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
9408ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
9418ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
9428ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
9438ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
9448ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
9458ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
9468ffdff6aSGreg Kroah-Hartman 	{ 0 }
9478ffdff6aSGreg Kroah-Hartman };
9488ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
9498ffdff6aSGreg Kroah-Hartman 
9508ffdff6aSGreg Kroah-Hartman static struct pci_driver apci3xxx_pci_driver = {
9518ffdff6aSGreg Kroah-Hartman 	.name		= "addi_apci_3xxx",
9528ffdff6aSGreg Kroah-Hartman 	.id_table	= apci3xxx_pci_table,
9538ffdff6aSGreg Kroah-Hartman 	.probe		= apci3xxx_pci_probe,
9548ffdff6aSGreg Kroah-Hartman 	.remove		= comedi_pci_auto_unconfig,
9558ffdff6aSGreg Kroah-Hartman };
9568ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
9578ffdff6aSGreg Kroah-Hartman 
9588ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
9598ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi low-level driver");
9608ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
961