staging: comedi: ni_65xx: refactor edge detection configuration
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / ni_65xx.c
index 9a139d6b8ef4e7aa68db819788ab504c119f7088..f7566fe461b42f748df1090fcf25705361f554d3 100644 (file)
@@ -1,46 +1,73 @@
 /*
-    comedi/drivers/ni_6514.c
-    driver for National Instruments PCI-6514
-
-    Copyright (C) 2006 Jon Grierson <jd@renko.co.uk>
-    Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
-
-    COMEDI - Linux Control and Measurement Device Interface
-    Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
-
-    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.
+ * ni_65xx.c
+ * Comedi driver for National Instruments PCI-65xx static dio boards
+ *
+ * Copyright (C) 2006 Jon Grierson <jd@renko.co.uk>
+ * Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
+ *
+ * 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.
+ */
 
-    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_65xx
-Description: National Instruments 65xx static dio boards
-Author: Jon Grierson <jd@renko.co.uk>,
-       Frank Mori Hess <fmhess@users.sourceforge.net>
-Status: testing
-Devices: [National Instruments] PCI-6509 (ni_65xx), PXI-6509, PCI-6510,
-  PCI-6511, PXI-6511, PCI-6512, PXI-6512, PCI-6513, PXI-6513, PCI-6514,
-  PXI-6514, PCI-6515, PXI-6515, PCI-6516, PCI-6517, PCI-6518, PCI-6519,
-  PCI-6520, PCI-6521, PXI-6521, PCI-6528, PXI-6528
-Updated: Wed Oct 18 08:59:11 EDT 2006
-
-Based on the PCI-6527 driver by ds.
-The interrupt subdevice (subdevice 3) is probably broken for all boards
-except maybe the 6514.
-
-*/
+ * Driver: ni_65xx
+ * Description: National Instruments 65xx static dio boards
+ * Author: Jon Grierson <jd@renko.co.uk>,
+ *        Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: testing
+ * Devices: (National Instruments) PCI-6509 [ni_65xx]
+ *         (National Instruments) PXI-6509 [ni_65xx]
+ *         (National Instruments) PCI-6510 [ni_65xx]
+ *         (National Instruments) PCI-6511 [ni_65xx]
+ *         (National Instruments) PXI-6511 [ni_65xx]
+ *         (National Instruments) PCI-6512 [ni_65xx]
+ *         (National Instruments) PXI-6512 [ni_65xx]
+ *         (National Instruments) PCI-6513 [ni_65xx]
+ *         (National Instruments) PXI-6513 [ni_65xx]
+ *         (National Instruments) PCI-6514 [ni_65xx]
+ *         (National Instruments) PXI-6514 [ni_65xx]
+ *         (National Instruments) PCI-6515 [ni_65xx]
+ *         (National Instruments) PXI-6515 [ni_65xx]
+ *         (National Instruments) PCI-6516 [ni_65xx]
+ *         (National Instruments) PCI-6517 [ni_65xx]
+ *         (National Instruments) PCI-6518 [ni_65xx]
+ *         (National Instruments) PCI-6519 [ni_65xx]
+ *         (National Instruments) PCI-6520 [ni_65xx]
+ *         (National Instruments) PCI-6521 [ni_65xx]
+ *         (National Instruments) PXI-6521 [ni_65xx]
+ *         (National Instruments) PCI-6528 [ni_65xx]
+ *         (National Instruments) PXI-6528 [ni_65xx]
+ * Updated: Mon, 21 Jul 2014 12:49:58 +0000
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ *
+ * Based on the PCI-6527 driver by ds.
+ * The interrupt subdevice (subdevice 3) is probably broken for all
+ * boards except maybe the 6514.
+ *
+ * This driver previously inverted the outputs on PCI-6513 through to
+ * PCI-6519 and on PXI-6513 through to PXI-6515.  It no longer inverts
+ * outputs on those cards by default as it didn't make much sense.  If
+ * you require the outputs to be inverted on those cards for legacy
+ * reasons, set the module parameter "legacy_invert_outputs=true" when
+ * loading the module, or set "ni_65xx.legacy_invert_outputs=true" on
+ * the kernel command line if the driver is built in to the kernel.
+ */
 
 /*
-   Manuals (available from ftp://ftp.natinst.com/support/manuals)
-
-       370106b.pdf     6514 Register Level Programmer Manual
-
+ * Manuals (available from ftp://ftp.natinst.com/support/manuals)
+ *
+ *     370106b.pdf     6514 Register Level Programmer Manual
  */
 
 #include <linux/module.h>
@@ -50,59 +77,69 @@ except maybe the 6514.
 #include "../comedidev.h"
 
 #include "comedi_fc.h"
