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