xref: /linux/drivers/comedi/drivers/ni_660x.c (revision 8ffdff6a8cfbdc174a3a390b6f825a277b5bb895)
1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2*8ffdff6aSGreg Kroah-Hartman /*
3*8ffdff6aSGreg Kroah-Hartman  * Hardware driver for NI 660x devices
4*8ffdff6aSGreg Kroah-Hartman  */
5*8ffdff6aSGreg Kroah-Hartman 
6*8ffdff6aSGreg Kroah-Hartman /*
7*8ffdff6aSGreg Kroah-Hartman  * Driver: ni_660x
8*8ffdff6aSGreg Kroah-Hartman  * Description: National Instruments 660x counter/timer boards
9*8ffdff6aSGreg Kroah-Hartman  * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602,
10*8ffdff6aSGreg Kroah-Hartman  *   PCI-6608, PXI-6608, PCI-6624, PXI-6624
11*8ffdff6aSGreg Kroah-Hartman  * Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
12*8ffdff6aSGreg Kroah-Hartman  *   Herman.Bruyninckx@mech.kuleuven.ac.be,
13*8ffdff6aSGreg Kroah-Hartman  *   Wim.Meeussen@mech.kuleuven.ac.be,
14*8ffdff6aSGreg Kroah-Hartman  *   Klaas.Gadeyne@mech.kuleuven.ac.be,
15*8ffdff6aSGreg Kroah-Hartman  *   Frank Mori Hess <fmhess@users.sourceforge.net>
16*8ffdff6aSGreg Kroah-Hartman  * Updated: Mon, 16 Jan 2017 14:00:43 +0000
17*8ffdff6aSGreg Kroah-Hartman  * Status: experimental
18*8ffdff6aSGreg Kroah-Hartman  *
19*8ffdff6aSGreg Kroah-Hartman  * Encoders work.  PulseGeneration (both single pulse and pulse train)
20*8ffdff6aSGreg Kroah-Hartman  * works.  Buffered commands work for input but not output.
21*8ffdff6aSGreg Kroah-Hartman  *
22*8ffdff6aSGreg Kroah-Hartman  * References:
23*8ffdff6aSGreg Kroah-Hartman  * DAQ 660x Register-Level Programmer Manual  (NI 370505A-01)
24*8ffdff6aSGreg Kroah-Hartman  * DAQ 6601/6602 User Manual (NI 322137B-01)
25*8ffdff6aSGreg Kroah-Hartman  */
26*8ffdff6aSGreg Kroah-Hartman 
27*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
28*8ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h>
29*8ffdff6aSGreg Kroah-Hartman 
30*8ffdff6aSGreg Kroah-Hartman #include "../comedi_pci.h"
31*8ffdff6aSGreg Kroah-Hartman 
32*8ffdff6aSGreg Kroah-Hartman #include "mite.h"
33*8ffdff6aSGreg Kroah-Hartman #include "ni_tio.h"
34*8ffdff6aSGreg Kroah-Hartman #include "ni_routes.h"
35*8ffdff6aSGreg Kroah-Hartman 
36*8ffdff6aSGreg Kroah-Hartman /* See Register-Level Programmer Manual page 3.1 */
37*8ffdff6aSGreg Kroah-Hartman enum ni_660x_register {
38*8ffdff6aSGreg Kroah-Hartman 	/* see enum ni_gpct_register */
39*8ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_PARALLEL_INPUT = NITIO_NUM_REGS,
40*8ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_OUTPUT,
41*8ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_CONTROL,
42*8ffdff6aSGreg Kroah-Hartman 	NI660X_STC_DIO_SERIAL_INPUT,
43*8ffdff6aSGreg Kroah-Hartman 	NI660X_DIO32_INPUT,
44*8ffdff6aSGreg Kroah-Hartman 	NI660X_DIO32_OUTPUT,
45*8ffdff6aSGreg Kroah-Hartman 	NI660X_CLK_CFG,
46*8ffdff6aSGreg Kroah-Hartman 	NI660X_GLOBAL_INT_STATUS,
47*8ffdff6aSGreg Kroah-Hartman 	NI660X_DMA_CFG,
48*8ffdff6aSGreg Kroah-Hartman 	NI660X_GLOBAL_INT_CFG,
49*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_0_1,
50*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_2_3,
51*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_4_5,
52*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_6_7,
53*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_8_9,
54*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_10_11,
55*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_12_13,
56*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_14_15,
57*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_16_17,
58*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_18_19,
59*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_20_21,
60*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_22_23,
61*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_24_25,
62*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_26_27,
63*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_28_29,
64*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_30_31,
65*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_32_33,
66*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_34_35,
67*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_36_37,
68*8ffdff6aSGreg Kroah-Hartman 	NI660X_IO_CFG_38_39,
69*8ffdff6aSGreg Kroah-Hartman 	NI660X_NUM_REGS,
70*8ffdff6aSGreg Kroah-Hartman };
71*8ffdff6aSGreg Kroah-Hartman 
72*8ffdff6aSGreg Kroah-Hartman #define NI660X_CLK_CFG_COUNTER_SWAP	BIT(21)
73*8ffdff6aSGreg Kroah-Hartman 
74*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER0	BIT(8)
75*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER1	BIT(9)
76*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER2	BIT(10)
77*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_COUNTER3	BIT(11)
78*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_CASCADE	BIT(29)
79*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_GLOBAL_POL	BIT(30)
80*8ffdff6aSGreg Kroah-Hartman #define NI660X_GLOBAL_INT_GLOBAL	BIT(31)
81*8ffdff6aSGreg Kroah-Hartman 
82*8ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_SEL(_c, _s)	(((_s) & 0x1f) << (8 * (_c)))
83*8ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_SEL_MASK(_c)	NI660X_DMA_CFG_SEL((_c), 0x1f)
84*8ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_SEL_NONE(_c)	NI660X_DMA_CFG_SEL((_c), 0x1f)
85*8ffdff6aSGreg Kroah-Hartman #define NI660X_DMA_CFG_RESET(_c)	NI660X_DMA_CFG_SEL((_c), 0x80)
86*8ffdff6aSGreg Kroah-Hartman 
87*8ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG(x)		(NI660X_IO_CFG_0_1 + ((x) / 2))
88*8ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_OUT_SEL(_c, _s)	(((_s) & 0x3) << (((_c) % 2) ? 0 : 8))
89*8ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_OUT_SEL_MASK(_c)	NI660X_IO_CFG_OUT_SEL((_c), 0x3)
90*8ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_IN_SEL(_c, _s)	(((_s) & 0x7) << (((_c) % 2) ? 4 : 12))
91*8ffdff6aSGreg Kroah-Hartman #define NI660X_IO_CFG_IN_SEL_MASK(_c)	NI660X_IO_CFG_IN_SEL((_c), 0x7)
92*8ffdff6aSGreg Kroah-Hartman 
93*8ffdff6aSGreg Kroah-Hartman struct ni_660x_register_data {
94*8ffdff6aSGreg Kroah-Hartman 	int offset;		/*  Offset from base address from GPCT chip */
95*8ffdff6aSGreg Kroah-Hartman 	char size;		/* 2 or 4 bytes */
96*8ffdff6aSGreg Kroah-Hartman };
97*8ffdff6aSGreg Kroah-Hartman 
98*8ffdff6aSGreg Kroah-Hartman static const struct ni_660x_register_data ni_660x_reg_data[NI660X_NUM_REGS] = {
99*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_INT_ACK]		= { 0x004, 2 },	/* write */
100*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_STATUS]		= { 0x004, 2 },	/* read */
101*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_INT_ACK]		= { 0x006, 2 },	/* write */
102*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_STATUS]		= { 0x006, 2 },	/* read */
103*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_STATUS]		= { 0x008, 2 },	/* read */
104*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_CMD]			= { 0x00c, 2 },	/* write */
105*8ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_PARALLEL_INPUT]	= { 0x00e, 2 },	/* read */
106*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_CMD]			= { 0x00e, 2 },	/* write */
107*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_HW_SAVE]		= { 0x010, 4 },	/* read */
108*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_HW_SAVE]		= { 0x014, 4 },	/* read */
109*8ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_OUTPUT]		= { 0x014, 2 },	/* write */
110*8ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_CONTROL]	= { 0x016, 2 },	/* write */
111*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_SW_SAVE]		= { 0x018, 4 },	/* read */
112*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_SW_SAVE]		= { 0x01c, 4 },	/* read */
113*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_MODE]			= { 0x034, 2 },	/* write */
114*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_STATUS1]		= { 0x036, 2 },	/* read */
115*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_MODE]			= { 0x036, 2 },	/* write */
116*8ffdff6aSGreg Kroah-Hartman 	[NI660X_STC_DIO_SERIAL_INPUT]	= { 0x038, 2 },	/* read */
117*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_LOADA]		= { 0x038, 4 },	/* write */
118*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_STATUS2]		= { 0x03a, 2 },	/* read */
119*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_LOADB]		= { 0x03c, 4 },	/* write */
120*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_LOADA]		= { 0x040, 4 },	/* write */
121*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_LOADB]		= { 0x044, 4 },	/* write */
122*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_INPUT_SEL]		= { 0x048, 2 },	/* write */
123*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_INPUT_SEL]		= { 0x04a, 2 },	/* write */
124*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_AUTO_INC]		= { 0x088, 2 },	/* write */
125*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_AUTO_INC]		= { 0x08a, 2 },	/* write */
126*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G01_RESET]		= { 0x090, 2 },	/* write */
127*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_INT_ENA]		= { 0x092, 2 },	/* write */
128*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_INT_ENA]		= { 0x096, 2 },	/* write */
129*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_CNT_MODE]		= { 0x0b0, 2 },	/* write */
130*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_CNT_MODE]		= { 0x0b2, 2 },	/* write */
131*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_GATE2]		= { 0x0b4, 2 },	/* write */
132*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_GATE2]		= { 0x0b6, 2 },	/* write */
133*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_DMA_CFG]		= { 0x0b8, 2 },	/* write */
134*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G0_DMA_STATUS]		= { 0x0b8, 2 },	/* read */
135*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_DMA_CFG]		= { 0x0ba, 2 },	/* write */
136*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G1_DMA_STATUS]		= { 0x0ba, 2 },	/* read */
137*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_INT_ACK]		= { 0x104, 2 },	/* write */
138*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_STATUS]		= { 0x104, 2 },	/* read */
139*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_INT_ACK]		= { 0x106, 2 },	/* write */
140*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_STATUS]		= { 0x106, 2 },	/* read */
141*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_STATUS]		= { 0x108, 2 },	/* read */
142*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_CMD]			= { 0x10c, 2 },	/* write */
143*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_CMD]			= { 0x10e, 2 },	/* write */
144*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_HW_SAVE]		= { 0x110, 4 },	/* read */
145*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_HW_SAVE]		= { 0x114, 4 },	/* read */
146*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_SW_SAVE]		= { 0x118, 4 },	/* read */
147*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_SW_SAVE]		= { 0x11c, 4 },	/* read */
148*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_MODE]			= { 0x134, 2 },	/* write */
149*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_STATUS1]		= { 0x136, 2 },	/* read */
150*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_MODE]			= { 0x136, 2 },	/* write */
151*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_LOADA]		= { 0x138, 4 },	/* write */
152*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_STATUS2]		= { 0x13a, 2 },	/* read */
153*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_LOADB]		= { 0x13c, 4 },	/* write */
154*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_LOADA]		= { 0x140, 4 },	/* write */
155*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_LOADB]		= { 0x144, 4 },	/* write */
156*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_INPUT_SEL]		= { 0x148, 2 },	/* write */
157*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_INPUT_SEL]		= { 0x14a, 2 },	/* write */
158*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_AUTO_INC]		= { 0x188, 2 },	/* write */
159*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_AUTO_INC]		= { 0x18a, 2 },	/* write */
160*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G23_RESET]		= { 0x190, 2 },	/* write */
161*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_INT_ENA]		= { 0x192, 2 },	/* write */
162*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_INT_ENA]		= { 0x196, 2 },	/* write */
163*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_CNT_MODE]		= { 0x1b0, 2 },	/* write */
164*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_CNT_MODE]		= { 0x1b2, 2 },	/* write */
165*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_GATE2]		= { 0x1b4, 2 },	/* write */
166*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_GATE2]		= { 0x1b6, 2 },	/* write */
167*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_DMA_CFG]		= { 0x1b8, 2 },	/* write */
168*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G2_DMA_STATUS]		= { 0x1b8, 2 },	/* read */
169*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_DMA_CFG]		= { 0x1ba, 2 },	/* write */
170*8ffdff6aSGreg Kroah-Hartman 	[NITIO_G3_DMA_STATUS]		= { 0x1ba, 2 },	/* read */
171*8ffdff6aSGreg Kroah-Hartman 	[NI660X_DIO32_INPUT]		= { 0x414, 4 },	/* read */
172*8ffdff6aSGreg Kroah-Hartman 	[NI660X_DIO32_OUTPUT]		= { 0x510, 4 },	/* write */
173*8ffdff6aSGreg Kroah-Hartman 	[NI660X_CLK_CFG]		= { 0x73c, 4 },	/* write */
174*8ffdff6aSGreg Kroah-Hartman 	[NI660X_GLOBAL_INT_STATUS]	= { 0x754, 4 },	/* read */
175*8ffdff6aSGreg Kroah-Hartman 	[NI660X_DMA_CFG]		= { 0x76c, 4 },	/* write */
176*8ffdff6aSGreg Kroah-Hartman 	[NI660X_GLOBAL_INT_CFG]		= { 0x770, 4 },	/* write */
177*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_0_1]		= { 0x77c, 2 },	/* read/write */
178*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_2_3]		= { 0x77e, 2 },	/* read/write */
179*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_4_5]		= { 0x780, 2 },	/* read/write */
180*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_6_7]		= { 0x782, 2 },	/* read/write */
181*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_8_9]		= { 0x784, 2 },	/* read/write */
182*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_10_11]		= { 0x786, 2 },	/* read/write */
183*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_12_13]		= { 0x788, 2 },	/* read/write */
184*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_14_15]		= { 0x78a, 2 },	/* read/write */
185*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_16_17]		= { 0x78c, 2 },	/* read/write */
186*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_18_19]		= { 0x78e, 2 },	/* read/write */
187*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_20_21]		= { 0x790, 2 },	/* read/write */
188*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_22_23]		= { 0x792, 2 },	/* read/write */
189*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_24_25]		= { 0x794, 2 },	/* read/write */
190*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_26_27]		= { 0x796, 2 },	/* read/write */
191*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_28_29]		= { 0x798, 2 },	/* read/write */
192*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_30_31]		= { 0x79a, 2 },	/* read/write */
193*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_32_33]		= { 0x79c, 2 },	/* read/write */
194*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_34_35]		= { 0x79e, 2 },	/* read/write */
195*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_36_37]		= { 0x7a0, 2 },	/* read/write */
196*8ffdff6aSGreg Kroah-Hartman 	[NI660X_IO_CFG_38_39]		= { 0x7a2, 2 }	/* read/write */
197*8ffdff6aSGreg Kroah-Hartman };
198*8ffdff6aSGreg Kroah-Hartman 
199*8ffdff6aSGreg Kroah-Hartman #define NI660X_CHIP_OFFSET		0x800
200*8ffdff6aSGreg Kroah-Hartman 
201*8ffdff6aSGreg Kroah-Hartman enum ni_660x_boardid {
202*8ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6601,
203*8ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6602,
204*8ffdff6aSGreg Kroah-Hartman 	BOARD_PXI6602,
205*8ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6608,
206*8ffdff6aSGreg Kroah-Hartman 	BOARD_PXI6608,
207*8ffdff6aSGreg Kroah-Hartman 	BOARD_PCI6624,
208*8ffdff6aSGreg Kroah-Hartman 	BOARD_PXI6624
209*8ffdff6aSGreg Kroah-Hartman };
210*8ffdff6aSGreg Kroah-Hartman 
211*8ffdff6aSGreg Kroah-Hartman struct ni_660x_board {
212*8ffdff6aSGreg Kroah-Hartman 	const char *name;
213*8ffdff6aSGreg Kroah-Hartman 	unsigned int n_chips;	/* total number of TIO chips */
214*8ffdff6aSGreg Kroah-Hartman };
215*8ffdff6aSGreg Kroah-Hartman 
216*8ffdff6aSGreg Kroah-Hartman static const struct ni_660x_board ni_660x_boards[] = {
217*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6601] = {
218*8ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6601",
219*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 1,
220*8ffdff6aSGreg Kroah-Hartman 	},
221*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6602] = {
222*8ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6602",
223*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
224*8ffdff6aSGreg Kroah-Hartman 	},
225*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PXI6602] = {
226*8ffdff6aSGreg Kroah-Hartman 		.name		= "PXI-6602",
227*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
228*8ffdff6aSGreg Kroah-Hartman 	},
229*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6608] = {
230*8ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6608",
231*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
232*8ffdff6aSGreg Kroah-Hartman 	},
233*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PXI6608] = {
234*8ffdff6aSGreg Kroah-Hartman 		.name		= "PXI-6608",
235*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
236*8ffdff6aSGreg Kroah-Hartman 	},
237*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PCI6624] = {
238*8ffdff6aSGreg Kroah-Hartman 		.name		= "PCI-6624",
239*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
240*8ffdff6aSGreg Kroah-Hartman 	},
241*8ffdff6aSGreg Kroah-Hartman 	[BOARD_PXI6624] = {
242*8ffdff6aSGreg Kroah-Hartman 		.name		= "PXI-6624",
243*8ffdff6aSGreg Kroah-Hartman 		.n_chips	= 2,
244*8ffdff6aSGreg Kroah-Hartman 	},
245*8ffdff6aSGreg Kroah-Hartman };
246*8ffdff6aSGreg Kroah-Hartman 
247*8ffdff6aSGreg Kroah-Hartman #define NI660X_NUM_PFI_CHANNELS		40
248*8ffdff6aSGreg Kroah-Hartman 
249*8ffdff6aSGreg Kroah-Hartman /* there are only up to 3 dma channels, but the register layout allows for 4 */
250*8ffdff6aSGreg Kroah-Hartman #define NI660X_MAX_DMA_CHANNEL		4
251*8ffdff6aSGreg Kroah-Hartman 
252*8ffdff6aSGreg Kroah-Hartman #define NI660X_COUNTERS_PER_CHIP	4
253*8ffdff6aSGreg Kroah-Hartman #define NI660X_MAX_CHIPS		2
254*8ffdff6aSGreg Kroah-Hartman #define NI660X_MAX_COUNTERS		(NI660X_MAX_CHIPS *	\
255*8ffdff6aSGreg Kroah-Hartman 					 NI660X_COUNTERS_PER_CHIP)
256*8ffdff6aSGreg Kroah-Hartman 
257*8ffdff6aSGreg Kroah-Hartman struct ni_660x_private {
258*8ffdff6aSGreg Kroah-Hartman 	struct mite *mite;
259*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct_device *counter_dev;
260*8ffdff6aSGreg Kroah-Hartman 	struct mite_ring *ring[NI660X_MAX_CHIPS][NI660X_COUNTERS_PER_CHIP];
261*8ffdff6aSGreg Kroah-Hartman 	/* protects mite channel request/release */
262*8ffdff6aSGreg Kroah-Hartman 	spinlock_t mite_channel_lock;
263*8ffdff6aSGreg Kroah-Hartman 	/* prevents races between interrupt and comedi_poll */
264*8ffdff6aSGreg Kroah-Hartman 	spinlock_t interrupt_lock;
265*8ffdff6aSGreg Kroah-Hartman 	unsigned int dma_cfg[NI660X_MAX_CHIPS];
266*8ffdff6aSGreg Kroah-Hartman 	unsigned int io_cfg[NI660X_NUM_PFI_CHANNELS];
267*8ffdff6aSGreg Kroah-Hartman 	u64 io_dir;
268*8ffdff6aSGreg Kroah-Hartman 	struct ni_route_tables routing_tables;
269*8ffdff6aSGreg Kroah-Hartman };
270*8ffdff6aSGreg Kroah-Hartman 
271*8ffdff6aSGreg Kroah-Hartman static void ni_660x_write(struct comedi_device *dev, unsigned int chip,
272*8ffdff6aSGreg Kroah-Hartman 			  unsigned int bits, unsigned int reg)
273*8ffdff6aSGreg Kroah-Hartman {
274*8ffdff6aSGreg Kroah-Hartman 	unsigned int addr = (chip * NI660X_CHIP_OFFSET) +
275*8ffdff6aSGreg Kroah-Hartman 			    ni_660x_reg_data[reg].offset;
276*8ffdff6aSGreg Kroah-Hartman 
277*8ffdff6aSGreg Kroah-Hartman 	if (ni_660x_reg_data[reg].size == 2)
278*8ffdff6aSGreg Kroah-Hartman 		writew(bits, dev->mmio + addr);
279*8ffdff6aSGreg Kroah-Hartman 	else
280*8ffdff6aSGreg Kroah-Hartman 		writel(bits, dev->mmio + addr);
281*8ffdff6aSGreg Kroah-Hartman }
282*8ffdff6aSGreg Kroah-Hartman 
283*8ffdff6aSGreg Kroah-Hartman static unsigned int ni_660x_read(struct comedi_device *dev,
284*8ffdff6aSGreg Kroah-Hartman 				 unsigned int chip, unsigned int reg)
285*8ffdff6aSGreg Kroah-Hartman {
286*8ffdff6aSGreg Kroah-Hartman 	unsigned int addr = (chip * NI660X_CHIP_OFFSET) +
287*8ffdff6aSGreg Kroah-Hartman 			    ni_660x_reg_data[reg].offset;
288*8ffdff6aSGreg Kroah-Hartman 
289*8ffdff6aSGreg Kroah-Hartman 	if (ni_660x_reg_data[reg].size == 2)
290*8ffdff6aSGreg Kroah-Hartman 		return readw(dev->mmio + addr);
291*8ffdff6aSGreg Kroah-Hartman 	return readl(dev->mmio + addr);
292*8ffdff6aSGreg Kroah-Hartman }
293*8ffdff6aSGreg Kroah-Hartman 
294*8ffdff6aSGreg Kroah-Hartman static void ni_660x_gpct_write(struct ni_gpct *counter, unsigned int bits,
295*8ffdff6aSGreg Kroah-Hartman 			       enum ni_gpct_register reg)
296*8ffdff6aSGreg Kroah-Hartman {
297*8ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = counter->counter_dev->dev;
298*8ffdff6aSGreg Kroah-Hartman 
299*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, counter->chip_index, bits, reg);
300*8ffdff6aSGreg Kroah-Hartman }
301*8ffdff6aSGreg Kroah-Hartman 
302*8ffdff6aSGreg Kroah-Hartman static unsigned int ni_660x_gpct_read(struct ni_gpct *counter,
303*8ffdff6aSGreg Kroah-Hartman 				      enum ni_gpct_register reg)
304*8ffdff6aSGreg Kroah-Hartman {
305*8ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = counter->counter_dev->dev;
306*8ffdff6aSGreg Kroah-Hartman 
307*8ffdff6aSGreg Kroah-Hartman 	return ni_660x_read(dev, counter->chip_index, reg);
308*8ffdff6aSGreg Kroah-Hartman }
309*8ffdff6aSGreg Kroah-Hartman 
310*8ffdff6aSGreg Kroah-Hartman static inline void ni_660x_set_dma_channel(struct comedi_device *dev,
311*8ffdff6aSGreg Kroah-Hartman 					   unsigned int mite_channel,
312*8ffdff6aSGreg Kroah-Hartman 					   struct ni_gpct *counter)
313*8ffdff6aSGreg Kroah-Hartman {
314*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
315*8ffdff6aSGreg Kroah-Hartman 	unsigned int chip = counter->chip_index;
316*8ffdff6aSGreg Kroah-Hartman 
317*8ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel);
318*8ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL(mite_channel,
319*8ffdff6aSGreg Kroah-Hartman 						     counter->counter_index);
320*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, chip, devpriv->dma_cfg[chip] |
321*8ffdff6aSGreg Kroah-Hartman 		      NI660X_DMA_CFG_RESET(mite_channel),
322*8ffdff6aSGreg Kroah-Hartman 		      NI660X_DMA_CFG);
323*8ffdff6aSGreg Kroah-Hartman }
324*8ffdff6aSGreg Kroah-Hartman 
325*8ffdff6aSGreg Kroah-Hartman static inline void ni_660x_unset_dma_channel(struct comedi_device *dev,
326*8ffdff6aSGreg Kroah-Hartman 					     unsigned int mite_channel,
327*8ffdff6aSGreg Kroah-Hartman 					     struct ni_gpct *counter)
328*8ffdff6aSGreg Kroah-Hartman {
329*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
330*8ffdff6aSGreg Kroah-Hartman 	unsigned int chip = counter->chip_index;
331*8ffdff6aSGreg Kroah-Hartman 
332*8ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel);
333*8ffdff6aSGreg Kroah-Hartman 	devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(mite_channel);
334*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, chip, devpriv->dma_cfg[chip], NI660X_DMA_CFG);
335*8ffdff6aSGreg Kroah-Hartman }
336*8ffdff6aSGreg Kroah-Hartman 
337*8ffdff6aSGreg Kroah-Hartman static int ni_660x_request_mite_channel(struct comedi_device *dev,
338*8ffdff6aSGreg Kroah-Hartman 					struct ni_gpct *counter,
339*8ffdff6aSGreg Kroah-Hartman 					enum comedi_io_direction direction)
340*8ffdff6aSGreg Kroah-Hartman {
341*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
342*8ffdff6aSGreg Kroah-Hartman 	struct mite_ring *ring;
343*8ffdff6aSGreg Kroah-Hartman 	struct mite_channel *mite_chan;
344*8ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
345*8ffdff6aSGreg Kroah-Hartman 
346*8ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
347*8ffdff6aSGreg Kroah-Hartman 	ring = devpriv->ring[counter->chip_index][counter->counter_index];
348*8ffdff6aSGreg Kroah-Hartman 	mite_chan = mite_request_channel(devpriv->mite, ring);
349*8ffdff6aSGreg Kroah-Hartman 	if (!mite_chan) {
350*8ffdff6aSGreg Kroah-Hartman 		spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
351*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
352*8ffdff6aSGreg Kroah-Hartman 			"failed to reserve mite dma channel for counter\n");
353*8ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
354*8ffdff6aSGreg Kroah-Hartman 	}
355*8ffdff6aSGreg Kroah-Hartman 	mite_chan->dir = direction;
356*8ffdff6aSGreg Kroah-Hartman 	ni_tio_set_mite_channel(counter, mite_chan);
357*8ffdff6aSGreg Kroah-Hartman 	ni_660x_set_dma_channel(dev, mite_chan->channel, counter);
358*8ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
359*8ffdff6aSGreg Kroah-Hartman 	return 0;
360*8ffdff6aSGreg Kroah-Hartman }
361*8ffdff6aSGreg Kroah-Hartman 
362*8ffdff6aSGreg Kroah-Hartman static void ni_660x_release_mite_channel(struct comedi_device *dev,
363*8ffdff6aSGreg Kroah-Hartman 					 struct ni_gpct *counter)
364*8ffdff6aSGreg Kroah-Hartman {
365*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
366*8ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
367*8ffdff6aSGreg Kroah-Hartman 
368*8ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
369*8ffdff6aSGreg Kroah-Hartman 	if (counter->mite_chan) {
370*8ffdff6aSGreg Kroah-Hartman 		struct mite_channel *mite_chan = counter->mite_chan;
371*8ffdff6aSGreg Kroah-Hartman 
372*8ffdff6aSGreg Kroah-Hartman 		ni_660x_unset_dma_channel(dev, mite_chan->channel, counter);
373*8ffdff6aSGreg Kroah-Hartman 		ni_tio_set_mite_channel(counter, NULL);
374*8ffdff6aSGreg Kroah-Hartman 		mite_release_channel(mite_chan);
375*8ffdff6aSGreg Kroah-Hartman 	}
376*8ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
377*8ffdff6aSGreg Kroah-Hartman }
378*8ffdff6aSGreg Kroah-Hartman 
379*8ffdff6aSGreg Kroah-Hartman static int ni_660x_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
380*8ffdff6aSGreg Kroah-Hartman {
381*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
382*8ffdff6aSGreg Kroah-Hartman 	int retval;
383*8ffdff6aSGreg Kroah-Hartman 
384*8ffdff6aSGreg Kroah-Hartman 	retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT);
385*8ffdff6aSGreg Kroah-Hartman 	if (retval) {
386*8ffdff6aSGreg Kroah-Hartman 		dev_err(dev->class_dev,
387*8ffdff6aSGreg Kroah-Hartman 			"no dma channel available for use by counter\n");
388*8ffdff6aSGreg Kroah-Hartman 		return retval;
389*8ffdff6aSGreg Kroah-Hartman 	}
390*8ffdff6aSGreg Kroah-Hartman 	ni_tio_acknowledge(counter);
391*8ffdff6aSGreg Kroah-Hartman 
392*8ffdff6aSGreg Kroah-Hartman 	return ni_tio_cmd(dev, s);
393*8ffdff6aSGreg Kroah-Hartman }
394*8ffdff6aSGreg Kroah-Hartman 
395*8ffdff6aSGreg Kroah-Hartman static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
396*8ffdff6aSGreg Kroah-Hartman {
397*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
398*8ffdff6aSGreg Kroah-Hartman 	int retval;
399*8ffdff6aSGreg Kroah-Hartman 
400*8ffdff6aSGreg Kroah-Hartman 	retval = ni_tio_cancel(counter);
401*8ffdff6aSGreg Kroah-Hartman 	ni_660x_release_mite_channel(dev, counter);
402*8ffdff6aSGreg Kroah-Hartman 	return retval;
403*8ffdff6aSGreg Kroah-Hartman }
404*8ffdff6aSGreg Kroah-Hartman 
405*8ffdff6aSGreg Kroah-Hartman static void set_tio_counterswap(struct comedi_device *dev, int chip)
406*8ffdff6aSGreg Kroah-Hartman {
407*8ffdff6aSGreg Kroah-Hartman 	unsigned int bits = 0;
408*8ffdff6aSGreg Kroah-Hartman 
409*8ffdff6aSGreg Kroah-Hartman 	/*
410*8ffdff6aSGreg Kroah-Hartman 	 * See P. 3.5 of the Register-Level Programming manual.
411*8ffdff6aSGreg Kroah-Hartman 	 * The CounterSwap bit has to be set on the second chip,
412*8ffdff6aSGreg Kroah-Hartman 	 * otherwise it will try to use the same pins as the
413*8ffdff6aSGreg Kroah-Hartman 	 * first chip.
414*8ffdff6aSGreg Kroah-Hartman 	 */
415*8ffdff6aSGreg Kroah-Hartman 	if (chip)
416*8ffdff6aSGreg Kroah-Hartman 		bits = NI660X_CLK_CFG_COUNTER_SWAP;
417*8ffdff6aSGreg Kroah-Hartman 
418*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, chip, bits, NI660X_CLK_CFG);
419*8ffdff6aSGreg Kroah-Hartman }
420*8ffdff6aSGreg Kroah-Hartman 
421*8ffdff6aSGreg Kroah-Hartman static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev,
422*8ffdff6aSGreg Kroah-Hartman 					  struct comedi_subdevice *s)
423*8ffdff6aSGreg Kroah-Hartman {
424*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
425*8ffdff6aSGreg Kroah-Hartman 
426*8ffdff6aSGreg Kroah-Hartman 	ni_tio_handle_interrupt(counter, s);
427*8ffdff6aSGreg Kroah-Hartman 	comedi_handle_events(dev, s);
428*8ffdff6aSGreg Kroah-Hartman }
429*8ffdff6aSGreg Kroah-Hartman 
430*8ffdff6aSGreg Kroah-Hartman static irqreturn_t ni_660x_interrupt(int irq, void *d)
431*8ffdff6aSGreg Kroah-Hartman {
432*8ffdff6aSGreg Kroah-Hartman 	struct comedi_device *dev = d;
433*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
434*8ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
435*8ffdff6aSGreg Kroah-Hartman 	unsigned int i;
436*8ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
437*8ffdff6aSGreg Kroah-Hartman 
438*8ffdff6aSGreg Kroah-Hartman 	if (!dev->attached)
439*8ffdff6aSGreg Kroah-Hartman 		return IRQ_NONE;
440*8ffdff6aSGreg Kroah-Hartman 	/* make sure dev->attached is checked before doing anything else */
441*8ffdff6aSGreg Kroah-Hartman 	smp_mb();
442*8ffdff6aSGreg Kroah-Hartman 
443*8ffdff6aSGreg Kroah-Hartman 	/* lock to avoid race with comedi_poll */
444*8ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->interrupt_lock, flags);
445*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < dev->n_subdevices; ++i) {
446*8ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[i];
447*8ffdff6aSGreg Kroah-Hartman 		if (s->type == COMEDI_SUBD_COUNTER)
448*8ffdff6aSGreg Kroah-Hartman 			ni_660x_handle_gpct_interrupt(dev, s);
449*8ffdff6aSGreg Kroah-Hartman 	}
450*8ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
451*8ffdff6aSGreg Kroah-Hartman 	return IRQ_HANDLED;
452*8ffdff6aSGreg Kroah-Hartman }
453*8ffdff6aSGreg Kroah-Hartman 
454*8ffdff6aSGreg Kroah-Hartman static int ni_660x_input_poll(struct comedi_device *dev,
455*8ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s)
456*8ffdff6aSGreg Kroah-Hartman {
457*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
458*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
459*8ffdff6aSGreg Kroah-Hartman 	unsigned long flags;
460*8ffdff6aSGreg Kroah-Hartman 
461*8ffdff6aSGreg Kroah-Hartman 	/* lock to avoid race with comedi_poll */
462*8ffdff6aSGreg Kroah-Hartman 	spin_lock_irqsave(&devpriv->interrupt_lock, flags);
463*8ffdff6aSGreg Kroah-Hartman 	mite_sync_dma(counter->mite_chan, s);
464*8ffdff6aSGreg Kroah-Hartman 	spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
465*8ffdff6aSGreg Kroah-Hartman 	return comedi_buf_read_n_available(s);
466*8ffdff6aSGreg Kroah-Hartman }
467*8ffdff6aSGreg Kroah-Hartman 
468*8ffdff6aSGreg Kroah-Hartman static int ni_660x_buf_change(struct comedi_device *dev,
469*8ffdff6aSGreg Kroah-Hartman 			      struct comedi_subdevice *s)
470*8ffdff6aSGreg Kroah-Hartman {
471*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
472*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct *counter = s->private;
473*8ffdff6aSGreg Kroah-Hartman 	struct mite_ring *ring;
474*8ffdff6aSGreg Kroah-Hartman 	int ret;
475*8ffdff6aSGreg Kroah-Hartman 
476*8ffdff6aSGreg Kroah-Hartman 	ring = devpriv->ring[counter->chip_index][counter->counter_index];
477*8ffdff6aSGreg Kroah-Hartman 	ret = mite_buf_change(ring, s);
478*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
479*8ffdff6aSGreg Kroah-Hartman 		return ret;
480*8ffdff6aSGreg Kroah-Hartman 
481*8ffdff6aSGreg Kroah-Hartman 	return 0;
482*8ffdff6aSGreg Kroah-Hartman }
483*8ffdff6aSGreg Kroah-Hartman 
484*8ffdff6aSGreg Kroah-Hartman static int ni_660x_allocate_private(struct comedi_device *dev)
485*8ffdff6aSGreg Kroah-Hartman {
486*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv;
487*8ffdff6aSGreg Kroah-Hartman 	unsigned int i;
488*8ffdff6aSGreg Kroah-Hartman 
489*8ffdff6aSGreg Kroah-Hartman 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
490*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv)
491*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
492*8ffdff6aSGreg Kroah-Hartman 
493*8ffdff6aSGreg Kroah-Hartman 	spin_lock_init(&devpriv->mite_channel_lock);
494*8ffdff6aSGreg Kroah-Hartman 	spin_lock_init(&devpriv->interrupt_lock);
495*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < NI660X_NUM_PFI_CHANNELS; ++i)
496*8ffdff6aSGreg Kroah-Hartman 		devpriv->io_cfg[i] = NI_660X_PFI_OUTPUT_COUNTER;
497*8ffdff6aSGreg Kroah-Hartman 
498*8ffdff6aSGreg Kroah-Hartman 	return 0;
499*8ffdff6aSGreg Kroah-Hartman }
500*8ffdff6aSGreg Kroah-Hartman 
501*8ffdff6aSGreg Kroah-Hartman static int ni_660x_alloc_mite_rings(struct comedi_device *dev)
502*8ffdff6aSGreg Kroah-Hartman {
503*8ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = dev->board_ptr;
504*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
505*8ffdff6aSGreg Kroah-Hartman 	unsigned int i;
506*8ffdff6aSGreg Kroah-Hartman 	unsigned int j;
507*8ffdff6aSGreg Kroah-Hartman 
508*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < board->n_chips; ++i) {
509*8ffdff6aSGreg Kroah-Hartman 		for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j) {
510*8ffdff6aSGreg Kroah-Hartman 			devpriv->ring[i][j] = mite_alloc_ring(devpriv->mite);
511*8ffdff6aSGreg Kroah-Hartman 			if (!devpriv->ring[i][j])
512*8ffdff6aSGreg Kroah-Hartman 				return -ENOMEM;
513*8ffdff6aSGreg Kroah-Hartman 		}
514*8ffdff6aSGreg Kroah-Hartman 	}
515*8ffdff6aSGreg Kroah-Hartman 	return 0;
516*8ffdff6aSGreg Kroah-Hartman }
517*8ffdff6aSGreg Kroah-Hartman 
518*8ffdff6aSGreg Kroah-Hartman static void ni_660x_free_mite_rings(struct comedi_device *dev)
519*8ffdff6aSGreg Kroah-Hartman {
520*8ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = dev->board_ptr;
521*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
522*8ffdff6aSGreg Kroah-Hartman 	unsigned int i;
523*8ffdff6aSGreg Kroah-Hartman 	unsigned int j;
524*8ffdff6aSGreg Kroah-Hartman 
525*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < board->n_chips; ++i) {
526*8ffdff6aSGreg Kroah-Hartman 		for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j)
527*8ffdff6aSGreg Kroah-Hartman 			mite_free_ring(devpriv->ring[i][j]);
528*8ffdff6aSGreg Kroah-Hartman 	}
529*8ffdff6aSGreg Kroah-Hartman }
530*8ffdff6aSGreg Kroah-Hartman 
531*8ffdff6aSGreg Kroah-Hartman static int ni_660x_dio_insn_bits(struct comedi_device *dev,
532*8ffdff6aSGreg Kroah-Hartman 				 struct comedi_subdevice *s,
533*8ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
534*8ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
535*8ffdff6aSGreg Kroah-Hartman {
536*8ffdff6aSGreg Kroah-Hartman 	unsigned int shift = CR_CHAN(insn->chanspec);
537*8ffdff6aSGreg Kroah-Hartman 	unsigned int mask = data[0] << shift;
538*8ffdff6aSGreg Kroah-Hartman 	unsigned int bits = data[1] << shift;
539*8ffdff6aSGreg Kroah-Hartman 
540*8ffdff6aSGreg Kroah-Hartman 	/*
541*8ffdff6aSGreg Kroah-Hartman 	 * There are 40 channels in this subdevice but only 32 are usable
542*8ffdff6aSGreg Kroah-Hartman 	 * as DIO. The shift adjusts the mask/bits to account for the base
543*8ffdff6aSGreg Kroah-Hartman 	 * channel in insn->chanspec. The state update can then be handled
544*8ffdff6aSGreg Kroah-Hartman 	 * normally for the 32 usable channels.
545*8ffdff6aSGreg Kroah-Hartman 	 */
546*8ffdff6aSGreg Kroah-Hartman 	if (mask) {
547*8ffdff6aSGreg Kroah-Hartman 		s->state &= ~mask;
548*8ffdff6aSGreg Kroah-Hartman 		s->state |= (bits & mask);
549*8ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, 0, s->state, NI660X_DIO32_OUTPUT);
550*8ffdff6aSGreg Kroah-Hartman 	}
551*8ffdff6aSGreg Kroah-Hartman 
552*8ffdff6aSGreg Kroah-Hartman 	/*
553*8ffdff6aSGreg Kroah-Hartman 	 * Return the input channels, shifted back to account for the base
554*8ffdff6aSGreg Kroah-Hartman 	 * channel.
555*8ffdff6aSGreg Kroah-Hartman 	 */
556*8ffdff6aSGreg Kroah-Hartman 	data[1] = ni_660x_read(dev, 0, NI660X_DIO32_INPUT) >> shift;
557*8ffdff6aSGreg Kroah-Hartman 
558*8ffdff6aSGreg Kroah-Hartman 	return insn->n;
559*8ffdff6aSGreg Kroah-Hartman }
560*8ffdff6aSGreg Kroah-Hartman 
561*8ffdff6aSGreg Kroah-Hartman static void ni_660x_select_pfi_output(struct comedi_device *dev,
562*8ffdff6aSGreg Kroah-Hartman 				      unsigned int chan, unsigned int out_sel)
563*8ffdff6aSGreg Kroah-Hartman {
564*8ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = dev->board_ptr;
565*8ffdff6aSGreg Kroah-Hartman 	unsigned int active_chip = 0;
566*8ffdff6aSGreg Kroah-Hartman 	unsigned int idle_chip = 0;
567*8ffdff6aSGreg Kroah-Hartman 	unsigned int bits;
568*8ffdff6aSGreg Kroah-Hartman 
569*8ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
570*8ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
571*8ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
572*8ffdff6aSGreg Kroah-Hartman 
573*8ffdff6aSGreg Kroah-Hartman 	if (board->n_chips > 1) {
574*8ffdff6aSGreg Kroah-Hartman 		if (out_sel == NI_660X_PFI_OUTPUT_COUNTER &&
575*8ffdff6aSGreg Kroah-Hartman 		    chan >= 8 && chan <= 23) {
576*8ffdff6aSGreg Kroah-Hartman 			/* counters 4-7 pfi channels */
577*8ffdff6aSGreg Kroah-Hartman 			active_chip = 1;
578*8ffdff6aSGreg Kroah-Hartman 			idle_chip = 0;
579*8ffdff6aSGreg Kroah-Hartman 		} else {
580*8ffdff6aSGreg Kroah-Hartman 			/* counters 0-3 pfi channels */
581*8ffdff6aSGreg Kroah-Hartman 			active_chip = 0;
582*8ffdff6aSGreg Kroah-Hartman 			idle_chip = 1;
583*8ffdff6aSGreg Kroah-Hartman 		}
584*8ffdff6aSGreg Kroah-Hartman 	}
585*8ffdff6aSGreg Kroah-Hartman 
586*8ffdff6aSGreg Kroah-Hartman 	if (idle_chip != active_chip) {
587*8ffdff6aSGreg Kroah-Hartman 		/* set the pfi channel to high-z on the inactive chip */
588*8ffdff6aSGreg Kroah-Hartman 		bits = ni_660x_read(dev, idle_chip, NI660X_IO_CFG(chan));
589*8ffdff6aSGreg Kroah-Hartman 		bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan);
590*8ffdff6aSGreg Kroah-Hartman 		bits |= NI660X_IO_CFG_OUT_SEL(chan, 0);		/* high-z */
591*8ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, idle_chip, bits, NI660X_IO_CFG(chan));
592*8ffdff6aSGreg Kroah-Hartman 	}
593*8ffdff6aSGreg Kroah-Hartman 
594*8ffdff6aSGreg Kroah-Hartman 	/* set the pfi channel output on the active chip */
595*8ffdff6aSGreg Kroah-Hartman 	bits = ni_660x_read(dev, active_chip, NI660X_IO_CFG(chan));
596*8ffdff6aSGreg Kroah-Hartman 	bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan);
597*8ffdff6aSGreg Kroah-Hartman 	bits |= NI660X_IO_CFG_OUT_SEL(chan, out_sel);
598*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, active_chip, bits, NI660X_IO_CFG(chan));
599*8ffdff6aSGreg Kroah-Hartman }
600*8ffdff6aSGreg Kroah-Hartman 
601*8ffdff6aSGreg Kroah-Hartman static void ni_660x_set_pfi_direction(struct comedi_device *dev,
602*8ffdff6aSGreg Kroah-Hartman 				      unsigned int chan,
603*8ffdff6aSGreg Kroah-Hartman 				      unsigned int direction)
604*8ffdff6aSGreg Kroah-Hartman {
605*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
606*8ffdff6aSGreg Kroah-Hartman 	u64 bit;
607*8ffdff6aSGreg Kroah-Hartman 
608*8ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
609*8ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
610*8ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
611*8ffdff6aSGreg Kroah-Hartman 
612*8ffdff6aSGreg Kroah-Hartman 	bit = 1ULL << chan;
613*8ffdff6aSGreg Kroah-Hartman 
614*8ffdff6aSGreg Kroah-Hartman 	if (direction == COMEDI_OUTPUT) {
615*8ffdff6aSGreg Kroah-Hartman 		devpriv->io_dir |= bit;
616*8ffdff6aSGreg Kroah-Hartman 		/* reset the output to currently assigned output value */
617*8ffdff6aSGreg Kroah-Hartman 		ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]);
618*8ffdff6aSGreg Kroah-Hartman 	} else {
619*8ffdff6aSGreg Kroah-Hartman 		devpriv->io_dir &= ~bit;
620*8ffdff6aSGreg Kroah-Hartman 		/* set pin to high-z; do not change currently assigned route */
621*8ffdff6aSGreg Kroah-Hartman 		ni_660x_select_pfi_output(dev, chan, 0);
622*8ffdff6aSGreg Kroah-Hartman 	}
623*8ffdff6aSGreg Kroah-Hartman }
624*8ffdff6aSGreg Kroah-Hartman 
625*8ffdff6aSGreg Kroah-Hartman static unsigned int ni_660x_get_pfi_direction(struct comedi_device *dev,
626*8ffdff6aSGreg Kroah-Hartman 					      unsigned int chan)
627*8ffdff6aSGreg Kroah-Hartman {
628*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
629*8ffdff6aSGreg Kroah-Hartman 	u64 bit;
630*8ffdff6aSGreg Kroah-Hartman 
631*8ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
632*8ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
633*8ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
634*8ffdff6aSGreg Kroah-Hartman 
635*8ffdff6aSGreg Kroah-Hartman 	bit = 1ULL << chan;
636*8ffdff6aSGreg Kroah-Hartman 
637*8ffdff6aSGreg Kroah-Hartman 	return (devpriv->io_dir & bit) ? COMEDI_OUTPUT : COMEDI_INPUT;
638*8ffdff6aSGreg Kroah-Hartman }
639*8ffdff6aSGreg Kroah-Hartman 
640*8ffdff6aSGreg Kroah-Hartman static int ni_660x_set_pfi_routing(struct comedi_device *dev,
641*8ffdff6aSGreg Kroah-Hartman 				   unsigned int chan, unsigned int source)
642*8ffdff6aSGreg Kroah-Hartman {
643*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
644*8ffdff6aSGreg Kroah-Hartman 
645*8ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
646*8ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
647*8ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
648*8ffdff6aSGreg Kroah-Hartman 
649*8ffdff6aSGreg Kroah-Hartman 	switch (source) {
650*8ffdff6aSGreg Kroah-Hartman 	case NI_660X_PFI_OUTPUT_COUNTER:
651*8ffdff6aSGreg Kroah-Hartman 		if (chan < 8)
652*8ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
653*8ffdff6aSGreg Kroah-Hartman 		break;
654*8ffdff6aSGreg Kroah-Hartman 	case NI_660X_PFI_OUTPUT_DIO:
655*8ffdff6aSGreg Kroah-Hartman 		if (chan > 31)
656*8ffdff6aSGreg Kroah-Hartman 			return -EINVAL;
657*8ffdff6aSGreg Kroah-Hartman 		break;
658*8ffdff6aSGreg Kroah-Hartman 	default:
659*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
660*8ffdff6aSGreg Kroah-Hartman 	}
661*8ffdff6aSGreg Kroah-Hartman 
662*8ffdff6aSGreg Kroah-Hartman 	devpriv->io_cfg[chan] = source;
663*8ffdff6aSGreg Kroah-Hartman 	if (ni_660x_get_pfi_direction(dev, chan) == COMEDI_OUTPUT)
664*8ffdff6aSGreg Kroah-Hartman 		ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]);
665*8ffdff6aSGreg Kroah-Hartman 	return 0;
666*8ffdff6aSGreg Kroah-Hartman }
667*8ffdff6aSGreg Kroah-Hartman 
668*8ffdff6aSGreg Kroah-Hartman static int ni_660x_get_pfi_routing(struct comedi_device *dev, unsigned int chan)
669*8ffdff6aSGreg Kroah-Hartman {
670*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
671*8ffdff6aSGreg Kroah-Hartman 
672*8ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
673*8ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
674*8ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
675*8ffdff6aSGreg Kroah-Hartman 
676*8ffdff6aSGreg Kroah-Hartman 	return devpriv->io_cfg[chan];
677*8ffdff6aSGreg Kroah-Hartman }
678*8ffdff6aSGreg Kroah-Hartman 
679*8ffdff6aSGreg Kroah-Hartman static void ni_660x_set_pfi_filter(struct comedi_device *dev,
680*8ffdff6aSGreg Kroah-Hartman 				   unsigned int chan, unsigned int value)
681*8ffdff6aSGreg Kroah-Hartman {
682*8ffdff6aSGreg Kroah-Hartman 	unsigned int val;
683*8ffdff6aSGreg Kroah-Hartman 
684*8ffdff6aSGreg Kroah-Hartman 	if (chan >= NI_PFI(0))
685*8ffdff6aSGreg Kroah-Hartman 		/* allow new and old names of pfi channels to work. */
686*8ffdff6aSGreg Kroah-Hartman 		chan -= NI_PFI(0);
687*8ffdff6aSGreg Kroah-Hartman 
688*8ffdff6aSGreg Kroah-Hartman 	val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan));
689*8ffdff6aSGreg Kroah-Hartman 	val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan);
690*8ffdff6aSGreg Kroah-Hartman 	val |= NI660X_IO_CFG_IN_SEL(chan, value);
691*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, 0, val, NI660X_IO_CFG(chan));
692*8ffdff6aSGreg Kroah-Hartman }
693*8ffdff6aSGreg Kroah-Hartman 
694*8ffdff6aSGreg Kroah-Hartman static int ni_660x_dio_insn_config(struct comedi_device *dev,
695*8ffdff6aSGreg Kroah-Hartman 				   struct comedi_subdevice *s,
696*8ffdff6aSGreg Kroah-Hartman 				   struct comedi_insn *insn,
697*8ffdff6aSGreg Kroah-Hartman 				   unsigned int *data)
698*8ffdff6aSGreg Kroah-Hartman {
699*8ffdff6aSGreg Kroah-Hartman 	unsigned int chan = CR_CHAN(insn->chanspec);
700*8ffdff6aSGreg Kroah-Hartman 	int ret;
701*8ffdff6aSGreg Kroah-Hartman 
702*8ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
703*8ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_DIO_OUTPUT:
704*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, chan, COMEDI_OUTPUT);
705*8ffdff6aSGreg Kroah-Hartman 		break;
706*8ffdff6aSGreg Kroah-Hartman 
707*8ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_DIO_INPUT:
708*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, chan, COMEDI_INPUT);
709*8ffdff6aSGreg Kroah-Hartman 		break;
710*8ffdff6aSGreg Kroah-Hartman 
711*8ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_DIO_QUERY:
712*8ffdff6aSGreg Kroah-Hartman 		data[1] = ni_660x_get_pfi_direction(dev, chan);
713*8ffdff6aSGreg Kroah-Hartman 		break;
714*8ffdff6aSGreg Kroah-Hartman 
715*8ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_SET_ROUTING:
716*8ffdff6aSGreg Kroah-Hartman 		ret = ni_660x_set_pfi_routing(dev, chan, data[1]);
717*8ffdff6aSGreg Kroah-Hartman 		if (ret)
718*8ffdff6aSGreg Kroah-Hartman 			return ret;
719*8ffdff6aSGreg Kroah-Hartman 		break;
720*8ffdff6aSGreg Kroah-Hartman 
721*8ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_GET_ROUTING:
722*8ffdff6aSGreg Kroah-Hartman 		data[1] = ni_660x_get_pfi_routing(dev, chan);
723*8ffdff6aSGreg Kroah-Hartman 		break;
724*8ffdff6aSGreg Kroah-Hartman 
725*8ffdff6aSGreg Kroah-Hartman 	case INSN_CONFIG_FILTER:
726*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_filter(dev, chan, data[1]);
727*8ffdff6aSGreg Kroah-Hartman 		break;
728*8ffdff6aSGreg Kroah-Hartman 
729*8ffdff6aSGreg Kroah-Hartman 	default:
730*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
731*8ffdff6aSGreg Kroah-Hartman 	}
732*8ffdff6aSGreg Kroah-Hartman 
733*8ffdff6aSGreg Kroah-Hartman 	return insn->n;
734*8ffdff6aSGreg Kroah-Hartman }
735*8ffdff6aSGreg Kroah-Hartman 
736*8ffdff6aSGreg Kroah-Hartman static unsigned int _ni_get_valid_routes(struct comedi_device *dev,
737*8ffdff6aSGreg Kroah-Hartman 					 unsigned int n_pairs,
738*8ffdff6aSGreg Kroah-Hartman 					 unsigned int *pair_data)
739*8ffdff6aSGreg Kroah-Hartman {
740*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
741*8ffdff6aSGreg Kroah-Hartman 
742*8ffdff6aSGreg Kroah-Hartman 	return ni_get_valid_routes(&devpriv->routing_tables, n_pairs,
743*8ffdff6aSGreg Kroah-Hartman 				   pair_data);
744*8ffdff6aSGreg Kroah-Hartman }
745*8ffdff6aSGreg Kroah-Hartman 
746*8ffdff6aSGreg Kroah-Hartman /*
747*8ffdff6aSGreg Kroah-Hartman  * Retrieves the current source of the output selector for the given
748*8ffdff6aSGreg Kroah-Hartman  * destination.  If the terminal for the destination is not already configured
749*8ffdff6aSGreg Kroah-Hartman  * as an output, this function returns -EINVAL as error.
750*8ffdff6aSGreg Kroah-Hartman  *
751*8ffdff6aSGreg Kroah-Hartman  * Return: The register value of the destination output selector;
752*8ffdff6aSGreg Kroah-Hartman  *	   -EINVAL if terminal is not configured for output.
753*8ffdff6aSGreg Kroah-Hartman  */
754*8ffdff6aSGreg Kroah-Hartman static inline int get_output_select_source(int dest, struct comedi_device *dev)
755*8ffdff6aSGreg Kroah-Hartman {
756*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
757*8ffdff6aSGreg Kroah-Hartman 	int reg = -1;
758*8ffdff6aSGreg Kroah-Hartman 
759*8ffdff6aSGreg Kroah-Hartman 	if (channel_is_pfi(dest)) {
760*8ffdff6aSGreg Kroah-Hartman 		if (ni_660x_get_pfi_direction(dev, dest) == COMEDI_OUTPUT)
761*8ffdff6aSGreg Kroah-Hartman 			reg = ni_660x_get_pfi_routing(dev, dest);
762*8ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_rtsi(dest)) {
763*8ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev,
764*8ffdff6aSGreg Kroah-Hartman 			"%s: unhandled rtsi destination (%d) queried\n",
765*8ffdff6aSGreg Kroah-Hartman 			__func__, dest);
766*8ffdff6aSGreg Kroah-Hartman 		/*
767*8ffdff6aSGreg Kroah-Hartman 		 * The following can be enabled when RTSI routing info is
768*8ffdff6aSGreg Kroah-Hartman 		 * determined (not currently documented):
769*8ffdff6aSGreg Kroah-Hartman 		 * if (ni_get_rtsi_direction(dev, dest) == COMEDI_OUTPUT) {
770*8ffdff6aSGreg Kroah-Hartman 		 *	reg = ni_get_rtsi_routing(dev, dest);
771*8ffdff6aSGreg Kroah-Hartman 
772*8ffdff6aSGreg Kroah-Hartman 		 *	if (reg == NI_RTSI_OUTPUT_RGOUT0) {
773*8ffdff6aSGreg Kroah-Hartman 		 *		dest = NI_RGOUT0; ** prepare for lookup below **
774*8ffdff6aSGreg Kroah-Hartman 		 *		reg = get_rgout0_reg(dev);
775*8ffdff6aSGreg Kroah-Hartman 		 *	} else if (reg >= NI_RTSI_OUTPUT_RTSI_BRD(0) &&
776*8ffdff6aSGreg Kroah-Hartman 		 *		   reg <= NI_RTSI_OUTPUT_RTSI_BRD(3)) {
777*8ffdff6aSGreg Kroah-Hartman 		 *		const int i = reg - NI_RTSI_OUTPUT_RTSI_BRD(0);
778*8ffdff6aSGreg Kroah-Hartman 
779*8ffdff6aSGreg Kroah-Hartman 		 *		dest = NI_RTSI_BRD(i); ** prepare for lookup **
780*8ffdff6aSGreg Kroah-Hartman 		 *		reg = get_ith_rtsi_brd_reg(i, dev);
781*8ffdff6aSGreg Kroah-Hartman 		 *	}
782*8ffdff6aSGreg Kroah-Hartman 		 * }
783*8ffdff6aSGreg Kroah-Hartman 		 */
784*8ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_ctr(dest)) {
785*8ffdff6aSGreg Kroah-Hartman 		reg = ni_tio_get_routing(devpriv->counter_dev, dest);
786*8ffdff6aSGreg Kroah-Hartman 	} else {
787*8ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev,
788*8ffdff6aSGreg Kroah-Hartman 			"%s: unhandled destination (%d) queried\n",
789*8ffdff6aSGreg Kroah-Hartman 			__func__, dest);
790*8ffdff6aSGreg Kroah-Hartman 	}
791*8ffdff6aSGreg Kroah-Hartman 
792*8ffdff6aSGreg Kroah-Hartman 	if (reg >= 0)
793*8ffdff6aSGreg Kroah-Hartman 		return ni_find_route_source(CR_CHAN(reg), dest,
794*8ffdff6aSGreg Kroah-Hartman 					    &devpriv->routing_tables);
795*8ffdff6aSGreg Kroah-Hartman 	return -EINVAL;
796*8ffdff6aSGreg Kroah-Hartman }
797*8ffdff6aSGreg Kroah-Hartman 
798*8ffdff6aSGreg Kroah-Hartman /*
799*8ffdff6aSGreg Kroah-Hartman  * Test a route:
800*8ffdff6aSGreg Kroah-Hartman  *
801*8ffdff6aSGreg Kroah-Hartman  * Return: -1 if not connectible;
802*8ffdff6aSGreg Kroah-Hartman  *	    0 if connectible and not connected;
803*8ffdff6aSGreg Kroah-Hartman  *	    1 if connectible and connected.
804*8ffdff6aSGreg Kroah-Hartman  */
805*8ffdff6aSGreg Kroah-Hartman static inline int test_route(unsigned int src, unsigned int dest,
806*8ffdff6aSGreg Kroah-Hartman 			     struct comedi_device *dev)
807*8ffdff6aSGreg Kroah-Hartman {
808*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
809*8ffdff6aSGreg Kroah-Hartman 	s8 reg = ni_route_to_register(CR_CHAN(src), dest,
810*8ffdff6aSGreg Kroah-Hartman 				      &devpriv->routing_tables);
811*8ffdff6aSGreg Kroah-Hartman 
812*8ffdff6aSGreg Kroah-Hartman 	if (reg < 0)
813*8ffdff6aSGreg Kroah-Hartman 		return -1;
814*8ffdff6aSGreg Kroah-Hartman 	if (get_output_select_source(dest, dev) != CR_CHAN(src))
815*8ffdff6aSGreg Kroah-Hartman 		return 0;
816*8ffdff6aSGreg Kroah-Hartman 	return 1;
817*8ffdff6aSGreg Kroah-Hartman }
818*8ffdff6aSGreg Kroah-Hartman 
819*8ffdff6aSGreg Kroah-Hartman /* Connect the actual route.  */
820*8ffdff6aSGreg Kroah-Hartman static inline int connect_route(unsigned int src, unsigned int dest,
821*8ffdff6aSGreg Kroah-Hartman 				struct comedi_device *dev)
822*8ffdff6aSGreg Kroah-Hartman {
823*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
824*8ffdff6aSGreg Kroah-Hartman 	s8 reg = ni_route_to_register(CR_CHAN(src), dest,
825*8ffdff6aSGreg Kroah-Hartman 				      &devpriv->routing_tables);
826*8ffdff6aSGreg Kroah-Hartman 	s8 current_src;
827*8ffdff6aSGreg Kroah-Hartman 
828*8ffdff6aSGreg Kroah-Hartman 	if (reg < 0)
829*8ffdff6aSGreg Kroah-Hartman 		/* route is not valid */
830*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
831*8ffdff6aSGreg Kroah-Hartman 
832*8ffdff6aSGreg Kroah-Hartman 	current_src = get_output_select_source(dest, dev);
833*8ffdff6aSGreg Kroah-Hartman 	if (current_src == CR_CHAN(src))
834*8ffdff6aSGreg Kroah-Hartman 		return -EALREADY;
835*8ffdff6aSGreg Kroah-Hartman 	if (current_src >= 0)
836*8ffdff6aSGreg Kroah-Hartman 		/* destination mux is already busy. complain, don't overwrite */
837*8ffdff6aSGreg Kroah-Hartman 		return -EBUSY;
838*8ffdff6aSGreg Kroah-Hartman 
839*8ffdff6aSGreg Kroah-Hartman 	/* The route is valid and available. Now connect... */
840*8ffdff6aSGreg Kroah-Hartman 	if (channel_is_pfi(CR_CHAN(dest))) {
841*8ffdff6aSGreg Kroah-Hartman 		/*
842*8ffdff6aSGreg Kroah-Hartman 		 * set routing and then direction so that the output does not
843*8ffdff6aSGreg Kroah-Hartman 		 * first get generated with the wrong pin
844*8ffdff6aSGreg Kroah-Hartman 		 */
845*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_routing(dev, dest, reg);
846*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, dest, COMEDI_OUTPUT);
847*8ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_rtsi(CR_CHAN(dest))) {
848*8ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
849*8ffdff6aSGreg Kroah-Hartman 			__func__, dest);
850*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
851*8ffdff6aSGreg Kroah-Hartman 		/*
852*8ffdff6aSGreg Kroah-Hartman 		 * The following can be enabled when RTSI routing info is
853*8ffdff6aSGreg Kroah-Hartman 		 * determined (not currently documented):
854*8ffdff6aSGreg Kroah-Hartman 		 * if (reg == NI_RTSI_OUTPUT_RGOUT0) {
855*8ffdff6aSGreg Kroah-Hartman 		 *	int ret = incr_rgout0_src_use(src, dev);
856*8ffdff6aSGreg Kroah-Hartman 
857*8ffdff6aSGreg Kroah-Hartman 		 *	if (ret < 0)
858*8ffdff6aSGreg Kroah-Hartman 		 *		return ret;
859*8ffdff6aSGreg Kroah-Hartman 		 * } else if (ni_rtsi_route_requires_mux(reg)) {
860*8ffdff6aSGreg Kroah-Hartman 		 *	** Attempt to allocate and  route (src->brd) **
861*8ffdff6aSGreg Kroah-Hartman 		 *	int brd = incr_rtsi_brd_src_use(src, dev);
862*8ffdff6aSGreg Kroah-Hartman 
863*8ffdff6aSGreg Kroah-Hartman 		 *	if (brd < 0)
864*8ffdff6aSGreg Kroah-Hartman 		 *		return brd;
865*8ffdff6aSGreg Kroah-Hartman 
866*8ffdff6aSGreg Kroah-Hartman 		 *	** Now lookup the register value for (brd->dest) **
867*8ffdff6aSGreg Kroah-Hartman 		 *	reg = ni_lookup_route_register(brd, CR_CHAN(dest),
868*8ffdff6aSGreg Kroah-Hartman 		 *				       &devpriv->routing_tables);
869*8ffdff6aSGreg Kroah-Hartman 		 * }
870*8ffdff6aSGreg Kroah-Hartman 
871*8ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_direction(dev, dest, COMEDI_OUTPUT);
872*8ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_routing(dev, dest, reg);
873*8ffdff6aSGreg Kroah-Hartman 		 */
874*8ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_ctr(CR_CHAN(dest))) {
875*8ffdff6aSGreg Kroah-Hartman 		/*
876*8ffdff6aSGreg Kroah-Hartman 		 * we are adding back the channel modifier info to set
877*8ffdff6aSGreg Kroah-Hartman 		 * invert/edge info passed by the user
878*8ffdff6aSGreg Kroah-Hartman 		 */
879*8ffdff6aSGreg Kroah-Hartman 		ni_tio_set_routing(devpriv->counter_dev, dest,
880*8ffdff6aSGreg Kroah-Hartman 				   reg | (src & ~CR_CHAN(-1)));
881*8ffdff6aSGreg Kroah-Hartman 	} else {
882*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
883*8ffdff6aSGreg Kroah-Hartman 	}
884*8ffdff6aSGreg Kroah-Hartman 	return 0;
885*8ffdff6aSGreg Kroah-Hartman }
886*8ffdff6aSGreg Kroah-Hartman 
887*8ffdff6aSGreg Kroah-Hartman static inline int disconnect_route(unsigned int src, unsigned int dest,
888*8ffdff6aSGreg Kroah-Hartman 				   struct comedi_device *dev)
889*8ffdff6aSGreg Kroah-Hartman {
890*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
891*8ffdff6aSGreg Kroah-Hartman 	s8 reg = ni_route_to_register(CR_CHAN(src), CR_CHAN(dest),
892*8ffdff6aSGreg Kroah-Hartman 				      &devpriv->routing_tables);
893*8ffdff6aSGreg Kroah-Hartman 
894*8ffdff6aSGreg Kroah-Hartman 	if (reg < 0)
895*8ffdff6aSGreg Kroah-Hartman 		/* route is not valid */
896*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
897*8ffdff6aSGreg Kroah-Hartman 	if (get_output_select_source(dest, dev) != CR_CHAN(src))
898*8ffdff6aSGreg Kroah-Hartman 		/* cannot disconnect something not connected */
899*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
900*8ffdff6aSGreg Kroah-Hartman 
901*8ffdff6aSGreg Kroah-Hartman 	/* The route is valid and is connected.  Now disconnect... */
902*8ffdff6aSGreg Kroah-Hartman 	if (channel_is_pfi(CR_CHAN(dest))) {
903*8ffdff6aSGreg Kroah-Hartman 		unsigned int source = ((CR_CHAN(dest) - NI_PFI(0)) < 8)
904*8ffdff6aSGreg Kroah-Hartman 					? NI_660X_PFI_OUTPUT_DIO
905*8ffdff6aSGreg Kroah-Hartman 					: NI_660X_PFI_OUTPUT_COUNTER;
906*8ffdff6aSGreg Kroah-Hartman 
907*8ffdff6aSGreg Kroah-Hartman 		/* set the pfi to high impedance, and disconnect */
908*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, dest, COMEDI_INPUT);
909*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_routing(dev, dest, source);
910*8ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_rtsi(CR_CHAN(dest))) {
911*8ffdff6aSGreg Kroah-Hartman 		dev_dbg(dev->class_dev, "%s: unhandled rtsi destination (%d)\n",
912*8ffdff6aSGreg Kroah-Hartman 			__func__, dest);
913*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
914*8ffdff6aSGreg Kroah-Hartman 		/*
915*8ffdff6aSGreg Kroah-Hartman 		 * The following can be enabled when RTSI routing info is
916*8ffdff6aSGreg Kroah-Hartman 		 * determined (not currently documented):
917*8ffdff6aSGreg Kroah-Hartman 		 * if (reg == NI_RTSI_OUTPUT_RGOUT0) {
918*8ffdff6aSGreg Kroah-Hartman 		 *	int ret = decr_rgout0_src_use(src, dev);
919*8ffdff6aSGreg Kroah-Hartman 
920*8ffdff6aSGreg Kroah-Hartman 		 *	if (ret < 0)
921*8ffdff6aSGreg Kroah-Hartman 		 *		return ret;
922*8ffdff6aSGreg Kroah-Hartman 		 * } else if (ni_rtsi_route_requires_mux(reg)) {
923*8ffdff6aSGreg Kroah-Hartman 		 *	** find which RTSI_BRD line is source for rtsi pin **
924*8ffdff6aSGreg Kroah-Hartman 		 *	int brd = ni_find_route_source(
925*8ffdff6aSGreg Kroah-Hartman 		 *		ni_get_rtsi_routing(dev, dest), CR_CHAN(dest),
926*8ffdff6aSGreg Kroah-Hartman 		 *		&devpriv->routing_tables);
927*8ffdff6aSGreg Kroah-Hartman 
928*8ffdff6aSGreg Kroah-Hartman 		 *	if (brd < 0)
929*8ffdff6aSGreg Kroah-Hartman 		 *		return brd;
930*8ffdff6aSGreg Kroah-Hartman 
931*8ffdff6aSGreg Kroah-Hartman 		 *	** decrement/disconnect RTSI_BRD line from source **
932*8ffdff6aSGreg Kroah-Hartman 		 *	decr_rtsi_brd_src_use(src, brd, dev);
933*8ffdff6aSGreg Kroah-Hartman 		 * }
934*8ffdff6aSGreg Kroah-Hartman 
935*8ffdff6aSGreg Kroah-Hartman 		 * ** set rtsi output selector to default state **
936*8ffdff6aSGreg Kroah-Hartman 		 * reg = default_rtsi_routing[CR_CHAN(dest) - TRIGGER_LINE(0)];
937*8ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_direction(dev, dest, COMEDI_INPUT);
938*8ffdff6aSGreg Kroah-Hartman 		 * ni_set_rtsi_routing(dev, dest, reg);
939*8ffdff6aSGreg Kroah-Hartman 		 */
940*8ffdff6aSGreg Kroah-Hartman 	} else if (channel_is_ctr(CR_CHAN(dest))) {
941*8ffdff6aSGreg Kroah-Hartman 		ni_tio_unset_routing(devpriv->counter_dev, dest);
942*8ffdff6aSGreg Kroah-Hartman 	} else {
943*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
944*8ffdff6aSGreg Kroah-Hartman 	}
945*8ffdff6aSGreg Kroah-Hartman 	return 0;
946*8ffdff6aSGreg Kroah-Hartman }
947*8ffdff6aSGreg Kroah-Hartman 
948*8ffdff6aSGreg Kroah-Hartman static int ni_global_insn_config(struct comedi_device *dev,
949*8ffdff6aSGreg Kroah-Hartman 				 struct comedi_insn *insn,
950*8ffdff6aSGreg Kroah-Hartman 				 unsigned int *data)
951*8ffdff6aSGreg Kroah-Hartman {
952*8ffdff6aSGreg Kroah-Hartman 	switch (data[0]) {
953*8ffdff6aSGreg Kroah-Hartman 	case INSN_DEVICE_CONFIG_TEST_ROUTE:
954*8ffdff6aSGreg Kroah-Hartman 		data[0] = test_route(data[1], data[2], dev);
955*8ffdff6aSGreg Kroah-Hartman 		return 2;
956*8ffdff6aSGreg Kroah-Hartman 	case INSN_DEVICE_CONFIG_CONNECT_ROUTE:
957*8ffdff6aSGreg Kroah-Hartman 		return connect_route(data[1], data[2], dev);
958*8ffdff6aSGreg Kroah-Hartman 	case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:
959*8ffdff6aSGreg Kroah-Hartman 		return disconnect_route(data[1], data[2], dev);
960*8ffdff6aSGreg Kroah-Hartman 	/*
961*8ffdff6aSGreg Kroah-Hartman 	 * This case is already handled one level up.
962*8ffdff6aSGreg Kroah-Hartman 	 * case INSN_DEVICE_CONFIG_GET_ROUTES:
963*8ffdff6aSGreg Kroah-Hartman 	 */
964*8ffdff6aSGreg Kroah-Hartman 	default:
965*8ffdff6aSGreg Kroah-Hartman 		return -EINVAL;
966*8ffdff6aSGreg Kroah-Hartman 	}
967*8ffdff6aSGreg Kroah-Hartman 	return 1;
968*8ffdff6aSGreg Kroah-Hartman }
969*8ffdff6aSGreg Kroah-Hartman 
970*8ffdff6aSGreg Kroah-Hartman static void ni_660x_init_tio_chips(struct comedi_device *dev,
971*8ffdff6aSGreg Kroah-Hartman 				   unsigned int n_chips)
972*8ffdff6aSGreg Kroah-Hartman {
973*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
974*8ffdff6aSGreg Kroah-Hartman 	unsigned int chip;
975*8ffdff6aSGreg Kroah-Hartman 	unsigned int chan;
976*8ffdff6aSGreg Kroah-Hartman 
977*8ffdff6aSGreg Kroah-Hartman 	/*
978*8ffdff6aSGreg Kroah-Hartman 	 * We use the ioconfig registers to control dio direction, so zero
979*8ffdff6aSGreg Kroah-Hartman 	 * output enables in stc dio control reg.
980*8ffdff6aSGreg Kroah-Hartman 	 */
981*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, 0, 0, NI660X_STC_DIO_CONTROL);
982*8ffdff6aSGreg Kroah-Hartman 
983*8ffdff6aSGreg Kroah-Hartman 	for (chip = 0; chip < n_chips; ++chip) {
984*8ffdff6aSGreg Kroah-Hartman 		/* init dma configuration register */
985*8ffdff6aSGreg Kroah-Hartman 		devpriv->dma_cfg[chip] = 0;
986*8ffdff6aSGreg Kroah-Hartman 		for (chan = 0; chan < NI660X_MAX_DMA_CHANNEL; ++chan)
987*8ffdff6aSGreg Kroah-Hartman 			devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(chan);
988*8ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, chip, devpriv->dma_cfg[chip],
989*8ffdff6aSGreg Kroah-Hartman 			      NI660X_DMA_CFG);
990*8ffdff6aSGreg Kroah-Hartman 
991*8ffdff6aSGreg Kroah-Hartman 		/* init ioconfig registers */
992*8ffdff6aSGreg Kroah-Hartman 		for (chan = 0; chan < NI660X_NUM_PFI_CHANNELS; ++chan)
993*8ffdff6aSGreg Kroah-Hartman 			ni_660x_write(dev, chip, 0, NI660X_IO_CFG(chan));
994*8ffdff6aSGreg Kroah-Hartman 	}
995*8ffdff6aSGreg Kroah-Hartman }
996*8ffdff6aSGreg Kroah-Hartman 
997*8ffdff6aSGreg Kroah-Hartman static int ni_660x_auto_attach(struct comedi_device *dev,
998*8ffdff6aSGreg Kroah-Hartman 			       unsigned long context)
999*8ffdff6aSGreg Kroah-Hartman {
1000*8ffdff6aSGreg Kroah-Hartman 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1001*8ffdff6aSGreg Kroah-Hartman 	const struct ni_660x_board *board = NULL;
1002*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv;
1003*8ffdff6aSGreg Kroah-Hartman 	struct comedi_subdevice *s;
1004*8ffdff6aSGreg Kroah-Hartman 	struct ni_gpct_device *gpct_dev;
1005*8ffdff6aSGreg Kroah-Hartman 	unsigned int n_counters;
1006*8ffdff6aSGreg Kroah-Hartman 	int subdev;
1007*8ffdff6aSGreg Kroah-Hartman 	int ret;
1008*8ffdff6aSGreg Kroah-Hartman 	unsigned int i;
1009*8ffdff6aSGreg Kroah-Hartman 	unsigned int global_interrupt_config_bits;
1010*8ffdff6aSGreg Kroah-Hartman 
1011*8ffdff6aSGreg Kroah-Hartman 	if (context < ARRAY_SIZE(ni_660x_boards))
1012*8ffdff6aSGreg Kroah-Hartman 		board = &ni_660x_boards[context];
1013*8ffdff6aSGreg Kroah-Hartman 	if (!board)
1014*8ffdff6aSGreg Kroah-Hartman 		return -ENODEV;
1015*8ffdff6aSGreg Kroah-Hartman 	dev->board_ptr = board;
1016*8ffdff6aSGreg Kroah-Hartman 	dev->board_name = board->name;
1017*8ffdff6aSGreg Kroah-Hartman 
1018*8ffdff6aSGreg Kroah-Hartman 	ret = comedi_pci_enable(dev);
1019*8ffdff6aSGreg Kroah-Hartman 	if (ret)
1020*8ffdff6aSGreg Kroah-Hartman 		return ret;
1021*8ffdff6aSGreg Kroah-Hartman 
1022*8ffdff6aSGreg Kroah-Hartman 	ret = ni_660x_allocate_private(dev);
1023*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
1024*8ffdff6aSGreg Kroah-Hartman 		return ret;
1025*8ffdff6aSGreg Kroah-Hartman 	devpriv = dev->private;
1026*8ffdff6aSGreg Kroah-Hartman 
1027*8ffdff6aSGreg Kroah-Hartman 	devpriv->mite = mite_attach(dev, true);		/* use win1 */
1028*8ffdff6aSGreg Kroah-Hartman 	if (!devpriv->mite)
1029*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
1030*8ffdff6aSGreg Kroah-Hartman 
1031*8ffdff6aSGreg Kroah-Hartman 	ret = ni_660x_alloc_mite_rings(dev);
1032*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0)
1033*8ffdff6aSGreg Kroah-Hartman 		return ret;
1034*8ffdff6aSGreg Kroah-Hartman 
1035*8ffdff6aSGreg Kroah-Hartman 	ni_660x_init_tio_chips(dev, board->n_chips);
1036*8ffdff6aSGreg Kroah-Hartman 
1037*8ffdff6aSGreg Kroah-Hartman 	/* prepare the device for globally-named routes. */
1038*8ffdff6aSGreg Kroah-Hartman 	if (ni_assign_device_routes("ni_660x", board->name, NULL,
1039*8ffdff6aSGreg Kroah-Hartman 				    &devpriv->routing_tables) < 0) {
1040*8ffdff6aSGreg Kroah-Hartman 		dev_warn(dev->class_dev, "%s: %s device has no signal routing table.\n",
1041*8ffdff6aSGreg Kroah-Hartman 			 __func__, board->name);
1042*8ffdff6aSGreg Kroah-Hartman 		dev_warn(dev->class_dev, "%s: High level NI signal names will not be available for this %s board.\n",
1043*8ffdff6aSGreg Kroah-Hartman 			 __func__, board->name);
1044*8ffdff6aSGreg Kroah-Hartman 	} else {
1045*8ffdff6aSGreg Kroah-Hartman 		/*
1046*8ffdff6aSGreg Kroah-Hartman 		 * only(?) assign insn_device_config if we have global names for
1047*8ffdff6aSGreg Kroah-Hartman 		 * this device.
1048*8ffdff6aSGreg Kroah-Hartman 		 */
1049*8ffdff6aSGreg Kroah-Hartman 		dev->insn_device_config = ni_global_insn_config;
1050*8ffdff6aSGreg Kroah-Hartman 		dev->get_valid_routes = _ni_get_valid_routes;
1051*8ffdff6aSGreg Kroah-Hartman 	}
1052*8ffdff6aSGreg Kroah-Hartman 
1053*8ffdff6aSGreg Kroah-Hartman 	n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP;
1054*8ffdff6aSGreg Kroah-Hartman 	gpct_dev = ni_gpct_device_construct(dev,
1055*8ffdff6aSGreg Kroah-Hartman 					    ni_660x_gpct_write,
1056*8ffdff6aSGreg Kroah-Hartman 					    ni_660x_gpct_read,
1057*8ffdff6aSGreg Kroah-Hartman 					    ni_gpct_variant_660x,
1058*8ffdff6aSGreg Kroah-Hartman 					    n_counters,
1059*8ffdff6aSGreg Kroah-Hartman 					    NI660X_COUNTERS_PER_CHIP,
1060*8ffdff6aSGreg Kroah-Hartman 					    &devpriv->routing_tables);
1061*8ffdff6aSGreg Kroah-Hartman 	if (!gpct_dev)
1062*8ffdff6aSGreg Kroah-Hartman 		return -ENOMEM;
1063*8ffdff6aSGreg Kroah-Hartman 	devpriv->counter_dev = gpct_dev;
1064*8ffdff6aSGreg Kroah-Hartman 
1065*8ffdff6aSGreg Kroah-Hartman 	ret = comedi_alloc_subdevices(dev, 2 + NI660X_MAX_COUNTERS);
1066*8ffdff6aSGreg Kroah-Hartman 	if (ret)
1067*8ffdff6aSGreg Kroah-Hartman 		return ret;
1068*8ffdff6aSGreg Kroah-Hartman 
1069*8ffdff6aSGreg Kroah-Hartman 	subdev = 0;
1070*8ffdff6aSGreg Kroah-Hartman 
1071*8ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[subdev++];
1072*8ffdff6aSGreg Kroah-Hartman 	/* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
1073*8ffdff6aSGreg Kroah-Hartman 	s->type = COMEDI_SUBD_UNUSED;
1074*8ffdff6aSGreg Kroah-Hartman 
1075*8ffdff6aSGreg Kroah-Hartman 	/*
1076*8ffdff6aSGreg Kroah-Hartman 	 * Digital I/O subdevice
1077*8ffdff6aSGreg Kroah-Hartman 	 *
1078*8ffdff6aSGreg Kroah-Hartman 	 * There are 40 channels but only the first 32 can be digital I/Os.
1079*8ffdff6aSGreg Kroah-Hartman 	 * The last 8 are dedicated to counters 0 and 1.
1080*8ffdff6aSGreg Kroah-Hartman 	 *
1081*8ffdff6aSGreg Kroah-Hartman 	 * Counter 0-3 signals are from the first TIO chip.
1082*8ffdff6aSGreg Kroah-Hartman 	 * Counter 4-7 signals are from the second TIO chip.
1083*8ffdff6aSGreg Kroah-Hartman 	 *
1084*8ffdff6aSGreg Kroah-Hartman 	 * Comedi	External
1085*8ffdff6aSGreg Kroah-Hartman 	 * PFI Chan	DIO Chan        Counter Signal
1086*8ffdff6aSGreg Kroah-Hartman 	 * -------	--------	--------------
1087*8ffdff6aSGreg Kroah-Hartman 	 *     0	    0
1088*8ffdff6aSGreg Kroah-Hartman 	 *     1	    1
1089*8ffdff6aSGreg Kroah-Hartman 	 *     2	    2
1090*8ffdff6aSGreg Kroah-Hartman 	 *     3	    3
1091*8ffdff6aSGreg Kroah-Hartman 	 *     4	    4
1092*8ffdff6aSGreg Kroah-Hartman 	 *     5	    5
1093*8ffdff6aSGreg Kroah-Hartman 	 *     6	    6
1094*8ffdff6aSGreg Kroah-Hartman 	 *     7	    7
1095*8ffdff6aSGreg Kroah-Hartman 	 *     8	    8		CTR 7 OUT
1096*8ffdff6aSGreg Kroah-Hartman 	 *     9	    9		CTR 7 AUX
1097*8ffdff6aSGreg Kroah-Hartman 	 *    10	   10		CTR 7 GATE
1098*8ffdff6aSGreg Kroah-Hartman 	 *    11	   11		CTR 7 SOURCE
1099*8ffdff6aSGreg Kroah-Hartman 	 *    12	   12		CTR 6 OUT
1100*8ffdff6aSGreg Kroah-Hartman 	 *    13	   13		CTR 6 AUX
1101*8ffdff6aSGreg Kroah-Hartman 	 *    14	   14		CTR 6 GATE
1102*8ffdff6aSGreg Kroah-Hartman 	 *    15	   15		CTR 6 SOURCE
1103*8ffdff6aSGreg Kroah-Hartman 	 *    16	   16		CTR 5 OUT
1104*8ffdff6aSGreg Kroah-Hartman 	 *    17	   17		CTR 5 AUX
1105*8ffdff6aSGreg Kroah-Hartman 	 *    18	   18		CTR 5 GATE
1106*8ffdff6aSGreg Kroah-Hartman 	 *    19	   19		CTR 5 SOURCE
1107*8ffdff6aSGreg Kroah-Hartman 	 *    20	   20		CTR 4 OUT
1108*8ffdff6aSGreg Kroah-Hartman 	 *    21	   21		CTR 4 AUX
1109*8ffdff6aSGreg Kroah-Hartman 	 *    22	   22		CTR 4 GATE
1110*8ffdff6aSGreg Kroah-Hartman 	 *    23	   23		CTR 4 SOURCE
1111*8ffdff6aSGreg Kroah-Hartman 	 *    24	   24		CTR 3 OUT
1112*8ffdff6aSGreg Kroah-Hartman 	 *    25	   25		CTR 3 AUX
1113*8ffdff6aSGreg Kroah-Hartman 	 *    26	   26		CTR 3 GATE
1114*8ffdff6aSGreg Kroah-Hartman 	 *    27	   27		CTR 3 SOURCE
1115*8ffdff6aSGreg Kroah-Hartman 	 *    28	   28		CTR 2 OUT
1116*8ffdff6aSGreg Kroah-Hartman 	 *    29	   29		CTR 2 AUX
1117*8ffdff6aSGreg Kroah-Hartman 	 *    30	   30		CTR 2 GATE
1118*8ffdff6aSGreg Kroah-Hartman 	 *    31	   31		CTR 2 SOURCE
1119*8ffdff6aSGreg Kroah-Hartman 	 *    32			CTR 1 OUT
1120*8ffdff6aSGreg Kroah-Hartman 	 *    33			CTR 1 AUX
1121*8ffdff6aSGreg Kroah-Hartman 	 *    34			CTR 1 GATE
1122*8ffdff6aSGreg Kroah-Hartman 	 *    35			CTR 1 SOURCE
1123*8ffdff6aSGreg Kroah-Hartman 	 *    36			CTR 0 OUT
1124*8ffdff6aSGreg Kroah-Hartman 	 *    37			CTR 0 AUX
1125*8ffdff6aSGreg Kroah-Hartman 	 *    38			CTR 0 GATE
1126*8ffdff6aSGreg Kroah-Hartman 	 *    39			CTR 0 SOURCE
1127*8ffdff6aSGreg Kroah-Hartman 	 */
1128*8ffdff6aSGreg Kroah-Hartman 	s = &dev->subdevices[subdev++];
1129*8ffdff6aSGreg Kroah-Hartman 	s->type		= COMEDI_SUBD_DIO;
1130*8ffdff6aSGreg Kroah-Hartman 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
1131*8ffdff6aSGreg Kroah-Hartman 	s->n_chan	= NI660X_NUM_PFI_CHANNELS;
1132*8ffdff6aSGreg Kroah-Hartman 	s->maxdata	= 1;
1133*8ffdff6aSGreg Kroah-Hartman 	s->range_table	= &range_digital;
1134*8ffdff6aSGreg Kroah-Hartman 	s->insn_bits	= ni_660x_dio_insn_bits;
1135*8ffdff6aSGreg Kroah-Hartman 	s->insn_config	= ni_660x_dio_insn_config;
1136*8ffdff6aSGreg Kroah-Hartman 
1137*8ffdff6aSGreg Kroah-Hartman 	 /*
1138*8ffdff6aSGreg Kroah-Hartman 	  * Default the DIO channels as:
1139*8ffdff6aSGreg Kroah-Hartman 	  *   chan 0-7:  DIO inputs
1140*8ffdff6aSGreg Kroah-Hartman 	  *   chan 8-39: counter signal inputs
1141*8ffdff6aSGreg Kroah-Hartman 	  */
1142*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < s->n_chan; ++i) {
1143*8ffdff6aSGreg Kroah-Hartman 		unsigned int source = (i < 8) ? NI_660X_PFI_OUTPUT_DIO
1144*8ffdff6aSGreg Kroah-Hartman 					      : NI_660X_PFI_OUTPUT_COUNTER;
1145*8ffdff6aSGreg Kroah-Hartman 
1146*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_routing(dev, i, source);
1147*8ffdff6aSGreg Kroah-Hartman 		ni_660x_set_pfi_direction(dev, i, COMEDI_INPUT);/* high-z */
1148*8ffdff6aSGreg Kroah-Hartman 	}
1149*8ffdff6aSGreg Kroah-Hartman 
1150*8ffdff6aSGreg Kroah-Hartman 	/* Counter subdevices (4 NI TIO General Purpose Counters per chip) */
1151*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < NI660X_MAX_COUNTERS; ++i) {
1152*8ffdff6aSGreg Kroah-Hartman 		s = &dev->subdevices[subdev++];
1153*8ffdff6aSGreg Kroah-Hartman 		if (i < n_counters) {
1154*8ffdff6aSGreg Kroah-Hartman 			struct ni_gpct *counter = &gpct_dev->counters[i];
1155*8ffdff6aSGreg Kroah-Hartman 
1156*8ffdff6aSGreg Kroah-Hartman 			s->type		= COMEDI_SUBD_COUNTER;
1157*8ffdff6aSGreg Kroah-Hartman 			s->subdev_flags	= SDF_READABLE | SDF_WRITABLE |
1158*8ffdff6aSGreg Kroah-Hartman 					  SDF_LSAMPL | SDF_CMD_READ;
1159*8ffdff6aSGreg Kroah-Hartman 			s->n_chan	= 3;
1160*8ffdff6aSGreg Kroah-Hartman 			s->maxdata	= 0xffffffff;
1161*8ffdff6aSGreg Kroah-Hartman 			s->insn_read	= ni_tio_insn_read;
1162*8ffdff6aSGreg Kroah-Hartman 			s->insn_write	= ni_tio_insn_write;
1163*8ffdff6aSGreg Kroah-Hartman 			s->insn_config	= ni_tio_insn_config;
1164*8ffdff6aSGreg Kroah-Hartman 			s->len_chanlist	= 1;
1165*8ffdff6aSGreg Kroah-Hartman 			s->do_cmd	= ni_660x_cmd;
1166*8ffdff6aSGreg Kroah-Hartman 			s->do_cmdtest	= ni_tio_cmdtest;
1167*8ffdff6aSGreg Kroah-Hartman 			s->cancel	= ni_660x_cancel;
1168*8ffdff6aSGreg Kroah-Hartman 			s->poll		= ni_660x_input_poll;
1169*8ffdff6aSGreg Kroah-Hartman 			s->buf_change	= ni_660x_buf_change;
1170*8ffdff6aSGreg Kroah-Hartman 			s->async_dma_dir = DMA_BIDIRECTIONAL;
1171*8ffdff6aSGreg Kroah-Hartman 			s->private	= counter;
1172*8ffdff6aSGreg Kroah-Hartman 
1173*8ffdff6aSGreg Kroah-Hartman 			ni_tio_init_counter(counter);
1174*8ffdff6aSGreg Kroah-Hartman 		} else {
1175*8ffdff6aSGreg Kroah-Hartman 			s->type		= COMEDI_SUBD_UNUSED;
1176*8ffdff6aSGreg Kroah-Hartman 		}
1177*8ffdff6aSGreg Kroah-Hartman 	}
1178*8ffdff6aSGreg Kroah-Hartman 
1179*8ffdff6aSGreg Kroah-Hartman 	/*
1180*8ffdff6aSGreg Kroah-Hartman 	 * To be safe, set counterswap bits on tio chips after all the counter
1181*8ffdff6aSGreg Kroah-Hartman 	 * outputs have been set to high impedance mode.
1182*8ffdff6aSGreg Kroah-Hartman 	 */
1183*8ffdff6aSGreg Kroah-Hartman 	for (i = 0; i < board->n_chips; ++i)
1184*8ffdff6aSGreg Kroah-Hartman 		set_tio_counterswap(dev, i);
1185*8ffdff6aSGreg Kroah-Hartman 
1186*8ffdff6aSGreg Kroah-Hartman 	ret = request_irq(pcidev->irq, ni_660x_interrupt, IRQF_SHARED,
1187*8ffdff6aSGreg Kroah-Hartman 			  dev->board_name, dev);
1188*8ffdff6aSGreg Kroah-Hartman 	if (ret < 0) {
1189*8ffdff6aSGreg Kroah-Hartman 		dev_warn(dev->class_dev, " irq not available\n");
1190*8ffdff6aSGreg Kroah-Hartman 		return ret;
1191*8ffdff6aSGreg Kroah-Hartman 	}
1192*8ffdff6aSGreg Kroah-Hartman 	dev->irq = pcidev->irq;
1193*8ffdff6aSGreg Kroah-Hartman 	global_interrupt_config_bits = NI660X_GLOBAL_INT_GLOBAL;
1194*8ffdff6aSGreg Kroah-Hartman 	if (board->n_chips > 1)
1195*8ffdff6aSGreg Kroah-Hartman 		global_interrupt_config_bits |= NI660X_GLOBAL_INT_CASCADE;
1196*8ffdff6aSGreg Kroah-Hartman 	ni_660x_write(dev, 0, global_interrupt_config_bits,
1197*8ffdff6aSGreg Kroah-Hartman 		      NI660X_GLOBAL_INT_CFG);
1198*8ffdff6aSGreg Kroah-Hartman 
1199*8ffdff6aSGreg Kroah-Hartman 	return 0;
1200*8ffdff6aSGreg Kroah-Hartman }
1201*8ffdff6aSGreg Kroah-Hartman 
1202*8ffdff6aSGreg Kroah-Hartman static void ni_660x_detach(struct comedi_device *dev)
1203*8ffdff6aSGreg Kroah-Hartman {
1204*8ffdff6aSGreg Kroah-Hartman 	struct ni_660x_private *devpriv = dev->private;
1205*8ffdff6aSGreg Kroah-Hartman 
1206*8ffdff6aSGreg Kroah-Hartman 	if (dev->irq) {
1207*8ffdff6aSGreg Kroah-Hartman 		ni_660x_write(dev, 0, 0, NI660X_GLOBAL_INT_CFG);
1208*8ffdff6aSGreg Kroah-Hartman 		free_irq(dev->irq, dev);
1209*8ffdff6aSGreg Kroah-Hartman 	}
1210*8ffdff6aSGreg Kroah-Hartman 	if (devpriv) {
1211*8ffdff6aSGreg Kroah-Hartman 		ni_gpct_device_destroy(devpriv->counter_dev);
1212*8ffdff6aSGreg Kroah-Hartman 		ni_660x_free_mite_rings(dev);
1213*8ffdff6aSGreg Kroah-Hartman 		mite_detach(devpriv->mite);
1214*8ffdff6aSGreg Kroah-Hartman 	}
1215*8ffdff6aSGreg Kroah-Hartman 	if (dev->mmio)
1216*8ffdff6aSGreg Kroah-Hartman 		iounmap(dev->mmio);
1217*8ffdff6aSGreg Kroah-Hartman 	comedi_pci_disable(dev);
1218*8ffdff6aSGreg Kroah-Hartman }
1219*8ffdff6aSGreg Kroah-Hartman 
1220*8ffdff6aSGreg Kroah-Hartman static struct comedi_driver ni_660x_driver = {
1221*8ffdff6aSGreg Kroah-Hartman 	.driver_name	= "ni_660x",
1222*8ffdff6aSGreg Kroah-Hartman 	.module		= THIS_MODULE,
1223*8ffdff6aSGreg Kroah-Hartman 	.auto_attach	= ni_660x_auto_attach,
1224*8ffdff6aSGreg Kroah-Hartman 	.detach		= ni_660x_detach,
1225*8ffdff6aSGreg Kroah-Hartman };
1226*8ffdff6aSGreg Kroah-Hartman 
1227*8ffdff6aSGreg Kroah-Hartman static int ni_660x_pci_probe(struct pci_dev *dev,
1228*8ffdff6aSGreg Kroah-Hartman 			     const struct pci_device_id *id)
1229*8ffdff6aSGreg Kroah-Hartman {
1230*8ffdff6aSGreg Kroah-Hartman 	return comedi_pci_auto_config(dev, &ni_660x_driver, id->driver_data);
1231*8ffdff6aSGreg Kroah-Hartman }
1232*8ffdff6aSGreg Kroah-Hartman 
1233*8ffdff6aSGreg Kroah-Hartman static const struct pci_device_id ni_660x_pci_table[] = {
1234*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 },
1235*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 },
1236*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 },
1237*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x2db0), BOARD_PCI6608 },
1238*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 },
1239*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1e30), BOARD_PCI6624 },
1240*8ffdff6aSGreg Kroah-Hartman 	{ PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 },
1241*8ffdff6aSGreg Kroah-Hartman 	{ 0 }
1242*8ffdff6aSGreg Kroah-Hartman };
1243*8ffdff6aSGreg Kroah-Hartman MODULE_DEVICE_TABLE(pci, ni_660x_pci_table);
1244*8ffdff6aSGreg Kroah-Hartman 
1245*8ffdff6aSGreg Kroah-Hartman static struct pci_driver ni_660x_pci_driver = {
1246*8ffdff6aSGreg Kroah-Hartman 	.name		= "ni_660x",
1247*8ffdff6aSGreg Kroah-Hartman 	.id_table	= ni_660x_pci_table,
1248*8ffdff6aSGreg Kroah-Hartman 	.probe		= ni_660x_pci_probe,
1249*8ffdff6aSGreg Kroah-Hartman 	.remove		= comedi_pci_auto_unconfig,
1250*8ffdff6aSGreg Kroah-Hartman };
1251*8ffdff6aSGreg Kroah-Hartman module_comedi_pci_driver(ni_660x_driver, ni_660x_pci_driver);
1252*8ffdff6aSGreg Kroah-Hartman 
1253*8ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("Comedi https://www.comedi.org");
1254*8ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi driver for NI 660x counter/timer boards");
1255*8ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
1256