-#include "mite.h"
-
-#define NI6514_DIO_SIZE 4096
-#define NI6514_MITE_SIZE 4096
-
-#define NI_65XX_MAX_NUM_PORTS 12
-static const unsigned ni_65xx_channels_per_port = 8;
-static const unsigned ni_65xx_port_offset = 0x10;
-
-static inline unsigned Port_Data(unsigned port)
-{
-       return 0x40 + port * ni_65xx_port_offset;
-}
-
-static inline unsigned Port_Select(unsigned port)
-{
-       return 0x41 + port * ni_65xx_port_offset;
-}
-
-static inline unsigned Rising_Edge_Detection_Enable(unsigned port)
-{
-       return 0x42 + port * ni_65xx_port_offset;
-}
-
-static inline unsigned Falling_Edge_Detection_Enable(unsigned port)
-{
-       return 0x43 + port * ni_65xx_port_offset;
-}
-
-static inline unsigned Filter_Enable(unsigned port)
-{
-       return 0x44 + port * ni_65xx_port_offset;
-}
-
-#define ID_Register                            0x00
 
-#define Clear_Register                         0x01
-#define ClrEdge                                0x08
-#define ClrOverflow                    0x04
-
-#define Filter_Interval                        0x08
-
-#define Change_Status                          0x02
-#define MasterInterruptStatus          0x04
-#define Overflow                       0x02
-#define EdgeStatus                     0x01
+/*
+ * PCI BAR1 Register Map
+ */
 
