* (National Instruments) PXI-6521 [ni_65xx]
* (National Instruments) PCI-6528 [ni_65xx]
* (National Instruments) PXI-6528 [ni_65xx]
- * Updated: Wed Oct 18 08:59:11 EDT 2006
+ * 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.
*/
/*
#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_MAX_NUM_PORTS 12
#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))
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[] = {
[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",
},
};
-static inline unsigned ni_65xx_total_num_ports(const struct ni_65xx_board
- *board)
-{
- return board->num_dio_ports + board->num_di_ports + board->num_do_ports;
-}
+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");
struct ni_65xx_private {
void __iomem *mmio;
- unsigned short output_bits[NI_65XX_MAX_NUM_PORTS];
};
+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;
+}
+
+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;
+
+ /* disable input filtering on all ports */
+ for (i = 0; i < num_ports; ++i)
+ writeb(0x00, devpriv->mmio + NI_65XX_FILTER_ENA(i));
+
+ /* set filter interval to 0 (32bit reg) */
+ writel(0x00000000, devpriv->mmio + NI_65XX_FILTER_REG);
+}
+
+/* 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;
+ unsigned int num_ports = ni_65xx_num_ports(dev);
+ unsigned int port;
+
+ if (base_chan >= NI_65XX_PORT_TO_CHAN(num_ports))
+ return;
+
+ 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;
+
+ if (bitshift >= 32)
+ break;
+
+ 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,
struct comedi_insn *insn,
unsigned int *data)
{
- const struct ni_65xx_board *board = comedi_board(dev);
struct ni_65xx_private *devpriv = dev->private;
unsigned long base_port = (unsigned long)s->private;
unsigned int base_chan = CR_CHAN(insn->chanspec);
port_offset <= last_port_offset; port_offset++) {
unsigned port = base_port + port_offset;
int base_port_channel = NI_65XX_PORT_TO_CHAN(port_offset);
- unsigned port_mask, port_data, port_read_bits;
+ unsigned port_mask, port_data, bits;
int bitshift = base_port_channel - base_chan;
if (bitshift >= 32)
}
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;
+ 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));
}
- port_read_bits = readb(devpriv->mmio +
- NI_65XX_IO_DATA_REG(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;
- }
+
+ /* 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;
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;
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() */
/*
* This only works for the first 4 ports (32 channels)!
*/
-
- /* set the channels to monitor for rising edges */
- writeb(data[1] & 0xff,
- devpriv->mmio + NI_65XX_RISE_EDGE_ENA_REG(0));
- writeb((data[1] >> 8) & 0xff,
- devpriv->mmio + NI_65XX_RISE_EDGE_ENA_REG(1));
- writeb((data[1] >> 16) & 0xff,
- devpriv->mmio + NI_65XX_RISE_EDGE_ENA_REG(2));
- writeb((data[1] >> 24) & 0xff,
- devpriv->mmio + NI_65XX_RISE_EDGE_ENA_REG(3));
-
- /* set the channels to monitor for falling edges */
- writeb(data[2] & 0xff,
- devpriv->mmio + NI_65XX_FALL_EDGE_ENA_REG(0));
- writeb((data[2] >> 8) & 0xff,
- devpriv->mmio + NI_65XX_FALL_EDGE_ENA_REG(1));
- writeb((data[2] >> 16) & 0xff,
- devpriv->mmio + NI_65XX_FALL_EDGE_ENA_REG(2));
- writeb((data[2] >> 24) & 0xff,
- devpriv->mmio + NI_65XX_FALL_EDGE_ENA_REG(3));
+ ni_65xx_update_edge_detection(dev, 0, data[1], data[2]);
break;
default:
return -EINVAL;
/* 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->cancel = ni_65xx_intr_cancel;
}
- for (i = 0; i < ni_65xx_total_num_ports(board); ++i) {
- writeb(0x00, devpriv->mmio + NI_65XX_FILTER_ENA(i));
- if (board->invert_outputs)
- writeb(0x01, devpriv->mmio + NI_65XX_IO_DATA_REG(i));
- else
- writeb(0x00, devpriv->mmio + NI_65XX_IO_DATA_REG(i));
- }
-
- /* Set filter interval to 0 (32bit reg) */
- writel(0x00000000, devpriv->mmio + NI_65XX_FILTER_REG);
+ ni_65xx_disable_input_filters(dev);
return 0;
}