staging: comedi: ni_660x: init TIO chips before subdevice init
[cascardo/linux.git] / drivers / staging / comedi / drivers / ni_660x.c
index 46647c6..afe62bf 100644 (file)
@@ -1,17 +1,16 @@
 /*
-  comedi/drivers/ni_660x.c
-  Hardware driver for NI 660x devices
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-*/
+ * Hardware driver for NI 660x devices
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
 
 /*
  * Driver: ni_660x
 #include "mite.h"
 #include "ni_tio.h"
 
-enum ni_660x_constants {
-       min_counter_pfi_chan = 8,
-       max_dio_pfi_chan = 31,
-       counters_per_chip = 4
-};
-
-#define NUM_PFI_CHANNELS 40
-/* really there are only up to 3 dma channels, but the register layout allows
-for 4 */
-#define MAX_DMA_CHANNEL 4
-
 /* See Register-Level Programmer Manual page 3.1 */
 enum ni_660x_register {
        NI660X_G0_INT_ACK,
@@ -156,219 +144,202 @@ enum ni_660x_register {
        NI660X_NUM_REGS,
 };
 
-static inline unsigned IOConfigReg(unsigned pfi_channel)
-{
-       unsigned reg = NI660X_IO_CFG_0_1 + pfi_channel / 2;
+#define NI660X_CLK_CFG_COUNTER_SWAP    BIT(21)
 
-       BUG_ON(reg > NI660X_IO_CFG_38_39);
-       return reg;
-}
+#define NI660X_GLOBAL_INT_COUNTER0     BIT(8)
+#define NI660X_GLOBAL_INT_COUNTER1     BIT(9)
+#define NI660X_GLOBAL_INT_COUNTER2     BIT(10)
+#define NI660X_GLOBAL_INT_COUNTER3     BIT(11)
+#define NI660X_GLOBAL_INT_CASCADE      BIT(29)
+#define NI660X_GLOBAL_INT_GLOBAL_POL   BIT(30)
+#define NI660X_GLOBAL_INT_GLOBAL       BIT(31)
 
-enum ni_660x_register_width {
-       DATA_1B,
-       DATA_2B,
-       DATA_4B
-};
+#define NI660X_DMA_CFG_SEL(_c, _s)     (((_s) & 0x1f) << (8 * (_c)))
+#define NI660X_DMA_CFG_SEL_MASK(_c)    NI660X_DMA_CFG_SEL((_c), 0x1f)
+#define NI660X_DMA_CFG_SEL_NONE(_c)    NI660X_DMA_CFG_SEL((_c), 0x1f)
+#define NI660X_DMA_CFG_RESET(_c)       NI660X_DMA_CFG_SEL((_c), 0x80)
 
-enum ni_660x_register_direction {
-       NI_660x_READ,
-       NI_660x_WRITE,
-       NI_660x_READ_WRITE
-};
+#define NI660X_IO_CFG(x)               (NI660X_IO_CFG_0_1 + ((x) / 2))
+#define NI660X_IO_CFG_OUT_SEL(_c, _s)  (((_s) & 0x3) << (((_c) % 2) ? 0 : 8))
+#define NI660X_IO_CFG_OUT_SEL_MASK(_c) NI660X_IO_CFG_OUT_SEL((_c), 0x3)
+#define NI660X_IO_CFG_IN_SEL(_c, _s)   (((_s) & 0x7) << (((_c) % 2) ? 4 : 12))
+#define NI660X_IO_CFG_IN_SEL_MASK(_c)  NI660X_IO_CFG_IN_SEL((_c), 0x7)
 
-enum ni_660x_pfi_output_select {
-       pfi_output_select_high_Z = 0,
-       pfi_output_select_counter = 1,
-       pfi_output_select_do = 2,
-       num_pfi_output_selects
-};
-
-enum ni_660x_subdevices {
-       NI_660X_DIO_SUBDEV = 1,
-       NI_660X_GPCT_SUBDEV_0 = 2
-};
-static inline unsigned NI_660X_GPCT_SUBDEV(unsigned index)
-{
-       return NI_660X_GPCT_SUBDEV_0 + index;
-}
-
-struct NI_660xRegisterData {
-       const char *name;       /*  Register Name */
+struct ni_660x_register_data {
        int offset;             /*  Offset from base address from GPCT chip */
-       enum ni_660x_register_direction direction;
-       enum ni_660x_register_width size; /* 1 byte, 2 bytes, or 4 bytes */
-};
-
-static const struct NI_660xRegisterData registerData[NI660X_NUM_REGS] = {
-       {"G0 Interrupt Acknowledge", 0x004, NI_660x_WRITE, DATA_2B},
-       {"G0 Status Register", 0x004, NI_660x_READ, DATA_2B},
-       {"G1 Interrupt Acknowledge", 0x006, NI_660x_WRITE, DATA_2B},
-       {"G1 Status Register", 0x006, NI_660x_READ, DATA_2B},
-       {"G01 Status Register ", 0x008, NI_660x_READ, DATA_2B},
-       {"G0 Command Register", 0x00C, NI_660x_WRITE, DATA_2B},
-       {"STC DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B},
-       {"G1 Command Register", 0x00E, NI_660x_WRITE, DATA_2B},
-       {"G0 HW Save Register", 0x010, NI_660x_READ, DATA_4B},
-       {"G1 HW Save Register", 0x014, NI_660x_READ, DATA_4B},
-       {"STC DIO Output", 0x014, NI_660x_WRITE, DATA_2B},
-       {"STC DIO Control", 0x016, NI_660x_WRITE, DATA_2B},
-       {"G0 SW Save Register", 0x018, NI_660x_READ, DATA_4B},
-       {"G1 SW Save Register", 0x01C, NI_660x_READ, DATA_4B},
-       {"G0 Mode Register", 0x034, NI_660x_WRITE, DATA_2B},
-       {"G01 Joint Status 1 Register", 0x036, NI_660x_READ, DATA_2B},
-       {"G1 Mode Register", 0x036, NI_660x_WRITE, DATA_2B},
-       {"STC DIO Serial Input", 0x038, NI_660x_READ, DATA_2B},
-       {"G0 Load A Register", 0x038, NI_660x_WRITE, DATA_4B},
-       {"G01 Joint Status 2 Register", 0x03A, NI_660x_READ, DATA_2B},
-       {"G0 Load B Register", 0x03C, NI_660x_WRITE, DATA_4B},
-       {"G1 Load A Register", 0x040, NI_660x_WRITE, DATA_4B},
-       {"G1 Load B Register", 0x044, NI_660x_WRITE, DATA_4B},
-       {"G0 Input Select Register", 0x048, NI_660x_WRITE, DATA_2B},
-       {"G1 Input Select Register", 0x04A, NI_660x_WRITE, DATA_2B},
-       {"G0 Autoincrement Register", 0x088, NI_660x_WRITE, DATA_2B},
-       {"G1 Autoincrement Register", 0x08A, NI_660x_WRITE, DATA_2B},
-       {"G01 Joint Reset Register", 0x090, NI_660x_WRITE, DATA_2B},
-       {"G0 Interrupt Enable", 0x092, NI_660x_WRITE, DATA_2B},
-       {"G1 Interrupt Enable", 0x096, NI_660x_WRITE, DATA_2B},
-       {"G0 Counting Mode Register", 0x0B0, NI_660x_WRITE, DATA_2B},
-       {"G1 Counting Mode Register", 0x0B2, NI_660x_WRITE, DATA_2B},
-       {"G0 Second Gate Register", 0x0B4, NI_660x_WRITE, DATA_2B},
-       {"G1 Second Gate Register", 0x0B6, NI_660x_WRITE, DATA_2B},
-       {"G0 DMA Config Register", 0x0B8, NI_660x_WRITE, DATA_2B},
-       {"G0 DMA Status Register", 0x0B8, NI_660x_READ, DATA_2B},
-       {"G1 DMA Config Register", 0x0BA, NI_660x_WRITE, DATA_2B},
-       {"G1 DMA Status Register", 0x0BA, NI_660x_READ, DATA_2B},
-       {"G2 Interrupt Acknowledge", 0x104, NI_660x_WRITE, DATA_2B},
-       {"G2 Status Register", 0x104, NI_660x_READ, DATA_2B},
-       {"G3 Interrupt Acknowledge", 0x106, NI_660x_WRITE, DATA_2B},
-       {"G3 Status Register", 0x106, NI_660x_READ, DATA_2B},
-       {"G23 Status Register", 0x108, NI_660x_READ, DATA_2B},
-       {"G2 Command Register", 0x10C, NI_660x_WRITE, DATA_2B},
-       {"G3 Command Register", 0x10E, NI_660x_WRITE, DATA_2B},
-       {"G2 HW Save Register", 0x110, NI_660x_READ, DATA_4B},
-       {"G3 HW Save Register", 0x114, NI_660x_READ, DATA_4B},
-       {"G2 SW Save Register", 0x118, NI_660x_READ, DATA_4B},
-       {"G3 SW Save Register", 0x11C, NI_660x_READ, DATA_4B},
-       {"G2 Mode Register", 0x134, NI_660x_WRITE, DATA_2B},
-       {"G23 Joint Status 1 Register", 0x136, NI_660x_READ, DATA_2B},
-       {"G3 Mode Register", 0x136, NI_660x_WRITE, DATA_2B},
-       {"G2 Load A Register", 0x138, NI_660x_WRITE, DATA_4B},
-       {"G23 Joint Status 2 Register", 0x13A, NI_660x_READ, DATA_2B},
-       {"G2 Load B Register", 0x13C, NI_660x_WRITE, DATA_4B},
-       {"G3 Load A Register", 0x140, NI_660x_WRITE, DATA_4B},
-       {"G3 Load B Register", 0x144, NI_660x_WRITE, DATA_4B},
-       {"G2 Input Select Register", 0x148, NI_660x_WRITE, DATA_2B},
-       {"G3 Input Select Register", 0x14A, NI_660x_WRITE, DATA_2B},
-       {"G2 Autoincrement Register", 0x188, NI_660x_WRITE, DATA_2B},
-       {"G3 Autoincrement Register", 0x18A, NI_660x_WRITE, DATA_2B},
-       {"G23 Joint Reset Register", 0x190, NI_660x_WRITE, DATA_2B},
-       {"G2 Interrupt Enable", 0x192, NI_660x_WRITE, DATA_2B},
-       {"G3 Interrupt Enable", 0x196, NI_660x_WRITE, DATA_2B},
-       {"G2 Counting Mode Register", 0x1B0, NI_660x_WRITE, DATA_2B},
-       {"G3 Counting Mode Register", 0x1B2, NI_660x_WRITE, DATA_2B},
-       {"G3 Second Gate Register", 0x1B6, NI_660x_WRITE, DATA_2B},
-       {"G2 Second Gate Register", 0x1B4, NI_660x_WRITE, DATA_2B},
-       {"G2 DMA Config Register", 0x1B8, NI_660x_WRITE, DATA_2B},
-       {"G2 DMA Status Register", 0x1B8, NI_660x_READ, DATA_2B},
-       {"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B},
-       {"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B},
-       {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B},
-       {"32 bit Digital Output", 0x510, NI_660x_WRITE, DATA_4B},
-       {"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B},
-       {"Global Interrupt Status Register", 0x754, NI_660x_READ, DATA_4B},
-       {"DMA Configuration Register", 0x76C, NI_660x_WRITE, DATA_4B},
-       {"Global Interrupt Config Register", 0x770, NI_660x_WRITE, DATA_4B},
-       {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 8-9", 0x784, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 10-11", 0x786, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 12-13", 0x788, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 14-15", 0x78A, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 16-17", 0x78C, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 18-19", 0x78E, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 20-21", 0x790, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 22-23", 0x792, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 24-25", 0x794, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 26-27", 0x796, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 28-29", 0x798, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 30-31", 0x79A, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B},
-       {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B}
-};
-
-/* kind of ENABLE for the second counter */
-enum clock_config_register_bits {
-       CounterSwap = 0x1 << 21
-};
-
-/* ioconfigreg */
-static inline unsigned ioconfig_bitshift(unsigned pfi_channel)
-{
-       return (pfi_channel % 2) ? 0 : 8;
-}
-
-static inline unsigned pfi_output_select_mask(unsigned pfi_channel)
-{
-       return 0x3 << ioconfig_bitshift(pfi_channel);
-}
-
-static inline unsigned pfi_output_select_bits(unsigned pfi_channel,
-                                             unsigned output_select)
-{
-       return (output_select & 0x3) << ioconfig_bitshift(pfi_channel);
-}
-
-static inline unsigned pfi_input_select_mask(unsigned pfi_channel)
-{
-       return 0x7 << (4 + ioconfig_bitshift(pfi_channel));
-}
-
-static inline unsigned pfi_input_select_bits(unsigned pfi_channel,
-                                            unsigned input_select)
-{
-       return (input_select & 0x7) << (4 + ioconfig_bitshift(pfi_channel));
-}
-
-/* dma configuration register bits */
-static inline unsigned dma_select_mask(unsigned dma_channel)
-{
-       BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
-       return 0x1f << (8 * dma_channel);
-}
-
-enum dma_selection {
-       dma_selection_none = 0x1f,
+       char size;              /* 2 or 4 bytes */
 };
 
-static inline unsigned dma_select_bits(unsigned dma_channel, unsigned selection)
-{
-       BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
-       return (selection << (8 * dma_channel)) & dma_select_mask(dma_channel);
-}
-
-static inline unsigned dma_reset_bit(unsigned dma_channel)
-{
-       BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
-       return 0x80 << (8 * dma_channel);
-}
-
-enum global_interrupt_status_register_bits {
-       Counter_0_Int_Bit = 0x100,
-       Counter_1_Int_Bit = 0x200,
-       Counter_2_Int_Bit = 0x400,
-       Counter_3_Int_Bit = 0x800,
-       Cascade_Int_Bit = 0x20000000,
-       Global_Int_Bit = 0x80000000
+static const struct ni_660x_register_data ni_660x_reg_data[NI660X_NUM_REGS] = {
+       [NI660X_G0_INT_ACK]             = { 0x004, 2 }, /* write */
+       [NI660X_G0_STATUS]              = { 0x004, 2 }, /* read */
+       [NI660X_G1_INT_ACK]             = { 0x006, 2 }, /* write */
+       [NI660X_G1_STATUS]              = { 0x006, 2 }, /* read */
+       [NI660X_G01_STATUS]             = { 0x008, 2 }, /* read */
+       [NI660X_G0_CMD]                 = { 0x00c, 2 }, /* write */
+       [NI660X_STC_DIO_PARALLEL_INPUT] = { 0x00e, 2 }, /* read */
+       [NI660X_G1_CMD]                 = { 0x00e, 2 }, /* write */
+       [NI660X_G0_HW_SAVE]             = { 0x010, 4 }, /* read */
+       [NI660X_G1_HW_SAVE]             = { 0x014, 4 }, /* read */
+       [NI660X_STC_DIO_OUTPUT]         = { 0x014, 2 }, /* write */
+       [NI660X_STC_DIO_CONTROL]        = { 0x016, 2 }, /* write */
+       [NI660X_G0_SW_SAVE]             = { 0x018, 4 }, /* read */
+       [NI660X_G1_SW_SAVE]             = { 0x01c, 4 }, /* read */
+       [NI660X_G0_MODE]                = { 0x034, 2 }, /* write */
+       [NI660X_G01_STATUS1]            = { 0x036, 2 }, /* read */
+       [NI660X_G1_MODE]                = { 0x036, 2 }, /* write */
+       [NI660X_STC_DIO_SERIAL_INPUT]   = { 0x038, 2 }, /* read */
+       [NI660X_G0_LOADA]               = { 0x038, 4 }, /* write */
+       [NI660X_G01_STATUS2]            = { 0x03a, 2 }, /* read */
+       [NI660X_G0_LOADB]               = { 0x03c, 4 }, /* write */
+       [NI660X_G1_LOADA]               = { 0x040, 4 }, /* write */
+       [NI660X_G1_LOADB]               = { 0x044, 4 }, /* write */
+       [NI660X_G0_INPUT_SEL]           = { 0x048, 2 }, /* write */
+       [NI660X_G1_INPUT_SEL]           = { 0x04a, 2 }, /* write */
+       [NI660X_G0_AUTO_INC]            = { 0x088, 2 }, /* write */
+       [NI660X_G1_AUTO_INC]            = { 0x08a, 2 }, /* write */
+       [NI660X_G01_RESET]              = { 0x090, 2 }, /* write */
+       [NI660X_G0_INT_ENA]             = { 0x092, 2 }, /* write */
+       [NI660X_G1_INT_ENA]             = { 0x096, 2 }, /* write */
+       [NI660X_G0_CNT_MODE]            = { 0x0b0, 2 }, /* write */
+       [NI660X_G1_CNT_MODE]            = { 0x0b2, 2 }, /* write */
+       [NI660X_G0_GATE2]               = { 0x0b4, 2 }, /* write */
+       [NI660X_G1_GATE2]               = { 0x0b6, 2 }, /* write */
+       [NI660X_G0_DMA_CFG]             = { 0x0b8, 2 }, /* write */
+       [NI660X_G0_DMA_STATUS]          = { 0x0b8, 2 }, /* read */
+       [NI660X_G1_DMA_CFG]             = { 0x0ba, 2 }, /* write */
+       [NI660X_G1_DMA_STATUS]          = { 0x0ba, 2 }, /* read */
+       [NI660X_G2_INT_ACK]             = { 0x104, 2 }, /* write */
+       [NI660X_G2_STATUS]              = { 0x104, 2 }, /* read */
+       [NI660X_G3_INT_ACK]             = { 0x106, 2 }, /* write */
+       [NI660X_G3_STATUS]              = { 0x106, 2 }, /* read */
+       [NI660X_G23_STATUS]             = { 0x108, 2 }, /* read */
+       [NI660X_G2_CMD]                 = { 0x10c, 2 }, /* write */
+       [NI660X_G3_CMD]                 = { 0x10e, 2 }, /* write */
+       [NI660X_G2_HW_SAVE]             = { 0x110, 4 }, /* read */
+       [NI660X_G3_HW_SAVE]             = { 0x114, 4 }, /* read */
+       [NI660X_G2_SW_SAVE]             = { 0x118, 4 }, /* read */
+       [NI660X_G3_SW_SAVE]             = { 0x11c, 4 }, /* read */
+       [NI660X_G2_MODE]                = { 0x134, 2 }, /* write */
+       [NI660X_G23_STATUS1]            = { 0x136, 2 }, /* read */
+       [NI660X_G3_MODE]                = { 0x136, 2 }, /* write */
+       [NI660X_G2_LOADA]               = { 0x138, 4 }, /* write */
+       [NI660X_G23_STATUS2]            = { 0x13a, 2 }, /* read */
+       [NI660X_G2_LOADB]               = { 0x13c, 4 }, /* write */
+       [NI660X_G3_LOADA]               = { 0x140, 4 }, /* write */
+       [NI660X_G3_LOADB]               = { 0x144, 4 }, /* write */
+       [NI660X_G2_INPUT_SEL]           = { 0x148, 2 }, /* write */
+       [NI660X_G3_INPUT_SEL]           = { 0x14a, 2 }, /* write */
+       [NI660X_G2_AUTO_INC]            = { 0x188, 2 }, /* write */
+       [NI660X_G3_AUTO_INC]            = { 0x18a, 2 }, /* write */
+       [NI660X_G23_RESET]              = { 0x190, 2 }, /* write */
+       [NI660X_G2_INT_ENA]             = { 0x192, 2 }, /* write */
+       [NI660X_G3_INT_ENA]             = { 0x196, 2 }, /* write */
+       [NI660X_G2_CNT_MODE]            = { 0x1b0, 2 }, /* write */
+       [NI660X_G3_CNT_MODE]            = { 0x1b2, 2 }, /* write */
+       [NI660X_G3_GATE2]               = { 0x1b6, 2 }, /* write */
+       [NI660X_G2_GATE2]               = { 0x1b4, 2 }, /* write */
+       [NI660X_G2_DMA_CFG]             = { 0x1b8, 2 }, /* write */
+       [NI660X_G2_DMA_STATUS]          = { 0x1b8, 2 }, /* read */
+       [NI660X_G3_DMA_CFG]             = { 0x1ba, 2 }, /* write */
+       [NI660X_G3_DMA_STATUS]          = { 0x1ba, 2 }, /* read */
+       [NI660X_DIO32_INPUT]            = { 0x414, 4 }, /* read */
+       [NI660X_DIO32_OUTPUT]           = { 0x510, 4 }, /* write */
+       [NI660X_CLK_CFG]                = { 0x73c, 4 }, /* write */
+       [NI660X_GLOBAL_INT_STATUS]      = { 0x754, 4 }, /* read */
+       [NI660X_DMA_CFG]                = { 0x76c, 4 }, /* write */
+       [NI660X_GLOBAL_INT_CFG]         = { 0x770, 4 }, /* write */
+       [NI660X_IO_CFG_0_1]             = { 0x77c, 2 }, /* read/write */
+       [NI660X_IO_CFG_2_3]             = { 0x77e, 2 }, /* read/write */
+       [NI660X_IO_CFG_4_5]             = { 0x780, 2 }, /* read/write */
+       [NI660X_IO_CFG_6_7]             = { 0x782, 2 }, /* read/write */
+       [NI660X_IO_CFG_8_9]             = { 0x784, 2 }, /* read/write */
+       [NI660X_IO_CFG_10_11]           = { 0x786, 2 }, /* read/write */
+       [NI660X_IO_CFG_12_13]           = { 0x788, 2 }, /* read/write */
+       [NI660X_IO_CFG_14_15]           = { 0x78a, 2 }, /* read/write */
+       [NI660X_IO_CFG_16_17]           = { 0x78c, 2 }, /* read/write */
+       [NI660X_IO_CFG_18_19]           = { 0x78e, 2 }, /* read/write */
+       [NI660X_IO_CFG_20_21]           = { 0x790, 2 }, /* read/write */
+       [NI660X_IO_CFG_22_23]           = { 0x792, 2 }, /* read/write */
+       [NI660X_IO_CFG_24_25]           = { 0x794, 2 }, /* read/write */
+       [NI660X_IO_CFG_26_27]           = { 0x796, 2 }, /* read/write */
+       [NI660X_IO_CFG_28_29]           = { 0x798, 2 }, /* read/write */
+       [NI660X_IO_CFG_30_31]           = { 0x79a, 2 }, /* read/write */
+       [NI660X_IO_CFG_32_33]           = { 0x79c, 2 }, /* read/write */
+       [NI660X_IO_CFG_34_35]           = { 0x79e, 2 }, /* read/write */
+       [NI660X_IO_CFG_36_37]           = { 0x7a0, 2 }, /* read/write */
+       [NI660X_IO_CFG_38_39]           = { 0x7a2, 2 }  /* read/write */
 };
 
-enum global_interrupt_config_register_bits {
-       Cascade_Int_Enable_Bit = 0x20000000,
-       Global_Int_Polarity_Bit = 0x40000000,
-       Global_Int_Enable_Bit = 0x80000000
+static const enum ni_660x_register ni_gpct_to_660x_register[] = {
+       [NITIO_G0_AUTO_INC]             = NI660X_G0_AUTO_INC,
+       [NITIO_G1_AUTO_INC]             = NI660X_G1_AUTO_INC,
+       [NITIO_G2_AUTO_INC]             = NI660X_G2_AUTO_INC,
+       [NITIO_G3_AUTO_INC]             = NI660X_G3_AUTO_INC,
+       [NITIO_G0_CMD]                  = NI660X_G0_CMD,
+       [NITIO_G1_CMD]                  = NI660X_G1_CMD,
+       [NITIO_G2_CMD]                  = NI660X_G2_CMD,
+       [NITIO_G3_CMD]                  = NI660X_G3_CMD,
+       [NITIO_G0_HW_SAVE]              = NI660X_G0_HW_SAVE,
+       [NITIO_G1_HW_SAVE]              = NI660X_G1_HW_SAVE,
+       [NITIO_G2_HW_SAVE]              = NI660X_G2_HW_SAVE,
+       [NITIO_G3_HW_SAVE]              = NI660X_G3_HW_SAVE,
+       [NITIO_G0_SW_SAVE]              = NI660X_G0_SW_SAVE,
+       [NITIO_G1_SW_SAVE]              = NI660X_G1_SW_SAVE,
+       [NITIO_G2_SW_SAVE]              = NI660X_G2_SW_SAVE,
+       [NITIO_G3_SW_SAVE]              = NI660X_G3_SW_SAVE,
+       [NITIO_G0_MODE]                 = NI660X_G0_MODE,
+       [NITIO_G1_MODE]                 = NI660X_G1_MODE,
+       [NITIO_G2_MODE]                 = NI660X_G2_MODE,
+       [NITIO_G3_MODE]                 = NI660X_G3_MODE,
+       [NITIO_G0_LOADA]                = NI660X_G0_LOADA,
+       [NITIO_G1_LOADA]                = NI660X_G1_LOADA,
+       [NITIO_G2_LOADA]                = NI660X_G2_LOADA,
+       [NITIO_G3_LOADA]                = NI660X_G3_LOADA,
+       [NITIO_G0_LOADB]                = NI660X_G0_LOADB,
+       [NITIO_G1_LOADB]                = NI660X_G1_LOADB,
+       [NITIO_G2_LOADB]                = NI660X_G2_LOADB,
+       [NITIO_G3_LOADB]                = NI660X_G3_LOADB,
+       [NITIO_G0_INPUT_SEL]            = NI660X_G0_INPUT_SEL,
+       [NITIO_G1_INPUT_SEL]            = NI660X_G1_INPUT_SEL,
+       [NITIO_G2_INPUT_SEL]            = NI660X_G2_INPUT_SEL,
+       [NITIO_G3_INPUT_SEL]            = NI660X_G3_INPUT_SEL,
+       [NITIO_G0_CNT_MODE]             = NI660X_G0_CNT_MODE,
+       [NITIO_G1_CNT_MODE]             = NI660X_G1_CNT_MODE,
+       [NITIO_G2_CNT_MODE]             = NI660X_G2_CNT_MODE,
+       [NITIO_G3_CNT_MODE]             = NI660X_G3_CNT_MODE,
+       [NITIO_G0_GATE2]                = NI660X_G0_GATE2,
+       [NITIO_G1_GATE2]                = NI660X_G1_GATE2,
+       [NITIO_G2_GATE2]                = NI660X_G2_GATE2,
+       [NITIO_G3_GATE2]                = NI660X_G3_GATE2,
+       [NITIO_G01_STATUS]              = NI660X_G01_STATUS,
+       [NITIO_G23_STATUS]              = NI660X_G23_STATUS,
+       [NITIO_G01_RESET]               = NI660X_G01_RESET,
+       [NITIO_G23_RESET]               = NI660X_G23_RESET,
+       [NITIO_G01_STATUS1]             = NI660X_G01_STATUS1,
+       [NITIO_G23_STATUS1]             = NI660X_G23_STATUS1,
+       [NITIO_G01_STATUS2]             = NI660X_G01_STATUS2,
+       [NITIO_G23_STATUS2]             = NI660X_G23_STATUS2,
+       [NITIO_G0_DMA_CFG]              = NI660X_G0_DMA_CFG,
+       [NITIO_G1_DMA_CFG]              = NI660X_G1_DMA_CFG,
+       [NITIO_G2_DMA_CFG]              = NI660X_G2_DMA_CFG,
+       [NITIO_G3_DMA_CFG]              = NI660X_G3_DMA_CFG,
+       [NITIO_G0_DMA_STATUS]           = NI660X_G0_DMA_STATUS,
+       [NITIO_G1_DMA_STATUS]           = NI660X_G1_DMA_STATUS,
+       [NITIO_G2_DMA_STATUS]           = NI660X_G2_DMA_STATUS,
+       [NITIO_G3_DMA_STATUS]           = NI660X_G3_DMA_STATUS,
+       [NITIO_G0_INT_ACK]              = NI660X_G0_INT_ACK,
+       [NITIO_G1_INT_ACK]              = NI660X_G1_INT_ACK,
+       [NITIO_G2_INT_ACK]              = NI660X_G2_INT_ACK,
+       [NITIO_G3_INT_ACK]              = NI660X_G3_INT_ACK,
+       [NITIO_G0_STATUS]               = NI660X_G0_STATUS,
+       [NITIO_G1_STATUS]               = NI660X_G1_STATUS,
+       [NITIO_G2_STATUS]               = NI660X_G2_STATUS,
+       [NITIO_G3_STATUS]               = NI660X_G3_STATUS,
+       [NITIO_G0_INT_ENA]              = NI660X_G0_INT_ENA,
+       [NITIO_G1_INT_ENA]              = NI660X_G1_INT_ENA,
+       [NITIO_G2_INT_ENA]              = NI660X_G2_INT_ENA,
+       [NITIO_G3_INT_ENA]              = NI660X_G3_INT_ENA,
 };
 
 /* Offset of the GPCT chips from the base-address of the card */
@@ -385,7 +356,7 @@ enum ni_660x_boardid {
 
 struct ni_660x_board {
        const char *name;
-       unsigned n_chips;       /* total number of TIO chips */
+       unsigned int n_chips;   /* total number of TIO chips */
 };
 
 static const struct ni_660x_board ni_660x_boards[] = {
@@ -411,230 +382,87 @@ static const struct ni_660x_board ni_660x_boards[] = {
        },
 };
 
-#define NI_660X_MAX_NUM_CHIPS 2
-#define NI_660X_MAX_NUM_COUNTERS (NI_660X_MAX_NUM_CHIPS * counters_per_chip)
+#define NI660X_NUM_PFI_CHANNELS                40
+
+/* there are only up to 3 dma channels, but the register layout allows for 4 */
+#define NI660X_MAX_DMA_CHANNEL         4
+
+#define NI660X_COUNTERS_PER_CHIP       4
+#define NI660X_MAX_CHIPS               2
+#define NI660X_MAX_COUNTERS            (NI660X_MAX_CHIPS *     \
+                                        NI660X_COUNTERS_PER_CHIP)
 
 struct ni_660x_private {
        struct mite_struct *mite;
        struct ni_gpct_device *counter_dev;
-       uint64_t pfi_direction_bits;
        struct mite_dma_descriptor_ring
-       *mite_rings[NI_660X_MAX_NUM_CHIPS][counters_per_chip];
+       *mite_rings[NI660X_MAX_CHIPS][NI660X_COUNTERS_PER_CHIP];
+       /* protects mite channel request/release */
        spinlock_t mite_channel_lock;
-       /* interrupt_lock prevents races between interrupt and comedi_poll */
+       /* prevents races between interrupt and comedi_poll */
        spinlock_t interrupt_lock;
-       unsigned dma_configuration_soft_copies[NI_660X_MAX_NUM_CHIPS];
-       spinlock_t soft_reg_copy_lock;
-       unsigned short pfi_output_selects[NUM_PFI_CHANNELS];
+       /* protects dma_cfg changes */
+       spinlock_t dma_cfg_lock;
+       unsigned int dma_cfg[NI660X_MAX_CHIPS];
+       unsigned int io_cfg[NI660X_NUM_PFI_CHANNELS];
+       u64 io_dir;
 };
 
-static inline unsigned ni_660x_num_counters(struct comedi_device *dev)
-{
-       const struct ni_660x_board *board = dev->board_ptr;
-
-       return board->n_chips * counters_per_chip;
-}
-
-static enum ni_660x_register ni_gpct_to_660x_register(enum ni_gpct_register reg)
-{
-       switch (reg) {
-       case NITIO_G0_AUTO_INC:
-               return NI660X_G0_AUTO_INC;
-       case NITIO_G1_AUTO_INC:
-               return NI660X_G1_AUTO_INC;
-       case NITIO_G2_AUTO_INC:
-               return NI660X_G2_AUTO_INC;
-       case NITIO_G3_AUTO_INC:
-               return NI660X_G3_AUTO_INC;
-       case NITIO_G0_CMD:
-               return NI660X_G0_CMD;
-       case NITIO_G1_CMD:
-               return NI660X_G1_CMD;
-       case NITIO_G2_CMD:
-               return NI660X_G2_CMD;
-       case NITIO_G3_CMD:
-               return NI660X_G3_CMD;
-       case NITIO_G0_HW_SAVE:
-               return NI660X_G0_HW_SAVE;
-       case NITIO_G1_HW_SAVE:
-               return NI660X_G1_HW_SAVE;
-       case NITIO_G2_HW_SAVE:
-               return NI660X_G2_HW_SAVE;
-       case NITIO_G3_HW_SAVE:
-               return NI660X_G3_HW_SAVE;
-       case NITIO_G0_SW_SAVE:
-               return NI660X_G0_SW_SAVE;
-       case NITIO_G1_SW_SAVE:
-               return NI660X_G1_SW_SAVE;
-       case NITIO_G2_SW_SAVE:
-               return NI660X_G2_SW_SAVE;
-       case NITIO_G3_SW_SAVE:
-               return NI660X_G3_SW_SAVE;
-       case NITIO_G0_MODE:
-               return NI660X_G0_MODE;
-       case NITIO_G1_MODE:
-               return NI660X_G1_MODE;
-       case NITIO_G2_MODE:
-               return NI660X_G2_MODE;
-       case NITIO_G3_MODE:
-               return NI660X_G3_MODE;
-       case NITIO_G0_LOADA:
-               return NI660X_G0_LOADA;
-       case NITIO_G1_LOADA:
-               return NI660X_G1_LOADA;
-       case NITIO_G2_LOADA:
-               return NI660X_G2_LOADA;
-       case NITIO_G3_LOADA:
-               return NI660X_G3_LOADA;
-       case NITIO_G0_LOADB:
-               return NI660X_G0_LOADB;
-       case NITIO_G1_LOADB:
-               return NI660X_G1_LOADB;
-       case NITIO_G2_LOADB:
-               return NI660X_G2_LOADB;
-       case NITIO_G3_LOADB:
-               return NI660X_G3_LOADB;
-       case NITIO_G0_INPUT_SEL:
-               return NI660X_G0_INPUT_SEL;
-       case NITIO_G1_INPUT_SEL:
-               return NI660X_G1_INPUT_SEL;
-       case NITIO_G2_INPUT_SEL:
-               return NI660X_G2_INPUT_SEL;
-       case NITIO_G3_INPUT_SEL:
-               return NI660X_G3_INPUT_SEL;
-       case NITIO_G01_STATUS:
-               return NI660X_G01_STATUS;
-       case NITIO_G23_STATUS:
-               return NI660X_G23_STATUS;
-       case NITIO_G01_RESET:
-               return NI660X_G01_RESET;
-       case NITIO_G23_RESET:
-               return NI660X_G23_RESET;
-       case NITIO_G01_STATUS1:
-               return NI660X_G01_STATUS1;
-       case NITIO_G23_STATUS1:
-               return NI660X_G23_STATUS1;
-       case NITIO_G01_STATUS2:
-               return NI660X_G01_STATUS2;
-       case NITIO_G23_STATUS2:
-               return NI660X_G23_STATUS2;
-       case NITIO_G0_CNT_MODE:
-               return NI660X_G0_CNT_MODE;
-       case NITIO_G1_CNT_MODE:
-               return NI660X_G1_CNT_MODE;
-       case NITIO_G2_CNT_MODE:
-               return NI660X_G2_CNT_MODE;
-       case NITIO_G3_CNT_MODE:
-               return NI660X_G3_CNT_MODE;
-       case NITIO_G0_GATE2:
-               return NI660X_G0_GATE2;
-       case NITIO_G1_GATE2:
-               return NI660X_G1_GATE2;
-       case NITIO_G2_GATE2:
-               return NI660X_G2_GATE2;
-       case NITIO_G3_GATE2:
-               return NI660X_G3_GATE2;
-       case NITIO_G0_DMA_CFG:
-               return NI660X_G0_DMA_CFG;
-       case NITIO_G0_DMA_STATUS:
-               return NI660X_G0_DMA_STATUS;
-       case NITIO_G1_DMA_CFG:
-               return NI660X_G1_DMA_CFG;
-       case NITIO_G1_DMA_STATUS:
-               return NI660X_G1_DMA_STATUS;
-       case NITIO_G2_DMA_CFG:
-               return NI660X_G2_DMA_CFG;
-       case NITIO_G2_DMA_STATUS:
-               return NI660X_G2_DMA_STATUS;
-       case NITIO_G3_DMA_CFG:
-               return NI660X_G3_DMA_CFG;
-       case NITIO_G3_DMA_STATUS:
-               return NI660X_G3_DMA_STATUS;
-       case NITIO_G0_INT_ACK:
-               return NI660X_G0_INT_ACK;
-       case NITIO_G1_INT_ACK:
-               return NI660X_G1_INT_ACK;
-       case NITIO_G2_INT_ACK:
-               return NI660X_G2_INT_ACK;
-       case NITIO_G3_INT_ACK:
-               return NI660X_G3_INT_ACK;
-       case NITIO_G0_STATUS:
-               return NI660X_G0_STATUS;
-       case NITIO_G1_STATUS:
-               return NI660X_G1_STATUS;
-       case NITIO_G2_STATUS:
-               return NI660X_G2_STATUS;
-       case NITIO_G3_STATUS:
-               return NI660X_G3_STATUS;
-       case NITIO_G0_INT_ENA:
-               return NI660X_G0_INT_ENA;
-       case NITIO_G1_INT_ENA:
-               return NI660X_G1_INT_ENA;
-       case NITIO_G2_INT_ENA:
-               return NI660X_G2_INT_ENA;
-       case NITIO_G3_INT_ENA:
-               return NI660X_G3_INT_ENA;
-       default:
-               BUG();
-               return 0;
-       }
-}
-
-static inline void ni_660x_write_register(struct comedi_device *dev,
-                                         unsigned chip, unsigned bits,
-                                         enum ni_660x_register reg)
+static void ni_660x_write(struct comedi_device *dev,
+                         unsigned int chip, unsigned int bits,
+                         enum ni_660x_register reg)
 {
-       unsigned int addr = GPCT_OFFSET[chip] + registerData[reg].offset;
+       unsigned int addr = GPCT_OFFSET[chip] + ni_660x_reg_data[reg].offset;
 
-       switch (registerData[reg].size) {
-       case DATA_2B:
+       if (ni_660x_reg_data[reg].size == 2)
                writew(bits, dev->mmio + addr);
-               break;
-       case DATA_4B:
+       else
                writel(bits, dev->mmio + addr);
-               break;
-       default:
-               BUG();
-               break;
-       }
 }
 
-static inline unsigned ni_660x_read_register(struct comedi_device *dev,
-                                            unsigned chip,
-                                            enum ni_660x_register reg)
+static unsigned int ni_660x_read(struct comedi_device *dev,
+                                unsigned int chip,
+                                enum ni_660x_register reg)
 {
-       unsigned int addr = GPCT_OFFSET[chip] + registerData[reg].offset;
+       unsigned int addr = GPCT_OFFSET[chip] + ni_660x_reg_data[reg].offset;
 
-       switch (registerData[reg].size) {
-       case DATA_2B:
+       if (ni_660x_reg_data[reg].size == 2)
                return readw(dev->mmio + addr);
-       case DATA_4B:
-               return readl(dev->mmio + addr);
-       default:
-               BUG();
-               break;
-       }
-       return 0;
+       return readl(dev->mmio + addr);
 }
 
-static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits,
-                                  enum ni_gpct_register reg)
+static void ni_660x_gpct_write(struct ni_gpct *counter, unsigned int bits,
+                              enum ni_gpct_register reg)
 {
        struct comedi_device *dev = counter->counter_dev->dev;
-       enum ni_660x_register ni_660x_register = ni_gpct_to_660x_register(reg);
-       unsigned chip = counter->chip_index;
+       enum ni_660x_register ni_660x_register;
+
+       if (reg < ARRAY_SIZE(ni_gpct_to_660x_register)) {
+               ni_660x_register = ni_gpct_to_660x_register[reg];
+       } else {
+               dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n",
+                        __func__, reg);
+               return;
+       }
 
-       ni_660x_write_register(dev, chip, bits, ni_660x_register);
+       ni_660x_write(dev, counter->chip_index, bits, ni_660x_register);
 }
 
-static unsigned ni_gpct_read_register(struct ni_gpct *counter,
+static unsigned int ni_660x_gpct_read(struct ni_gpct *counter,
                                      enum ni_gpct_register reg)
 {
        struct comedi_device *dev = counter->counter_dev->dev;
-       enum ni_660x_register ni_660x_register = ni_gpct_to_660x_register(reg);
-       unsigned chip = counter->chip_index;
+       enum ni_660x_register ni_660x_register;
 
-       return ni_660x_read_register(dev, chip, ni_660x_register);
+       if (reg < ARRAY_SIZE(ni_gpct_to_660x_register)) {
+               ni_660x_register = ni_gpct_to_660x_register[reg];
+       } else {
+               dev_warn(dev->class_dev, "%s: unhandled register=0x%x\n",
+                        __func__, reg);
+               return 0;
+       }
+
+       return ni_660x_read(dev, counter->chip_index, ni_660x_register);
 }
 
 static inline struct mite_dma_descriptor_ring *mite_ring(struct ni_660x_private
@@ -642,49 +470,44 @@ static inline struct mite_dma_descriptor_ring *mite_ring(struct ni_660x_private
                                                         struct ni_gpct
                                                         *counter)
 {
-       unsigned chip = counter->chip_index;
+       unsigned int chip = counter->chip_index;
 
        return priv->mite_rings[chip][counter->counter_index];
 }
 
 static inline void ni_660x_set_dma_channel(struct comedi_device *dev,
-                                          unsigned mite_channel,
+                                          unsigned int mite_channel,
                                           struct ni_gpct *counter)
 {
        struct ni_660x_private *devpriv = dev->private;
-       unsigned chip = counter->chip_index;
+       unsigned int chip = counter->chip_index;
        unsigned long flags;
 
-       spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
-       devpriv->dma_configuration_soft_copies[chip] &=
-               ~dma_select_mask(mite_channel);
-       devpriv->dma_configuration_soft_copies[chip] |=
-               dma_select_bits(mite_channel, counter->counter_index);
-       ni_660x_write_register(dev, chip,
-                              devpriv->dma_configuration_soft_copies[chip] |
-                              dma_reset_bit(mite_channel), NI660X_DMA_CFG);
+       spin_lock_irqsave(&devpriv->dma_cfg_lock, flags);
+       devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel);
+       devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL(mite_channel,
+                                                    counter->counter_index);
+       ni_660x_write(dev, chip, devpriv->dma_cfg[chip] |
+                     NI660X_DMA_CFG_RESET(mite_channel),
+                     NI660X_DMA_CFG);
        mmiowb();
-       spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+       spin_unlock_irqrestore(&devpriv->dma_cfg_lock, flags);
 }
 
 static inline void ni_660x_unset_dma_channel(struct comedi_device *dev,
-                                            unsigned mite_channel,
+                                            unsigned int mite_channel,
                                             struct ni_gpct *counter)
 {
        struct ni_660x_private *devpriv = dev->private;
-       unsigned chip = counter->chip_index;
+       unsigned int chip = counter->chip_index;
        unsigned long flags;
 
-       spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
-       devpriv->dma_configuration_soft_copies[chip] &=
-           ~dma_select_mask(mite_channel);
-       devpriv->dma_configuration_soft_copies[chip] |=
-           dma_select_bits(mite_channel, dma_selection_none);
-       ni_660x_write_register(dev, chip,
-                              devpriv->dma_configuration_soft_copies[chip],
-                              NI660X_DMA_CFG);
+       spin_lock_irqsave(&devpriv->dma_cfg_lock, flags);
+       devpriv->dma_cfg[chip] &= ~NI660X_DMA_CFG_SEL_MASK(mite_channel);
+       devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(mite_channel);
+       ni_660x_write(dev, chip, devpriv->dma_cfg[chip], NI660X_DMA_CFG);
        mmiowb();
-       spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+       spin_unlock_irqrestore(&devpriv->dma_cfg_lock, flags);
 }
 
 static int ni_660x_request_mite_channel(struct comedi_device *dev,
@@ -696,7 +519,6 @@ static int ni_660x_request_mite_channel(struct comedi_device *dev,
        struct mite_channel *mite_chan;
 
        spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
-       BUG_ON(counter->mite_chan);
        mite_chan = mite_request_channel(devpriv->mite,
                                         mite_ring(devpriv, counter));
        if (!mite_chan) {
@@ -757,7 +579,7 @@ static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 
 static void set_tio_counterswap(struct comedi_device *dev, int chip)
 {
-       unsigned bits = 0;
+       unsigned int bits = 0;
 
        /*
         * See P. 3.5 of the Register-Level Programming manual.
@@ -766,9 +588,9 @@ static void set_tio_counterswap(struct comedi_device *dev, int chip)
         * first chip.
         */
        if (chip)
-               bits = CounterSwap;
+               bits = NI660X_CLK_CFG_COUNTER_SWAP;
 
-       ni_660x_write_register(dev, chip, bits, NI660X_CLK_CFG);
+       ni_660x_write(dev, chip, bits, NI660X_CLK_CFG);
 }
 
 static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev,
@@ -785,17 +607,20 @@ static irqreturn_t ni_660x_interrupt(int irq, void *d)
        struct comedi_device *dev = d;
        struct ni_660x_private *devpriv = dev->private;
        struct comedi_subdevice *s;
-       unsigned i;
+       unsigned int i;
        unsigned long flags;
 
        if (!dev->attached)
                return IRQ_NONE;
+       /* make sure dev->attached is checked before doing anything else */
+       smp_mb();
+
        /* lock to avoid race with comedi_poll */
        spin_lock_irqsave(&devpriv->interrupt_lock, flags);
-       smp_mb();
-       for (i = 0; i < ni_660x_num_counters(dev); ++i) {
-               s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)];
-               ni_660x_handle_gpct_interrupt(dev, s);
+       for (i = 0; i < dev->n_subdevices; ++i) {
+               s = &dev->subdevices[i];
+               if (s->type == COMEDI_SUBD_COUNTER)
+                       ni_660x_handle_gpct_interrupt(dev, s);
        }
        spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
        return IRQ_HANDLED;
@@ -832,7 +657,7 @@ static int ni_660x_buf_change(struct comedi_device *dev,
 static int ni_660x_allocate_private(struct comedi_device *dev)
 {
        struct ni_660x_private *devpriv;
-       unsigned i;
+       unsigned int i;
 
        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
        if (!devpriv)
@@ -840,9 +665,9 @@ static int ni_660x_allocate_private(struct comedi_device *dev)
 
        spin_lock_init(&devpriv->mite_channel_lock);
        spin_lock_init(&devpriv->interrupt_lock);
-       spin_lock_init(&devpriv->soft_reg_copy_lock);
-       for (i = 0; i < NUM_PFI_CHANNELS; ++i)
-               devpriv->pfi_output_selects[i] = pfi_output_select_counter;
+       spin_lock_init(&devpriv->dma_cfg_lock);
+       for (i = 0; i < NI660X_NUM_PFI_CHANNELS; ++i)
+               devpriv->io_cfg[i] = NI_660X_PFI_OUTPUT_COUNTER;
 
        return 0;
 }
@@ -851,11 +676,11 @@ static int ni_660x_alloc_mite_rings(struct comedi_device *dev)
 {
        const struct ni_660x_board *board = dev->board_ptr;
        struct ni_660x_private *devpriv = dev->private;
-       unsigned i;
-       unsigned j;
+       unsigned int i;
+       unsigned int j;
 
        for (i = 0; i < board->n_chips; ++i) {
-               for (j = 0; j < counters_per_chip; ++j) {
+               for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j) {
                        devpriv->mite_rings[i][j] =
                            mite_alloc_ring(devpriv->mite);
                        if (!devpriv->mite_rings[i][j])
@@ -869,120 +694,101 @@ static void ni_660x_free_mite_rings(struct comedi_device *dev)
 {
        const struct ni_660x_board *board = dev->board_ptr;
        struct ni_660x_private *devpriv = dev->private;
-       unsigned i;
-       unsigned j;
+       unsigned int i;
+       unsigned int j;
 
        for (i = 0; i < board->n_chips; ++i) {
-               for (j = 0; j < counters_per_chip; ++j)
+               for (j = 0; j < NI660X_COUNTERS_PER_CHIP; ++j)
                        mite_free_ring(devpriv->mite_rings[i][j]);
        }
 }
 
-static void init_tio_chip(struct comedi_device *dev, int chipset)
-{
-       struct ni_660x_private *devpriv = dev->private;
-       unsigned i;
-
-       /*  init dma configuration register */
-       devpriv->dma_configuration_soft_copies[chipset] = 0;
-       for (i = 0; i < MAX_DMA_CHANNEL; ++i) {
-               devpriv->dma_configuration_soft_copies[chipset] |=
-                   dma_select_bits(i, dma_selection_none) & dma_select_mask(i);
-       }
-       ni_660x_write_register(dev, chipset,
-                              devpriv->dma_configuration_soft_copies[chipset],
-                              NI660X_DMA_CFG);
-       for (i = 0; i < NUM_PFI_CHANNELS; ++i)
-               ni_660x_write_register(dev, chipset, 0, IOConfigReg(i));
-}
-
 static int ni_660x_dio_insn_bits(struct comedi_device *dev,
                                 struct comedi_subdevice *s,
-                                struct comedi_insn *insn, unsigned int *data)
+                                struct comedi_insn *insn,
+                                unsigned int *data)
 {
-       unsigned base_bitfield_channel = CR_CHAN(insn->chanspec);
-
-       /*  Check if we have to write some bits */
-       if (data[0]) {
-               s->state &= ~(data[0] << base_bitfield_channel);
-               s->state |= (data[0] & data[1]) << base_bitfield_channel;
-               /* Write out the new digital output lines */
-               ni_660x_write_register(dev, 0, s->state, NI660X_DIO32_OUTPUT);
+       unsigned int shift = CR_CHAN(insn->chanspec);
+       unsigned int mask = data[0] << shift;
+       unsigned int bits = data[1] << shift;
+
+       /*
+        * There are 40 channels in this subdevice but only 32 are usable
+        * as DIO. The shift adjusts the mask/bits to account for the base
+        * channel in insn->chanspec. The state update can then be handled
+        * normally for the 32 usable channels.
+        */
+       if (mask) {
+               s->state &= ~mask;
+               s->state |= (bits & mask);
+               ni_660x_write(dev, 0, s->state, NI660X_DIO32_OUTPUT);
        }
-       /* on return, data[1] contains the value of the digital
-        * input and output lines. */
-       data[1] = (ni_660x_read_register(dev, 0, NI660X_DIO32_INPUT) >>
-                       base_bitfield_channel);
+
+       /*
+        * Return the input channels, shifted back to account for the base
+        * channel.
+        */
+       data[1] = ni_660x_read(dev, 0, NI660X_DIO32_INPUT) >> shift;
 
        return insn->n;
 }
 
 static void ni_660x_select_pfi_output(struct comedi_device *dev,
-                                     unsigned pfi_channel,
-                                     unsigned output_select)
+                                     unsigned int chan, unsigned int out_sel)
 {
        const struct ni_660x_board *board = dev->board_ptr;
-       static const unsigned counter_4_7_first_pfi = 8;
-       static const unsigned counter_4_7_last_pfi = 23;
-       unsigned active_chipset = 0;
-       unsigned idle_chipset = 0;
-       unsigned active_bits;
-       unsigned idle_bits;
+       unsigned int active_chip = 0;
+       unsigned int idle_chip = 0;
+       unsigned int bits;
 
        if (board->n_chips > 1) {
-               if (output_select == pfi_output_select_counter &&
-                   pfi_channel >= counter_4_7_first_pfi &&
-                   pfi_channel <= counter_4_7_last_pfi) {
-                       active_chipset = 1;
-                       idle_chipset = 0;
+               if (out_sel == NI_660X_PFI_OUTPUT_COUNTER &&
+                   chan >= 8 && chan <= 23) {
+                       /* counters 4-7 pfi channels */
+                       active_chip = 1;
+                       idle_chip = 0;
                } else {
-                       active_chipset = 0;
-                       idle_chipset = 1;
+                       /* counters 0-3 pfi channels */
+                       active_chip = 0;
+                       idle_chip = 1;
                }
        }
 
-       if (idle_chipset != active_chipset) {
-               idle_bits =
-                   ni_660x_read_register(dev, idle_chipset,
-                                         IOConfigReg(pfi_channel));
-               idle_bits &= ~pfi_output_select_mask(pfi_channel);
-               idle_bits |=
-                   pfi_output_select_bits(pfi_channel,
-                                          pfi_output_select_high_Z);
-               ni_660x_write_register(dev, idle_chipset, idle_bits,
-                                      IOConfigReg(pfi_channel));
+       if (idle_chip != active_chip) {
+               /* set the pfi channel to high-z on the inactive chip */
+               bits = ni_660x_read(dev, idle_chip, NI660X_IO_CFG(chan));
+               bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan);
+               bits |= NI660X_IO_CFG_OUT_SEL(chan, 0);         /* high-z */
+               ni_660x_write(dev, idle_chip, bits, NI660X_IO_CFG(chan));
        }
 
-       active_bits =
-           ni_660x_read_register(dev, active_chipset,
-                                 IOConfigReg(pfi_channel));
-       active_bits &= ~pfi_output_select_mask(pfi_channel);
-       active_bits |= pfi_output_select_bits(pfi_channel, output_select);
-       ni_660x_write_register(dev, active_chipset, active_bits,
-                              IOConfigReg(pfi_channel));
+       /* set the pfi channel output on the active chip */
+       bits = ni_660x_read(dev, active_chip, NI660X_IO_CFG(chan));
+       bits &= ~NI660X_IO_CFG_OUT_SEL_MASK(chan);
+       bits |= NI660X_IO_CFG_OUT_SEL(chan, out_sel);
+       ni_660x_write(dev, active_chip, bits, NI660X_IO_CFG(chan));
 }
 
-static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned chan,
-                                  unsigned source)
+static int ni_660x_set_pfi_routing(struct comedi_device *dev,
+                                  unsigned int chan, unsigned int source)
 {
        struct ni_660x_private *devpriv = dev->private;
 
-       if (source > num_pfi_output_selects)
-               return -EINVAL;
-       if (source == pfi_output_select_high_Z)
-               return -EINVAL;
-       if (chan < min_counter_pfi_chan) {
-               if (source == pfi_output_select_counter)
+       switch (source) {
+       case NI_660X_PFI_OUTPUT_COUNTER:
+               if (chan < 8)
                        return -EINVAL;
-       } else if (chan > max_dio_pfi_chan) {
-               if (source == pfi_output_select_do)
+               break;
+       case NI_660X_PFI_OUTPUT_DIO:
+               if (chan > 31)
                        return -EINVAL;
+       default:
+               return -EINVAL;
        }
 
-       devpriv->pfi_output_selects[chan] = source;
-       if (devpriv->pfi_direction_bits & (((uint64_t) 1) << chan))
-               ni_660x_select_pfi_output(dev, chan,
-                                         devpriv->pfi_output_selects[chan]);
+       devpriv->io_cfg[chan] = source;
+       if (devpriv->io_dir & (1ULL << chan))
+               ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]);
        return 0;
 }
 
@@ -993,25 +799,24 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev,
 {
        struct ni_660x_private *devpriv = dev->private;
        unsigned int chan = CR_CHAN(insn->chanspec);
-       uint64_t bit = 1ULL << chan;
+       u64 bit = 1ULL << chan;
        unsigned int val;
        int ret;
 
        switch (data[0]) {
        case INSN_CONFIG_DIO_OUTPUT:
-               devpriv->pfi_direction_bits |= bit;
-               ni_660x_select_pfi_output(dev, chan,
-                                         devpriv->pfi_output_selects[chan]);
+               devpriv->io_dir |= bit;
+               ni_660x_select_pfi_output(dev, chan, devpriv->io_cfg[chan]);
                break;
 
        case INSN_CONFIG_DIO_INPUT:
-               devpriv->pfi_direction_bits &= ~bit;
-               ni_660x_select_pfi_output(dev, chan, pfi_output_select_high_Z);
+               devpriv->io_dir &= ~bit;
+               ni_660x_select_pfi_output(dev, chan, 0);        /* high-z */
                break;
 
        case INSN_CONFIG_DIO_QUERY:
-               data[1] = (devpriv->pfi_direction_bits & bit) ? COMEDI_OUTPUT
-                                                             : COMEDI_INPUT;
+               data[1] = (devpriv->io_dir & bit) ? COMEDI_OUTPUT
+                                                 : COMEDI_INPUT;
                break;
 
        case INSN_CONFIG_SET_ROUTING:
@@ -1021,14 +826,14 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev,
                break;
 
        case INSN_CONFIG_GET_ROUTING:
-               data[1] = devpriv->pfi_output_selects[chan];
+               data[1] = devpriv->io_cfg[chan];
                break;
 
        case INSN_CONFIG_FILTER:
-               val = ni_660x_read_register(dev, 0, IOConfigReg(chan));
-               val &= ~pfi_input_select_mask(chan);
-               val |= pfi_input_select_bits(chan, data[1]);
-               ni_660x_write_register(dev, 0, val, IOConfigReg(chan));
+               val = ni_660x_read(dev, 0, NI660X_IO_CFG(chan));
+               val &= ~NI660X_IO_CFG_IN_SEL_MASK(chan);
+               val |= NI660X_IO_CFG_IN_SEL(chan, data[1]);
+               ni_660x_write(dev, 0, val, NI660X_IO_CFG(chan));
                break;
 
        default:
@@ -1038,6 +843,33 @@ static int ni_660x_dio_insn_config(struct comedi_device *dev,
        return insn->n;
 }
 
+static void ni_660x_init_tio_chips(struct comedi_device *dev,
+                                  unsigned int n_chips)
+{
+       struct ni_660x_private *devpriv = dev->private;
+       unsigned int chip;
+       unsigned int chan;
+
+       /*
+        * We use the ioconfig registers to control dio direction, so zero
+        * output enables in stc dio control reg.
+        */
+       ni_660x_write(dev, 0, 0, NI660X_STC_DIO_CONTROL);
+
+       for (chip = 0; chip < n_chips; ++chip) {
+               /* init dma configuration register */
+               devpriv->dma_cfg[chip] = 0;
+               for (chan = 0; chan < NI660X_MAX_DMA_CHANNEL; ++chan)
+                       devpriv->dma_cfg[chip] |= NI660X_DMA_CFG_SEL_NONE(chan);
+               ni_660x_write(dev, chip, devpriv->dma_cfg[chip],
+                             NI660X_DMA_CFG);
+
+               /* init ioconfig registers */
+               for (chan = 0; chan < NI660X_NUM_PFI_CHANNELS; ++chan)
+                       ni_660x_write(dev, chip, 0, NI660X_IO_CFG(chan));
+       }
+}
+
 static int ni_660x_auto_attach(struct comedi_device *dev,
                               unsigned long context)
 {
@@ -1045,9 +877,12 @@ static int ni_660x_auto_attach(struct comedi_device *dev,
        const struct ni_660x_board *board = NULL;
        struct ni_660x_private *devpriv;
        struct comedi_subdevice *s;
+       struct ni_gpct_device *gpct_dev;
+       unsigned int n_counters;
+       int subdev;
        int ret;
-       unsigned i;
-       unsigned global_interrupt_config_bits;
+       unsigned int i;
+       unsigned int global_interrupt_config_bits;
 
        if (context < ARRAY_SIZE(ni_660x_boards))
                board = &ni_660x_boards[context];
@@ -1077,79 +912,141 @@ static int ni_660x_auto_attach(struct comedi_device *dev,
        if (ret < 0)
                return ret;
 
-       ret = comedi_alloc_subdevices(dev, 2 + NI_660X_MAX_NUM_COUNTERS);
+       ni_660x_init_tio_chips(dev, board->n_chips);
+
+       ret = comedi_alloc_subdevices(dev, 2 + NI660X_MAX_COUNTERS);
        if (ret)
                return ret;
 
-       s = &dev->subdevices[0];
+       subdev = 0;
+
+       s = &dev->subdevices[subdev++];
        /* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
        s->type = COMEDI_SUBD_UNUSED;
 
-       s = &dev->subdevices[NI_660X_DIO_SUBDEV];
-       /* DIGITAL I/O SUBDEVICE */
-       s->type = COMEDI_SUBD_DIO;
-       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-       s->n_chan = NUM_PFI_CHANNELS;
-       s->maxdata = 1;
-       s->range_table = &range_digital;
-       s->insn_bits = ni_660x_dio_insn_bits;
-       s->insn_config = ni_660x_dio_insn_config;
-       /*  we use the ioconfig registers to control dio direction, so zero
-       output enables in stc dio control reg */
-       ni_660x_write_register(dev, 0, 0, NI660X_STC_DIO_CONTROL);
-
-       devpriv->counter_dev = ni_gpct_device_construct(dev,
-                                                    &ni_gpct_write_register,
-                                                    &ni_gpct_read_register,
-                                                    ni_gpct_variant_660x,
-                                                    ni_660x_num_counters
-                                                    (dev));
-       if (!devpriv->counter_dev)
+       /*
+        * Digital I/O subdevice
+        *
+        * There are 40 channels but only the first 32 can be digital I/Os.
+        * The last 8 are dedicated to counters 0 and 1.
+        *
+        * Counter 0-3 signals are from the first TIO chip.
+        * Counter 4-7 signals are from the second TIO chip.
+        *
+        * Comedi       External
+        * PFI Chan     DIO Chan        Counter Signal
+        * -------      --------        --------------
+        *     0            0
+        *     1            1
+        *     2            2
+        *     3            3
+        *     4            4
+        *     5            5
+        *     6            6
+        *     7            7
+        *     8            8           CTR 7 OUT
+        *     9            9           CTR 7 AUX
+        *    10           10           CTR 7 GATE
+        *    11           11           CTR 7 SOURCE
+        *    12           12           CTR 6 OUT
+        *    13           13           CTR 6 AUX
+        *    14           14           CTR 6 GATE
+        *    15           15           CTR 6 SOURCE
+        *    16           16           CTR 5 OUT
+        *    17           17           CTR 5 AUX
+        *    18           18           CTR 5 GATE
+        *    19           19           CTR 5 SOURCE
+        *    20           20           CTR 4 OUT
+        *    21           21           CTR 4 AUX
+        *    22           22           CTR 4 GATE
+        *    23           23           CTR 4 SOURCE
+        *    24           24           CTR 3 OUT
+        *    25           25           CTR 3 AUX
+        *    26           26           CTR 3 GATE
+        *    27           27           CTR 3 SOURCE
+        *    28           28           CTR 2 OUT
+        *    29           29           CTR 2 AUX
+        *    30           30           CTR 2 GATE
+        *    31           31           CTR 2 SOURCE
+        *    32                        CTR 1 OUT
+        *    33                        CTR 1 AUX
+        *    34                        CTR 1 GATE
+        *    35                        CTR 1 SOURCE
+        *    36                        CTR 0 OUT
+        *    37                        CTR 0 AUX
+        *    38                        CTR 0 GATE
+        *    39                        CTR 0 SOURCE
+        */
+       s = &dev->subdevices[subdev++];
+       s->type         = COMEDI_SUBD_DIO;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+       s->n_chan       = NI660X_NUM_PFI_CHANNELS;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_bits    = ni_660x_dio_insn_bits;
+       s->insn_config  = ni_660x_dio_insn_config;
+
+       n_counters = board->n_chips * NI660X_COUNTERS_PER_CHIP;
+       gpct_dev = ni_gpct_device_construct(dev,
+                                           ni_660x_gpct_write,
+                                           ni_660x_gpct_read,
+                                           ni_gpct_variant_660x,
+                                           n_counters);
+       if (!gpct_dev)
                return -ENOMEM;
-       for (i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) {
-               s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)];
-               if (i < ni_660x_num_counters(dev)) {
-                       s->type = COMEDI_SUBD_COUNTER;
-                       s->subdev_flags = SDF_READABLE | SDF_WRITABLE |
+       devpriv->counter_dev = gpct_dev;
+
+       /* Counter subdevices (4 NI TIO General Purpose Counters per chip) */
+       for (i = 0; i < NI660X_MAX_COUNTERS; ++i) {
+               s = &dev->subdevices[subdev++];
+               if (i < n_counters) {
+                       struct ni_gpct *counter = &gpct_dev->counters[i];
+
+                       counter->chip_index = i / NI660X_COUNTERS_PER_CHIP;
+                       counter->counter_index = i % NI660X_COUNTERS_PER_CHIP;
+
+                       s->type         = COMEDI_SUBD_COUNTER;
+                       s->subdev_flags = SDF_READABLE | SDF_WRITABLE |
                                          SDF_LSAMPL | SDF_CMD_READ;
-                       s->n_chan = 3;
-                       s->maxdata = 0xffffffff;
-                       s->insn_read = ni_tio_insn_read;
-                       s->insn_write = ni_tio_insn_write;
-                       s->insn_config = ni_tio_insn_config;
-                       s->do_cmd = &ni_660x_cmd;
-                       s->len_chanlist = 1;
-                       s->do_cmdtest = ni_tio_cmdtest;
-                       s->cancel = &ni_660x_cancel;
-                       s->poll = &ni_660x_input_poll;
+                       s->n_chan       = 3;
+                       s->maxdata      = 0xffffffff;
+                       s->insn_read    = ni_tio_insn_read;
+                       s->insn_write   = ni_tio_insn_write;
+                       s->insn_config  = ni_tio_insn_config;
+                       s->len_chanlist = 1;
+                       s->do_cmd       = ni_660x_cmd;
+                       s->do_cmdtest   = ni_tio_cmdtest;
+                       s->cancel       = ni_660x_cancel;
+                       s->poll         = ni_660x_input_poll;
+                       s->buf_change   = ni_660x_buf_change;
                        s->async_dma_dir = DMA_BIDIRECTIONAL;
-                       s->buf_change = &ni_660x_buf_change;
-                       s->private = &devpriv->counter_dev->counters[i];
-
-                       devpriv->counter_dev->counters[i].chip_index =
-                           i / counters_per_chip;
-                       devpriv->counter_dev->counters[i].counter_index =
-                           i % counters_per_chip;
+                       s->private      = counter;
                } else {
-                       s->type = COMEDI_SUBD_UNUSED;
+                       s->type         = COMEDI_SUBD_UNUSED;
                }
        }
-       for (i = 0; i < board->n_chips; ++i)
-               init_tio_chip(dev, i);
 
-       for (i = 0; i < ni_660x_num_counters(dev); ++i)
-               ni_tio_init_counter(&devpriv->counter_dev->counters[i]);
-
-       for (i = 0; i < NUM_PFI_CHANNELS; ++i) {
-               if (i < min_counter_pfi_chan)
-                       ni_660x_set_pfi_routing(dev, i, pfi_output_select_do);
+       for (i = 0; i < n_counters; ++i)
+               ni_tio_init_counter(&gpct_dev->counters[i]);
+
+        /*
+         * Default the DIO channels as:
+         *   chan 0-7:  DIO inputs
+         *   chan 8-39: counter signal inputs
+         */
+       for (i = 0; i < NI660X_NUM_PFI_CHANNELS; ++i) {
+               if (i < 8)
+                       ni_660x_set_pfi_routing(dev, i, NI_660X_PFI_OUTPUT_DIO);
                else
                        ni_660x_set_pfi_routing(dev, i,
-                                               pfi_output_select_counter);
-               ni_660x_select_pfi_output(dev, i, pfi_output_select_high_Z);
+                                               NI_660X_PFI_OUTPUT_COUNTER);
+               ni_660x_select_pfi_output(dev, i, 0);           /* high-z */
        }
-       /* to be safe, set counterswap bits on tio chips after all the counter
-          outputs have been set to high impedance mode */
+
+       /*
+        * To be safe, set counterswap bits on tio chips after all the counter
+        * outputs have been set to high impedance mode.
+        */
        for (i = 0; i < board->n_chips; ++i)
                set_tio_counterswap(dev, i);
 
@@ -1160,11 +1057,11 @@ static int ni_660x_auto_attach(struct comedi_device *dev,
                return ret;
        }
        dev->irq = pcidev->irq;
-       global_interrupt_config_bits = Global_Int_Enable_Bit;
+       global_interrupt_config_bits = NI660X_GLOBAL_INT_GLOBAL;
        if (board->n_chips > 1)
-               global_interrupt_config_bits |= Cascade_Int_Enable_Bit;
-       ni_660x_write_register(dev, 0, global_interrupt_config_bits,
-                              NI660X_GLOBAL_INT_CFG);
+               global_interrupt_config_bits |= NI660X_GLOBAL_INT_CASCADE;
+       ni_660x_write(dev, 0, global_interrupt_config_bits,
+                     NI660X_GLOBAL_INT_CFG);
 
        return 0;
 }
@@ -1173,11 +1070,12 @@ static void ni_660x_detach(struct comedi_device *dev)
 {
        struct ni_660x_private *devpriv = dev->private;
 
-       if (dev->irq)
+       if (dev->irq) {
+               ni_660x_write(dev, 0, 0, NI660X_GLOBAL_INT_CFG);
                free_irq(dev->irq, dev);
+       }
        if (devpriv) {
-               if (devpriv->counter_dev)
-                       ni_gpct_device_destroy(devpriv->counter_dev);
+               ni_gpct_device_destroy(devpriv->counter_dev);
                ni_660x_free_mite_rings(dev);
                mite_detach(devpriv->mite);
        }