-#define Master_Interrupt_Control               0x03
-#define FallingEdgeIntEnable           0x10
-#define RisingEdgeIntEnable            0x08
-#define MasterInterruptEnable          0x04
-#define OverflowIntEnable              0x02
-#define EdgeIntEnable                  0x01
+/* Non-recurring Registers (8-bit except where noted) */
+#define NI_65XX_ID_REG                 0x00
+#define NI_65XX_CLR_REG                        0x01
+#define NI_65XX_CLR_WDOG_INT           (1 << 6)
+#define NI_65XX_CLR_WDOG_PING          (1 << 5)
+#define NI_65XX_CLR_WDOG_EXP           (1 << 4)
+#define NI_65XX_CLR_EDGE_INT           (1 << 3)
+#define NI_65XX_CLR_OVERFLOW_INT       (1 << 2)
+#define NI_65XX_STATUS_REG             0x02
+#define NI_65XX_STATUS_WDOG_INT                (1 << 5)
+#define NI_65XX_STATUS_FALL_EDGE       (1 << 4)
+#define NI_65XX_STATUS_RISE_EDGE       (1 << 3)
+#define NI_65XX_STATUS_INT             (1 << 2)
+#define NI_65XX_STATUS_OVERFLOW_INT    (1 << 1)
+#define NI_65XX_STATUS_EDGE_INT                (1 << 0)
+#define NI_65XX_CTRL_REG               0x03
+#define NI_65XX_CTRL_WDOG_ENA          (1 << 5)
+#define NI_65XX_CTRL_FALL_EDGE_ENA     (1 << 4)
+#define NI_65XX_CTRL_RISE_EDGE_ENA     (1 << 3)
+#define NI_65XX_CTRL_INT_ENA           (1 << 2)
+#define NI_65XX_CTRL_OVERFLOW_ENA      (1 << 1)
+#define NI_65XX_CTRL_EDGE_ENA          (1 << 0)
+#define NI_65XX_REV_REG                        0x04 /* 32-bit */
+#define NI_65XX_FILTER_REG             0x08 /* 32-bit */
+#define NI_65XX_RTSI_ROUTE_REG         0x0c /* 16-bit */
+#define NI_65XX_RTSI_EDGE_REG          0x0e /* 16-bit */
+#define NI_65XX_RTSI_WDOG_REG          0x10 /* 16-bit */
+#define NI_65XX_RTSI_TRIG_REG          0x12 /* 16-bit */
+#define NI_65XX_AUTO_CLK_SEL_REG       0x14 /* PXI-6528 only */
+#define NI_65XX_AUTO_CLK_SEL_STATUS    (1 << 1)
+#define NI_65XX_AUTO_CLK_SEL_DISABLE   (1 << 0)
+#define NI_65XX_WDOG_CTRL_REG          0x15
+#define NI_65XX_WDOG_CTRL_ENA          (1 << 0)
+#define NI_65XX_RTSI_CFG_REG           0x16
+#define NI_65XX_RTSI_CFG_RISE_SENSE    (1 << 2)
+#define NI_65XX_RTSI_CFG_FALL_SENSE    (1 << 1)
+#define NI_65XX_RTSI_CFG_SYNC_DETECT   (1 << 0)
+#define NI_65XX_WDOG_STATUS_REG                0x17
+#define NI_65XX_WDOG_STATUS_EXP                (1 << 0)
+#define NI_65XX_WDOG_INTERVAL_REG      0x18 /* 32-bit */
+
+/* Recurring port registers (8-bit) */
+#define NI_65XX_PORT(x)                        ((x) * 0x10)
+#define NI_65XX_IO_DATA_REG(x)         (0x40 + NI_65XX_PORT(x))
+#define NI_65XX_IO_SEL_REG(x)          (0x41 + NI_65XX_PORT(x))
+#define NI_65XX_IO_SEL_OUTPUT          (0 << 0)
+#define NI_65XX_IO_SEL_INPUT           (1 << 0)
+#define NI_65XX_RISE_EDGE_ENA_REG(x)   (0x42 + NI_65XX_PORT(x))
+#define NI_65XX_FALL_EDGE_ENA_REG(x)   (0x43 + NI_65XX_PORT(x))
+#define NI_65XX_FILTER_ENA(x)          (0x44 + NI_65XX_PORT(x))
+#define NI_65XX_WDOG_HIZ_REG(x)                (0x46 + NI_65XX_PORT(x))
+#define NI_65XX_WDOG_ENA(x)            (0x47 + NI_65XX_PORT(x))
+#define NI_65XX_WDOG_HI_LO_REG(x)      (0x48 + NI_65XX_PORT(x))
+#define NI_65XX_RTSI_ENA(x)            (0x49 + NI_65XX_PORT(x))
+
+#define NI_65XX_PORT_TO_CHAN(x)                ((x) * 8)
+#define NI_65XX_CHAN_TO_PORT(x)                ((x) / 8)
+#define NI_65XX_CHAN_TO_MASK(x)                (1 << ((x) % 8))
 
 enum ni_65xx_boardid {
        BOARD_PCI6509,
@@ -134,7 +171,7 @@ struct ni_65xx_board {
        unsigned num_dio_ports;
        unsigned num_di_ports;
        unsigned num_do_ports;
-       unsigned invert_outputs:1;
+       unsigned legacy_invert:1;
 };
 
 static const struct ni_65xx_board ni_65xx_boards[] = {
@@ -169,58 +206,58 @@ static const struct ni_65xx_board ni_65xx_boards[] = {
        [BOARD_PCI6513] = {
                .name           = "pci-6513",
                .num_do_ports   = 8,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PXI6513] = {
                .name           = "pxi-6513",
                .num_do_ports   = 8,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6514] = {
                .name           = "pci-6514",
                .num_di_ports   = 4,
                .num_do_ports   = 4,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PXI6514] = {
                .name           = "pxi-6514",
                .num_di_ports   = 4,
                .num_do_ports   = 4,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6515] = {
                .name           = "pci-6515",
                .num_di_ports   = 4,
                .num_do_ports   = 4,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PXI6515] = {
                .name           = "pxi-6515",
                .num_di_ports   = 4,
                .num_do_ports   = 4,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6516] = {
                .name           = "pci-6516",
                .num_do_ports   = 4,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6517] = {
                .name           = "pci-6517",
                .num_do_ports   = 4,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6518] = {
                .name           = "pci-6518",
                .num_di_ports   = 2,
                .num_do_ports   = 2,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6519] = {
                .name           = "pci-6519",
                .num_di_ports   = 2,
                .num_do_ports   = 2,
-               .invert_outputs = 1,
+               .legacy_invert  = 1,
        },
        [BOARD_PCI6520] = {
                .name           = "pci-6520",
@@ -249,135 +286,172 @@ static const struct ni_65xx_board ni_65xx_boards[] = {
        },
 };
 
-static inline unsigned ni_65xx_port_by_channel(unsigned channel)
-{
-       return channel / ni_65xx_channels_per_port;
-}
+static bool ni_65xx_legacy_invert_outputs;
+module_param_named(legacy_invert_outputs, ni_65xx_legacy_invert_outputs,
+                  bool, 0444);
+MODULE_PARM_DESC(legacy_invert_outputs,
+                "invert outputs of PCI/PXI-6513/6514/6515/6516/6517/6518/6519 for compatibility with old user code");
 
-static inline unsigned ni_65xx_total_num_ports(const struct ni_65xx_board
-                                              *board)
+struct ni_65xx_private {
+       void __iomem *mmio;
+};
+
+static unsigned int ni_65xx_num_ports(struct comedi_device *dev)
 {
+       const struct ni_65xx_board *board = comedi_board(dev);
+
        return board->num_dio_ports + board->num_di_ports + board->num_do_ports;
 }
 
-struct ni_65xx_private {
-       struct mite_struct *mite;
-       unsigned int filter_interval;
-       unsigned short filter_enable[NI_65XX_MAX_NUM_PORTS];
-       unsigned short output_bits[NI_65XX_MAX_NUM_PORTS];
-       unsigned short dio_direction[NI_65XX_MAX_NUM_PORTS];
-};
+static void ni_65xx_disable_input_filters(struct comedi_device *dev)
+{
+       struct ni_65xx_private *devpriv = dev->private;
+       unsigned int num_ports = ni_65xx_num_ports(dev);
+       int i;
 
-struct ni_65xx_subdevice_private {
-       unsigned base_port;
-};
+       /* disable input filtering on all ports */
+       for (i = 0; i < num_ports; ++i)
+               writeb(0x00, devpriv->mmio + NI_65XX_FILTER_ENA(i));
 
-static inline struct ni_65xx_subdevice_private *sprivate(struct comedi_subdevice
-                                                        *subdev)
-{
-       return subdev->private;
+       /* set filter interval to 0 (32bit reg) */
+       writel(0x00000000, devpriv->mmio + NI_65XX_FILTER_REG);
 }
 
-static int ni_65xx_config_filter(struct comedi_device *dev,
-                                struct comedi_subdevice *s,
-                                struct comedi_insn *insn, unsigned int *data)
+/* updates edge detection for base_chan to base_chan+31 */
+static void ni_65xx_update_edge_detection(struct comedi_device *dev,
+                                         unsigned int base_chan,
+                                         unsigned int rising,
+                                         unsigned int falling)
 {
        struct ni_65xx_private *devpriv = dev->private;
-       const unsigned chan = CR_CHAN(insn->chanspec);
-       const unsigned port =
-           sprivate(s)->base_port + ni_65xx_port_by_channel(chan);
+       unsigned int num_ports = ni_65xx_num_ports(dev);
+       unsigned int port;
 
-       if (data[0] != INSN_CONFIG_FILTER)
-               return -EINVAL;
-       if (data[1]) {
-               static const unsigned filter_resolution_ns = 200;
-               static const unsigned max_filter_interval = 0xfffff;
-               unsigned interval =
-                   (data[1] +
-                    (filter_resolution_ns / 2)) / filter_resolution_ns;
-               if (interval > max_filter_interval)
-                       interval = max_filter_interval;
-               data[1] = interval * filter_resolution_ns;
-
-               if (interval != devpriv->filter_interval) {
-                       writeb(interval,
-                              devpriv->mite->daq_io_addr +
-                              Filter_Interval);
-                       devpriv->filter_interval = interval;
-               }
+       if (base_chan >= NI_65XX_PORT_TO_CHAN(num_ports))
+               return;
 
-               devpriv->filter_enable[port] |=
-                   1 << (chan % ni_65xx_channels_per_port);
-       } else {
-               devpriv->filter_enable[port] &=
-                   ~(1 << (chan % ni_65xx_channels_per_port));
-       }
+       for (port = NI_65XX_CHAN_TO_PORT(base_chan); port < num_ports; port++) {
+               int bitshift = (int)(NI_65XX_PORT_TO_CHAN(port) - base_chan);
+               unsigned int port_mask, port_rising, port_falling;
 
-       writeb(devpriv->filter_enable[port],
-              devpriv->mite->daq_io_addr + Filter_Enable(port));
+               if (bitshift >= 32)
+                       break;
 
-       return 2;
+               if (bitshift >= 0) {
+                       port_mask = ~0U >> bitshift;
+                       port_rising = rising >> bitshift;
+                       port_falling = falling >> bitshift;
+               } else {
+                       port_mask = ~0U << -bitshift;
+                       port_rising = rising << -bitshift;
+                       port_falling = falling << -bitshift;
+               }
+               if (port_mask & 0xff) {
+                       if (~port_mask & 0xff) {
+                               port_rising |=
+                                   readb(devpriv->mmio +
+                                         NI_65XX_RISE_EDGE_ENA_REG(port)) &
+                                   ~port_mask;
+                               port_falling |=
+                                   readb(devpriv->mmio +
+                                         NI_65XX_FALL_EDGE_ENA_REG(port)) &
+                                   ~port_mask;
+                       }
+                       writeb(port_rising & 0xff,
+                              devpriv->mmio + NI_65XX_RISE_EDGE_ENA_REG(port));
+                       writeb(port_falling & 0xff,
+                              devpriv->mmio + NI_65XX_FALL_EDGE_ENA_REG(port));
+               }
+       }
 }
 
 static int ni_65xx_dio_insn_config(struct comedi_device *dev,
                                   struct comedi_subdevice *s,
-                                  struct comedi_insn *insn, unsigned int *data)
+                                  struct comedi_insn *insn,
+                                  unsigned int *data)
 {
        struct ni_65xx_private *devpriv = dev->private;
-       unsigned port;
+       unsigned long base_port = (unsigned long)s->private;
+       unsigned int chan = CR_CHAN(insn->chanspec);
+       unsigned int chan_mask = NI_65XX_CHAN_TO_MASK(chan);
+       unsigned port = base_port + NI_65XX_CHAN_TO_PORT(chan);
+       unsigned int interval;
+       unsigned int val;
 
-       if (insn->n < 1)
-               return -EINVAL;
-       port = sprivate(s)->base_port +
-           ni_65xx_port_by_channel(CR_CHAN(insn->chanspec));
        switch (data[0]) {
        case INSN_CONFIG_FILTER:
-               return ni_65xx_config_filter(dev, s, insn, data);
+               /*
+                * The deglitch filter interval is specified in nanoseconds.
+                * The hardware supports intervals in 200ns increments. Round
+                * the user values up and return the actual interval.
+                */
+               interval = (data[1] + 100) / 200;
+               if (interval > 0xfffff)
+                       interval = 0xfffff;
+               data[1] = interval * 200;
+
+               /*
+                * Enable/disable the channel for deglitch filtering. Note
+                * that the filter interval is never set to '0'. This is done
+                * because other channels might still be enabled for filtering.
+                */
+               val = readb(devpriv->mmio + NI_65XX_FILTER_ENA(port));
+               if (interval) {
+                       writel(interval, devpriv->mmio + NI_65XX_FILTER_REG);
+                       val |= chan_mask;
+               } else {
+                       val &= ~chan_mask;
+               }
+               writeb(val, devpriv->mmio + NI_65XX_FILTER_ENA(port));
                break;
+
        case INSN_CONFIG_DIO_OUTPUT:
                if (s->type != COMEDI_SUBD_DIO)
                        return -EINVAL;
-               devpriv->dio_direction[port] = COMEDI_OUTPUT;
-               writeb(0, devpriv->mite->daq_io_addr + Port_Select(port));
-               return 1;
+               writeb(NI_65XX_IO_SEL_OUTPUT,
+                      devpriv->mmio + NI_65XX_IO_SEL_REG(port));
                break;
+
        case INSN_CONFIG_DIO_INPUT:
                if (s->type != COMEDI_SUBD_DIO)
                        return -EINVAL;
-               devpriv->dio_direction[port] = COMEDI_INPUT;
-               writeb(1, devpriv->mite->daq_io_addr + Port_Select(port));
-               return 1;
+               writeb(NI_65XX_IO_SEL_INPUT,
+                      devpriv->mmio + NI_65XX_IO_SEL_REG(port));
                break;
+
        case INSN_CONFIG_DIO_QUERY:
                if (s->type != COMEDI_SUBD_DIO)
                        return -EINVAL;
-               data[1] = devpriv->dio_direction[port];
-               return insn->n;
+               val = readb(devpriv->mmio + NI_65XX_IO_SEL_REG(port));
+               data[1] = (val == NI_65XX_IO_SEL_INPUT) ? COMEDI_INPUT
+                                                       : COMEDI_OUTPUT;
                break;
+
        default:
-               break;
+               return -EINVAL;
        }
-       return -EINVAL;
+
+       return insn->n;
 }
 
 static int ni_65xx_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)
 {
-       const struct ni_65xx_board *board = comedi_board(dev);
        struct ni_65xx_private *devpriv = dev->private;
-       int base_bitfield_channel;
+       unsigned long base_port = (unsigned long)s->private;
+       unsigned int base_chan = CR_CHAN(insn->chanspec);
+       int last_port_offset = NI_65XX_CHAN_TO_PORT(s->n_chan - 1);
        unsigned read_bits = 0;
-       int last_port_offset = ni_65xx_port_by_channel(s->n_chan - 1);
        int port_offset;
 
-       base_bitfield_channel = CR_CHAN(insn->chanspec);
-       for (port_offset = ni_65xx_port_by_channel(base_bitfield_channel);
+       for (port_offset = NI_65XX_CHAN_TO_PORT(base_chan);
             port_offset <= last_port_offset; port_offset++) {
-               unsigned port = sprivate(s)->base_port + port_offset;
-               int base_port_channel = port_offset * ni_65xx_channels_per_port;
-               unsigned port_mask, port_data, port_read_bits;
-               int bitshift = base_port_channel - base_bitfield_channel;
+               unsigned port = base_port + port_offset;
+               int base_port_channel = NI_65XX_PORT_TO_CHAN(port_offset);
+               unsigned port_mask, port_data, bits;
+               int bitshift = base_port_channel - base_chan;
 
                if (bitshift >= 32)
                        break;
@@ -392,32 +466,26 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev,
                }
                port_mask &= 0xff;
                port_data &= 0xff;
+
+               /* update the outputs */
                if (port_mask) {
-                       unsigned bits;
-                       devpriv->output_bits[port] &= ~port_mask;
-                       devpriv->output_bits[port] |=
-                           port_data & port_mask;
-                       bits = devpriv->output_bits[port];
-                       if (board->invert_outputs)
-                               bits = ~bits;
-                       writeb(bits,
-                              devpriv->mite->daq_io_addr +
-                              Port_Data(port));
-               }
-               port_read_bits =
-                   readb(devpriv->mite->daq_io_addr + Port_Data(port));
-               if (s->type == COMEDI_SUBD_DO && board->invert_outputs) {
-                       /* Outputs inverted, so invert value read back from
-                        * DO subdevice.  (Does not apply to boards with DIO
-                        * subdevice.) */
-                       port_read_bits ^= 0xFF;
+                       bits = readb(devpriv->mmio + NI_65XX_IO_DATA_REG(port));
+                       bits ^= s->io_bits;     /* invert if necessary */
+                       bits &= ~port_mask;
+                       bits |= (port_data & port_mask);
+                       bits ^= s->io_bits;     /* invert back */
+                       writeb(bits, devpriv->mmio + NI_65XX_IO_DATA_REG(port));
                }
+
+               /* read back the actual state */
+               bits = readb(devpriv->mmio + NI_65XX_IO_DATA_REG(port));
+               bits ^= s->io_bits;     /* invert if necessary */
                if (bitshift > 0)
-                       port_read_bits <<= bitshift;
+                       bits <<= bitshift;
                else
-                       port_read_bits >>= -bitshift;
+                       bits >>= -bitshift;
 
-               read_bits |= port_read_bits;
+               read_bits |= bits;
        }
        data[1] = read_bits;
        return insn->n;
@@ -430,14 +498,14 @@ static irqreturn_t ni_65xx_interrupt(int irq, void *d)
        struct comedi_subdevice *s = dev->read_subdev;
        unsigned int status;
 
-       status = readb(devpriv->mite->daq_io_addr + Change_Status);
-       if ((status & MasterInterruptStatus) == 0)
+       status = readb(devpriv->mmio + NI_65XX_STATUS_REG);
+       if ((status & NI_65XX_STATUS_INT) == 0)
                return IRQ_NONE;
-       if ((status & EdgeStatus) == 0)
+       if ((status & NI_65XX_STATUS_EDGE_INT) == 0)
                return IRQ_NONE;
 
-       writeb(ClrEdge | ClrOverflow,
-              devpriv->mite->daq_io_addr + Clear_Register);
+       writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT,
+              devpriv->mmio + NI_65XX_CLR_REG);
 
        comedi_buf_put(s, 0);
        s->async->events |= COMEDI_CB_EOS;
@@ -492,11 +560,11 @@ static int ni_65xx_intr_cmd(struct comedi_device *dev,
 {
        struct ni_65xx_private *devpriv = dev->private;
 
-       writeb(ClrEdge | ClrOverflow,
-              devpriv->mite->daq_io_addr + Clear_Register);
-       writeb(FallingEdgeIntEnable | RisingEdgeIntEnable |
-              MasterInterruptEnable | EdgeIntEnable,
-              devpriv->mite->daq_io_addr + Master_Interrupt_Control);
+       writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT,
+              devpriv->mmio + NI_65XX_CLR_REG);
+       writeb(NI_65XX_CTRL_FALL_EDGE_ENA | NI_65XX_CTRL_RISE_EDGE_ENA |
+              NI_65XX_CTRL_INT_ENA | NI_65XX_CTRL_EDGE_ENA,
+              devpriv->mmio + NI_65XX_CTRL_REG);
 
        return 0;
 }
@@ -506,14 +574,15 @@ static int ni_65xx_intr_cancel(struct comedi_device *dev,
 {
        struct ni_65xx_private *devpriv = dev->private;
 
-       writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
+       writeb(0x00, devpriv->mmio + NI_65XX_CTRL_REG);
 
        return 0;
 }
 
 static int ni_65xx_intr_insn_bits(struct comedi_device *dev,
                                  struct comedi_subdevice *s,
-                                 struct comedi_insn *insn, unsigned int *data)
+                                 struct comedi_insn *insn,
+                                 unsigned int *data)
 {
        data[1] = 0;
        return insn->n;
@@ -524,40 +593,45 @@ static int ni_65xx_intr_insn_config(struct comedi_device *dev,
                                    struct comedi_insn *insn,
                                    unsigned int *data)
 {
-       struct ni_65xx_private *devpriv = dev->private;
+       switch (data[0]) {
+       case INSN_CONFIG_CHANGE_NOTIFY:
+               /* add instruction to check_insn_config_length() */
+               if (insn->n != 3)
+                       return -EINVAL;
 
-       if (insn->n < 1)
-               return -EINVAL;
-       if (data[0] != INSN_CONFIG_CHANGE_NOTIFY)
+               /*
+                * This only works for the first 4 ports (32 channels)!
+                */
+               ni_65xx_update_edge_detection(dev, 0, data[1], data[2]);
+               break;
+       default:
                return -EINVAL;
+       }
+
+       return insn->n;
+}
+
+/* ripped from mite.h and mite_setup2() to avoid mite dependancy */
+#define MITE_IODWBSR   0xc0     /* IO Device Window Base Size Register */
+#define WENAB          (1 << 7) /* window enable */
+
+static int ni_65xx_mite_init(struct pci_dev *pcidev)
+{
+       void __iomem *mite_base;
+       u32 main_phys_addr;
+
+       /* ioremap the MITE registers (BAR 0) temporarily */
+       mite_base = pci_ioremap_bar(pcidev, 0);
+       if (!mite_base)
+               return -ENOMEM;
+
+       /* set data window to main registers (BAR 1) */
+       main_phys_addr = pci_resource_start(pcidev, 1);
+       writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR);
 
-       writeb(data[1],
-              devpriv->mite->daq_io_addr +
-              Rising_Edge_Detection_Enable(0));
-       writeb(data[1] >> 8,
-              devpriv->mite->daq_io_addr +
-              Rising_Edge_Detection_Enable(0x10));
-       writeb(data[1] >> 16,
-              devpriv->mite->daq_io_addr +
-              Rising_Edge_Detection_Enable(0x20));
-       writeb(data[1] >> 24,
-              devpriv->mite->daq_io_addr +
-              Rising_Edge_Detection_Enable(0x30));
-
-       writeb(data[2],
-              devpriv->mite->daq_io_addr +
-              Falling_Edge_Detection_Enable(0));
-       writeb(data[2] >> 8,
-              devpriv->mite->daq_io_addr +
-              Falling_Edge_Detection_Enable(0x10));
-       writeb(data[2] >> 16,
-              devpriv->mite->daq_io_addr +
-              Falling_Edge_Detection_Enable(0x20));
-       writeb(data[2] >> 24,
-              devpriv->mite->daq_io_addr +
-              Falling_Edge_Detection_Enable(0x30));
-
-       return 2;
+       /* finished with MITE registers */
+       iounmap(mite_base);
+       return 0;
 }
 
 static int ni_65xx_auto_attach(struct comedi_device *dev,
@@ -566,7 +640,6 @@ static int ni_65xx_auto_attach(struct comedi_device *dev,
        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
        const struct ni_65xx_board *board = NULL;
        struct ni_65xx_private *devpriv;
-       struct ni_65xx_subdevice_private *spriv;
        struct comedi_subdevice *s;
        unsigned i;
        int ret;
@@ -586,19 +659,27 @@ static int ni_65xx_auto_attach(struct comedi_device *dev,
        if (!devpriv)
                return -ENOMEM;
 
-       devpriv->mite = mite_alloc(pcidev);
-       if (!devpriv->mite)
+       ret = ni_65xx_mite_init(pcidev);
+       if (ret)
+               return ret;
+
+       devpriv->mmio = pci_ioremap_bar(pcidev, 1);
+       if (!devpriv->mmio)
                return -ENOMEM;
 
-       ret = mite_setup(devpriv->mite);
-       if (ret < 0) {
-               dev_warn(dev->class_dev, "error setting up mite\n");
-               return ret;
+       writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT,
+              devpriv->mmio + NI_65XX_CLR_REG);
+       writeb(0x00, devpriv->mmio + NI_65XX_CTRL_REG);
+
+       if (pcidev->irq) {
+               ret = request_irq(pcidev->irq, ni_65xx_interrupt, IRQF_SHARED,
+                                 dev->board_name, dev);
+               if (ret == 0)
+                       dev->irq = pcidev->irq;
        }
 
-       dev->irq = mite_irq(devpriv->mite);
        dev_info(dev->class_dev, "board: %s, ID=0x%02x", dev->board_name,
-              readb(devpriv->mite->daq_io_addr + ID_Register));
+              readb(devpriv->mmio + NI_65XX_ID_REG));
 
        ret = comedi_alloc_subdevices(dev, 4);
        if (ret)
@@ -606,102 +687,91 @@ static int ni_65xx_auto_attach(struct comedi_device *dev,
 
        s = &dev->subdevices[0];
        if (board->num_di_ports) {
-               s->type = COMEDI_SUBD_DI;
-               s->subdev_flags = SDF_READABLE;
-               s->n_chan =
-                   board->num_di_ports * ni_65xx_channels_per_port;
-               s->range_table = &range_digital;
-               s->maxdata = 1;
-               s->insn_config = ni_65xx_dio_insn_config;
-               s->insn_bits = ni_65xx_dio_insn_bits;
-               spriv = comedi_alloc_spriv(s, sizeof(*spriv));
-               if (!spriv)
-                       return -ENOMEM;
-               spriv->base_port = 0;
+               s->type         = COMEDI_SUBD_DI;
+               s->subdev_flags = SDF_READABLE;
+               s->n_chan       = NI_65XX_PORT_TO_CHAN(board->num_di_ports);
+               s->maxdata      = 1;
+               s->range_table  = &range_digital;
+               s->insn_bits    = ni_65xx_dio_insn_bits;
+               s->insn_config  = ni_65xx_dio_insn_config;
+
+               /* the input ports always start at port 0 */
+               s->private = (void *)0;
        } else {
-               s->type = COMEDI_SUBD_UNUSED;
+               s->type         = COMEDI_SUBD_UNUSED;
        }
 
        s = &dev->subdevices[1];
        if (board->num_do_ports) {
-               s->type = COMEDI_SUBD_DO;
-               s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-               s->n_chan =
-                   board->num_do_ports * ni_65xx_channels_per_port;
-               s->range_table = &range_digital;
-               s->maxdata = 1;
-               s->insn_bits = ni_65xx_dio_insn_bits;
-               spriv = comedi_alloc_spriv(s, sizeof(*spriv));
-               if (!spriv)
-                       return -ENOMEM;
-               spriv->base_port = board->num_di_ports;
+               s->type         = COMEDI_SUBD_DO;
+               s->subdev_flags = SDF_WRITABLE;
+               s->n_chan       = NI_65XX_PORT_TO_CHAN(board->num_do_ports);
+               s->maxdata      = 1;
+               s->range_table  = &range_digital;
+               s->insn_bits    = ni_65xx_dio_insn_bits;
+
+               /* the output ports always start after the input ports */
+               s->private = (void *)(unsigned long)board->num_di_ports;
+
+               /*
+                * Use the io_bits to handle the inverted outputs.  Inverted
+                * outputs are only supported if the "legacy_invert_outputs"
+                * module parameter is set to "true".
+                */
+               if (ni_65xx_legacy_invert_outputs && board->legacy_invert)
+                       s->io_bits = 0xff;
+
+               /* reset all output ports to comedi '0' */
+               for (i = 0; i < board->num_do_ports; ++i) {
+                       writeb(s->io_bits,      /* inverted if necessary */
+                              devpriv->mmio +
+                              NI_65XX_IO_DATA_REG(board->num_di_ports + i));
+               }
        } else {
-               s->type = COMEDI_SUBD_UNUSED;
+               s->type         = COMEDI_SUBD_UNUSED;
        }
 
        s = &dev->subdevices[2];
        if (board->num_dio_ports) {
-               s->type = COMEDI_SUBD_DIO;
-               s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
-               s->n_chan =
-                   board->num_dio_ports * ni_65xx_channels_per_port;
-               s->range_table = &range_digital;
-               s->maxdata = 1;
-               s->insn_config = ni_65xx_dio_insn_config;
-               s->insn_bits = ni_65xx_dio_insn_bits;
-               spriv = comedi_alloc_spriv(s, sizeof(*spriv));
-               if (!spriv)
-                       return -ENOMEM;
-               spriv->base_port = 0;
+               s->type         = COMEDI_SUBD_DIO;
+               s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+               s->n_chan       = NI_65XX_PORT_TO_CHAN(board->num_dio_ports);
+               s->maxdata      = 1;
+               s->range_table  = &range_digital;
+               s->insn_bits    = ni_65xx_dio_insn_bits;
+               s->insn_config  = ni_65xx_dio_insn_config;
+
+               /* the input/output ports always start at port 0 */
+               s->private = (void *)0;
+
+               /* configure all ports for input */
                for (i = 0; i < board->num_dio_ports; ++i) {
-                       /*  configure all ports for input */
-                       writeb(0x1,
-                              devpriv->mite->daq_io_addr +
-                              Port_Select(i));
+                       writeb(NI_65XX_IO_SEL_INPUT,
+                              devpriv->mmio + NI_65XX_IO_SEL_REG(i));
                }
        } else {
-               s->type = COMEDI_SUBD_UNUSED;
+               s->type         = COMEDI_SUBD_UNUSED;
        }
 
        s = &dev->subdevices[3];
-       dev->read_subdev = s;
-       s->type = COMEDI_SUBD_DI;
-       s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
-       s->n_chan = 1;
-       s->range_table = &range_unknown;
-       s->maxdata = 1;
-       s->len_chanlist = 1;
-       s->do_cmdtest = ni_65xx_intr_cmdtest;
-       s->do_cmd = ni_65xx_intr_cmd;
-       s->cancel = ni_65xx_intr_cancel;
-       s->insn_bits = ni_65xx_intr_insn_bits;
-       s->insn_config = ni_65xx_intr_insn_config;
-
-       for (i = 0; i < ni_65xx_total_num_ports(board); ++i) {
-               writeb(0x00,
-                      devpriv->mite->daq_io_addr + Filter_Enable(i));
-               if (board->invert_outputs)
-                       writeb(0x01,
-                              devpriv->mite->daq_io_addr + Port_Data(i));
-               else
-                       writeb(0x00,
-                              devpriv->mite->daq_io_addr + Port_Data(i));
-       }
-       writeb(ClrEdge | ClrOverflow,
-              devpriv->mite->daq_io_addr + Clear_Register);
-       writeb(0x00,
-              devpriv->mite->daq_io_addr + Master_Interrupt_Control);
-
-       /* Set filter interval to 0  (32bit reg) */
-       writeb(0x00000000, devpriv->mite->daq_io_addr + Filter_Interval);
-
-       ret = request_irq(dev->irq, ni_65xx_interrupt, IRQF_SHARED,
-                         "ni_65xx", dev);
-       if (ret < 0) {
-               dev->irq = 0;
-               dev_warn(dev->class_dev, "irq not available\n");
+       s->type         = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan       = 1;
+       s->maxdata      = 1;
+       s->range_table  = &range_digital;
+       s->insn_bits    = ni_65xx_intr_insn_bits;
+       if (dev->irq) {
+               dev->read_subdev = s;
+               s->subdev_flags |= SDF_CMD_READ;
+               s->len_chanlist = 1;
+               s->insn_config  = ni_65xx_intr_insn_config;
+               s->do_cmdtest   = ni_65xx_intr_cmdtest;
+               s->do_cmd       = ni_65xx_intr_cmd;
+               s->cancel       = ni_65xx_intr_cancel;
        }
 
+       ni_65xx_disable_input_filters(dev);
+
        return 0;
 }
 
@@ -709,27 +779,20 @@ static void ni_65xx_detach(struct comedi_device *dev)
 {
        struct ni_65xx_private *devpriv = dev->private;
 
-       if (devpriv && devpriv->mite && devpriv->mite->daq_io_addr) {
-               writeb(0x00,
-                      devpriv->mite->daq_io_addr +
-                      Master_Interrupt_Control);
+       if (devpriv && devpriv->mmio) {
+               writeb(0x00, devpriv->mmio + NI_65XX_CTRL_REG);
+               iounmap(devpriv->mmio);
        }
        if (dev->irq)
                free_irq(dev->irq, dev);
-       if (devpriv) {
-               if (devpriv->mite) {
-                       mite_unsetup(devpriv->mite);
-                       mite_free(devpriv->mite);
-               }
-       }
        comedi_pci_disable(dev);
 }
 
 static struct comedi_driver ni_65xx_driver = {
-       .driver_name = "ni_65xx",
-       .module = THIS_MODULE,
-       .auto_attach = ni_65xx_auto_attach,
-       .detach = ni_65xx_detach,
+       .driver_name    = "ni_65xx",
+       .module         = THIS_MODULE,
+       .auto_attach    = ni_65xx_auto_attach,
+       .detach         = ni_65xx_detach,
 };
 
 static int ni_65xx_pci_probe(struct pci_dev *dev,
@@ -774,5 +837,5 @@ static struct pci_driver ni_65xx_pci_driver = {
 module_comedi_pci_driver(ni_65xx_driver, ni_65xx_pci_driver);
 
 MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_DESCRIPTION("Comedi driver for NI PCI-65xx static dio boards");
 MODULE_LICENSE("GPL");