diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/ni_65xx.c')
-rw-r--r-- | drivers/staging/comedi/drivers/ni_65xx.c | 797 |
1 files changed, 437 insertions, 360 deletions
diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c index 9a139d6b8ef4..873941be56cb 100644 --- a/drivers/staging/comedi/drivers/ni_65xx.c +++ b/drivers/staging/comedi/drivers/ni_65xx.c @@ -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,174 @@ 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) +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) +{ + 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, dev->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, dev->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(dev->mmio + + NI_65XX_RISE_EDGE_ENA_REG(port)) & + ~port_mask; + port_falling |= + readb(dev->mmio + + NI_65XX_FALL_EDGE_ENA_REG(port)) & + ~port_mask; + } + writeb(port_rising & 0xff, + dev->mmio + NI_65XX_RISE_EDGE_ENA_REG(port)); + writeb(port_falling & 0xff, + dev->mmio + NI_65XX_FALL_EDGE_ENA_REG(port)); + } + } +} + +static void ni_65xx_disable_edge_detection(struct comedi_device *dev) +{ + /* clear edge detection for channels 0 to 31 */ + ni_65xx_update_edge_detection(dev, 0, 0, 0); + /* clear edge detection for channels 32 to 63 */ + ni_65xx_update_edge_detection(dev, 32, 0, 0); + /* clear edge detection for channels 64 to 95 */ + ni_65xx_update_edge_detection(dev, 64, 0, 0); } 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(dev->mmio + NI_65XX_FILTER_ENA(port)); + if (interval) { + writel(interval, dev->mmio + NI_65XX_FILTER_REG); + val |= chan_mask; + } else { + val &= ~chan_mask; + } + writeb(val, dev->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, + dev->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, + dev->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(dev->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 +468,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(dev->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, dev->mmio + NI_65XX_IO_DATA_REG(port)); } + + /* read back the actual state */ + bits = readb(dev->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; @@ -426,18 +496,17 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev, static irqreturn_t ni_65xx_interrupt(int irq, void *d) { struct comedi_device *dev = d; - struct ni_65xx_private *devpriv = dev->private; 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(dev->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, + dev->mmio + NI_65XX_CLR_REG); comedi_buf_put(s, 0); s->async->events |= COMEDI_CB_EOS; @@ -490,13 +559,11 @@ static int ni_65xx_intr_cmdtest(struct comedi_device *dev, static int ni_65xx_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { - 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, + dev->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, + dev->mmio + NI_65XX_CTRL_REG); return 0; } @@ -504,16 +571,15 @@ static int ni_65xx_intr_cmd(struct comedi_device *dev, static int ni_65xx_intr_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - struct ni_65xx_private *devpriv = dev->private; - - writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control); + writeb(0x00, dev->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 +590,68 @@ 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) + /* update edge detection for channels 0 to 31 */ + ni_65xx_update_edge_detection(dev, 0, data[1], data[2]); + /* clear edge detection for channels 32 to 63 */ + ni_65xx_update_edge_detection(dev, 32, 0, 0); + /* clear edge detection for channels 64 to 95 */ + ni_65xx_update_edge_detection(dev, 64, 0, 0); + break; + case INSN_CONFIG_DIGITAL_TRIG: + /* check trigger number */ + if (data[1] != 0) + return -EINVAL; + /* check digital trigger operation */ + switch (data[2]) { + case COMEDI_DIGITAL_TRIG_DISABLE: + ni_65xx_disable_edge_detection(dev); + break; + case COMEDI_DIGITAL_TRIG_ENABLE_EDGES: + /* + * update edge detection for channels data[3] + * to (data[3] + 31) + */ + ni_65xx_update_edge_detection(dev, data[3], + data[4], data[5]); + break; + default: + return -EINVAL; + } + 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, @@ -565,8 +659,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; @@ -582,23 +674,27 @@ static int ni_65xx_auto_attach(struct comedi_device *dev, if (ret) return ret; - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; + ret = ni_65xx_mite_init(pcidev); + if (ret) + return ret; - devpriv->mite = mite_alloc(pcidev); - if (!devpriv->mite) + dev->mmio = pci_ioremap_bar(pcidev, 1); + if (!dev->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, + dev->mmio + NI_65XX_CLR_REG); + writeb(0x00, dev->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(dev->mmio + NI_65XX_ID_REG)); ret = comedi_alloc_subdevices(dev, 4); if (ret) @@ -606,130 +702,111 @@ 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 */ + dev->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, + dev->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); + ni_65xx_disable_edge_detection(dev); + return 0; } 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 (dev->mmio) { + writeb(0x00, dev->mmio + NI_65XX_CTRL_REG); + iounmap(dev->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 +851,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"); |