xref: /linux/drivers/comedi/drivers/ni_660x.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman  * Hardware driver for NI 660x devices
48ffdff6aSGreg Kroah-Hartman  */
58ffdff6aSGreg Kroah-Hartman 
68ffdff6aSGreg Kroah-Hartman /*
78ffdff6aSGreg Kroah-Hartman  * Driver: ni_660x
88ffdff6aSGreg Kroah-Hartman  * Description: National Instruments 660x counter/timer boards
98ffdff6aSGreg Kroah-Hartman  * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602,
108ffdff6aSGreg Kroah-Hartman  *   PCI-6608, PXI-6608, PCI-6624, PXI-6624
118ffdff6aSGreg Kroah-Hartman  * Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
128ffdff6aSGreg Kroah-Hartman  *   Herman.Bruyninckx@mech.kuleuven.ac.be,
138ffdff6aSGreg Kroah-Hartman  *   Wim.Meeussen@mech.kuleuven.ac.be,
148ffdff6aSGreg Kroah-Hartman  *   Klaas.Gadeyne@mech.kuleuven.ac.be,
158ffdff6aSGreg Kroah-Hartman  *   Frank Mori Hess <fmhess@users.sourceforge.net>
168ffdff6aSGreg Kroah-Hartman  * Updated: Mon, 16 Jan 2017 14:00:43 +0000
178ffdff6aSGreg Kroah-Hartman  * Status: experimental
188ffdff6aSGreg Kroah-Hartman  *
198ffdff6aSGreg Kroah-Hartman  * Encoders work.  PulseGeneration (both single pulse and pulse train)
208ffdff6aSGreg Kroah-Hartman  * works.  Buffered commands work for input but not output.
218ffdff6aSGreg Kroah-Hartman  *
228ffdff6aSGreg Kroah-Hartman  * References:
238ffdff6aSGreg Kroah-Hartman  * DAQ 660x Register-Level Programmer Manual  (NI 370505A-01)
248ffdff6aSGreg Kroah-Hartman  * DAQ 6601/6602 User Manual (NI 322137B-01)
258ffdff6aSGreg Kroah-Hartman  */
268ffdff6aSGreg Kroah-Hartman 
278ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
288ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
29*df0e68c1SIan Abbott #include <linux/comedi/comedi_pci.h>
308ffdff6aSGreg Kroah-Hartman 
318ffdff6aSGreg Kroah-Hartman #include "mite.h"
328ffdff6aSGreg Kroah-Hartman #include "ni_tio.h"
338ffdff6aSGreg Kroah-Hartman #include "ni_routes.h"
348ffdff6aSGreg Kroah-Hartman 
358ffdff6aSGreg Kroah-Hartman /* See Register-Level Programmer Manual page 3.1 */
368ffdff6aSGreg Kroah-Hartman enum ni_660x_register {
378ffdff6aSGreg Kroah-Hartman 	/* see enum ni_gpct_register */
388ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_PARALLEL_INPUT = NITIO_NUM_REGS,
398ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_OUTPUT,
408ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_CONTROL,
418ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_SERIAL_INPUT,
428ffdff6aSGreg Kroah-Hartman 	NI660X_DIO32_INPUT,
438ffdff6aSGreg Kroah-Hartman 	NI660X_DIO32_OUTPUT,
448ffdff6aSGreg Kroah-Hartman 	NI660X_CLK_CFG,
458ffdff6aSGreg Kroah-Hartman 	NI660X_GLOBAL_INT_STATUS,
468ffdff6aSGreg Kroah-Hartman 	NI660X_DMA_CFG,
478ffdff6aSGreg Kroah-Hartman 	NI660X_GLOBAL_INT_CFG,
488ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_0_1,
498ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_2_3,
508ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_4_5,
518ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_6_7,
528ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_8_9,
538ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_10_11,
548ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_12_13,
558ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_14_15,
568ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_16_17,
578ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_18_19,
588ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_20_21,
598ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_22_23,
608ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_24_25,
618ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_26_27,
628ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_28_29,
638ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_30_31,
648ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_32_33,
658ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_34_35,
668ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_36_37,
678ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_38_39,
688ffdff6aSGreg Kroah-Hartman 	NI660X_NUM_REGS,
698ffdff6aSGreg Kroah-Hartman };
708ffdff6aSGreg Kroah-Hartman 
718ffdff6aSGreg Kroah-Hartman #define NI660X_CLK_CFG_COUNTER_SWAP	BIT(21)
728ffdff6aSGreg Kroah-Hartman 
738ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER0	BIT(8)
748ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER1	BIT(9)
758ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER2	BIT(10)
768ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER3	BIT(11)
778ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_CASCADE	BIT(29)
788ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_GLOBAL_POL	BIT(30)
798ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_GLOBAL	BIT(31)
808ffdff6aSGreg Kroah-Hartman 
818ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_SEL(_c, _s)	(((_s) & 0x1f) << (8 * (_c)))
828ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_SEL_MASK(_c)	NI660X_DMA_CFG_SEL((_c), 0x1f)
838ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_SEL_NONE(_c)	NI660X_DMA_CFG_SEL((_c), 0x1f)
848ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_RESET(_c)	NI660X_DMA_CFG_SEL((_c), 0x80)
858ffdff6aSGreg Kroah-Hartman 
868ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG(x)		(NI660X_IO_CFG_0_1 + ((x) / 2))
878ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_OUT_SEL(_c, _s)	(((_s) & 0x3) << (((_c) % 2) ? 0 : 8))
888ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_OUT_SEL_MASK(_c)	NI660X_IO_CFG_OUT_SEL((_c), 0x3)
898ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_IN_SEL(_c, _s)	(((_s) & 0x7) << (((_c) % 2) ? 4 : 12))
908ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_IN_SEL_MASK(_c)	NI660X_IO_CFG_IN_SEL((_c), 0x7)
918ffdff6aSGreg Kroah-Hartman 
928ffdff6aSGreg Kroah-Hartman struct ni_660x_register_data {
938ffdff6aSGreg Kroah-Hartman 	int offset;		/*  Offset from base address from GPCT chip */
948ffdff6aSGreg Kroah-Hartman 	char size;		/* 2 or 4 bytes */
958ffdff6aSGreg Kroah-Hartman };
968ffdff6aSGreg Kroah-Hartman 
978ffdff6aSGreg Kroah-Hartman static const struct ni_660x_register_data ni_660x_reg_data[NI660X_NUM_REGS] = {
988ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_INT_ACK]		= { 0x004, 2 },	/* write */
998ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_STATUS]		= { 0x004, 2 },	/* read */
1008ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_INT_ACK]		= { 0x006, 2 },	/* write */
1018ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_STATUS]		= { 0x006, 2 },	/* read */
1028ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_STATUS]		= { 0x008, 2 },	/* read */
1038ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_CMD]			= { 0x00c, 2 },	/* write */
1048ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_PARALLEL_INPUT]	= { 0x00e, 2 },	/* read */
1058ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_CMD]			= { 0x00e, 2 },	/* write */
1068ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_HW_SAVE]		= { 0x010, 4 },	/* read */
1078ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_HW_SAVE]		= { 0x014, 4 },	/* read */
1088ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_OUTPUT]		= { 0x014, 2 },	/* write */
1098ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_CONTROL]	= { 0x016, 2 },	/* write */
1108ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_SW_SAVE]		= { 0x018, 4 },	/* read */
1118ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_SW_SAVE]		= { 0x01c, 4 },	/* read */
1128ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_MODE]			= { 0x034, 2 },	/* write */
1138ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_STATUS1]		= { 0x036, 2 },	/* read */
1148ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_MODE]			= { 0x036, 2 },	/* write */
1158ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_SERIAL_INPUT]	= { 0x038, 2 },	/* read */
1168ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_LOADA]		= { 0x038, 4 },	/* write */
1178ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_STATUS2]		= { 0x03a, 2 },	/* read */
1188ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_LOADB]		= { 0x03c, 4 },	/* write */
1198ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_LOADA]		= { 0x040, 4 },	/* write */
1208ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_LOADB]		= { 0x044, 4 },	/* write */
1218ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_INPUT_SEL]		= { 0x048, 2 },	/* write */
1228ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_INPUT_SEL]		= { 0x04a, 2 },	/* write */
1238ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_AUTO_INC]		= { 0x088, 2 },	/* write */
1248ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_AUTO_INC]		= { 0x08a, 2 },	/* write */
1258ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_RESET]		= { 0x090, 2 },	/* write */
1268ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_INT_ENA]		= { 0x092, 2 },	/* write */
1278ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_INT_ENA]		= { 0x096, 2 },	/* write */
1288ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_CNT_MODE]		= { 0x0b0, 2 },	/* write */
1298ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_CNT_MODE]		= { 0x0b2, 2 },	/* write */
1308ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_GATE2]		= { 0x0b4, 2 },	/* write */
1318ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_GATE2]		= { 0x0b6, 2 },	/* write */
1328ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_DMA_CFG]		= { 0x0b8, 2 },	/* write */
1338ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_DMA_STATUS]		= { 0x0b8, 2 },	/* read */
1348ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_DMA_CFG]		= { 0x0ba, 2 },	/* write */
1358ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_DMA_STATUS]		= { 0x0ba, 2 },	/* read */
1368ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_INT_ACK]		= { 0x104, 2 },	/* write */
1378ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_STATUS]		= { 0x104, 2 },	/* read */
1388ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_INT_ACK]		= { 0x106, 2 },	/* write */
1398ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_STATUS]		= { 0x106, 2 },	/* read */
1408ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_STATUS]		= { 0x108, 2 },	/* read */
1418ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_CMD]			= { 0x10c, 2 },	/* write */
1428ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_CMD]			= { 0x10e, 2 },	/* write */
1438ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_HW_SAVE]		= { 0x110, 4 },	/* read */
1448ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_HW_SAVE]		= { 0x114, 4 },	/* read */
1458ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_SW_SAVE]		= { 0x118, 4 },	/* read */
1468ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_SW_SAVE]		= { 0x11c, 4 },	/* read */
1478ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_MODE]			= { 0x134, 2 },	/* write */
1488ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_STATUS1]		= { 0x136, 2 },	/* read */
1498ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_MODE]			= { 0x136, 2 },	/* write */
1508ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_LOADA]		= { 0x138, 4 },	/* write */
1518ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_STATUS2]		= { 0x13a, 2 },	/* read */
1528ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_LOADB]		= { 0x13c, 4 },	/* write */
1538ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_LOADA]		= { 0x140, 4 },	/* write */
1548ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_LOADB]		= { 0x144, 4 },	/* write */
1558ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_INPUT_SEL]		= { 0x148, 2 },	/* write */
1568ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_INPUT_SEL]		= { 0x14a, 2 },	/* write */
1578ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_AUTO_INC]		= { 0x188, 2 },	/* write */
1588ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_AUTO_INC]		= { 0x18a, 2 },	/* write */
1598ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_RESET]		= { 0x190, 2 },	/* write */
1608ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_INT_ENA]		= { 0x192, 2 },	/* write */
1618ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_INT_ENA]		= { 0x196, 2 },	/* write */
1628ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_CNT_MODE]		= { 0x1b0, 2 },	/* write */
1638ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_CNT_MODE]		= { 0x1b2, 2 },	/* write */
1648ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_GATE2]		= { 0x1b4, 2 },	/* write */
1658ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_GATE2]		= { 0x1b6, 2 },	/* write */
1668ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_DMA_CFG]		= { 0x1b8, 2 },	/* write */
1678ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_DMA_STATUS]		= { 0x1b8, 2 },	/* read */
1688ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_DMA_CFG]		= { 0x1ba, 2 },	/* write */
1698ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_DMA_STATUS]		= { 0x1ba, 2 },	/* read */
1708ffdff6aSGreg Kroah-Hartman 	[NI660X_DIO32_INPUT]		= { 0x414, 4 },	/* read */
1718ffdff6aSGreg Kroah-Hartman 	[NI660X_DIO32_OUTPUT]		= { 0x510, 4 },	/* write */
1728ffdff6aSGreg Kroah-Hartman 	[NI660X_CLK_CFG]		= { 0x73c, 4 },	/* write */
1738ffdff6aSGreg Kroah-Hartman 	[NI660X_GLOBAL_INT_STATUS]	= { 0x754, 4 },	/* read */
1748ffdff6aSGreg Kroah-Hartman 	[NI660X_DMA_CFG]		= { 0x76c, 4 },	/* write */
1758ffdff6aSGreg Kroah-Hartman 	[NI660X_GLOBAL_INT_CFG]		= { 0x770, 4 },	/* write */
1768ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_0_1]		= { 0x77c, 2 },	/* read/write */
1778ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_2_3]		= { 0x77e, 2 },	/* read/write */
1788ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_4_5]		= { 0x780, 2 },	/* read/write */
1798ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_6_7]		= { 0x782, 2 },	/* read/write */
1808ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_8_9]		= { 0x784, 2 },	/* read/write */
1818ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_10_11]		= { 0x786, 2 },	/* read/write */
1828ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_12_13]		= { 0x788, 2 },	/* read/write */
1838ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_14_15]		= { 0x78a, 2 },	/* read/write */
1848ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_16_17]		= { 0x78c, 2 },	/* read/write */
1858ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_18_19]		= { 0x78e, 2 },	/* read/write */
1868ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_20_21]		= { 0x790, 2 },	/* read/write */
1878ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_22_23]		= { 0x792, 2 },	/* read/write */
1888ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_24_25]		= { 0x794, 2 },	/* read/write */
1898ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_26_27]		= { 0x796, 2 },	/* read/write */
1908ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_28_29]		= { 0x798, 2 },	/* read/write */
1918ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_30_31]		= { 0x79a, 2 },	/* read/write */
1928ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_32_33]		= { 0x79c, 2 },	/* read/write */
1938ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_34_35]		= { 0x79e, 2 },	/* read/write */
1948ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_36_37]		= { 0x7a0, 2 },	/* read/write */
1958ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_38_39]		= { 0x7a2, 2 }	/* read/write */
1968ffdff6aSGreg Kroah-Hartman };
1978ffdff6aSGreg Kroah-Hartman 
1988ffdff6aSGreg Kroah-Hartman #define NI660X_CHIP_OFFSET		0x800
1998ffdff6aSGreg Kroah-Hartman 
2008ffdff6aSGreg Kroah-Hartman enum ni_660x_boardid {
2018ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6601,
2028ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6602,
2038ffdff6aSGreg Kroah-Hartman 	BOARD_PXI6602,
2048ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6608,
2058ffdff6aSGreg Kroah-Hartman 	BOARD_PXI6608,
2068ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6624,
2078ffdff6aSGreg Kroah-Hartman 	BOARD_PXI6624
2088ffdff6aSGreg Kroah-Hartman };
2098ffdff6aSGreg Kroah-Hartman 
2108ffdff6aSGreg Kroah-Hartman struct ni_660x_board {
2118ffdff6aSGreg Kroah-Hartman 	const char *name;
2128ffdff6aSGreg Kroah-Hartman 	unsigned int n_chips;	/* total number of TIO chips */
2138ffdff6aSGreg Kroah-Hartman };
2148ffdff6aSGreg Kroah-Hartman 
2158ffdff6aSGreg Kroah-Hartman static const struct ni_660x_board ni_660x_boards[] = {
2168ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6601] = {
2178ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6601",
2188ffdff6aSGreg Kroah-Hartman 		.n_chips	= 1,
2198ffdff6aSGreg Kroah-Hartman 	},
2208ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6602] = {
2218ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6602",
2228ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
2238ffdff6aSGreg Kroah-Hartman 	},
2248ffdff6aSGreg Kroah-Hartman 	[BOARD_PXI6602] = {
2258ffdff6aSGreg Kroah-Hartman 		.name		= "PXI-6602",
2268ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
2278ffdff6aSGreg Kroah-Hartman 	},
2288ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6608] = {
2298ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6608",
2308ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
2318ffdff6aSGreg Kroah-Hartman 	},
2328ffdff6aSGreg Kroah-Hartman 	[BOARD_PXI6608] = {
2338ffdff6aSGreg Kroah-Hartman 		.name		= "PXI-6608",
2348ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
2358ffdff6aSGreg Kroah-Hartman 	},
2368ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6624] = {
2378ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6624",
2388ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
2398ffdff6aSGreg Kroah-Hartman 	},
2408ffdff6aSGreg Kroah-Hartman 	[BOARD_PXI6624] = {
2418ffdff6aSGreg Kroah-Hartman 		.name		= "PXI-6624",
2428ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
2438ffdff6aSGreg Kroah-Hartman 	},
2448ffdff6aSGreg Kroah-Hartman };
2458ffdff6aSGreg Kroah-Hartman 
2468ffdff6aSGreg Kroah-Hartman #define NI660X_NUM_PFI_CHANNELS		40
2478ffdff6aSGreg Kroah-Hartman 
2488ffdff6aSGreg Kroah-Hartman /* there are only up to 3 dma channels, but the register layout allows for 4 */
2498ffdff6aSGreg Kroah-Hartman #define NI660X_MAX_DMA_CHANNEL		4
2508ffdff6aSGreg Kroah-Hartman 
2518ffdff6aSGreg Kroah-Hartman #define NI660X_COUNTERS_PER_CHIP	4
2528ffdff6aSGreg Kroah-Hartman #define NI660X_MAX_CHIPS		2
2538ffdff6aSGreg Kroah-Hartman #define NI660X_MAX_COUNTERS		(NI660X_MAX_CHIPS *	\
2548ffdff6aSGreg Kroah-Hartman 					 NI660X_COUNTERS_PER_CHIP)
2558ffdff6aSGreg Kroah-Hartman 
2568ffdff6aSGreg Kroah-Hartman struct ni_660x_private {
2578ffdff6aSGreg Kroah-Hartman 	struct mite *mite;
2588ffdff6aSGreg Kroah-Hartman 	struct ni_gpct_device *counter_dev;
2598ffdff6aSGreg Kroah-Hartman 	struct mite_ring *ring[NI660X_MAX_CHIPS][NI660X_COUNTERS_PER_CHIP];
2608ffdff6aSGreg Kroah-Hartman 	/* protects mite channel request/release */
2618ffdff6aSGreg Kroah-Hartman 	spinlock_t mite_channel_lock;
2628ffdff6aSGreg Kroah-Hartman 	/* prevents races between interrupt and comedi_poll */
2638ffdff6aSGreg Kroah-Hartman 	spinlock_t interrupt_lock;
2648ffdff6aSGreg Kroah-Hartman 	unsigned int dma_cfg[NI660X_MAX_CHIPS];
2658ffdff6aSGreg Kroah-Hartman 	unsigned int io_cfg[NI660X_NUM_PFI_CHANNELS];
2668ffdff6aSGreg Kroah-Hartman 	u64 io_dir;
2678ffdff6aSGreg Kroah-Hartman 	struct ni_route_tables routing_tables;
2688ffdff6aSGreg Kroah-Hartman };
2698ffdff6aSGreg Kroah-Hartman 
ni_660x_write(struct comedi_device * dev,unsigned int chip,unsigned int bits,unsigned int reg)2708ffdff6aSGreg Kroah-Hartman static void ni_660x_write(struct comedi_device *dev, unsigned int chip,
2718ffdff6aSGreg Kroah-Hartman 			  unsigned int bits, unsigned int reg)
2728ffdff6aSGreg Kroah-Hartman {
2738ffdff6aSGreg Kroah-Hartman 	unsigned int addr = (chip * NI660X_CHIP_OFFSET) +
2748ffdff6aSGreg Kroah-Hartman 			    ni_660x_reg_data[reg].offset;
2758ffdff6aSGreg Kroah-Hartman 
2768ffdff6aSGreg Kroah-Hartman 	if (ni_660x_reg_data[reg].size == 2)
2778ffdff6aSGreg Kroah-Hartman 		writew(bits, dev->mmio + addr);
2788ffdff6aSGreg Kroah-Hartman 	else
2798ffdff6aSGreg Kroah-Hartman 		writel(bits, dev->mmio + addr);
2808ffdff6aSGreg Kroah-Hartman }
2818ffdff6aSGreg Kroah-Hartman 
ni_660x_read(struct comedi_device * dev,unsigned int chip,unsigned int reg)2828ffdff6aSGreg Kroah-Hartman static unsigned int ni_660x_read(struct comedi_device *dev,
2838ffdff6aSGreg Kroah-Hartman 				 unsigned int chip, unsigned int reg)
2848ffdff6aSGreg Kroah-Hartman {
2858ffdff6aSGreg Kroah-Hartman 	unsigned int addr = (chip * NI660X_CHIP_OFFSET) +
2868ffdff6aSGreg Kroah-Hartman 			    ni_660x_reg_data[reg].offset;
2878ffdff6aSGreg Kroah-Hartman 
2888ffdff6aSGreg Kroah-Hartman 	if (ni_660x_reg_data[reg].size == 2)
2898ffdff6aSGreg Kroah-Hartman 		return readw(dev->mmio + addr);
2908ffdff6aSGreg Kroah-Hartman 	return readl(dev->mmio + addr);
2918ffdff6aSGreg Kroah-Hartman }
2928ffdff6aSGreg Kroah-Hartman 
ni_660x_gpct_write(struct ni_gpct * counter,unsigned int bits,enum ni_gpct_register reg)2938ffdff6aSGreg Kroah-Hartman static void ni_660x_gpct_write(struct ni_gpct *counter, unsigned int bits,
2948ffdff6aSGreg Kroah-Hartman 			       enum ni_gpct_register reg)
2958ffdff6aSGreg Kroah-Hartman {
2968ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = counter->counter_dev->dev;
2978ffdff6aSGreg Kroah-Hartman 
2988ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, counter->chip_index, bits, reg);
2998ffdff6aSGreg Kroah-Hartman }
3008ffdff6aSGreg Kroah-Hartman 
ni_660x_gpct_read(struct ni_gpct * counter,enum ni_gpct_register reg)3018ffdff6aSGreg Kroah-Hartman static unsigned int ni_660x_gpct_read(struct ni_gpct *counter,
3028ffdff6aSGreg Kroah-Hartman 				      enum ni_gpct_register reg)
3038ffdff6aSGreg Kroah-Hartman {
3048ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = counter->counter_dev->dev;
3058ffdff6aSGreg Kroah-Hartman 
3068ffdff6aSGreg Kroah-Hartman 	return ni_660x_read(dev, counter->chip_index, reg);
3078ffdff6aSGreg Kroah-Hartman }
3088ffdff6aSGreg Kroah-Hartman 
ni_660x_set_dma_channel(struct comedi_device * dev,unsigned int mite_channel,struct ni_gpct * counter)3098ffdff6aSGreg Kroah-Hartman static inline void ni_660x_set_dma_channel(struct comedi_device *dev,
3108ffdff6aSGreg Kroah-Hartman 					   unsigned int mite_channel,
3118ffdff6aSGreg Kroah-Hartman 					   struct ni_gpct *counter)
3128ffdff6aSGreg Kroah-Hartman {
3138ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
3148ffdff6aSGreg Kroah-Hartman 	unsigned int chip = counter->chip_index;
3158ffdff6aSGreg Kroah-Hartman 
3168ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel);
3178ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL(mite_channel,
3188ffdff6aSGreg Kroah-Hartman 						     counter->counter_index);
3198ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, chip, devpriv->dma_cfg[chip] |
3208ffdff6aSGreg Kroah-Hartman 		      NI660X_DMA_CFG_RESET(mite_channel),
3218ffdff6aSGreg Kroah-Hartman 		      NI660X_DMA_CFG);
3228ffdff6aSGreg Kroah-Hartman }
3238ffdff6aSGreg Kroah-Hartman 
ni_660x_unset_dma_channel(struct comedi_device * dev,unsigned int mite_channel,struct ni_gpct * counter)3248ffdff6aSGreg Kroah-Hartman static inline void ni_660x_unset_dma_channel(struct comedi_device *dev,
3258ffdff6aSGreg Kroah-Hartman 					     unsigned int mite_channel,
3268ffdff6aSGreg Kroah-Hartman 					     struct ni_gpct *counter)
3278ffdff6aSGreg Kroah-Hartman {
3288ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
3298ffdff6aSGreg Kroah-Hartman 	unsigned int chip = counter->chip_index;
3308ffdff6aSGreg Kroah-Hartman 
3318ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel);
3328ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(mite_channel);
3338ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, chip, devpriv->dma_cfg[chip], NI660X_DMA_CFG);
3348ffdff6aSGreg Kroah-Hartman }
3358ffdff6aSGreg Kroah-Hartman 
ni_660x_request_mite_channel(struct comedi_device * dev,struct ni_gpct * counter,enum comedi_io_direction direction)3368ffdff6aSGreg Kroah-Hartman static int ni_660x_request_mite_channel(struct comedi_device *dev,
3378ffdff6aSGreg Kroah-Hartman 					struct ni_gpct *counter,
3388ffdff6aSGreg Kroah-Hartman 					enum comedi_io_direction direction)
3398ffdff6aSGreg Kroah-Hartman {
3408ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
3418ffdff6aSGreg Kroah-Hartman 	struct mite_ring *ring;
3428ffdff6aSGreg Kroah-Hartman 	struct mite_channel *mite_chan;
3438ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
3448ffdff6aSGreg Kroah-Hartman 
3458ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
3468ffdff6aSGreg Kroah-Hartman 	ring = devpriv->ring[counter->chip_index][counter->counter_index];
3478ffdff6aSGreg Kroah-Hartman 	mite_chan = mite_request_channel(devpriv->mite, ring);
3488ffdff6aSGreg Kroah-Hartman 	if (!mite_chan) {
3498ffdff6aSGreg Kroah-Hartman 		spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
3508ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
3518ffdff6aSGreg Kroah-Hartman 			"failed to reserve mite dma channel for counter\n");
3528ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
3538ffdff6aSGreg Kroah-Hartman 	}
3548ffdff6aSGreg Kroah-Hartman 	mite_chan->dir = direction;
3558ffdff6aSGreg Kroah-Hartman 	ni_tio_set_mite_channel(counter, mite_chan);
3568ffdff6aSGreg Kroah-Hartman 	ni_660x_set_dma_channel(dev, mite_chan->channel, counter);
3578ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
3588ffdff6aSGreg Kroah-Hartman 	return 0;
3598ffdff6aSGreg Kroah-Hartman }
3608ffdff6aSGreg Kroah-Hartman 
ni_660x_release_mite_channel(struct comedi_device * dev,struct ni_gpct * counter)3618ffdff6aSGreg Kroah-Hartman static void ni_660x_release_mite_channel(struct comedi_device *dev,
3628ffdff6aSGreg Kroah-Hartman 					 struct ni_gpct *counter)
3638ffdff6aSGreg Kroah-Hartman {
3648ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
3658ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
3668ffdff6aSGreg Kroah-Hartman 
3678ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
3688ffdff6aSGreg Kroah-Hartman 	if (counter->mite_chan) {
3698ffdff6aSGreg Kroah-Hartman 		struct mite_channel *mite_chan = counter->mite_chan;
3708ffdff6aSGreg Kroah-Hartman 
3718ffdff6aSGreg Kroah-Hartman 		ni_660x_unset_dma_channel(dev, mite_chan->channel, counter);
3728ffdff6aSGreg Kroah-Hartman 		ni_tio_set_mite_channel(counter, NULL);
3738ffdff6aSGreg Kroah-Hartman 		mite_release_channel(mite_chan);
3748ffdff6aSGreg Kroah-Hartman 	}
3758ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
3768ffdff6aSGreg Kroah-Hartman }
3778ffdff6aSGreg Kroah-Hartman 
ni_660x_cmd(struct comedi_device * dev,struct comedi_subdevice * s)3788ffdff6aSGreg Kroah-Hartman static int ni_660x_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
3798ffdff6aSGreg Kroah-Hartman {
3808ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
3818ffdff6aSGreg Kroah-Hartman 	int retval;
3828ffdff6aSGreg Kroah-Hartman 
3838ffdff6aSGreg Kroah-Hartman 	retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT);
3848ffdff6aSGreg Kroah-Hartman 	if (retval) {
3858ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
3868ffdff6aSGreg Kroah-Hartman 			"no dma channel available for use by counter\n");
3878ffdff6aSGreg Kroah-Hartman 		return retval;
3888ffdff6aSGreg Kroah-Hartman 	}
3898ffdff6aSGreg Kroah-Hartman 	ni_tio_acknowledge(counter);
3908ffdff6aSGreg Kroah-Hartman 
3918ffdff6aSGreg Kroah-Hartman 	return ni_tio_cmd(dev, s);
3928ffdff6aSGreg Kroah-Hartman }
3938ffdff6aSGreg Kroah-Hartman 
ni_660x_cancel(struct comedi_device * dev,struct comedi_subdevice * s)3948ffdff6aSGreg Kroah-Hartman static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
3958ffdff6aSGreg Kroah-Hartman {
3968ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
3978ffdff6aSGreg Kroah-Hartman 	int retval;
3988ffdff6aSGreg Kroah-Hartman 
3998ffdff6aSGreg Kroah-Hartman 	retval = ni_tio_cancel(counter);
4008ffdff6aSGreg Kroah-Hartman 	ni_660x_release_mite_channel(dev, counter);
4018ffdff6aSGreg Kroah-Hartman 	return retval;
4028ffdff6aSGreg Kroah-Hartman }
4038ffdff6aSGreg Kroah-Hartman 
set_tio_counterswap(struct comedi_device * dev,int chip)4048ffdff6aSGreg Kroah-Hartman static void set_tio_counterswap(struct comedi_device *dev, int chip)
4058ffdff6aSGreg Kroah-Hartman {
4068ffdff6aSGreg Kroah-Hartman 	unsigned int bits = 0;
4078ffdff6aSGreg Kroah-Hartman 
4088ffdff6aSGreg Kroah-Hartman 	/*
4098ffdff6aSGreg Kroah-Hartman 	 * See P. 3.5 of the Register-Level Programming manual.
4108ffdff6aSGreg Kroah-Hartman 	 * The CounterSwap bit has to be set on the second chip,
4118ffdff6aSGreg Kroah-Hartman 	 * otherwise it will try to use the same pins as the
4128ffdff6aSGreg Kroah-Hartman 	 * first chip.
4138ffdff6aSGreg Kroah-Hartman 	 */
4148ffdff6aSGreg Kroah-Hartman 	if (chip)
4158ffdff6aSGreg Kroah-Hartman 		bits = NI660X_CLK_CFG_COUNTER_SWAP;
4168ffdff6aSGreg Kroah-Hartman 
4178ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, chip, bits, NI660X_CLK_CFG);
4188ffdff6aSGreg Kroah-Hartman }
4198ffdff6aSGreg Kroah-Hartman 
ni_660x_handle_gpct_interrupt(struct comedi_device * dev,struct comedi_subdevice * s)4208ffdff6aSGreg Kroah-Hartman static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev,
4218ffdff6aSGreg Kroah-Hartman 					  struct comedi_subdevice *s)
4228ffdff6aSGreg Kroah-Hartman {
4238ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
4248ffdff6aSGreg Kroah-Hartman 
4258ffdff6aSGreg Kroah-Hartman 	ni_tio_handle_interrupt(counter, s);
4268ffdff6aSGreg Kroah-Hartman 	comedi_handle_events(dev, s);
4278ffdff6aSGreg Kroah-Hartman }
4288ffdff6aSGreg Kroah-Hartman 
ni_660x_interrupt(int irq,void * d)4298ffdff6aSGreg Kroah-Hartman static irqreturn_t ni_660x_interrupt(int irq, void *d)
4308ffdff6aSGreg Kroah-Hartman {
4318ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
4328ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
4338ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
4348ffdff6aSGreg Kroah-Hartman 	unsigned int i;
4358ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
4368ffdff6aSGreg Kroah-Hartman 
4378ffdff6aSGreg Kroah-Hartman 	if (!dev->attached)
4388ffdff6aSGreg Kroah-Hartman 		return IRQ_NONE;
4398ffdff6aSGreg Kroah-Hartman 	/* make sure dev->attached is checked before doing anything else */
4408ffdff6aSGreg Kroah-Hartman 	smp_mb();
4418ffdff6aSGreg Kroah-Hartman 
4428ffdff6aSGreg Kroah-Hartman 	/* lock to avoid race with comedi_poll */
4438ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->interrupt_lock, flags);
4448ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < dev->n_subdevices; ++i) {
4458ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[i];
4468ffdff6aSGreg Kroah-Hartman 		if (s->type == COMEDI_SUBD_COUNTER)
4478ffdff6aSGreg Kroah-Hartman 			ni_660x_handle_gpct_interrupt(dev, s);
4488ffdff6aSGreg Kroah-Hartman 	}
4498ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
4508ffdff6aSGreg Kroah-Hartman 	return IRQ_HANDLED;
4518ffdff6aSGreg Kroah-Hartman }
4528ffdff6aSGreg Kroah-Hartman 
ni_660x_input_poll(struct comedi_device * dev,struct comedi_subdevice * s)4538ffdff6aSGreg Kroah-Hartman static int ni_660x_input_poll(struct comedi_device *dev,
4548ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s)
4558ffdff6aSGreg Kroah-Hartman {
4568ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
4578ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
4588ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
4598ffdff6aSGreg Kroah-Hartman 
4608ffdff6aSGreg Kroah-Hartman 	/* lock to avoid race with comedi_poll */
4618ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->interrupt_lock, flags);
4628ffdff6aSGreg Kroah-Hartman 	mite_sync_dma(counter->mite_chan, s);
4638ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
4648ffdff6aSGreg Kroah-Hartman 	return comedi_buf_read_n_available(s);
4658ffdff6aSGreg Kroah-Hartman }
4668ffdff6aSGreg Kroah-Hartman 
ni_660x_buf_change(struct comedi_device * dev,struct comedi_subdevice * s)4678ffdff6aSGreg Kroah-Hartman static int ni_660x_buf_change(struct comedi_device *dev,
4688ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s)
4698ffdff6aSGreg Kroah-Hartman {
4708ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
4718ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
4728ffdff6aSGreg Kroah-Hartman 	struct mite_ring *ring;
4738ffdff6aSGreg Kroah-Hartman 	int ret;
4748ffdff6aSGreg Kroah-Hartman 
4758ffdff6aSGreg Kroah-Hartman 	ring = devpriv->ring[counter->chip_index][counter->counter_index];
4768ffdff6aSGreg Kroah-Hartman 	ret = mite_buf_change(ring, s);
4778ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
4788ffdff6aSGreg Kroah-Hartman 		return ret;
4798ffdff6aSGreg Kroah-Hartman 
4808ffdff6aSGreg Kroah-Hartman 	return 0;
4818ffdff6aSGreg Kroah-Hartman }
4828ffdff6aSGreg Kroah-Hartman 
ni_660x_allocate_private(struct comedi_device * dev)4838ffdff6aSGreg Kroah-Hartman static int ni_660x_allocate_private(struct comedi_device *dev)
4848ffdff6aSGreg Kroah-Hartman {
4858ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv;
4868ffdff6aSGreg Kroah-Hartman 	unsigned int i;
4878ffdff6aSGreg Kroah-Hartman 
4888ffdff6aSGreg Kroah-Hartman 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
4898ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
4908ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
4918ffdff6aSGreg Kroah-Hartman 
4928ffdff6aSGreg Kroah-Hartman 	spin_lock_init(&devpriv->mite_channel_lock);
4938ffdff6aSGreg Kroah-Hartman 	spin_lock_init(&devpriv->interrupt_lock);
4948ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < NI660X_NUM_PFI_CHANNELS; ++i)
4958ffdff6aSGreg Kroah-Hartman 		devpriv->io_cfg[i] = NI_660X_PFI_OUTPUT_COUNTER;
4968ffdff6aSGreg Kroah-Hartman 
4978ffdff6aSGreg Kroah-Hartman 	return 0;
4988ffdff6aSGreg Kroah-Hartman }
4998ffdff6aSGreg Kroah-Hartman 
ni_660x_alloc_mite_rings(struct comedi_device * dev)5008ffdff6aSGreg Kroah-Hartman static int ni_660x_alloc_mite_rings(struct comedi_device *dev)
5018ffdff6aSGreg Kroah-Hartman {
5028ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = dev->board_ptr;
5038ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
5048ffdff6aSGreg Kroah-Hartman 	unsigned int i;
5058ffdff6aSGreg Kroah-Hartman 	unsigned int j;
5068ffdff6aSGreg Kroah-Hartman 
5078ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < board->n_chips; ++i) {
5088ffdff6aSGreg Kroah-Hartman 		for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j) {
5098ffdff6aSGreg Kroah-Hartman 			devpriv->ring[i][j] = mite_alloc_ring(devpriv->mite);
5108ffdff6aSGreg Kroah-Hartman 			if (!devpriv->ring[i][j])
5118ffdff6aSGreg Kroah-Hartman 				return -ENOMEM;
5128ffdff6aSGreg Kroah-Hartman 		}
5138ffdff6aSGreg Kroah-Hartman 	}
5148ffdff6aSGreg Kroah-Hartman 	return 0;
5158ffdff6aSGreg Kroah-Hartman }
5168ffdff6aSGreg Kroah-Hartman 
ni_660x_free_mite_rings(struct comedi_device * dev)5178ffdff6aSGreg Kroah-Hartman static void ni_660x_free_mite_rings(struct comedi_device *dev)
5188ffdff6aSGreg Kroah-Hartman {
5198ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = dev->board_ptr;
5208ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
5218ffdff6aSGreg Kroah-Hartman 	unsigned int i;
5228ffdff6aSGreg Kroah-Hartman 	unsigned int j;
5238ffdff6aSGreg Kroah-Hartman 
5248ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < board->n_chips; ++i) {
5258ffdff6aSGreg Kroah-Hartman 		for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j)
5268ffdff6aSGreg Kroah-Hartman 			mite_free_ring(devpriv->ring[i][j]);
5278ffdff6aSGreg Kroah-Hartman 	}
5288ffdff6aSGreg Kroah-Hartman }
5298ffdff6aSGreg Kroah-Hartman 
ni_660x_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)5308ffdff6aSGreg Kroah-Hartman static int ni_660x_dio_insn_bits(struct comedi_device *dev,
5318ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
5328ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
5338ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
5348ffdff6aSGreg Kroah-Hartman {
5358ffdff6aSGreg Kroah-Hartman 	unsigned int shift = CR_CHAN(insn->chanspec);
5368ffdff6aSGreg Kroah-Hartman 	unsigned int mask = data[0] << shift;
5378ffdff6aSGreg Kroah-Hartman 	unsigned int bits = data[1] << shift;
5388ffdff6aSGreg Kroah-Hartman 
5398ffdff6aSGreg Kroah-Hartman 	/*
5408ffdff6aSGreg Kroah-Hartman 	 * There are 40 channels in this subdevice but only 32 are usable
5418ffdff6aSGreg Kroah-Hartman 	 * as DIO. The shift adjusts the mask/bits to account for the base
5428ffdff6aSGreg Kroah-Hartman 	 * channel in insn->chanspec. The state update can then be handled
5438ffdff6aSGreg Kroah-Hartman 	 * normally for the 32 usable channels.
5448ffdff6aSGreg Kroah-Hartman 	 */
5458ffdff6aSGreg Kroah-Hartman 	if (mask) {
5468ffdff6aSGreg Kroah-Hartman 		s->state &= ~mask;
5478ffdff6aSGreg Kroah-Hartman 		s->state |= (bits & mask);
5488ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, 0, s->state, NI660X_DIO32_OUTPUT);
5498ffdff6aSGreg Kroah-Hartman 	}
5508ffdff6aSGreg Kroah-Hartman 
5518ffdff6aSGreg Kroah-Hartman 	/*
5528ffdff6aSGreg Kroah-Hartman 	 * Return the input channels, shifted back to account for the base
5538ffdff6aSGreg Kroah-Hartman 	 * channel.
5548ffdff6aSGreg Kroah-Hartman 	 */
5558ffdff6aSGreg Kroah-Hartman 	data[1] = ni_660x_read(dev, 0, NI660X_DIO32_INPUT) >> shift;
5568ffdff6aSGreg Kroah-Hartman 
5578ffdff6aSGreg Kroah-Hartman 	return insn->n;
5588ffdff6aSGreg Kroah-Hartman }
5598ffdff6aSGreg Kroah-Hartman 
ni_660x_select_pfi_output(struct comedi_device * dev,unsigned int chan,unsigned int out_sel)5608ffdff6aSGreg Kroah-Hartman static void ni_660x_select_pfi_output(struct comedi_device *dev,
5618ffdff6aSGreg Kroah-Hartman 				      unsigned int chan, unsigned int out_sel)
5628ffdff6aSGreg Kroah-Hartman {
5638ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = dev->board_ptr;
5648ffdff6aSGreg Kroah-Hartman 	unsigned int active_chip = 0;
5658ffdff6aSGreg Kroah-Hartman 	unsigned int idle_chip = 0;
5668ffdff6aSGreg Kroah-Hartman 	unsigned int bits;
5678ffdff6aSGreg Kroah-Hartman 
5688ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
5698ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
5708ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
5718ffdff6aSGreg Kroah-Hartman 
5728ffdff6aSGreg Kroah-Hartman 	if (board->n_chips > 1) {
5738ffdff6aSGreg Kroah-Hartman 		if (out_sel == NI_660X_PFI_OUTPUT_COUNTER &&
5748ffdff6aSGreg Kroah-Hartman 		    chan >= 8 && chan <= 23) {
5758ffdff6aSGreg Kroah-Hartman 			/* counters 4-7 pfi channels */
5768ffdff6aSGreg Kroah-Hartman 			active_chip = 1;
5778ffdff6aSGreg Kroah-Hartman 			idle_chip = 0;
5788ffdff6aSGreg Kroah-Hartman 		} else {
5798ffdff6aSGreg Kroah-Hartman 			/* counters 0-3 pfi channels */
5808ffdff6aSGreg Kroah-Hartman 			active_chip = 0;
5818ffdff6aSGreg Kroah-Hartman 			idle_chip = 1;
5828ffdff6aSGreg Kroah-Hartman 		}
5838ffdff6aSGreg Kroah-Hartman 	}
5848ffdff6aSGreg Kroah-Hartman 
5858ffdff6aSGreg Kroah-Hartman 	if (idle_chip != active_chip) {
5868ffdff6aSGreg Kroah-Hartman 		/* set the pfi channel to high-z on the inactive chip */
5878ffdff6aSGreg Kroah-Hartman 		bits = ni_660x_read(dev, idle_chip, NI660X_IO_CFG(chan));
5888ffdff6aSGreg Kroah-Hartman 		bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan);
5898ffdff6aSGreg Kroah-Hartman 		bits |= NI660X_IO_CFG_OUT_SEL(chan, 0);		/* high-z */
5908ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, idle_chip, bits, NI660X_IO_CFG(chan));
5918ffdff6aSGreg Kroah-Hartman 	}
5928ffdff6aSGreg Kroah-Hartman 
5938ffdff6aSGreg Kroah-Hartman 	/* set the pfi channel output on the active chip */
5948ffdff6aSGreg Kroah-Hartman 	bits = ni_660x_read(dev, active_chip, NI660X_IO_CFG(chan));
5958ffdff6aSGreg Kroah-Hartman 	bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan);
5968ffdff6aSGreg Kroah-Hartman 	bits |= NI660X_IO_CFG_OUT_SEL(chan, out_sel);
5978ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, active_chip, bits, NI660X_IO_CFG(chan));
5988ffdff6aSGreg Kroah-Hartman }
5998ffdff6aSGreg Kroah-Hartman 
ni_660x_set_pfi_direction(struct comedi_device * dev,unsigned int chan,unsigned int direction)6008ffdff6aSGreg Kroah-Hartman static void ni_660x_set_pfi_direction(struct comedi_device *dev,
6018ffdff6aSGreg Kroah-Hartman 				      unsigned int chan,
6028ffdff6aSGreg Kroah-Hartman 				      unsigned int direction)
6038ffdff6aSGreg Kroah-Hartman {
6048ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
6058ffdff6aSGreg Kroah-Hartman 	u64 bit;
6068ffdff6aSGreg Kroah-Hartman 
6078ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
6088ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
6098ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
6108ffdff6aSGreg Kroah-Hartman 
6118ffdff6aSGreg Kroah-Hartman 	bit = 1ULL << chan;
6128ffdff6aSGreg Kroah-Hartman 
6138ffdff6aSGreg Kroah-Hartman 	if (direction == COMEDI_OUTPUT) {
6148ffdff6aSGreg Kroah-Hartman 		devpriv->io_dir |= bit;
6158ffdff6aSGreg Kroah-Hartman 		/* reset the output to currently assigned output value */
6168ffdff6aSGreg Kroah-Hartman 		ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]);
6178ffdff6aSGreg Kroah-Hartman 	} else {
6188ffdff6aSGreg Kroah-Hartman 		devpriv->io_dir &= ~bit;
6198ffdff6aSGreg Kroah-Hartman 		/* set pin to high-z; do not change currently assigned route */
6208ffdff6aSGreg Kroah-Hartman 		ni_660x_select_pfi_output(dev, chan, 0);
6218ffdff6aSGreg Kroah-Hartman 	}
6228ffdff6aSGreg Kroah-Hartman }
6238ffdff6aSGreg Kroah-Hartman 
ni_660x_get_pfi_direction(struct comedi_device * dev,unsigned int chan)6248ffdff6aSGreg Kroah-Hartman static unsigned int ni_660x_get_pfi_direction(struct comedi_device *dev,
6258ffdff6aSGreg Kroah-Hartman 					      unsigned int chan)
6268ffdff6aSGreg Kroah-Hartman {
6278ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
6288ffdff6aSGreg Kroah-Hartman 	u64 bit;
6298ffdff6aSGreg Kroah-Hartman 
6308ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
6318ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
6328ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
6338ffdff6aSGreg Kroah-Hartman 
6348ffdff6aSGreg Kroah-Hartman 	bit = 1ULL << chan;
6358ffdff6aSGreg Kroah-Hartman 
6368ffdff6aSGreg Kroah-Hartman 	return (devpriv->io_dir & bit) ? COMEDI_OUTPUT : COMEDI_INPUT;
6378ffdff6aSGreg Kroah-Hartman }
6388ffdff6aSGreg Kroah-Hartman 
ni_660x_set_pfi_routing(struct comedi_device * dev,unsigned int chan,unsigned int source)6398ffdff6aSGreg Kroah-Hartman static int ni_660x_set_pfi_routing(struct comedi_device *dev,
6408ffdff6aSGreg Kroah-Hartman 				   unsigned int chan, unsigned int source)
6418ffdff6aSGreg Kroah-Hartman {
6428ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
6438ffdff6aSGreg Kroah-Hartman 
6448ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
6458ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
6468ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
6478ffdff6aSGreg Kroah-Hartman 
6488ffdff6aSGreg Kroah-Hartman 	switch (source) {
6498ffdff6aSGreg Kroah-Hartman 	case NI_660X_PFI_OUTPUT_COUNTER:
6508ffdff6aSGreg Kroah-Hartman 		if (chan < 8)
6518ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
6528ffdff6aSGreg Kroah-Hartman 		break;
6538ffdff6aSGreg Kroah-Hartman 	case NI_660X_PFI_OUTPUT_DIO:
6548ffdff6aSGreg Kroah-Hartman 		if (chan > 31)
6558ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
6568ffdff6aSGreg Kroah-Hartman 		break;
6578ffdff6aSGreg Kroah-Hartman 	default:
6588ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
6598ffdff6aSGreg Kroah-Hartman 	}
6608ffdff6aSGreg Kroah-Hartman 
6618ffdff6aSGreg Kroah-Hartman 	devpriv->io_cfg[chan] = source;
6628ffdff6aSGreg Kroah-Hartman 	if (ni_660x_get_pfi_direction(dev, chan) == COMEDI_OUTPUT)
6638ffdff6aSGreg Kroah-Hartman 		ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]);
6648ffdff6aSGreg Kroah-Hartman 	return 0;
6658ffdff6aSGreg Kroah-Hartman }
6668ffdff6aSGreg Kroah-Hartman 
ni_660x_get_pfi_routing(struct comedi_device * dev,unsigned int chan)6678ffdff6aSGreg Kroah-Hartman static int ni_660x_get_pfi_routing(struct comedi_device *dev, unsigned int chan)
6688ffdff6aSGreg Kroah-Hartman {
6698ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
6708ffdff6aSGreg Kroah-Hartman 
6718ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
6728ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
6738ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
6748ffdff6aSGreg Kroah-Hartman 
6758ffdff6aSGreg Kroah-Hartman 	return devpriv->io_cfg[chan];
6768ffdff6aSGreg Kroah-Hartman }
6778ffdff6aSGreg Kroah-Hartman 
ni_660x_set_pfi_filter(struct comedi_device * dev,unsigned int chan,unsigned int value)6788ffdff6aSGreg Kroah-Hartman static void ni_660x_set_pfi_filter(struct comedi_device *dev,
6798ffdff6aSGreg Kroah-Hartman 				   unsigned int chan, unsigned int value)
6808ffdff6aSGreg Kroah-Hartman {
6818ffdff6aSGreg Kroah-Hartman 	unsigned int val;
6828ffdff6aSGreg Kroah-Hartman 
6838ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
6848ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
6858ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
6868ffdff6aSGreg Kroah-Hartman 
6878ffdff6aSGreg Kroah-Hartman 	val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan));
6888ffdff6aSGreg Kroah-Hartman 	val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan);
6898ffdff6aSGreg Kroah-Hartman 	val |= NI660X_IO_CFG_IN_SEL(chan, value);
6908ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, 0, val, NI660X_IO_CFG(chan));
6918ffdff6aSGreg Kroah-Hartman }
6928ffdff6aSGreg Kroah-Hartman 
ni_660x_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)6938ffdff6aSGreg Kroah-Hartman static int ni_660x_dio_insn_config(struct comedi_device *dev,
6948ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
6958ffdff6aSGreg Kroah-Hartman 				   struct comedi_insn *insn,
6968ffdff6aSGreg Kroah-Hartman 				   unsigned int *data)
6978ffdff6aSGreg Kroah-Hartman {
6988ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
6998ffdff6aSGreg Kroah-Hartman 	int ret;
7008ffdff6aSGreg Kroah-Hartman 
7018ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
7028ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_DIO_OUTPUT:
7038ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, chan, COMEDI_OUTPUT);
7048ffdff6aSGreg Kroah-Hartman 		break;
7058ffdff6aSGreg Kroah-Hartman 
7068ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_DIO_INPUT:
7078ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, chan, COMEDI_INPUT);
7088ffdff6aSGreg Kroah-Hartman 		break;
7098ffdff6aSGreg Kroah-Hartman 
7108ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_DIO_QUERY:
7118ffdff6aSGreg Kroah-Hartman 		data[1] = ni_660x_get_pfi_direction(dev, chan);
7128ffdff6aSGreg Kroah-Hartman 		break;
7138ffdff6aSGreg Kroah-Hartman 
7148ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_SET_ROUTING:
7158ffdff6aSGreg Kroah-Hartman 		ret = ni_660x_set_pfi_routing(dev, chan, data[1]);
7168ffdff6aSGreg Kroah-Hartman 		if (ret)
7178ffdff6aSGreg Kroah-Hartman 			return ret;
7188ffdff6aSGreg Kroah-Hartman 		break;
7198ffdff6aSGreg Kroah-Hartman 
7208ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_GET_ROUTING:
7218ffdff6aSGreg Kroah-Hartman 		data[1] = ni_660x_get_pfi_routing(dev, chan);
7228ffdff6aSGreg Kroah-Hartman 		break;
7238ffdff6aSGreg Kroah-Hartman 
7248ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_FILTER:
7258ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_filter(dev, chan, data[1]);
7268ffdff6aSGreg Kroah-Hartman 		break;
7278ffdff6aSGreg Kroah-Hartman 
7288ffdff6aSGreg Kroah-Hartman 	default:
7298ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
7308ffdff6aSGreg Kroah-Hartman 	}
7318ffdff6aSGreg Kroah-Hartman 
7328ffdff6aSGreg Kroah-Hartman 	return insn->n;
7338ffdff6aSGreg Kroah-Hartman }
7348ffdff6aSGreg Kroah-Hartman 
_ni_get_valid_routes(struct comedi_device * dev,unsigned int n_pairs,unsigned int * pair_data)7358ffdff6aSGreg Kroah-Hartman static unsigned int _ni_get_valid_routes(struct comedi_device *dev,
7368ffdff6aSGreg Kroah-Hartman 					 unsigned int n_pairs,
7378ffdff6aSGreg Kroah-Hartman 					 unsigned int *pair_data)
7388ffdff6aSGreg Kroah-Hartman {
7398ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
7408ffdff6aSGreg Kroah-Hartman 
7418ffdff6aSGreg Kroah-Hartman 	return ni_get_valid_routes(&devpriv->routing_tables, n_pairs,
7428ffdff6aSGreg Kroah-Hartman 				   pair_data);
7438ffdff6aSGreg Kroah-Hartman }
7448ffdff6aSGreg Kroah-Hartman 
7458ffdff6aSGreg Kroah-Hartman /*
7468ffdff6aSGreg Kroah-Hartman  * Retrieves the current source of the output selector for the given
7478ffdff6aSGreg Kroah-Hartman  * destination.  If the terminal for the destination is not already configured
7488ffdff6aSGreg Kroah-Hartman  * as an output, this function returns -EINVAL as error.
7498ffdff6aSGreg Kroah-Hartman  *
7508ffdff6aSGreg Kroah-Hartman  * Return: The register value of the destination output selector;
7518ffdff6aSGreg Kroah-Hartman  *	   -EINVAL if terminal is not configured for output.
7528ffdff6aSGreg Kroah-Hartman  */
get_output_select_source(int dest,struct comedi_device * dev)7538ffdff6aSGreg Kroah-Hartman static inline int get_output_select_source(int dest, struct comedi_device *dev)
7548ffdff6aSGreg Kroah-Hartman {
7558ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
7568ffdff6aSGreg Kroah-Hartman 	int reg = -1;
7578ffdff6aSGreg Kroah-Hartman 
7588ffdff6aSGreg Kroah-Hartman 	if (channel_is_pfi(dest)) {
7598ffdff6aSGreg Kroah-Hartman 		if (ni_660x_get_pfi_direction(dev, dest) == COMEDI_OUTPUT)
7608ffdff6aSGreg Kroah-Hartman 			reg = ni_660x_get_pfi_routing(dev, dest);
7618ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_rtsi(dest)) {
7628ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev,
7638ffdff6aSGreg Kroah-Hartman 			"%s: unhandled rtsi destination (%d) queried\n",
7648ffdff6aSGreg Kroah-Hartman 			__func__, dest);
7658ffdff6aSGreg Kroah-Hartman 		/*
7668ffdff6aSGreg Kroah-Hartman 		 * The following can be enabled when RTSI routing info is
7678ffdff6aSGreg Kroah-Hartman 		 * determined (not currently documented):
7688ffdff6aSGreg Kroah-Hartman 		 * if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) {
7698ffdff6aSGreg Kroah-Hartman 		 *	reg = ni_get_rtsi_routing(dev, dest);
7708ffdff6aSGreg Kroah-Hartman 
7718ffdff6aSGreg Kroah-Hartman 		 *	if (reg == NI_RTSI_OUTPUT_RGOUT0) {
7728ffdff6aSGreg Kroah-Hartman 		 *		dest = NI_RGOUT0; ** prepare for lookup below **
7738ffdff6aSGreg Kroah-Hartman 		 *		reg = get_rgout0_reg(dev);
7748ffdff6aSGreg Kroah-Hartman 		 *	} else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) &&
7758ffdff6aSGreg Kroah-Hartman 		 *		   reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) {
7768ffdff6aSGreg Kroah-Hartman 		 *		const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0);
7778ffdff6aSGreg Kroah-Hartman 
7788ffdff6aSGreg Kroah-Hartman 		 *		dest = NI_RTSI_BRD(i); ** prepare for lookup **
7798ffdff6aSGreg Kroah-Hartman 		 *		reg = get_ith_rtsi_brd_reg(i, dev);
7808ffdff6aSGreg Kroah-Hartman 		 *	}
7818ffdff6aSGreg Kroah-Hartman 		 * }
7828ffdff6aSGreg Kroah-Hartman 		 */
7838ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_ctr(dest)) {
7848ffdff6aSGreg Kroah-Hartman 		reg = ni_tio_get_routing(devpriv->counter_dev, dest);
7858ffdff6aSGreg Kroah-Hartman 	} else {
7868ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev,
7878ffdff6aSGreg Kroah-Hartman 			"%s: unhandled destination (%d) queried\n",
7888ffdff6aSGreg Kroah-Hartman 			__func__, dest);
7898ffdff6aSGreg Kroah-Hartman 	}
7908ffdff6aSGreg Kroah-Hartman 
7918ffdff6aSGreg Kroah-Hartman 	if (reg >= 0)
7928ffdff6aSGreg Kroah-Hartman 		return ni_find_route_source(CR_CHAN(reg), dest,
7938ffdff6aSGreg Kroah-Hartman 					    &devpriv->routing_tables);
7948ffdff6aSGreg Kroah-Hartman 	return -EINVAL;
7958ffdff6aSGreg Kroah-Hartman }
7968ffdff6aSGreg Kroah-Hartman 
7978ffdff6aSGreg Kroah-Hartman /*
7988ffdff6aSGreg Kroah-Hartman  * Test a route:
7998ffdff6aSGreg Kroah-Hartman  *
8008ffdff6aSGreg Kroah-Hartman  * Return: -1 if not connectible;
8018ffdff6aSGreg Kroah-Hartman  *	    0 if connectible and not connected;
8028ffdff6aSGreg Kroah-Hartman  *	    1 if connectible and connected.
8038ffdff6aSGreg Kroah-Hartman  */
test_route(unsigned int src,unsigned int dest,struct comedi_device * dev)8048ffdff6aSGreg Kroah-Hartman static inline int test_route(unsigned int src, unsigned int dest,
8058ffdff6aSGreg Kroah-Hartman 			     struct comedi_device *dev)
8068ffdff6aSGreg Kroah-Hartman {
8078ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
8088ffdff6aSGreg Kroah-Hartman 	s8 reg = ni_route_to_register(CR_CHAN(src), dest,
8098ffdff6aSGreg Kroah-Hartman 				      &devpriv->routing_tables);
8108ffdff6aSGreg Kroah-Hartman 
8118ffdff6aSGreg Kroah-Hartman 	if (reg < 0)
8128ffdff6aSGreg Kroah-Hartman 		return -1;
8138ffdff6aSGreg Kroah-Hartman 	if (get_output_select_source(dest, dev) != CR_CHAN(src))
8148ffdff6aSGreg Kroah-Hartman 		return 0;
8158ffdff6aSGreg Kroah-Hartman 	return 1;
8168ffdff6aSGreg Kroah-Hartman }
8178ffdff6aSGreg Kroah-Hartman 
8188ffdff6aSGreg Kroah-Hartman /* Connect the actual route.  */
connect_route(unsigned int src,unsigned int dest,struct comedi_device * dev)8198ffdff6aSGreg Kroah-Hartman static inline int connect_route(unsigned int src, unsigned int dest,
8208ffdff6aSGreg Kroah-Hartman 				struct comedi_device *dev)
8218ffdff6aSGreg Kroah-Hartman {
8228ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
8238ffdff6aSGreg Kroah-Hartman 	s8 reg = ni_route_to_register(CR_CHAN(src), dest,
8248ffdff6aSGreg Kroah-Hartman 				      &devpriv->routing_tables);
8258ffdff6aSGreg Kroah-Hartman 	s8 current_src;
8268ffdff6aSGreg Kroah-Hartman 
8278ffdff6aSGreg Kroah-Hartman 	if (reg < 0)
8288ffdff6aSGreg Kroah-Hartman 		/* route is not valid */
8298ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
8308ffdff6aSGreg Kroah-Hartman 
8318ffdff6aSGreg Kroah-Hartman 	current_src = get_output_select_source(dest, dev);
8328ffdff6aSGreg Kroah-Hartman 	if (current_src == CR_CHAN(src))
8338ffdff6aSGreg Kroah-Hartman 		return -EALREADY;
8348ffdff6aSGreg Kroah-Hartman 	if (current_src >= 0)
8358ffdff6aSGreg Kroah-Hartman 		/* destination mux is already busy. complain, don't overwrite */
8368ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
8378ffdff6aSGreg Kroah-Hartman 
8388ffdff6aSGreg Kroah-Hartman 	/* The route is valid and available. Now connect... */
8398ffdff6aSGreg Kroah-Hartman 	if (channel_is_pfi(CR_CHAN(dest))) {
8408ffdff6aSGreg Kroah-Hartman 		/*
8418ffdff6aSGreg Kroah-Hartman 		 * set routing and then direction so that the output does not
8428ffdff6aSGreg Kroah-Hartman 		 * first get generated with the wrong pin
8438ffdff6aSGreg Kroah-Hartman 		 */
8448ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_routing(dev, dest, reg);
8458ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, dest, COMEDI_OUTPUT);
8468ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_rtsi(CR_CHAN(dest))) {
8478ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
8488ffdff6aSGreg Kroah-Hartman 			__func__, dest);
8498ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
8508ffdff6aSGreg Kroah-Hartman 		/*
8518ffdff6aSGreg Kroah-Hartman 		 * The following can be enabled when RTSI routing info is
8528ffdff6aSGreg Kroah-Hartman 		 * determined (not currently documented):
8538ffdff6aSGreg Kroah-Hartman 		 * if (reg == NI_RTSI_OUTPUT_RGOUT0) {
8548ffdff6aSGreg Kroah-Hartman 		 *	int ret = incr_rgout0_src_use(src, dev);
8558ffdff6aSGreg Kroah-Hartman 
8568ffdff6aSGreg Kroah-Hartman 		 *	if (ret < 0)
8578ffdff6aSGreg Kroah-Hartman 		 *		return ret;
8588ffdff6aSGreg Kroah-Hartman 		 * } else if (ni_rtsi_route_requires_mux(reg)) {
8598ffdff6aSGreg Kroah-Hartman 		 *	** Attempt to allocate and  route (src->brd) **
8608ffdff6aSGreg Kroah-Hartman 		 *	int brd = incr_rtsi_brd_src_use(src, dev);
8618ffdff6aSGreg Kroah-Hartman 
8628ffdff6aSGreg Kroah-Hartman 		 *	if (brd < 0)
8638ffdff6aSGreg Kroah-Hartman 		 *		return brd;
8648ffdff6aSGreg Kroah-Hartman 
8658ffdff6aSGreg Kroah-Hartman 		 *	** Now lookup the register value for (brd->dest) **
8668ffdff6aSGreg Kroah-Hartman 		 *	reg = ni_lookup_route_register(brd, CR_CHAN(dest),
8678ffdff6aSGreg Kroah-Hartman 		 *				       &devpriv->routing_tables);
8688ffdff6aSGreg Kroah-Hartman 		 * }
8698ffdff6aSGreg Kroah-Hartman 
8708ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT);
8718ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_routing(dev, dest, reg);
8728ffdff6aSGreg Kroah-Hartman 		 */
8738ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_ctr(CR_CHAN(dest))) {
8748ffdff6aSGreg Kroah-Hartman 		/*
8758ffdff6aSGreg Kroah-Hartman 		 * we are adding back the channel modifier info to set
8768ffdff6aSGreg Kroah-Hartman 		 * invert/edge info passed by the user
8778ffdff6aSGreg Kroah-Hartman 		 */
8788ffdff6aSGreg Kroah-Hartman 		ni_tio_set_routing(devpriv->counter_dev, dest,
8798ffdff6aSGreg Kroah-Hartman 				   reg | (src & ~CR_CHAN(-1)));
8808ffdff6aSGreg Kroah-Hartman 	} else {
8818ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
8828ffdff6aSGreg Kroah-Hartman 	}
8838ffdff6aSGreg Kroah-Hartman 	return 0;
8848ffdff6aSGreg Kroah-Hartman }
8858ffdff6aSGreg Kroah-Hartman 
disconnect_route(unsigned int src,unsigned int dest,struct comedi_device * dev)8868ffdff6aSGreg Kroah-Hartman static inline int disconnect_route(unsigned int src, unsigned int dest,
8878ffdff6aSGreg Kroah-Hartman 				   struct comedi_device *dev)
8888ffdff6aSGreg Kroah-Hartman {
8898ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
8908ffdff6aSGreg Kroah-Hartman 	s8 reg = ni_route_to_register(CR_CHAN(src), CR_CHAN(dest),
8918ffdff6aSGreg Kroah-Hartman 				      &devpriv->routing_tables);
8928ffdff6aSGreg Kroah-Hartman 
8938ffdff6aSGreg Kroah-Hartman 	if (reg < 0)
8948ffdff6aSGreg Kroah-Hartman 		/* route is not valid */
8958ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
8968ffdff6aSGreg Kroah-Hartman 	if (get_output_select_source(dest, dev) != CR_CHAN(src))
8978ffdff6aSGreg Kroah-Hartman 		/* cannot disconnect something not connected */
8988ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
8998ffdff6aSGreg Kroah-Hartman 
9008ffdff6aSGreg Kroah-Hartman 	/* The route is valid and is connected.  Now disconnect... */
9018ffdff6aSGreg Kroah-Hartman 	if (channel_is_pfi(CR_CHAN(dest))) {
9028ffdff6aSGreg Kroah-Hartman 		unsigned int source = ((CR_CHAN(dest) - NI_PFI(0)) < 8)
9038ffdff6aSGreg Kroah-Hartman 					? NI_660X_PFI_OUTPUT_DIO
9048ffdff6aSGreg Kroah-Hartman 					: NI_660X_PFI_OUTPUT_COUNTER;
9058ffdff6aSGreg Kroah-Hartman 
9068ffdff6aSGreg Kroah-Hartman 		/* set the pfi to high impedance, and disconnect */
9078ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, dest, COMEDI_INPUT);
9088ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_routing(dev, dest, source);
9098ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_rtsi(CR_CHAN(dest))) {
9108ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
9118ffdff6aSGreg Kroah-Hartman 			__func__, dest);
9128ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
9138ffdff6aSGreg Kroah-Hartman 		/*
9148ffdff6aSGreg Kroah-Hartman 		 * The following can be enabled when RTSI routing info is
9158ffdff6aSGreg Kroah-Hartman 		 * determined (not currently documented):
9168ffdff6aSGreg Kroah-Hartman 		 * if (reg == NI_RTSI_OUTPUT_RGOUT0) {
9178ffdff6aSGreg Kroah-Hartman 		 *	int ret = decr_rgout0_src_use(src, dev);
9188ffdff6aSGreg Kroah-Hartman 
9198ffdff6aSGreg Kroah-Hartman 		 *	if (ret < 0)
9208ffdff6aSGreg Kroah-Hartman 		 *		return ret;
9218ffdff6aSGreg Kroah-Hartman 		 * } else if (ni_rtsi_route_requires_mux(reg)) {
9228ffdff6aSGreg Kroah-Hartman 		 *	** find which RTSI_BRD line is source for rtsi pin **
9238ffdff6aSGreg Kroah-Hartman 		 *	int brd = ni_find_route_source(
9248ffdff6aSGreg Kroah-Hartman 		 *		ni_get_rtsi_routing(dev, dest), CR_CHAN(dest),
9258ffdff6aSGreg Kroah-Hartman 		 *		&devpriv->routing_tables);
9268ffdff6aSGreg Kroah-Hartman 
9278ffdff6aSGreg Kroah-Hartman 		 *	if (brd < 0)
9288ffdff6aSGreg Kroah-Hartman 		 *		return brd;
9298ffdff6aSGreg Kroah-Hartman 
9308ffdff6aSGreg Kroah-Hartman 		 *	** decrement/disconnect RTSI_BRD line from source **
9318ffdff6aSGreg Kroah-Hartman 		 *	decr_rtsi_brd_src_use(src, brd, dev);
9328ffdff6aSGreg Kroah-Hartman 		 * }
9338ffdff6aSGreg Kroah-Hartman 
9348ffdff6aSGreg Kroah-Hartman 		 * ** set rtsi output selector to default state **
9358ffdff6aSGreg Kroah-Hartman 		 * reg = default_rtsi_routing[CR_CHAN(dest) - TRIGGER_LINE(0)];
9368ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_direction(dev, dest, COMEDI_INPUT);
9378ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_routing(dev, dest, reg);
9388ffdff6aSGreg Kroah-Hartman 		 */
9398ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_ctr(CR_CHAN(dest))) {
9408ffdff6aSGreg Kroah-Hartman 		ni_tio_unset_routing(devpriv->counter_dev, dest);
9418ffdff6aSGreg Kroah-Hartman 	} else {
9428ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
9438ffdff6aSGreg Kroah-Hartman 	}
9448ffdff6aSGreg Kroah-Hartman 	return 0;
9458ffdff6aSGreg Kroah-Hartman }
9468ffdff6aSGreg Kroah-Hartman 
ni_global_insn_config(struct comedi_device * dev,struct comedi_insn * insn,unsigned int * data)9478ffdff6aSGreg Kroah-Hartman static int ni_global_insn_config(struct comedi_device *dev,
9488ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
9498ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
9508ffdff6aSGreg Kroah-Hartman {
9518ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
9528ffdff6aSGreg Kroah-Hartman 	case INSN_DEVICE_CONFIG_TEST_ROUTE:
9538ffdff6aSGreg Kroah-Hartman 		data[0] = test_route(data[1], data[2], dev);
9548ffdff6aSGreg Kroah-Hartman 		return 2;
9558ffdff6aSGreg Kroah-Hartman 	case INSN_DEVICE_CONFIG_CONNECT_ROUTE:
9568ffdff6aSGreg Kroah-Hartman 		return connect_route(data[1], data[2], dev);
9578ffdff6aSGreg Kroah-Hartman 	case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:
9588ffdff6aSGreg Kroah-Hartman 		return disconnect_route(data[1], data[2], dev);
9598ffdff6aSGreg Kroah-Hartman 	/*
9608ffdff6aSGreg Kroah-Hartman 	 * This case is already handled one level up.
9618ffdff6aSGreg Kroah-Hartman 	 * case INSN_DEVICE_CONFIG_GET_ROUTES:
9628ffdff6aSGreg Kroah-Hartman 	 */
9638ffdff6aSGreg Kroah-Hartman 	default:
9648ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
9658ffdff6aSGreg Kroah-Hartman 	}
9668ffdff6aSGreg Kroah-Hartman 	return 1;
9678ffdff6aSGreg Kroah-Hartman }
9688ffdff6aSGreg Kroah-Hartman 
ni_660x_init_tio_chips(struct comedi_device * dev,unsigned int n_chips)9698ffdff6aSGreg Kroah-Hartman static void ni_660x_init_tio_chips(struct comedi_device *dev,
9708ffdff6aSGreg Kroah-Hartman 				   unsigned int n_chips)
9718ffdff6aSGreg Kroah-Hartman {
9728ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
9738ffdff6aSGreg Kroah-Hartman 	unsigned int chip;
9748ffdff6aSGreg Kroah-Hartman 	unsigned int chan;
9758ffdff6aSGreg Kroah-Hartman 
9768ffdff6aSGreg Kroah-Hartman 	/*
9778ffdff6aSGreg Kroah-Hartman 	 * We use the ioconfig registers to control dio direction, so zero
9788ffdff6aSGreg Kroah-Hartman 	 * output enables in stc dio control reg.
9798ffdff6aSGreg Kroah-Hartman 	 */
9808ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, 0, 0, NI660X_STC_DIO_CONTROL);
9818ffdff6aSGreg Kroah-Hartman 
9828ffdff6aSGreg Kroah-Hartman 	for (chip = 0; chip < n_chips; ++chip) {
9838ffdff6aSGreg Kroah-Hartman 		/* init dma configuration register */
9848ffdff6aSGreg Kroah-Hartman 		devpriv->dma_cfg[chip] = 0;
9858ffdff6aSGreg Kroah-Hartman 		for (chan = 0; chan < NI660X_MAX_DMA_CHANNEL; ++chan)
9868ffdff6aSGreg Kroah-Hartman 			devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(chan);
9878ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, chip, devpriv->dma_cfg[chip],
9888ffdff6aSGreg Kroah-Hartman 			      NI660X_DMA_CFG);
9898ffdff6aSGreg Kroah-Hartman 
9908ffdff6aSGreg Kroah-Hartman 		/* init ioconfig registers */
9918ffdff6aSGreg Kroah-Hartman 		for (chan = 0; chan < NI660X_NUM_PFI_CHANNELS; ++chan)
9928ffdff6aSGreg Kroah-Hartman 			ni_660x_write(dev, chip, 0, NI660X_IO_CFG(chan));
9938ffdff6aSGreg Kroah-Hartman 	}
9948ffdff6aSGreg Kroah-Hartman }
9958ffdff6aSGreg Kroah-Hartman 
ni_660x_auto_attach(struct comedi_device * dev,unsigned long context)9968ffdff6aSGreg Kroah-Hartman static int ni_660x_auto_attach(struct comedi_device *dev,
9978ffdff6aSGreg Kroah-Hartman 			       unsigned long context)
9988ffdff6aSGreg Kroah-Hartman {
9998ffdff6aSGreg Kroah-Hartman 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
10008ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = NULL;
10018ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv;
10028ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
10038ffdff6aSGreg Kroah-Hartman 	struct ni_gpct_device *gpct_dev;
10048ffdff6aSGreg Kroah-Hartman 	unsigned int n_counters;
10058ffdff6aSGreg Kroah-Hartman 	int subdev;
10068ffdff6aSGreg Kroah-Hartman 	int ret;
10078ffdff6aSGreg Kroah-Hartman 	unsigned int i;
10088ffdff6aSGreg Kroah-Hartman 	unsigned int global_interrupt_config_bits;
10098ffdff6aSGreg Kroah-Hartman 
10108ffdff6aSGreg Kroah-Hartman 	if (context < ARRAY_SIZE(ni_660x_boards))
10118ffdff6aSGreg Kroah-Hartman 		board = &ni_660x_boards[context];
10128ffdff6aSGreg Kroah-Hartman 	if (!board)
10138ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
10148ffdff6aSGreg Kroah-Hartman 	dev->board_ptr = board;
10158ffdff6aSGreg Kroah-Hartman 	dev->board_name = board->name;
10168ffdff6aSGreg Kroah-Hartman 
10178ffdff6aSGreg Kroah-Hartman 	ret = comedi_pci_enable(dev);
10188ffdff6aSGreg Kroah-Hartman 	if (ret)
10198ffdff6aSGreg Kroah-Hartman 		return ret;
10208ffdff6aSGreg Kroah-Hartman 
10218ffdff6aSGreg Kroah-Hartman 	ret = ni_660x_allocate_private(dev);
10228ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
10238ffdff6aSGreg Kroah-Hartman 		return ret;
10248ffdff6aSGreg Kroah-Hartman 	devpriv = dev->private;
10258ffdff6aSGreg Kroah-Hartman 
10268ffdff6aSGreg Kroah-Hartman 	devpriv->mite = mite_attach(dev, true);		/* use win1 */
10278ffdff6aSGreg Kroah-Hartman 	if (!devpriv->mite)
10288ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
10298ffdff6aSGreg Kroah-Hartman 
10308ffdff6aSGreg Kroah-Hartman 	ret = ni_660x_alloc_mite_rings(dev);
10318ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
10328ffdff6aSGreg Kroah-Hartman 		return ret;
10338ffdff6aSGreg Kroah-Hartman 
10348ffdff6aSGreg Kroah-Hartman 	ni_660x_init_tio_chips(dev, board->n_chips);
10358ffdff6aSGreg Kroah-Hartman 
10368ffdff6aSGreg Kroah-Hartman 	/* prepare the device for globally-named routes. */
10378ffdff6aSGreg Kroah-Hartman 	if (ni_assign_device_routes("ni_660x", board->name, NULL,
10388ffdff6aSGreg Kroah-Hartman 				    &devpriv->routing_tables) < 0) {
10398ffdff6aSGreg Kroah-Hartman 		dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n",
10408ffdff6aSGreg Kroah-Hartman 			 __func__, board->name);
10418ffdff6aSGreg Kroah-Hartman 		dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n",
10428ffdff6aSGreg Kroah-Hartman 			 __func__, board->name);
10438ffdff6aSGreg Kroah-Hartman 	} else {
10448ffdff6aSGreg Kroah-Hartman 		/*
10458ffdff6aSGreg Kroah-Hartman 		 * only(?) assign insn_device_config if we have global names for
10468ffdff6aSGreg Kroah-Hartman 		 * this device.
10478ffdff6aSGreg Kroah-Hartman 		 */
10488ffdff6aSGreg Kroah-Hartman 		dev->insn_device_config = ni_global_insn_config;
10498ffdff6aSGreg Kroah-Hartman 		dev->get_valid_routes = _ni_get_valid_routes;
10508ffdff6aSGreg Kroah-Hartman 	}
10518ffdff6aSGreg Kroah-Hartman 
10528ffdff6aSGreg Kroah-Hartman 	n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP;
10538ffdff6aSGreg Kroah-Hartman 	gpct_dev = ni_gpct_device_construct(dev,
10548ffdff6aSGreg Kroah-Hartman 					    ni_660x_gpct_write,
10558ffdff6aSGreg Kroah-Hartman 					    ni_660x_gpct_read,
10568ffdff6aSGreg Kroah-Hartman 					    ni_gpct_variant_660x,
10578ffdff6aSGreg Kroah-Hartman 					    n_counters,
10588ffdff6aSGreg Kroah-Hartman 					    NI660X_COUNTERS_PER_CHIP,
10598ffdff6aSGreg Kroah-Hartman 					    &devpriv->routing_tables);
10608ffdff6aSGreg Kroah-Hartman 	if (!gpct_dev)
10618ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
10628ffdff6aSGreg Kroah-Hartman 	devpriv->counter_dev = gpct_dev;
10638ffdff6aSGreg Kroah-Hartman 
10648ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, 2 + NI660X_MAX_COUNTERS);
10658ffdff6aSGreg Kroah-Hartman 	if (ret)
10668ffdff6aSGreg Kroah-Hartman 		return ret;
10678ffdff6aSGreg Kroah-Hartman 
10688ffdff6aSGreg Kroah-Hartman 	subdev = 0;
10698ffdff6aSGreg Kroah-Hartman 
10708ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[subdev++];
10718ffdff6aSGreg Kroah-Hartman 	/* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
10728ffdff6aSGreg Kroah-Hartman 	s->type = COMEDI_SUBD_UNUSED;
10738ffdff6aSGreg Kroah-Hartman 
10748ffdff6aSGreg Kroah-Hartman 	/*
10758ffdff6aSGreg Kroah-Hartman 	 * Digital I/O subdevice
10768ffdff6aSGreg Kroah-Hartman 	 *
10778ffdff6aSGreg Kroah-Hartman 	 * There are 40 channels but only the first 32 can be digital I/Os.
10788ffdff6aSGreg Kroah-Hartman 	 * The last 8 are dedicated to counters 0 and 1.
10798ffdff6aSGreg Kroah-Hartman 	 *
10808ffdff6aSGreg Kroah-Hartman 	 * Counter 0-3 signals are from the first TIO chip.
10818ffdff6aSGreg Kroah-Hartman 	 * Counter 4-7 signals are from the second TIO chip.
10828ffdff6aSGreg Kroah-Hartman 	 *
10838ffdff6aSGreg Kroah-Hartman 	 * Comedi	External
10848ffdff6aSGreg Kroah-Hartman 	 * PFI Chan	DIO Chan        Counter Signal
10858ffdff6aSGreg Kroah-Hartman 	 * -------	--------	--------------
10868ffdff6aSGreg Kroah-Hartman 	 *     0	    0
10878ffdff6aSGreg Kroah-Hartman 	 *     1	    1
10888ffdff6aSGreg Kroah-Hartman 	 *     2	    2
10898ffdff6aSGreg Kroah-Hartman 	 *     3	    3
10908ffdff6aSGreg Kroah-Hartman 	 *     4	    4
10918ffdff6aSGreg Kroah-Hartman 	 *     5	    5
10928ffdff6aSGreg Kroah-Hartman 	 *     6	    6
10938ffdff6aSGreg Kroah-Hartman 	 *     7	    7
10948ffdff6aSGreg Kroah-Hartman 	 *     8	    8		CTR 7 OUT
10958ffdff6aSGreg Kroah-Hartman 	 *     9	    9		CTR 7 AUX
10968ffdff6aSGreg Kroah-Hartman 	 *    10	   10		CTR 7 GATE
10978ffdff6aSGreg Kroah-Hartman 	 *    11	   11		CTR 7 SOURCE
10988ffdff6aSGreg Kroah-Hartman 	 *    12	   12		CTR 6 OUT
10998ffdff6aSGreg Kroah-Hartman 	 *    13	   13		CTR 6 AUX
11008ffdff6aSGreg Kroah-Hartman 	 *    14	   14		CTR 6 GATE
11018ffdff6aSGreg Kroah-Hartman 	 *    15	   15		CTR 6 SOURCE
11028ffdff6aSGreg Kroah-Hartman 	 *    16	   16		CTR 5 OUT
11038ffdff6aSGreg Kroah-Hartman 	 *    17	   17		CTR 5 AUX
11048ffdff6aSGreg Kroah-Hartman 	 *    18	   18		CTR 5 GATE
11058ffdff6aSGreg Kroah-Hartman 	 *    19	   19		CTR 5 SOURCE
11068ffdff6aSGreg Kroah-Hartman 	 *    20	   20		CTR 4 OUT
11078ffdff6aSGreg Kroah-Hartman 	 *    21	   21		CTR 4 AUX
11088ffdff6aSGreg Kroah-Hartman 	 *    22	   22		CTR 4 GATE
11098ffdff6aSGreg Kroah-Hartman 	 *    23	   23		CTR 4 SOURCE
11108ffdff6aSGreg Kroah-Hartman 	 *    24	   24		CTR 3 OUT
11118ffdff6aSGreg Kroah-Hartman 	 *    25	   25		CTR 3 AUX
11128ffdff6aSGreg Kroah-Hartman 	 *    26	   26		CTR 3 GATE
11138ffdff6aSGreg Kroah-Hartman 	 *    27	   27		CTR 3 SOURCE
11148ffdff6aSGreg Kroah-Hartman 	 *    28	   28		CTR 2 OUT
11158ffdff6aSGreg Kroah-Hartman 	 *    29	   29		CTR 2 AUX
11168ffdff6aSGreg Kroah-Hartman 	 *    30	   30		CTR 2 GATE
11178ffdff6aSGreg Kroah-Hartman 	 *    31	   31		CTR 2 SOURCE
11188ffdff6aSGreg Kroah-Hartman 	 *    32			CTR 1 OUT
11198ffdff6aSGreg Kroah-Hartman 	 *    33			CTR 1 AUX
11208ffdff6aSGreg Kroah-Hartman 	 *    34			CTR 1 GATE
11218ffdff6aSGreg Kroah-Hartman 	 *    35			CTR 1 SOURCE
11228ffdff6aSGreg Kroah-Hartman 	 *    36			CTR 0 OUT
11238ffdff6aSGreg Kroah-Hartman 	 *    37			CTR 0 AUX
11248ffdff6aSGreg Kroah-Hartman 	 *    38			CTR 0 GATE
11258ffdff6aSGreg Kroah-Hartman 	 *    39			CTR 0 SOURCE
11268ffdff6aSGreg Kroah-Hartman 	 */
11278ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[subdev++];
11288ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_DIO;
11298ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
11308ffdff6aSGreg Kroah-Hartman 	s->n_chan	= NI660X_NUM_PFI_CHANNELS;
11318ffdff6aSGreg Kroah-Hartman 	s->maxdata	= 1;
11328ffdff6aSGreg Kroah-Hartman 	s->range_table	= &range_digital;
11338ffdff6aSGreg Kroah-Hartman 	s->insn_bits	= ni_660x_dio_insn_bits;
11348ffdff6aSGreg Kroah-Hartman 	s->insn_config	= ni_660x_dio_insn_config;
11358ffdff6aSGreg Kroah-Hartman 
11368ffdff6aSGreg Kroah-Hartman 	 /*
11378ffdff6aSGreg Kroah-Hartman 	  * Default the DIO channels as:
11388ffdff6aSGreg Kroah-Hartman 	  *   chan 0-7:  DIO inputs
11398ffdff6aSGreg Kroah-Hartman 	  *   chan 8-39: counter signal inputs
11408ffdff6aSGreg Kroah-Hartman 	  */
11418ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < s->n_chan; ++i) {
11428ffdff6aSGreg Kroah-Hartman 		unsigned int source = (i < 8) ? NI_660X_PFI_OUTPUT_DIO
11438ffdff6aSGreg Kroah-Hartman 					      : NI_660X_PFI_OUTPUT_COUNTER;
11448ffdff6aSGreg Kroah-Hartman 
11458ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_routing(dev, i, source);
11468ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, i, COMEDI_INPUT);/* high-z */
11478ffdff6aSGreg Kroah-Hartman 	}
11488ffdff6aSGreg Kroah-Hartman 
11498ffdff6aSGreg Kroah-Hartman 	/* Counter subdevices (4 NI TIO General Purpose Counters per chip) */
11508ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < NI660X_MAX_COUNTERS; ++i) {
11518ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev++];
11528ffdff6aSGreg Kroah-Hartman 		if (i < n_counters) {
11538ffdff6aSGreg Kroah-Hartman 			struct ni_gpct *counter = &gpct_dev->counters[i];
11548ffdff6aSGreg Kroah-Hartman 
11558ffdff6aSGreg Kroah-Hartman 			s->type		= COMEDI_SUBD_COUNTER;
11568ffdff6aSGreg Kroah-Hartman 			s->subdev_flags	= SDF_READABLE | SDF_WRITABLE |
11578ffdff6aSGreg Kroah-Hartman 					  SDF_LSAMPL | SDF_CMD_READ;
11588ffdff6aSGreg Kroah-Hartman 			s->n_chan	= 3;
11598ffdff6aSGreg Kroah-Hartman 			s->maxdata	= 0xffffffff;
11608ffdff6aSGreg Kroah-Hartman 			s->insn_read	= ni_tio_insn_read;
11618ffdff6aSGreg Kroah-Hartman 			s->insn_write	= ni_tio_insn_write;
11628ffdff6aSGreg Kroah-Hartman 			s->insn_config	= ni_tio_insn_config;
11638ffdff6aSGreg Kroah-Hartman 			s->len_chanlist	= 1;
11648ffdff6aSGreg Kroah-Hartman 			s->do_cmd	= ni_660x_cmd;
11658ffdff6aSGreg Kroah-Hartman 			s->do_cmdtest	= ni_tio_cmdtest;
11668ffdff6aSGreg Kroah-Hartman 			s->cancel	= ni_660x_cancel;
11678ffdff6aSGreg Kroah-Hartman 			s->poll		= ni_660x_input_poll;
11688ffdff6aSGreg Kroah-Hartman 			s->buf_change	= ni_660x_buf_change;
11698ffdff6aSGreg Kroah-Hartman 			s->async_dma_dir = DMA_BIDIRECTIONAL;
11708ffdff6aSGreg Kroah-Hartman 			s->private	= counter;
11718ffdff6aSGreg Kroah-Hartman 
11728ffdff6aSGreg Kroah-Hartman 			ni_tio_init_counter(counter);
11738ffdff6aSGreg Kroah-Hartman 		} else {
11748ffdff6aSGreg Kroah-Hartman 			s->type		= COMEDI_SUBD_UNUSED;
11758ffdff6aSGreg Kroah-Hartman 		}
11768ffdff6aSGreg Kroah-Hartman 	}
11778ffdff6aSGreg Kroah-Hartman 
11788ffdff6aSGreg Kroah-Hartman 	/*
11798ffdff6aSGreg Kroah-Hartman 	 * To be safe, set counterswap bits on tio chips after all the counter
11808ffdff6aSGreg Kroah-Hartman 	 * outputs have been set to high impedance mode.
11818ffdff6aSGreg Kroah-Hartman 	 */
11828ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < board->n_chips; ++i)
11838ffdff6aSGreg Kroah-Hartman 		set_tio_counterswap(dev, i);
11848ffdff6aSGreg Kroah-Hartman 
11858ffdff6aSGreg Kroah-Hartman 	ret = request_irq(pcidev->irq, ni_660x_interrupt, IRQF_SHARED,
11868ffdff6aSGreg Kroah-Hartman 			  dev->board_name, dev);
11878ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
11888ffdff6aSGreg Kroah-Hartman 		dev_warn(dev->class_dev, " irq not available\n");
11898ffdff6aSGreg Kroah-Hartman 		return ret;
11908ffdff6aSGreg Kroah-Hartman 	}
11918ffdff6aSGreg Kroah-Hartman 	dev->irq = pcidev->irq;
11928ffdff6aSGreg Kroah-Hartman 	global_interrupt_config_bits = NI660X_GLOBAL_INT_GLOBAL;
11938ffdff6aSGreg Kroah-Hartman 	if (board->n_chips > 1)
11948ffdff6aSGreg Kroah-Hartman 		global_interrupt_config_bits |= NI660X_GLOBAL_INT_CASCADE;
11958ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, 0, global_interrupt_config_bits,
11968ffdff6aSGreg Kroah-Hartman 		      NI660X_GLOBAL_INT_CFG);
11978ffdff6aSGreg Kroah-Hartman 
11988ffdff6aSGreg Kroah-Hartman 	return 0;
11998ffdff6aSGreg Kroah-Hartman }
12008ffdff6aSGreg Kroah-Hartman 
ni_660x_detach(struct comedi_device * dev)12018ffdff6aSGreg Kroah-Hartman static void ni_660x_detach(struct comedi_device *dev)
12028ffdff6aSGreg Kroah-Hartman {
12038ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
12048ffdff6aSGreg Kroah-Hartman 
12058ffdff6aSGreg Kroah-Hartman 	if (dev->irq) {
12068ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, 0, 0, NI660X_GLOBAL_INT_CFG);
12078ffdff6aSGreg Kroah-Hartman 		free_irq(dev->irq, dev);
12088ffdff6aSGreg Kroah-Hartman 	}
12098ffdff6aSGreg Kroah-Hartman 	if (devpriv) {
12108ffdff6aSGreg Kroah-Hartman 		ni_gpct_device_destroy(devpriv->counter_dev);
12118ffdff6aSGreg Kroah-Hartman 		ni_660x_free_mite_rings(dev);
12128ffdff6aSGreg Kroah-Hartman 		mite_detach(devpriv->mite);
12138ffdff6aSGreg Kroah-Hartman 	}
12148ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
12158ffdff6aSGreg Kroah-Hartman 		iounmap(dev->mmio);
12168ffdff6aSGreg Kroah-Hartman 	comedi_pci_disable(dev);
12178ffdff6aSGreg Kroah-Hartman }
12188ffdff6aSGreg Kroah-Hartman 
12198ffdff6aSGreg Kroah-Hartman static struct comedi_driver ni_660x_driver = {
12208ffdff6aSGreg Kroah-Hartman 	.driver_name	= "ni_660x",
12218ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
12228ffdff6aSGreg Kroah-Hartman 	.auto_attach	= ni_660x_auto_attach,
12238ffdff6aSGreg Kroah-Hartman 	.detach		= ni_660x_detach,
12248ffdff6aSGreg Kroah-Hartman };
12258ffdff6aSGreg Kroah-Hartman 
ni_660x_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)12268ffdff6aSGreg Kroah-Hartman static int ni_660x_pci_probe(struct pci_dev *dev,
12278ffdff6aSGreg Kroah-Hartman 			     const struct pci_device_id *id)
12288ffdff6aSGreg Kroah-Hartman {
12298ffdff6aSGreg Kroah-Hartman 	return comedi_pci_auto_config(dev, &ni_660x_driver, id->driver_data);
12308ffdff6aSGreg Kroah-Hartman }
12318ffdff6aSGreg Kroah-Hartman 
12328ffdff6aSGreg Kroah-Hartman static const struct pci_device_id ni_660x_pci_table[] = {
12338ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 },
12348ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 },
12358ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 },
12368ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x2db0), BOARD_PCI6608 },
12378ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 },
12388ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1e30), BOARD_PCI6624 },
12398ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 },
12408ffdff6aSGreg Kroah-Hartman 	{ 0 }
12418ffdff6aSGreg Kroah-Hartman };
12428ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, ni_660x_pci_table);
12438ffdff6aSGreg Kroah-Hartman 
12448ffdff6aSGreg Kroah-Hartman static struct pci_driver ni_660x_pci_driver = {
12458ffdff6aSGreg Kroah-Hartman 	.name		= "ni_660x",
12468ffdff6aSGreg Kroah-Hartman 	.id_table	= ni_660x_pci_table,
12478ffdff6aSGreg Kroah-Hartman 	.probe		= ni_660x_pci_probe,
12488ffdff6aSGreg Kroah-Hartman 	.remove		= comedi_pci_auto_unconfig,
12498ffdff6aSGreg Kroah-Hartman };
12508ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(ni_660x_driver, ni_660x_pci_driver);
12518ffdff6aSGreg Kroah-Hartman 
12528ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
12538ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi driver for NI 660x counter/timer boards");
12548ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
1255