summaryrefslogtreecommitdiff
path: root/drivers/gpib
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpib')
-rw-r--r--drivers/gpib/Kconfig255
-rw-r--r--drivers/gpib/Makefile20
-rw-r--r--drivers/gpib/TODO10
-rw-r--r--drivers/gpib/agilent_82350b/Makefile2
-rw-r--r--drivers/gpib/agilent_82350b/agilent_82350b.c896
-rw-r--r--drivers/gpib/agilent_82350b/agilent_82350b.h157
-rw-r--r--drivers/gpib/agilent_82357a/Makefile4
-rw-r--r--drivers/gpib/agilent_82357a/agilent_82357a.c1691
-rw-r--r--drivers/gpib/agilent_82357a/agilent_82357a.h182
-rw-r--r--drivers/gpib/cb7210/Makefile3
-rw-r--r--drivers/gpib/cb7210/cb7210.c1586
-rw-r--r--drivers/gpib/cb7210/cb7210.h203
-rw-r--r--drivers/gpib/cec/Makefile3
-rw-r--r--drivers/gpib/cec/cec.h20
-rw-r--r--drivers/gpib/cec/cec_gpib.c393
-rw-r--r--drivers/gpib/common/Makefile6
-rw-r--r--drivers/gpib/common/gpib_os.c2271
-rw-r--r--drivers/gpib/common/iblib.c717
-rw-r--r--drivers/gpib/common/ibsys.h34
-rw-r--r--drivers/gpib/eastwood/Makefile3
-rw-r--r--drivers/gpib/eastwood/fluke_gpib.c1180
-rw-r--r--drivers/gpib/eastwood/fluke_gpib.h146
-rw-r--r--drivers/gpib/fmh_gpib/Makefile2
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.c1754
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.h177
-rw-r--r--drivers/gpib/gpio/Makefile4
-rw-r--r--drivers/gpib/gpio/gpib_bitbang.c1469
-rw-r--r--drivers/gpib/hp_82335/Makefile4
-rw-r--r--drivers/gpib/hp_82335/hp82335.c371
-rw-r--r--drivers/gpib/hp_82335/hp82335.h52
-rw-r--r--drivers/gpib/hp_82341/Makefile2
-rw-r--r--drivers/gpib/hp_82341/hp_82341.c907
-rw-r--r--drivers/gpib/hp_82341/hp_82341.h165
-rw-r--r--drivers/gpib/include/amcc5920.h49
-rw-r--r--drivers/gpib/include/amccs5933.h59
-rw-r--r--drivers/gpib/include/gpibP.h41
-rw-r--r--drivers/gpib/include/gpib_cmd.h112
-rw-r--r--drivers/gpib/include/gpib_pci_ids.h23
-rw-r--r--drivers/gpib/include/gpib_proto.h49
-rw-r--r--drivers/gpib/include/gpib_state_machines.h23
-rw-r--r--drivers/gpib/include/gpib_types.h381
-rw-r--r--drivers/gpib/include/nec7210.h141
-rw-r--r--drivers/gpib/include/nec7210_registers.h218
-rw-r--r--drivers/gpib/include/plx9050.h72
-rw-r--r--drivers/gpib/include/quancom_pci.h22
-rw-r--r--drivers/gpib/include/tms9914.h280
-rw-r--r--drivers/gpib/include/tnt4882_registers.h192
-rw-r--r--drivers/gpib/ines/Makefile3
-rw-r--r--drivers/gpib/ines/ines.h165
-rw-r--r--drivers/gpib/ines/ines_gpib.c1500
-rw-r--r--drivers/gpib/lpvo_usb_gpib/Makefile3
-rw-r--r--drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c2025
-rw-r--r--drivers/gpib/nec7210/Makefile4
-rw-r--r--drivers/gpib/nec7210/board.h19
-rw-r--r--drivers/gpib/nec7210/nec7210.c1121
-rw-r--r--drivers/gpib/ni_usb/Makefile4
-rw-r--r--drivers/gpib/ni_usb/ni_usb_gpib.c2678
-rw-r--r--drivers/gpib/ni_usb/ni_usb_gpib.h226
-rw-r--r--drivers/gpib/pc2/Makefile5
-rw-r--r--drivers/gpib/pc2/pc2_gpib.c684
-rw-r--r--drivers/gpib/tms9914/Makefile6
-rw-r--r--drivers/gpib/tms9914/tms9914.c914
-rw-r--r--drivers/gpib/tnt4882/Makefile6
-rw-r--r--drivers/gpib/tnt4882/mite.c133
-rw-r--r--drivers/gpib/tnt4882/mite.h234
-rw-r--r--drivers/gpib/tnt4882/tnt4882_gpib.c1838
66 files changed, 27919 insertions, 0 deletions
diff --git a/drivers/gpib/Kconfig b/drivers/gpib/Kconfig
new file mode 100644
index 000000000000..eeb50956ce85
--- /dev/null
+++ b/drivers/gpib/Kconfig
@@ -0,0 +1,255 @@
+# SPDX-License-Identifier: GPL-2.0
+menuconfig GPIB
+ tristate "GPIB drivers"
+ help
+ Enable support for GPIB cards and dongles. GPIB is the
+ General Purpose Interface Bus which conforms to the IEEE488
+ standard.
+
+ This set of drivers can be used with the corresponding user
+ space library that can be found on Sourceforge under linux-gpib.
+ Select the drivers for your hardware from the list.
+
+if GPIB
+
+config GPIB_COMMON
+ tristate "GPIB core"
+ help
+
+ Core common driver for all GPIB drivers. It provides the
+ interface for the userland library
+
+ To compile this driver as a module, choose M here: the module will be
+ called gpib_common
+
+config GPIB_AGILENT_82350B
+ tristate "Agilent 8235xx PCI(e) adapters"
+ depends on PCI
+ select GPIB_COMMON
+ select GPIB_TMS9914
+ help
+ Enable support for HP/Agilent/Keysight boards
+ 82350A
+ 82350B
+ 82351A
+
+ To compile this driver as a module, choose M here: the module will be
+ called agilent_82350b.
+
+config GPIB_AGILENT_82357A
+ tristate "Agilent 82357a/b USB dongles"
+ select GPIB_COMMON
+ depends on USB
+ help
+ Enable support for Agilent/Keysight 82357x USB dongles.
+
+ To compile this driver as a module, choose M here: the module will be
+ called agilent_82357a.
+
+config GPIB_CEC_PCI
+ tristate "CEC PCI board"
+ depends on PCI
+ depends on HAS_IOPORT
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ help
+ Enable support for Capital Equipment Corporation PCI-488
+ and Keithly KPCI-488 boards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cec_gpib.
+
+config GPIB_NI_PCI_ISA
+ tristate "NI PCI/ISA compatible boards"
+ depends on ISA_BUS || PCI || PCMCIA
+ depends on HAS_IOPORT
+ depends on PCMCIA || !PCMCIA
+ depends on HAS_IOPORT_MAP
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ help
+ Enable support for National Instruments boards based
+ on TNT4882 chips:
+ AT-GPIB (with NAT4882 chip)
+ AT-GPIB (with NEC7210 chip)
+ AT-GPIB/TNT
+ PCI-GPIB
+ PCIe-GPIB
+ PCI-GPIB+
+ PCM-GPIB
+ PXI-GPIB
+ PCMCIA-GPIB
+ and Capital Equipment Corporation CEC-488 board.
+
+ To compile this driver as a module, choose M here: the module will be
+ called tnt4882.
+
+config GPIB_CB7210
+ tristate "Measurement Computing compatible boards"
+ depends on HAS_IOPORT
+ depends on ISA_BUS || PCI || PCMCIA
+ depends on PCMCIA || !PCMCIA
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ help
+ Enable support for Measurement Computing (Computer Boards):
+ CPCI_GPIB, ISA-GPIB, ISA-GPIB/LC, PCI-GPIB/1M, PCI-GPIB/300K and
+ PCMCIA-GPIB
+ Quancom PCIGPIB-1 with MC cb7210 chip
+
+ To compile this driver as a module, choose M here: the module will be
+
+config GPIB_NI_USB
+ tristate "NI USB dongles"
+ select GPIB_COMMON
+ depends on USB
+ help
+ Enable support for National Instruments
+ GPIB-USB-B
+ GPIB-USB-HS
+ GPIB-USB-HS+
+ Keithly
+ KUSB-488
+ KUSB-488A
+ Measurement Computing (Computer Boards)
+ USB-488
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_usb.
+
+config GPIB_FLUKE
+ tristate "Fluke"
+ depends on OF
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ help
+ GPIB driver for Fluke based cda devices.
+
+ To compile this driver as a module, choose M here: the module will be
+ called fluke_gpib
+
+config GPIB_FMH
+ tristate "FMH FPGA based devices"
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ depends on !PPC
+ depends on OF && PCI
+ help
+ GPIB driver for fmhess FPGA based devices
+
+ To compile this driver as a module, choose M here: the module will be
+ called fmh_gpib
+
+config GPIB_GPIO
+ tristate "RPi GPIO bitbang"
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ select GPIB_COMMON
+ help
+ GPIB bitbang driver Raspberry Pi GPIO adapters
+
+ To compile this driver as a module, choose M here: the module will be
+ called gpib_bitbang
+
+config GPIB_HP82335
+ tristate "HP82335/HP27209"
+ depends on ISA_BUS
+ select GPIB_COMMON
+ select GPIB_TMS9914
+ help
+ GPIB driver for HP82335 and HP27209 boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called hp82335
+
+
+config GPIB_HP82341
+ tristate "HP82341x"
+ select GPIB_COMMON
+ select GPIB_TMS9914
+ depends on ISA_BUS || EISA
+ help
+ GPIB driver for HP82341 A/B/C/D boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called hp82341
+
+config GPIB_INES
+ tristate "INES"
+ depends on PCI || ISA_BUS || PCMCIA
+ depends on PCMCIA || !PCMCIA
+ depends on HAS_IOPORT
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ help
+ GPIB driver for Ines compatible boards
+ Ines
+ GPIB-HS-NT
+ GPIB for Compact PCI
+ GPIB for PCI
+ GPIB for PCMCIA
+ GPIB PC/104
+ Hameg
+ HO80-2
+ Quancom
+ PCIGPIB-1 based on Ines iGPIB 72010 chip
+
+ To compile this driver as a module, choose M here: the module will be
+ called ines_gpib
+ called cb7210.
+
+config GPIB_PCMCIA
+ def_bool y
+ depends on PCMCIA && (GPIB_NI_PCI_ISA || GPIB_CB7210 || GPIB_INES)
+ help
+ Enable PCMCIA/CArdbus support for National Instruments,
+ measurement computing boards and Ines boards.
+
+config GPIB_LPVO
+ tristate "LPVO DIY USB GPIB"
+ select GPIB_COMMON
+ depends on USB
+ help
+ Enable support for LPVO Self-made usb-gpib adapter
+
+ To compile this driver as a module, choose M here: the module will be
+ called lpvo_usb_gpib
+
+config GPIB_PC2
+ tristate "PC2 PC2a"
+ depends on ISA_BUS
+ depends on HAS_IOPORT
+ select GPIB_COMMON
+ select GPIB_NEC7210
+ help
+ Enable support for pc2 and pc2a compatible adapters
+ Capital Equipment Corporation PC-488
+ CONTEC GP-IB(PC)
+ Hameg HO80
+ Iotech GP488B
+ Keithly MBC-488
+ Measurement Computing ISA-GPIB-PCA2
+ National Instruments PCII, PCIIa and PCII/IIa
+
+ To compile this driver as a module, choose M here: the module will be
+ called pc2_gpib
+
+
+config GPIB_TMS9914
+ tristate
+ select GPIB_COMMON
+ help
+ Enable support for TMS 9914 chip.
+
+ To compile this driver as a module, choose M here: the module will be
+ called tms9914
+
+config GPIB_NEC7210
+ tristate
+ select GPIB_COMMON
+ help
+ Enable support for NEC 7210 compatible chips.
+
+ To compile this driver as a module, choose M here: the module will be
+ called nec7210
+
+endif # GPIB
diff --git a/drivers/gpib/Makefile b/drivers/gpib/Makefile
new file mode 100644
index 000000000000..2d44fed2a743
--- /dev/null
+++ b/drivers/gpib/Makefile
@@ -0,0 +1,20 @@
+
+subdir-ccflags-y += -I$(src)/include
+
+obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b/
+obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a/
+obj-$(CONFIG_GPIB_CB7210) += cb7210/
+obj-$(CONFIG_GPIB_CEC_PCI) += cec/
+obj-$(CONFIG_GPIB_COMMON) += common/
+obj-$(CONFIG_GPIB_FLUKE) += eastwood/
+obj-$(CONFIG_GPIB_FMH) += fmh_gpib/
+obj-$(CONFIG_GPIB_GPIO) += gpio/
+obj-$(CONFIG_GPIB_HP82335) += hp_82335/
+obj-$(CONFIG_GPIB_HP82341) += hp_82341/
+obj-$(CONFIG_GPIB_INES) += ines/
+obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib/
+obj-$(CONFIG_GPIB_NEC7210) += nec7210/
+obj-$(CONFIG_GPIB_NI_USB) += ni_usb/
+obj-$(CONFIG_GPIB_PC2) += pc2/
+obj-$(CONFIG_GPIB_TMS9914) += tms9914/
+obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882/
diff --git a/drivers/gpib/TODO b/drivers/gpib/TODO
new file mode 100644
index 000000000000..ac07dd90b4ef
--- /dev/null
+++ b/drivers/gpib/TODO
@@ -0,0 +1,10 @@
+TODO:
+- checkpatch.pl fixes
+ These checks should be ignored:
+ CHECK:ALLOC_SIZEOF_STRUCT: Prefer kmalloc(sizeof(*board->private_data)...) over kmalloc(sizeof(struct xxx_priv)...)
+ ./gpio/gpib_bitbang.c:50: ERROR:COMPLEX_MACRO: Macros with complex values should be enclosed in parenthese
+ This warning will be addressed later: WARNING:UNDOCUMENTED_DT_STRING: DT compatible string
+- resolve XXX notes where possible
+- fix FIXME notes
+- clean-up commented-out code
+- fix typos
diff --git a/drivers/gpib/agilent_82350b/Makefile b/drivers/gpib/agilent_82350b/Makefile
new file mode 100644
index 000000000000..f24e1e713a63
--- /dev/null
+++ b/drivers/gpib/agilent_82350b/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b.o
diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.c b/drivers/gpib/agilent_82350b/agilent_82350b.c
new file mode 100644
index 000000000000..01a5bb43cd2d
--- /dev/null
+++ b/drivers/gpib/agilent_82350b/agilent_82350b.c
@@ -0,0 +1,896 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2002, 2004 by Frank Mori Hess *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "agilent_82350b.h"
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for Agilent 82350b");
+
+static int read_transfer_counter(struct agilent_82350b_priv *a_priv);
+static unsigned short read_and_clear_event_status(struct gpib_board *board);
+static void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count);
+static int agilent_82350b_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written);
+
+static int agilent_82350b_accel_read(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
+ int retval = 0;
+ unsigned short event_status;
+ int i, num_fifo_bytes;
+ /* hardware doesn't support checking for end-of-string character when using fifo */
+ if (tms_priv->eos_flags & REOS)
+ return tms9914_read(board, tms_priv, buffer, length, end, bytes_read);
+
+ clear_bit(DEV_CLEAR_BN, &tms_priv->state);
+
+ read_and_clear_event_status(board);
+ *end = 0;
+ *bytes_read = 0;
+ if (length == 0)
+ return 0;
+ /* disable fifo for the moment */
+ writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+ /* handle corner case of board not in holdoff and one byte might slip in early */
+ if (tms_priv->holdoff_active == 0 && length > 1) {
+ size_t num_bytes;
+
+ retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0 || *end)
+ return retval;
+ ++buffer;
+ --length;
+ }
+ tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI);
+ tms9914_release_holdoff(tms_priv);
+ i = 0;
+ num_fifo_bytes = length - 1;
+ /* disable BI interrupts */
+ write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BIIE, IMR0);
+ while (i < num_fifo_bytes && *end == 0) {
+ int block_size;
+ int j;
+ int count;
+
+ block_size = min(num_fifo_bytes - i, agilent_82350b_fifo_size);
+ set_transfer_counter(a_priv, block_size);
+ writeb(ENABLE_TI_TO_SRAM | DIRECTION_GPIB_TO_HOST,
+ a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+ if (agilent_82350b_fifo_is_halted(a_priv))
+ writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG);
+
+ clear_bit(READ_READY_BN, &tms_priv->state);
+
+ retval = wait_event_interruptible(board->wait,
+ ((event_status =
+ read_and_clear_event_status(board)) &
+ (TERM_COUNT_STATUS_BIT |
+ BUFFER_END_STATUS_BIT)) ||
+ test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
+ test_bit(TIMO_NUM, &board->status));
+ if (retval) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ count = block_size - read_transfer_counter(a_priv);
+ for (j = 0; j < count && i < num_fifo_bytes; ++j)
+ buffer[i++] = readb(a_priv->sram_base + j);
+ if (event_status & BUFFER_END_STATUS_BIT) {
+ clear_bit(RECEIVED_END_BN, &tms_priv->state);
+
+ tms_priv->holdoff_active = 1;
+ *end = 1;
+ }
+ if (test_bit(TIMO_NUM, &board->status)) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+ }
+ /* re-enable BI interrupts */
+ write_byte(tms_priv, tms_priv->imr0_bits, IMR0);
+ *bytes_read += i;
+ buffer += i;
+ length -= i;
+ writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+ if (retval < 0)
+ return retval;
+ /* read last bytes if we havn't received an END yet */
+ if (*end == 0) {
+ size_t num_bytes;
+ /* try to make sure we holdoff after last byte read */
+ retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static int translate_wait_return_value(struct gpib_board *board, int retval)
+
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
+
+ if (retval)
+ return -ERESTARTSYS;
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &tms_priv->state))
+ return -EINTR;
+ return 0;
+}
+
+static int agilent_82350b_accel_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
+ int i, j;
+ unsigned short event_status;
+ int retval = 0;
+ int fifotransferlength = length;
+ int block_size = 0;
+ size_t num_bytes;
+
+ *bytes_written = 0;
+ if (send_eoi)
+ --fifotransferlength;
+
+ clear_bit(DEV_CLEAR_BN, &tms_priv->state);
+
+ writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+
+ event_status = read_and_clear_event_status(board);
+
+#ifdef EXPERIMENTAL
+ /* wait for previous BO to complete if any */
+ retval = wait_event_interruptible(board->wait,
+ test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
+ test_bit(WRITE_READY_BN, &tms_priv->state) ||
+ test_bit(TIMO_NUM, &board->status));
+ retval = translate_wait_return_value(board, retval);
+
+ if (retval)
+ return retval;
+#endif
+
+ if (fifotransferlength > 0) {
+ retval = agilent_82350b_write(board, buffer, 1, 0, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+
+ write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BOIE, IMR0);
+ for (i = 1; i < fifotransferlength;) {
+ clear_bit(WRITE_READY_BN, &tms_priv->state);
+
+ block_size = min(fifotransferlength - i, agilent_82350b_fifo_size);
+ set_transfer_counter(a_priv, block_size);
+ for (j = 0; j < block_size; ++j, ++i) {
+ /* load data into board's sram */
+ writeb(buffer[i], a_priv->sram_base + j);
+ }
+ writeb(ENABLE_TI_TO_SRAM, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+
+ if (agilent_82350b_fifo_is_halted(a_priv))
+ writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG);
+
+ retval = wait_event_interruptible(board->wait,
+ ((event_status =
+ read_and_clear_event_status(board)) &
+ TERM_COUNT_STATUS_BIT) ||
+ test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
+ test_bit(TIMO_NUM, &board->status));
+ writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+ num_bytes = block_size - read_transfer_counter(a_priv);
+
+ *bytes_written += num_bytes;
+ retval = translate_wait_return_value(board, retval);
+ if (retval)
+ break;
+ }
+ write_byte(tms_priv, tms_priv->imr0_bits, IMR0);
+ if (retval < 0)
+ return retval;
+
+ if (send_eoi) {
+ retval = agilent_82350b_write(board, buffer + fifotransferlength, 1, send_eoi,
+ &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static unsigned short read_and_clear_event_status(struct gpib_board *board)
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ unsigned long flags;
+ unsigned short status;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ status = a_priv->event_status_bits;
+ a_priv->event_status_bits = 0;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return status;
+}
+
+static irqreturn_t agilent_82350b_interrupt(int irq, void *arg)
+
+{
+ int tms9914_status1 = 0, tms9914_status2 = 0;
+ int event_status;
+ struct gpib_board *board = arg;
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ event_status = readb(a_priv->gpib_base + EVENT_STATUS_REG);
+ if (event_status & IRQ_STATUS_BIT)
+ retval = IRQ_HANDLED;
+
+ if (event_status & TMS9914_IRQ_STATUS_BIT) {
+ tms9914_status1 = read_byte(&a_priv->tms9914_priv, ISR0);
+ tms9914_status2 = read_byte(&a_priv->tms9914_priv, ISR1);
+ tms9914_interrupt_have_status(board, &a_priv->tms9914_priv, tms9914_status1,
+ tms9914_status2);
+ }
+ /* write-clear status bits */
+ if (event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT)) {
+ writeb(event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT),
+ a_priv->gpib_base + EVENT_STATUS_REG);
+ a_priv->event_status_bits |= event_status;
+ wake_up_interruptible(&board->wait);
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+static void agilent_82350b_detach(struct gpib_board *board);
+
+static int read_transfer_counter(struct agilent_82350b_priv *a_priv)
+{
+ int lo, mid, value;
+
+ lo = readb(a_priv->gpib_base + XFER_COUNT_LO_REG);
+ mid = readb(a_priv->gpib_base + XFER_COUNT_MID_REG);
+ value = (lo & 0xff) | ((mid << 8) & 0x7f00);
+ value = ~(value - 1) & 0x7fff;
+ return value;
+}
+
+static void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count)
+{
+ int complement = -count;
+
+ writeb(complement & 0xff, a_priv->gpib_base + XFER_COUNT_LO_REG);
+ writeb((complement >> 8) & 0xff, a_priv->gpib_base + XFER_COUNT_MID_REG);
+ /* I don't think the hi count reg is even used, but oh well */
+ writeb((complement >> 16) & 0xf, a_priv->gpib_base + XFER_COUNT_HI_REG);
+}
+
+/* wrappers for interface functions */
+static int agilent_82350b_read(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
+}
+
+static int agilent_82350b_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written)
+
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int agilent_82350b_command(struct gpib_board *board, u8 *buffer,
+ size_t length, size_t *bytes_written)
+
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
+}
+
+static int agilent_82350b_take_control(struct gpib_board *board, int synchronous)
+
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_take_control_workaround(board, &priv->tms9914_priv, synchronous);
+}
+
+static int agilent_82350b_go_to_standby(struct gpib_board *board)
+
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_go_to_standby(board, &priv->tms9914_priv);
+}
+
+static int agilent_82350b_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+
+ if (request_control) {
+ a_priv->card_mode_bits |= CM_SYSTEM_CONTROLLER_BIT;
+ if (a_priv->model != MODEL_82350A)
+ writeb(IC_SYSTEM_CONTROLLER_BIT, a_priv->gpib_base + INTERNAL_CONFIG_REG);
+ } else {
+ a_priv->card_mode_bits &= ~CM_SYSTEM_CONTROLLER_BIT;
+ if (a_priv->model != MODEL_82350A)
+ writeb(0, a_priv->gpib_base + INTERNAL_CONFIG_REG);
+ }
+ writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG);
+ return tms9914_request_system_control(board, &a_priv->tms9914_priv, request_control);
+}
+
+static void agilent_82350b_interface_clear(struct gpib_board *board, int assert)
+
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_interface_clear(board, &priv->tms9914_priv, assert);
+}
+
+static void agilent_82350b_remote_enable(struct gpib_board *board, int enable)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_remote_enable(board, &priv->tms9914_priv, enable);
+}
+
+static int agilent_82350b_enable_eos(struct gpib_board *board, u8 eos_byte,
+ int compare_8_bits)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
+}
+
+static void agilent_82350b_disable_eos(struct gpib_board *board)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_disable_eos(board, &priv->tms9914_priv);
+}
+
+static unsigned int agilent_82350b_update_status(struct gpib_board *board,
+ unsigned int clear_mask)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
+}
+
+static int agilent_82350b_primary_address(struct gpib_board *board,
+ unsigned int address)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_primary_address(board, &priv->tms9914_priv, address);
+}
+
+static int agilent_82350b_secondary_address(struct gpib_board *board,
+ unsigned int address, int enable)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
+}
+
+static int agilent_82350b_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
+}
+
+static void agilent_82350b_parallel_poll_configure(struct gpib_board *board,
+ u8 config)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
+}
+
+static void agilent_82350b_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
+}
+
+static void agilent_82350b_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
+}
+
+static u8 agilent_82350b_serial_poll_status(struct gpib_board *board)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_serial_poll_status(board, &priv->tms9914_priv);
+}
+
+static int agilent_82350b_line_status(const struct gpib_board *board)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ return tms9914_line_status(board, &priv->tms9914_priv);
+}
+
+static int agilent_82350b_t1_delay(struct gpib_board *board, unsigned int nanosec)
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ static const int nanosec_per_clock = 30;
+ unsigned int value;
+
+ tms9914_t1_delay(board, &a_priv->tms9914_priv, nanosec);
+
+ value = (nanosec + nanosec_per_clock - 1) / nanosec_per_clock;
+ if (value > 0xff)
+ value = 0xff;
+ writeb(value, a_priv->gpib_base + T1_DELAY_REG);
+ return value * nanosec_per_clock;
+}
+
+static void agilent_82350b_return_to_local(struct gpib_board *board)
+{
+ struct agilent_82350b_priv *priv = board->private_data;
+
+ tms9914_return_to_local(board, &priv->tms9914_priv);
+}
+
+static int agilent_82350b_allocate_private(struct gpib_board *board)
+{
+ board->private_data = kzalloc(sizeof(struct agilent_82350b_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+static void agilent_82350b_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static int init_82350a_hardware(struct gpib_board *board,
+ const struct gpib_board_config *config)
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ static const unsigned int firmware_length = 5302;
+ unsigned int borg_status;
+ static const unsigned int timeout = 1000;
+ int i, j;
+ const char *firmware_data = config->init_data;
+ const unsigned int plx_cntrl_static_bits = PLX9050_WAITO_NOT_USER0_SELECT_BIT |
+ PLX9050_USER0_OUTPUT_BIT |
+ PLX9050_LLOCK_NOT_USER1_SELECT_BIT |
+ PLX9050_USER1_OUTPUT_BIT |
+ PLX9050_USER2_OUTPUT_BIT |
+ PLX9050_USER3_OUTPUT_BIT |
+ PLX9050_PCI_READ_MODE_BIT |
+ PLX9050_PCI_WRITE_MODE_BIT |
+ PLX9050_PCI_RETRY_DELAY_BITS(64) |
+ PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT;
+
+ /* load borg data */
+ borg_status = readb(a_priv->borg_base);
+ if ((borg_status & BORG_DONE_BIT))
+ return 0;
+ /* need to programme borg */
+ if (!config->init_data || config->init_data_length != firmware_length) {
+ dev_err(board->gpib_dev, "the 82350A board requires firmware after powering on.\n");
+ return -EIO;
+ }
+ dev_dbg(board->gpib_dev, "Loading firmware...\n");
+
+ /* tickle the borg */
+ writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT,
+ a_priv->plx_base + PLX9050_CNTRL_REG);
+ usleep_range(1000, 2000);
+ writel(plx_cntrl_static_bits, a_priv->plx_base + PLX9050_CNTRL_REG);
+ usleep_range(1000, 2000);
+ writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT,
+ a_priv->plx_base + PLX9050_CNTRL_REG);
+ usleep_range(1000, 2000);
+
+ for (i = 0; i < config->init_data_length; ++i) {
+ for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_READY_BIT) == 0; ++j) {
+ if (need_resched())
+ schedule();
+ usleep_range(10, 20);
+ }
+ if (j == timeout) {
+ dev_err(board->gpib_dev, "timed out loading firmware.\n");
+ return -ETIMEDOUT;
+ }
+ writeb(firmware_data[i], a_priv->gpib_base + CONFIG_DATA_REG);
+ }
+ for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_DONE_BIT) == 0; ++j) {
+ if (need_resched())
+ schedule();
+ usleep_range(10, 20);
+ }
+ if (j == timeout) {
+ dev_err(board->gpib_dev, "timed out waiting for firmware load to complete.\n");
+ return -ETIMEDOUT;
+ }
+ dev_dbg(board->gpib_dev, " ...done.\n");
+ return 0;
+}
+
+static int test_sram(struct gpib_board *board)
+
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ unsigned int i;
+ const unsigned int sram_length = pci_resource_len(a_priv->pci_device, SRAM_82350A_REGION);
+ /* test SRAM */
+ const unsigned int byte_mask = 0xff;
+
+ for (i = 0; i < sram_length; ++i) {
+ writeb(i & byte_mask, a_priv->sram_base + i);
+ if (need_resched())
+ schedule();
+ }
+ for (i = 0; i < sram_length; ++i) {
+ unsigned int read_value = readb(a_priv->sram_base + i);
+
+ if ((i & byte_mask) != read_value) {
+ dev_err(board->gpib_dev, "SRAM test failed at %d wanted %d got %d\n",
+ i, (i & byte_mask), read_value);
+ return -EIO;
+ }
+ if (need_resched())
+ schedule();
+ }
+ dev_dbg(board->gpib_dev, "SRAM test passed 0x%x bytes checked\n", sram_length);
+ return 0;
+}
+
+static int agilent_82350b_generic_attach(struct gpib_board *board,
+ const struct gpib_board_config *config,
+ int use_fifos)
+
+{
+ struct agilent_82350b_priv *a_priv;
+ struct tms9914_priv *tms_priv;
+ int retval;
+
+ board->status = 0;
+
+ if (agilent_82350b_allocate_private(board))
+ return -ENOMEM;
+ a_priv = board->private_data;
+ a_priv->using_fifos = use_fifos;
+ tms_priv = &a_priv->tms9914_priv;
+ tms_priv->read_byte = tms9914_iomem_read_byte;
+ tms_priv->write_byte = tms9914_iomem_write_byte;
+ tms_priv->offset = 1;
+
+ /* find board */
+ a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT,
+ PCI_DEVICE_ID_82350B, NULL);
+ if (a_priv->pci_device) {
+ a_priv->model = MODEL_82350B;
+ dev_dbg(board->gpib_dev, "Agilent 82350B board found\n");
+
+ } else {
+ a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT,
+ PCI_DEVICE_ID_82351A, NULL);
+ if (a_priv->pci_device) {
+ a_priv->model = MODEL_82351A;
+ dev_dbg(board->gpib_dev, "Agilent 82351B board found\n");
+
+ } else {
+ a_priv->pci_device = gpib_pci_get_subsys(config, PCI_VENDOR_ID_PLX,
+ PCI_DEVICE_ID_PLX_9050,
+ PCI_VENDOR_ID_HP,
+ PCI_SUBDEVICE_ID_82350A,
+ a_priv->pci_device);
+ if (a_priv->pci_device) {
+ a_priv->model = MODEL_82350A;
+ dev_dbg(board->gpib_dev, "HP/Agilent 82350A board found\n");
+ } else {
+ dev_err(board->gpib_dev, "no 82350/82351 board found\n");
+ return -ENODEV;
+ }
+ }
+ }
+ if (pci_enable_device(a_priv->pci_device)) {
+ dev_err(board->gpib_dev, "error enabling pci device\n");
+ return -EIO;
+ }
+ if (pci_request_regions(a_priv->pci_device, DRV_NAME))
+ return -ENOMEM;
+ switch (a_priv->model) {
+ case MODEL_82350A:
+ a_priv->plx_base = ioremap(pci_resource_start(a_priv->pci_device, PLX_MEM_REGION),
+ pci_resource_len(a_priv->pci_device, PLX_MEM_REGION));
+ dev_dbg(board->gpib_dev, "plx base address remapped to 0x%p\n", a_priv->plx_base);
+ a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device,
+ GPIB_82350A_REGION),
+ pci_resource_len(a_priv->pci_device,
+ GPIB_82350A_REGION));
+ dev_dbg(board->gpib_dev, "chip base address remapped to 0x%p\n", a_priv->gpib_base);
+ tms_priv->mmiobase = a_priv->gpib_base + TMS9914_BASE_REG;
+ a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device,
+ SRAM_82350A_REGION),
+ pci_resource_len(a_priv->pci_device,
+ SRAM_82350A_REGION));
+ dev_dbg(board->gpib_dev, "sram base address remapped to 0x%p\n", a_priv->sram_base);
+ a_priv->borg_base = ioremap(pci_resource_start(a_priv->pci_device,
+ BORG_82350A_REGION),
+ pci_resource_len(a_priv->pci_device,
+ BORG_82350A_REGION));
+ dev_dbg(board->gpib_dev, "borg base address remapped to 0x%p\n", a_priv->borg_base);
+
+ retval = init_82350a_hardware(board, config);
+ if (retval < 0)
+ return retval;
+ break;
+ case MODEL_82350B:
+ case MODEL_82351A:
+ a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, GPIB_REGION),
+ pci_resource_len(a_priv->pci_device, GPIB_REGION));
+ dev_dbg(board->gpib_dev, "chip base address remapped to 0x%p\n", a_priv->gpib_base);
+ tms_priv->mmiobase = a_priv->gpib_base + TMS9914_BASE_REG;
+ a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, SRAM_REGION),
+ pci_resource_len(a_priv->pci_device, SRAM_REGION));
+ dev_dbg(board->gpib_dev, "sram base address remapped to 0x%p\n", a_priv->sram_base);
+ a_priv->misc_base = ioremap(pci_resource_start(a_priv->pci_device, MISC_REGION),
+ pci_resource_len(a_priv->pci_device, MISC_REGION));
+ dev_dbg(board->gpib_dev, "misc base address remapped to 0x%p\n", a_priv->misc_base);
+ break;
+ default:
+ dev_err(board->gpib_dev, "invalid board\n");
+ return -ENODEV;
+ }
+
+ retval = test_sram(board);
+ if (retval < 0)
+ return retval;
+
+ if (request_irq(a_priv->pci_device->irq, agilent_82350b_interrupt,
+ IRQF_SHARED, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "failed to obtain irq %d\n", a_priv->pci_device->irq);
+ return -EIO;
+ }
+ a_priv->irq = a_priv->pci_device->irq;
+ dev_dbg(board->gpib_dev, " IRQ %d\n", a_priv->irq);
+
+ writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
+ a_priv->card_mode_bits = ENABLE_PCI_IRQ_BIT;
+ writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG);
+
+ if (a_priv->model == MODEL_82350A) {
+ /* enable PCI interrupts for 82350a */
+ writel(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR2_POLARITY_BIT |
+ PLX9050_PCI_INTR_EN_BIT,
+ a_priv->plx_base + PLX9050_INTCSR_REG);
+ }
+
+ if (use_fifos) {
+ writeb(ENABLE_BUFFER_END_EVENTS_BIT | ENABLE_TERM_COUNT_EVENTS_BIT,
+ a_priv->gpib_base + EVENT_ENABLE_REG);
+ writeb(ENABLE_TERM_COUNT_INTERRUPT_BIT | ENABLE_BUFFER_END_INTERRUPT_BIT |
+ ENABLE_TMS9914_INTERRUPTS_BIT, a_priv->gpib_base + INTERRUPT_ENABLE_REG);
+ /* write-clear event status bits */
+ writeb(BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT,
+ a_priv->gpib_base + EVENT_STATUS_REG);
+ } else {
+ writeb(0, a_priv->gpib_base + EVENT_ENABLE_REG);
+ writeb(ENABLE_TMS9914_INTERRUPTS_BIT,
+ a_priv->gpib_base + INTERRUPT_ENABLE_REG);
+ }
+ board->t1_nano_sec = agilent_82350b_t1_delay(board, 2000);
+ tms9914_board_reset(tms_priv);
+
+ tms9914_online(board, tms_priv);
+
+ return 0;
+}
+
+static int agilent_82350b_unaccel_attach(struct gpib_board *board,
+ const struct gpib_board_config *config)
+{
+ return agilent_82350b_generic_attach(board, config, 0);
+}
+
+static int agilent_82350b_accel_attach(struct gpib_board *board,
+ const struct gpib_board_config *config)
+{
+ return agilent_82350b_generic_attach(board, config, 1);
+}
+
+static void agilent_82350b_detach(struct gpib_board *board)
+{
+ struct agilent_82350b_priv *a_priv = board->private_data;
+ struct tms9914_priv *tms_priv;
+
+ if (a_priv) {
+ if (a_priv->plx_base) /* disable interrupts */
+ writel(0, a_priv->plx_base + PLX9050_INTCSR_REG);
+
+ tms_priv = &a_priv->tms9914_priv;
+ if (a_priv->irq)
+ free_irq(a_priv->irq, board);
+ if (a_priv->gpib_base) {
+ tms9914_board_reset(tms_priv);
+ if (a_priv->misc_base)
+ iounmap(a_priv->misc_base);
+ if (a_priv->borg_base)
+ iounmap(a_priv->borg_base);
+ if (a_priv->sram_base)
+ iounmap(a_priv->sram_base);
+ if (a_priv->gpib_base)
+ iounmap(a_priv->gpib_base);
+ if (a_priv->plx_base)
+ iounmap(a_priv->plx_base);
+ pci_release_regions(a_priv->pci_device);
+ }
+ if (a_priv->pci_device)
+ pci_dev_put(a_priv->pci_device);
+ }
+ agilent_82350b_free_private(board);
+}
+
+static struct gpib_interface agilent_82350b_unaccel_interface = {
+ .name = "agilent_82350b_unaccel",
+ .attach = agilent_82350b_unaccel_attach,
+ .detach = agilent_82350b_detach,
+ .read = agilent_82350b_read,
+ .write = agilent_82350b_write,
+ .command = agilent_82350b_command,
+ .request_system_control = agilent_82350b_request_system_control,
+ .take_control = agilent_82350b_take_control,
+ .go_to_standby = agilent_82350b_go_to_standby,
+ .interface_clear = agilent_82350b_interface_clear,
+ .remote_enable = agilent_82350b_remote_enable,
+ .enable_eos = agilent_82350b_enable_eos,
+ .disable_eos = agilent_82350b_disable_eos,
+ .parallel_poll = agilent_82350b_parallel_poll,
+ .parallel_poll_configure = agilent_82350b_parallel_poll_configure,
+ .parallel_poll_response = agilent_82350b_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, /* XXX */
+ .line_status = agilent_82350b_line_status,
+ .update_status = agilent_82350b_update_status,
+ .primary_address = agilent_82350b_primary_address,
+ .secondary_address = agilent_82350b_secondary_address,
+ .serial_poll_response = agilent_82350b_serial_poll_response,
+ .serial_poll_status = agilent_82350b_serial_poll_status,
+ .t1_delay = agilent_82350b_t1_delay,
+ .return_to_local = agilent_82350b_return_to_local,
+};
+
+static struct gpib_interface agilent_82350b_interface = {
+ .name = "agilent_82350b",
+ .attach = agilent_82350b_accel_attach,
+ .detach = agilent_82350b_detach,
+ .read = agilent_82350b_accel_read,
+ .write = agilent_82350b_accel_write,
+ .command = agilent_82350b_command,
+ .request_system_control = agilent_82350b_request_system_control,
+ .take_control = agilent_82350b_take_control,
+ .go_to_standby = agilent_82350b_go_to_standby,
+ .interface_clear = agilent_82350b_interface_clear,
+ .remote_enable = agilent_82350b_remote_enable,
+ .enable_eos = agilent_82350b_enable_eos,
+ .disable_eos = agilent_82350b_disable_eos,
+ .parallel_poll = agilent_82350b_parallel_poll,
+ .parallel_poll_configure = agilent_82350b_parallel_poll_configure,
+ .parallel_poll_response = agilent_82350b_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, /* XXX */
+ .line_status = agilent_82350b_line_status,
+ .update_status = agilent_82350b_update_status,
+ .primary_address = agilent_82350b_primary_address,
+ .secondary_address = agilent_82350b_secondary_address,
+ .serial_poll_response = agilent_82350b_serial_poll_response,
+ .serial_poll_status = agilent_82350b_serial_poll_status,
+ .t1_delay = agilent_82350b_t1_delay,
+ .return_to_local = agilent_82350b_return_to_local,
+};
+
+static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+
+{
+ return 0;
+}
+
+static const struct pci_device_id agilent_82350b_pci_table[] = {
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP,
+ PCI_SUBDEVICE_ID_82350A, 0, 0, 0 },
+ { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table);
+
+static struct pci_driver agilent_82350b_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = agilent_82350b_pci_table,
+ .probe = &agilent_82350b_pci_probe
+};
+
+static int __init agilent_82350b_init_module(void)
+{
+ int result;
+
+ result = pci_register_driver(&agilent_82350b_pci_driver);
+ if (result) {
+ pr_err("pci_register_driver failed: error = %d\n", result);
+ return result;
+ }
+
+ result = gpib_register_driver(&agilent_82350b_unaccel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_unaccel;
+ }
+
+ result = gpib_register_driver(&agilent_82350b_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_interface;
+ }
+
+ return 0;
+
+err_interface:
+ gpib_unregister_driver(&agilent_82350b_unaccel_interface);
+err_unaccel:
+ pci_unregister_driver(&agilent_82350b_pci_driver);
+
+ return result;
+}
+
+static void __exit agilent_82350b_exit_module(void)
+{
+ gpib_unregister_driver(&agilent_82350b_interface);
+ gpib_unregister_driver(&agilent_82350b_unaccel_interface);
+
+ pci_unregister_driver(&agilent_82350b_pci_driver);
+}
+
+module_init(agilent_82350b_init_module);
+module_exit(agilent_82350b_exit_module);
diff --git a/drivers/gpib/agilent_82350b/agilent_82350b.h b/drivers/gpib/agilent_82350b/agilent_82350b.h
new file mode 100644
index 000000000000..ef841957297f
--- /dev/null
+++ b/drivers/gpib/agilent_82350b/agilent_82350b.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002, 2004 by Frank Mori Hess *
+ ***************************************************************************/
+
+#include "gpibP.h"
+#include "plx9050.h"
+#include "tms9914.h"
+
+enum pci_vendor_ids {
+ PCI_VENDOR_ID_AGILENT = 0x15bc,
+};
+
+enum pci_device_ids {
+ PCI_DEVICE_ID_82350B = 0x0b01,
+ PCI_DEVICE_ID_82351A = 0x1218
+};
+
+enum pci_subdevice_ids {
+ PCI_SUBDEVICE_ID_82350A = 0x10b0,
+};
+
+enum pci_regions_82350a {
+ PLX_MEM_REGION = 0,
+ PLX_IO_REGION = 1,
+ GPIB_82350A_REGION = 2,
+ SRAM_82350A_REGION = 3,
+ BORG_82350A_REGION = 4
+};
+
+enum pci_regions_82350b {
+ GPIB_REGION = 0,
+ SRAM_REGION = 1,
+ MISC_REGION = 2,
+};
+
+enum board_model {
+ MODEL_82350A,
+ MODEL_82350B,
+ MODEL_82351A
+};
+
+/* struct which defines private_data for board */
+struct agilent_82350b_priv {
+ struct tms9914_priv tms9914_priv;
+ struct pci_dev *pci_device;
+ void __iomem *plx_base; /* 82350a only */
+ void __iomem *gpib_base;
+ void __iomem *sram_base;
+ void __iomem *misc_base;
+ void __iomem *borg_base;
+ int irq;
+ unsigned short card_mode_bits;
+ unsigned short event_status_bits;
+ enum board_model model;
+ bool using_fifos;
+};
+
+/* registers */
+enum agilent_82350b_gpib_registers
+
+{
+ CARD_MODE_REG = 0x1,
+ CONFIG_DATA_REG = 0x2, /* 82350A specific */
+ INTERRUPT_ENABLE_REG = 0x3,
+ EVENT_STATUS_REG = 0x4,
+ EVENT_ENABLE_REG = 0x5,
+ STREAM_STATUS_REG = 0x7,
+ DEBUG_RAM0_REG = 0x8,
+ DEBUG_RAM1_REG = 0x9,
+ DEBUG_RAM2_REG = 0xa,
+ DEBUG_RAM3_REG = 0xb,
+ XFER_COUNT_LO_REG = 0xc,
+ XFER_COUNT_MID_REG = 0xd,
+ XFER_COUNT_HI_REG = 0xe,
+ TMS9914_BASE_REG = 0x10,
+ INTERNAL_CONFIG_REG = 0x18,
+ IMR0_READ_REG = 0x19, /* read */
+ T1_DELAY_REG = 0x19, /* write */
+ IMR1_READ_REG = 0x1a,
+ ADR_READ_REG = 0x1b,
+ SPMR_READ_REG = 0x1c,
+ PPR_READ_REG = 0x1d,
+ CDOR_READ_REG = 0x1e,
+ SRAM_ACCESS_CONTROL_REG = 0x1f,
+};
+
+enum card_mode_bits
+
+{
+ ACTIVE_CONTROLLER_BIT = 0x2, /* read-only */
+ CM_SYSTEM_CONTROLLER_BIT = 0x8,
+ ENABLE_BUS_MONITOR_BIT = 0x10,
+ ENABLE_PCI_IRQ_BIT = 0x20,
+};
+
+enum interrupt_enable_bits
+
+{
+ ENABLE_TMS9914_INTERRUPTS_BIT = 0x1,
+ ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10,
+ ENABLE_TERM_COUNT_INTERRUPT_BIT = 0x20,
+};
+
+enum event_enable_bits
+
+{
+ ENABLE_BUFFER_END_EVENTS_BIT = 0x10,
+ ENABLE_TERM_COUNT_EVENTS_BIT = 0x20,
+};
+
+enum event_status_bits
+
+{
+ TMS9914_IRQ_STATUS_BIT = 0x1,
+ IRQ_STATUS_BIT = 0x2,
+ BUFFER_END_STATUS_BIT = 0x10, /* write-clear */
+ TERM_COUNT_STATUS_BIT = 0x20, /* write-clear */
+};
+
+enum stream_status_bits
+
+{
+ HALTED_STATUS_BIT = 0x1, /* read */
+ RESTART_STREAM_BIT = 0x1, /* write */
+};
+
+enum internal_config_bits
+
+{
+ IC_SYSTEM_CONTROLLER_BIT = 0x80,
+};
+
+enum sram_access_control_bits
+
+{
+ DIRECTION_GPIB_TO_HOST = 0x20, /* transfer direction */
+ ENABLE_TI_TO_SRAM = 0x40, /* enable fifo */
+ ENABLE_FAST_TALKER = 0x80 /* added for 82350A (not used) */
+};
+
+enum borg_bits
+
+{
+ BORG_READY_BIT = 0x40,
+ BORG_DONE_BIT = 0x80
+};
+
+static const int agilent_82350b_fifo_size = 0x8000;
+
+static inline int agilent_82350b_fifo_is_halted(struct agilent_82350b_priv *a_priv)
+
+{
+ return readb(a_priv->gpib_base + STREAM_STATUS_REG) & HALTED_STATUS_BIT;
+}
+
diff --git a/drivers/gpib/agilent_82357a/Makefile b/drivers/gpib/agilent_82357a/Makefile
new file mode 100644
index 000000000000..81a55c257a6e
--- /dev/null
+++ b/drivers/gpib/agilent_82357a/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a.o
+
+
diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.c b/drivers/gpib/agilent_82357a/agilent_82357a.c
new file mode 100644
index 000000000000..77c8e549b208
--- /dev/null
+++ b/drivers/gpib/agilent_82357a/agilent_82357a.c
@@ -0,0 +1,1691 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * driver for Agilent 82357A/B usb to gpib adapters *
+ * copyright : (C) 2004 by Frank Mori Hess *
+ ***************************************************************************/
+
+#define _GNU_SOURCE
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "agilent_82357a.h"
+#include "gpibP.h"
+#include "tms9914.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for Agilent 82357A/B usb adapters");
+
+#define MAX_NUM_82357A_INTERFACES 128
+static struct usb_interface *agilent_82357a_driver_interfaces[MAX_NUM_82357A_INTERFACES];
+static DEFINE_MUTEX(agilent_82357a_hotplug_lock); // protect board insertion and removal
+
+static unsigned int agilent_82357a_update_status(struct gpib_board *board,
+ unsigned int clear_mask);
+
+static int agilent_82357a_take_control_internal(struct gpib_board *board, int synchronous);
+
+static void agilent_82357a_bulk_complete(struct urb *urb)
+{
+ struct agilent_82357a_urb_ctx *context = urb->context;
+
+ complete(&context->complete);
+}
+
+static void agilent_82357a_timeout_handler(struct timer_list *t)
+{
+ struct agilent_82357a_priv *a_priv = timer_container_of(a_priv, t,
+ bulk_timer);
+ struct agilent_82357a_urb_ctx *context = &a_priv->context;
+
+ context->timed_out = 1;
+ complete(&context->complete);
+}
+
+static int agilent_82357a_send_bulk_msg(struct agilent_82357a_priv *a_priv, void *data,
+ int data_length, int *actual_data_length,
+ int timeout_msecs)
+{
+ struct usb_device *usb_dev;
+ int retval;
+ unsigned int out_pipe;
+ struct agilent_82357a_urb_ctx *context = &a_priv->context;
+
+ *actual_data_length = 0;
+ retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock);
+ if (retval)
+ return retval;
+ if (!a_priv->bus_interface) {
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return -ENODEV;
+ }
+ if (a_priv->bulk_urb) {
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return -EAGAIN;
+ }
+ a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!a_priv->bulk_urb) {
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return -ENOMEM;
+ }
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ out_pipe = usb_sndbulkpipe(usb_dev, a_priv->bulk_out_endpoint);
+ init_completion(&context->complete);
+ context->timed_out = 0;
+ usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, out_pipe, data, data_length,
+ &agilent_82357a_bulk_complete, context);
+
+ if (timeout_msecs)
+ mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs));
+
+ retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&usb_dev->dev, "failed to submit bulk out urb, retval=%i\n", retval);
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ goto cleanup;
+ }
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ if (wait_for_completion_interruptible(&context->complete)) {
+ retval = -ERESTARTSYS;
+ goto cleanup;
+ }
+ if (context->timed_out) {
+ retval = -ETIMEDOUT;
+ } else {
+ retval = a_priv->bulk_urb->status;
+ *actual_data_length = a_priv->bulk_urb->actual_length;
+ }
+cleanup:
+ if (timeout_msecs) {
+ if (timer_pending(&a_priv->bulk_timer))
+ timer_delete_sync(&a_priv->bulk_timer);
+ }
+ mutex_lock(&a_priv->bulk_alloc_lock);
+ if (a_priv->bulk_urb) {
+ usb_kill_urb(a_priv->bulk_urb);
+ usb_free_urb(a_priv->bulk_urb);
+ a_priv->bulk_urb = NULL;
+ }
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return retval;
+}
+
+static int agilent_82357a_receive_bulk_msg(struct agilent_82357a_priv *a_priv, void *data,
+ int data_length, int *actual_data_length,
+ int timeout_msecs)
+{
+ struct usb_device *usb_dev;
+ int retval;
+ unsigned int in_pipe;
+ struct agilent_82357a_urb_ctx *context = &a_priv->context;
+
+ *actual_data_length = 0;
+ retval = mutex_lock_interruptible(&a_priv->bulk_alloc_lock);
+ if (retval)
+ return retval;
+ if (!a_priv->bus_interface) {
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return -ENODEV;
+ }
+ if (a_priv->bulk_urb) {
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return -EAGAIN;
+ }
+ a_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!a_priv->bulk_urb) {
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return -ENOMEM;
+ }
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ in_pipe = usb_rcvbulkpipe(usb_dev, AGILENT_82357_BULK_IN_ENDPOINT);
+ init_completion(&context->complete);
+ context->timed_out = 0;
+ usb_fill_bulk_urb(a_priv->bulk_urb, usb_dev, in_pipe, data, data_length,
+ &agilent_82357a_bulk_complete, context);
+
+ if (timeout_msecs)
+ mod_timer(&a_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs));
+
+ retval = usb_submit_urb(a_priv->bulk_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&usb_dev->dev, "failed to submit bulk in urb, retval=%i\n", retval);
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ goto cleanup;
+ }
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ if (wait_for_completion_interruptible(&context->complete)) {
+ retval = -ERESTARTSYS;
+ goto cleanup;
+ }
+ if (context->timed_out) {
+ retval = -ETIMEDOUT;
+ goto cleanup;
+ }
+ retval = a_priv->bulk_urb->status;
+ *actual_data_length = a_priv->bulk_urb->actual_length;
+cleanup:
+ if (timeout_msecs)
+ timer_delete_sync(&a_priv->bulk_timer);
+
+ mutex_lock(&a_priv->bulk_alloc_lock);
+ if (a_priv->bulk_urb) {
+ usb_kill_urb(a_priv->bulk_urb);
+ usb_free_urb(a_priv->bulk_urb);
+ a_priv->bulk_urb = NULL;
+ }
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ return retval;
+}
+
+static int agilent_82357a_receive_control_msg(struct agilent_82357a_priv *a_priv, __u8 request,
+ __u8 requesttype, __u16 value, __u16 index,
+ void *data, __u16 size, int timeout_msecs)
+{
+ struct usb_device *usb_dev;
+ int retval;
+ unsigned int in_pipe;
+
+ retval = mutex_lock_interruptible(&a_priv->control_alloc_lock);
+ if (retval)
+ return retval;
+ if (!a_priv->bus_interface) {
+ mutex_unlock(&a_priv->control_alloc_lock);
+ return -ENODEV;
+ }
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ in_pipe = usb_rcvctrlpipe(usb_dev, AGILENT_82357_CONTROL_ENDPOINT);
+ retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data,
+ size, timeout_msecs);
+ mutex_unlock(&a_priv->control_alloc_lock);
+ return retval;
+}
+
+static void agilent_82357a_dump_raw_block(const u8 *raw_data, int length)
+{
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true);
+}
+
+static int agilent_82357a_write_registers(struct agilent_82357a_priv *a_priv,
+ const struct agilent_82357a_register_pairlet *writes,
+ int num_writes)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ int retval;
+ u8 *out_data, *in_data;
+ int out_data_length, in_data_length;
+ int bytes_written, bytes_read;
+ int i = 0;
+ int j;
+ static const int bytes_per_write = 2;
+ static const int header_length = 2;
+ static const int max_writes = 31;
+
+ if (num_writes > max_writes) {
+ dev_err(&usb_dev->dev, "bug! num_writes=%i too large\n", num_writes);
+ return -EIO;
+ }
+ out_data_length = num_writes * bytes_per_write + header_length;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+
+ out_data[i++] = DATA_PIPE_CMD_WR_REGS;
+ out_data[i++] = num_writes;
+ for (j = 0; j < num_writes; j++) {
+ out_data[i++] = writes[j].address;
+ out_data[i++] = writes[j].value;
+ }
+
+ retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+ if (retval) {
+ kfree(out_data);
+ return retval;
+ }
+ retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval) {
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return retval;
+ }
+ in_data_length = 0x20;
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length,
+ &bytes_read, 1000);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+
+ if (retval) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ agilent_82357a_dump_raw_block(in_data, bytes_read);
+ kfree(in_data);
+ return -EIO;
+ }
+ if (in_data[0] != (0xff & ~DATA_PIPE_CMD_WR_REGS)) {
+ dev_err(&usb_dev->dev, "bulk command=0x%x != ~DATA_PIPE_CMD_WR_REGS\n", in_data[0]);
+ return -EIO;
+ }
+ if (in_data[1]) {
+ dev_err(&usb_dev->dev, "nonzero error code 0x%x in DATA_PIPE_CMD_WR_REGS response\n",
+ in_data[1]);
+ return -EIO;
+ }
+ kfree(in_data);
+ return 0;
+}
+
+static int agilent_82357a_read_registers(struct agilent_82357a_priv *a_priv,
+ struct agilent_82357a_register_pairlet *reads,
+ int num_reads, int blocking)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ int retval;
+ u8 *out_data, *in_data;
+ int out_data_length, in_data_length;
+ int bytes_written, bytes_read;
+ int i = 0;
+ int j;
+ static const int header_length = 2;
+ static const int max_reads = 62;
+
+ if (num_reads > max_reads) {
+ dev_err(&usb_dev->dev, "bug! num_reads=%i too large\n", num_reads);
+ return -EIO;
+ }
+ out_data_length = num_reads + header_length;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+
+ out_data[i++] = DATA_PIPE_CMD_RD_REGS;
+ out_data[i++] = num_reads;
+ for (j = 0; j < num_reads; j++)
+ out_data[i++] = reads[j].address;
+
+ if (blocking) {
+ retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+ if (retval) {
+ kfree(out_data);
+ return retval;
+ }
+ } else {
+ retval = mutex_trylock(&a_priv->bulk_transfer_lock);
+ if (retval == 0) {
+ kfree(out_data);
+ return -EAGAIN;
+ }
+ }
+ retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval) {
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return retval;
+ }
+ in_data_length = 0x20;
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length,
+ &bytes_read, 10000);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+
+ if (retval) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ agilent_82357a_dump_raw_block(in_data, bytes_read);
+ kfree(in_data);
+ return -EIO;
+ }
+ i = 0;
+ if (in_data[i++] != (0xff & ~DATA_PIPE_CMD_RD_REGS)) {
+ dev_err(&usb_dev->dev, "bulk command=0x%x != ~DATA_PIPE_CMD_RD_REGS\n", in_data[0]);
+ return -EIO;
+ }
+ if (in_data[i++]) {
+ dev_err(&usb_dev->dev, "nonzero error code 0x%x in DATA_PIPE_CMD_RD_REGS response\n",
+ in_data[1]);
+ return -EIO;
+ }
+ for (j = 0; j < num_reads; j++)
+ reads[j].value = in_data[i++];
+ kfree(in_data);
+ return 0;
+}
+
+static int agilent_82357a_abort(struct agilent_82357a_priv *a_priv, int flush)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ int retval = 0;
+ int receive_control_retval;
+ u16 wIndex = 0;
+ u8 *status_data;
+ static const unsigned int status_data_len = 2;
+
+ status_data = kmalloc(status_data_len, GFP_KERNEL);
+ if (!status_data)
+ return -ENOMEM;
+
+ if (flush)
+ wIndex |= XA_FLUSH;
+ receive_control_retval = agilent_82357a_receive_control_msg(a_priv,
+ agilent_82357a_control_request,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE, XFER_ABORT,
+ wIndex, status_data,
+ status_data_len, 100);
+ if (receive_control_retval < 0) {
+ dev_err(&usb_dev->dev, "82357a_receive_control_msg() returned %i\n",
+ receive_control_retval);
+ retval = -EIO;
+ goto cleanup;
+ }
+ if (status_data[0] != (~XFER_ABORT & 0xff)) {
+ dev_err(&usb_dev->dev, "major code=0x%x != ~XFER_ABORT\n", status_data[0]);
+ retval = -EIO;
+ goto cleanup;
+ }
+ switch (status_data[1]) {
+ case UGP_SUCCESS:
+ retval = 0;
+ break;
+ case UGP_ERR_FLUSHING:
+ if (flush) {
+ retval = 0;
+ break;
+ }
+ fallthrough;
+ case UGP_ERR_FLUSHING_ALREADY:
+ default:
+ dev_err(&usb_dev->dev, "abort returned error code=0x%x\n", status_data[1]);
+ retval = -EIO;
+ break;
+ }
+
+cleanup:
+ kfree(status_data);
+ return retval;
+}
+
+// interface functions
+int agilent_82357a_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written);
+
+static int agilent_82357a_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *nbytes)
+{
+ int retval;
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ int out_data_length, in_data_length;
+ int bytes_written, bytes_read;
+ int i = 0;
+ u8 trailing_flags;
+ unsigned long start_jiffies = jiffies;
+ int msec_timeout;
+
+ *nbytes = 0;
+ *end = 0;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ out_data_length = 0x9;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ out_data[i++] = DATA_PIPE_CMD_READ;
+ out_data[i++] = 0; // primary address when ARF_NO_ADDR is not set
+ out_data[i++] = 0; // secondary address when ARF_NO_ADDR is not set
+ out_data[i] = ARF_NO_ADDRESS | ARF_END_ON_EOI;
+ if (a_priv->eos_mode & REOS)
+ out_data[i] |= ARF_END_ON_EOS_CHAR;
+ ++i;
+ out_data[i++] = length & 0xff;
+ out_data[i++] = (length >> 8) & 0xff;
+ out_data[i++] = (length >> 16) & 0xff;
+ out_data[i++] = (length >> 24) & 0xff;
+ out_data[i++] = a_priv->eos_char;
+ msec_timeout = (board->usec_timeout + 999) / 1000;
+ retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+ if (retval) {
+ kfree(out_data);
+ return retval;
+ }
+ retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &bytes_written, msec_timeout);
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ if (retval < 0)
+ return retval;
+ return -EIO;
+ }
+ in_data_length = length + 1;
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return -ENOMEM;
+ }
+ if (board->usec_timeout != 0)
+ msec_timeout -= jiffies_to_msecs(jiffies - start_jiffies) - 1;
+ if (msec_timeout >= 0) {
+ retval = agilent_82357a_receive_bulk_msg(a_priv, in_data, in_data_length,
+ &bytes_read, msec_timeout);
+ } else {
+ retval = -ETIMEDOUT;
+ bytes_read = 0;
+ }
+ if (retval == -ETIMEDOUT) {
+ int extra_bytes_read;
+ int extra_bytes_retval;
+
+ agilent_82357a_abort(a_priv, 1);
+ extra_bytes_retval = agilent_82357a_receive_bulk_msg(a_priv, in_data + bytes_read,
+ in_data_length - bytes_read,
+ &extra_bytes_read, 100);
+ bytes_read += extra_bytes_read;
+ if (extra_bytes_retval) {
+ dev_err(&usb_dev->dev, "extra_bytes_retval=%i, bytes_read=%i\n",
+ extra_bytes_retval, bytes_read);
+ agilent_82357a_abort(a_priv, 0);
+ }
+ } else if (retval) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ agilent_82357a_abort(a_priv, 0);
+ }
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ if (bytes_read > length + 1) {
+ bytes_read = length + 1;
+ dev_warn(&usb_dev->dev, "bytes_read > length? truncating");
+ }
+
+ if (bytes_read >= 1) {
+ memcpy(buffer, in_data, bytes_read - 1);
+ trailing_flags = in_data[bytes_read - 1];
+ *nbytes = bytes_read - 1;
+ if (trailing_flags & (ATRF_EOI | ATRF_EOS))
+ *end = 1;
+ }
+ kfree(in_data);
+
+ /*
+ * Fix for a bug in 9914A that does not return the contents of ADSR
+ * when the board is in listener active state and ATN is not asserted.
+ * Set ATN here to obtain a valid board level ibsta
+ */
+ agilent_82357a_take_control_internal(board, 0);
+
+ // FIXME check trailing flags for error
+ return retval;
+}
+
+static ssize_t agilent_82357a_generic_write(struct gpib_board *board,
+ u8 *buffer, size_t length,
+ int send_commands, int send_eoi,
+ size_t *bytes_written)
+{
+ int retval;
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data = NULL;
+ u8 *status_data = NULL;
+ int out_data_length;
+ int raw_bytes_written;
+ int i = 0, j;
+ int msec_timeout;
+ unsigned short bsr, adsr;
+ struct agilent_82357a_register_pairlet read_reg;
+
+ *bytes_written = 0;
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ out_data_length = length + 0x8;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ out_data[i++] = DATA_PIPE_CMD_WRITE;
+ out_data[i++] = 0; // primary address when AWF_NO_ADDRESS is not set
+ out_data[i++] = 0; // secondary address when AWF_NO_ADDRESS is not set
+ out_data[i] = AWF_NO_ADDRESS | AWF_NO_FAST_TALKER_FIRST_BYTE;
+ if (send_commands)
+ out_data[i] |= AWF_ATN | AWF_NO_FAST_TALKER;
+ if (send_eoi)
+ out_data[i] |= AWF_SEND_EOI;
+ ++i;
+ out_data[i++] = length & 0xff;
+ out_data[i++] = (length >> 8) & 0xff;
+ out_data[i++] = (length >> 16) & 0xff;
+ out_data[i++] = (length >> 24) & 0xff;
+ for (j = 0; j < length; j++)
+ out_data[i++] = buffer[j];
+
+ clear_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags);
+
+ msec_timeout = (board->usec_timeout + 999) / 1000;
+ retval = mutex_lock_interruptible(&a_priv->bulk_transfer_lock);
+ if (retval) {
+ kfree(out_data);
+ return retval;
+ }
+ retval = agilent_82357a_send_bulk_msg(a_priv, out_data, i, &raw_bytes_written,
+ msec_timeout);
+ kfree(out_data);
+ if (retval || raw_bytes_written != i) {
+ agilent_82357a_abort(a_priv, 0);
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, raw_bytes_written=%i, i=%i\n",
+ retval, raw_bytes_written, i);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ if (retval < 0)
+ return retval;
+ return -EIO;
+ }
+
+ retval = wait_event_interruptible(board->wait,
+ test_bit(AIF_WRITE_COMPLETE_BN,
+ &a_priv->interrupt_flags) ||
+ test_bit(TIMO_NUM, &board->status));
+ if (retval) {
+ dev_dbg(&usb_dev->dev, "wait write complete interrupted\n");
+ agilent_82357a_abort(a_priv, 0);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags) == 0) {
+ dev_dbg(&usb_dev->dev, "write timed out ibs %i, tmo %i\n",
+ test_bit(TIMO_NUM, &board->status), msec_timeout);
+
+ agilent_82357a_abort(a_priv, 0);
+
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+
+ read_reg.address = BSR;
+ retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1);
+ if (retval) {
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return -ETIMEDOUT;
+ }
+
+ bsr = read_reg.value;
+ dev_dbg(&usb_dev->dev, "write aborted bsr 0x%x\n", bsr);
+
+ if (send_commands) {/* check for no listeners */
+ if ((bsr & BSR_ATN_BIT) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) {
+ dev_dbg(&usb_dev->dev, "No listener on command\n");
+ clear_bit(TIMO_NUM, &board->status);
+ return -ENOTCONN; // no listener on bus
+ }
+ } else {
+ read_reg.address = ADSR;
+ retval = agilent_82357a_read_registers(a_priv, &read_reg, 1, 1);
+ if (retval) {
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return -ETIMEDOUT;
+ }
+ adsr = read_reg.value;
+ if ((adsr & HR_TA) && !(bsr & (BSR_NDAC_BIT | BSR_NRFD_BIT))) {
+ dev_dbg(&usb_dev->dev, "No listener on write\n");
+ clear_bit(TIMO_NUM, &board->status);
+ return -ECOMM;
+ }
+ }
+
+ return -ETIMEDOUT;
+ }
+
+ status_data = kmalloc(STATUS_DATA_LEN, GFP_KERNEL);
+ if (!status_data) {
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ return -ENOMEM;
+ }
+
+ retval = agilent_82357a_receive_control_msg(a_priv, agilent_82357a_control_request,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ XFER_STATUS, 0, status_data, STATUS_DATA_LEN,
+ 100);
+ mutex_unlock(&a_priv->bulk_transfer_lock);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "receive_control_msg() returned %i\n", retval);
+ kfree(status_data);
+ return -EIO;
+ }
+ *bytes_written = (u32)status_data[2];
+ *bytes_written |= (u32)status_data[3] << 8;
+ *bytes_written |= (u32)status_data[4] << 16;
+ *bytes_written |= (u32)status_data[5] << 24;
+
+ kfree(status_data);
+ return 0;
+}
+
+static int agilent_82357a_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written)
+{
+ return agilent_82357a_generic_write(board, buffer, length, 0, send_eoi, bytes_written);
+}
+
+int agilent_82357a_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ return agilent_82357a_generic_write(board, buffer, length, 1, 0, bytes_written);
+}
+
+int agilent_82357a_take_control_internal(struct gpib_board *board, int synchronous)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ struct agilent_82357a_register_pairlet write;
+ int retval;
+
+ write.address = AUXCR;
+ if (synchronous)
+ write.value = AUX_TCS;
+ else
+ write.value = AUX_TCA;
+ retval = agilent_82357a_write_registers(a_priv, &write, 1);
+ if (retval)
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+
+ return retval;
+}
+
+static int agilent_82357a_take_control(struct gpib_board *board, int synchronous)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ const int timeout = 10;
+ int i;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+
+/*
+ * It looks like the 9914 does not handle tcs properly.
+ * See comment above tms9914_take_control_workaround() in
+ * drivers/gpib/tms9914/tms9914_aux.c
+ */
+ if (synchronous)
+ return -ETIMEDOUT;
+
+ agilent_82357a_take_control_internal(board, synchronous);
+ // busy wait until ATN is asserted
+ for (i = 0; i < timeout; ++i) {
+ agilent_82357a_update_status(board, 0);
+ if (test_bit(ATN_NUM, &board->status))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+ return 0;
+}
+
+static int agilent_82357a_go_to_standby(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet write;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ write.address = AUXCR;
+ write.value = AUX_GTS;
+ retval = agilent_82357a_write_registers(a_priv, &write, 1);
+ if (retval)
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return 0;
+}
+
+static int agilent_82357a_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet writes[2];
+ int retval;
+ int i = 0;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ /* 82357B needs bit to be set in 9914 AUXCR register */
+ writes[i].address = AUXCR;
+ if (request_control) {
+ writes[i].value = AUX_RQC;
+ a_priv->hw_control_bits |= SYSTEM_CONTROLLER;
+ } else {
+ return -EINVAL;
+ }
+ ++i;
+ writes[i].address = HW_CONTROL;
+ writes[i].value = a_priv->hw_control_bits;
+ ++i;
+ retval = agilent_82357a_write_registers(a_priv, writes, i);
+ if (retval)
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return retval;
+}
+
+static void agilent_82357a_interface_clear(struct gpib_board *board, int assert)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet write;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return; // -ENODEV;
+
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ write.address = AUXCR;
+ write.value = AUX_SIC;
+ if (assert) {
+ write.value |= AUX_CS;
+ a_priv->is_cic = 1;
+ }
+ retval = agilent_82357a_write_registers(a_priv, &write, 1);
+ if (retval)
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+}
+
+static void agilent_82357a_remote_enable(struct gpib_board *board, int enable)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet write;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return; //-ENODEV;
+
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ write.address = AUXCR;
+ write.value = AUX_SRE;
+ if (enable)
+ write.value |= AUX_CS;
+ retval = agilent_82357a_write_registers(a_priv, &write, 1);
+ if (retval)
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ a_priv->ren_state = enable;
+ return;// 0;
+}
+
+static int agilent_82357a_enable_eos(struct gpib_board *board, u8 eos_byte,
+ int compare_8_bits)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ if (compare_8_bits == 0)
+ return -EOPNOTSUPP;
+
+ a_priv->eos_char = eos_byte;
+ a_priv->eos_mode = REOS | BIN;
+ return 0;
+}
+
+static void agilent_82357a_disable_eos(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+
+ a_priv->eos_mode &= ~REOS;
+}
+
+static unsigned int agilent_82357a_update_status(struct gpib_board *board,
+ unsigned int clear_mask)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet address_status, bus_status;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ board->status &= ~clear_mask;
+ if (a_priv->is_cic)
+ set_bit(CIC_NUM, &board->status);
+ else
+ clear_bit(CIC_NUM, &board->status);
+ address_status.address = ADSR;
+ retval = agilent_82357a_read_registers(a_priv, &address_status, 1, 0);
+ if (retval) {
+ if (retval != -EAGAIN)
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return board->status;
+ }
+ // check for remote/local
+ if (address_status.value & HR_REM)
+ set_bit(REM_NUM, &board->status);
+ else
+ clear_bit(REM_NUM, &board->status);
+ // check for lockout
+ if (address_status.value & HR_LLO)
+ set_bit(LOK_NUM, &board->status);
+ else
+ clear_bit(LOK_NUM, &board->status);
+ // check for ATN
+ if (address_status.value & HR_ATN)
+ set_bit(ATN_NUM, &board->status);
+ else
+ clear_bit(ATN_NUM, &board->status);
+ // check for talker/listener addressed
+ if (address_status.value & HR_TA)
+ set_bit(TACS_NUM, &board->status);
+ else
+ clear_bit(TACS_NUM, &board->status);
+ if (address_status.value & HR_LA)
+ set_bit(LACS_NUM, &board->status);
+ else
+ clear_bit(LACS_NUM, &board->status);
+
+ bus_status.address = BSR;
+ retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0);
+ if (retval) {
+ if (retval != -EAGAIN)
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return board->status;
+ }
+ if (bus_status.value & BSR_SRQ_BIT)
+ set_bit(SRQI_NUM, &board->status);
+ else
+ clear_bit(SRQI_NUM, &board->status);
+
+ return board->status;
+}
+
+static int agilent_82357a_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ struct agilent_82357a_register_pairlet write;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ // put primary address in address0
+ write.address = ADR;
+ write.value = address & ADDRESS_MASK;
+ retval = agilent_82357a_write_registers(a_priv, &write, 1);
+ if (retval) {
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return retval;
+ }
+ return retval;
+}
+
+static int agilent_82357a_secondary_address(struct gpib_board *board,
+ unsigned int address, int enable)
+{
+ if (enable)
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static int agilent_82357a_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet writes[2];
+ struct agilent_82357a_register_pairlet read;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ // execute parallel poll
+ writes[0].address = AUXCR;
+ writes[0].value = AUX_CS | AUX_RPP;
+ writes[1].address = HW_CONTROL;
+ writes[1].value = a_priv->hw_control_bits & ~NOT_PARALLEL_POLL;
+ retval = agilent_82357a_write_registers(a_priv, writes, 2);
+ if (retval) {
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return retval;
+ }
+ udelay(2); // silly, since usb write will take way longer
+ read.address = CPTR;
+ retval = agilent_82357a_read_registers(a_priv, &read, 1, 1);
+ if (retval) {
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return retval;
+ }
+ *result = read.value;
+ // clear parallel poll state
+ writes[0].address = HW_CONTROL;
+ writes[0].value = a_priv->hw_control_bits | NOT_PARALLEL_POLL;
+ writes[1].address = AUXCR;
+ writes[1].value = AUX_RPP;
+ retval = agilent_82357a_write_registers(a_priv, writes, 2);
+ if (retval) {
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return retval;
+ }
+ return 0;
+}
+
+static void agilent_82357a_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ // board can only be system controller
+ return;// 0;
+}
+
+static void agilent_82357a_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ // board can only be system controller
+ return;// 0;
+}
+
+static void agilent_82357a_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ // board can only be system controller
+ return;// 0;
+}
+
+static u8 agilent_82357a_serial_poll_status(struct gpib_board *board)
+{
+ // board can only be system controller
+ return 0;
+}
+
+static void agilent_82357a_return_to_local(struct gpib_board *board)
+{
+ // board can only be system controller
+ return;// 0;
+}
+
+static int agilent_82357a_line_status(const struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet bus_status;
+ int retval;
+ int status = VALID_ALL;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ bus_status.address = BSR;
+ retval = agilent_82357a_read_registers(a_priv, &bus_status, 1, 0);
+ if (retval) {
+ if (retval != -EAGAIN)
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return retval;
+ }
+ if (bus_status.value & BSR_REN_BIT)
+ status |= BUS_REN;
+ if (bus_status.value & BSR_IFC_BIT)
+ status |= BUS_IFC;
+ if (bus_status.value & BSR_SRQ_BIT)
+ status |= BUS_SRQ;
+ if (bus_status.value & BSR_EOI_BIT)
+ status |= BUS_EOI;
+ if (bus_status.value & BSR_NRFD_BIT)
+ status |= BUS_NRFD;
+ if (bus_status.value & BSR_NDAC_BIT)
+ status |= BUS_NDAC;
+ if (bus_status.value & BSR_DAV_BIT)
+ status |= BUS_DAV;
+ if (bus_status.value & BSR_ATN_BIT)
+ status |= BUS_ATN;
+ return status;
+}
+
+static unsigned short nanosec_to_fast_talker_bits(unsigned int *nanosec)
+{
+ static const int nanosec_per_bit = 21;
+ static const int max_value = 0x72;
+ static const int min_value = 0x11;
+ unsigned short bits;
+
+ bits = (*nanosec + nanosec_per_bit / 2) / nanosec_per_bit;
+ if (bits < min_value)
+ bits = min_value;
+ if (bits > max_value)
+ bits = max_value;
+ *nanosec = bits * nanosec_per_bit;
+ return bits;
+}
+
+static int agilent_82357a_t1_delay(struct gpib_board *board, unsigned int nanosec)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct agilent_82357a_register_pairlet write;
+ int retval;
+
+ if (!a_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ write.address = FAST_TALKER_T1;
+ write.value = nanosec_to_fast_talker_bits(&nanosec);
+ retval = agilent_82357a_write_registers(a_priv, &write, 1);
+ if (retval)
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return nanosec;
+}
+
+static void agilent_82357a_interrupt_complete(struct urb *urb)
+{
+ struct gpib_board *board = urb->context;
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ int retval;
+ u8 *transfer_buffer = urb->transfer_buffer;
+ unsigned long interrupt_flags;
+
+ switch (urb->status) {
+ /* success */
+ case 0:
+ break;
+ /* unlinked, don't resubmit */
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* other error, resubmit */
+ retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n");
+ return;
+ }
+
+ interrupt_flags = transfer_buffer[0];
+ if (test_bit(AIF_READ_COMPLETE_BN, &interrupt_flags))
+ set_bit(AIF_READ_COMPLETE_BN, &a_priv->interrupt_flags);
+ if (test_bit(AIF_WRITE_COMPLETE_BN, &interrupt_flags))
+ set_bit(AIF_WRITE_COMPLETE_BN, &a_priv->interrupt_flags);
+ if (test_bit(AIF_SRQ_BN, &interrupt_flags))
+ set_bit(SRQI_NUM, &board->status);
+
+ wake_up_interruptible(&board->wait);
+
+ retval = usb_submit_urb(a_priv->interrupt_urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n");
+}
+
+static int agilent_82357a_setup_urbs(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int int_pipe;
+ int retval;
+
+ retval = mutex_lock_interruptible(&a_priv->interrupt_alloc_lock);
+ if (retval)
+ return retval;
+ if (!a_priv->bus_interface) {
+ retval = -ENODEV;
+ goto setup_exit;
+ }
+
+ a_priv->interrupt_buffer = kmalloc(INTERRUPT_BUF_LEN, GFP_KERNEL);
+ if (!a_priv->interrupt_buffer) {
+ retval = -ENOMEM;
+ goto setup_exit;
+ }
+ a_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!a_priv->interrupt_urb) {
+ retval = -ENOMEM;
+ goto setup_exit;
+ }
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ int_pipe = usb_rcvintpipe(usb_dev, a_priv->interrupt_in_endpoint);
+ usb_fill_int_urb(a_priv->interrupt_urb, usb_dev, int_pipe, a_priv->interrupt_buffer,
+ INTERRUPT_BUF_LEN, &agilent_82357a_interrupt_complete, board, 1);
+ retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL);
+ if (retval) {
+ usb_free_urb(a_priv->interrupt_urb);
+ a_priv->interrupt_urb = NULL;
+ dev_err(&usb_dev->dev, "failed to submit first interrupt urb, retval=%i\n", retval);
+ goto setup_exit;
+ }
+ mutex_unlock(&a_priv->interrupt_alloc_lock);
+ return 0;
+
+setup_exit:
+ kfree(a_priv->interrupt_buffer);
+ mutex_unlock(&a_priv->interrupt_alloc_lock);
+ return retval;
+}
+
+static void agilent_82357a_cleanup_urbs(struct agilent_82357a_priv *a_priv)
+{
+ if (a_priv && a_priv->bus_interface) {
+ if (a_priv->interrupt_urb)
+ usb_kill_urb(a_priv->interrupt_urb);
+ if (a_priv->bulk_urb)
+ usb_kill_urb(a_priv->bulk_urb);
+ }
+};
+
+static void agilent_82357a_release_urbs(struct agilent_82357a_priv *a_priv)
+{
+ if (a_priv) {
+ usb_free_urb(a_priv->interrupt_urb);
+ a_priv->interrupt_urb = NULL;
+ kfree(a_priv->interrupt_buffer);
+ }
+}
+
+static int agilent_82357a_allocate_private(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv;
+
+ board->private_data = kzalloc(sizeof(struct agilent_82357a_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ a_priv = board->private_data;
+ mutex_init(&a_priv->bulk_transfer_lock);
+ mutex_init(&a_priv->bulk_alloc_lock);
+ mutex_init(&a_priv->control_alloc_lock);
+ mutex_init(&a_priv->interrupt_alloc_lock);
+ return 0;
+}
+
+static void agilent_82357a_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+#define INIT_NUM_REG_WRITES 18
+static int agilent_82357a_init(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ struct agilent_82357a_register_pairlet hw_control;
+ struct agilent_82357a_register_pairlet writes[INIT_NUM_REG_WRITES];
+ int retval;
+ unsigned int nanosec;
+
+ writes[0].address = LED_CONTROL;
+ writes[0].value = FAIL_LED_ON;
+ writes[1].address = RESET_TO_POWERUP;
+ writes[1].value = RESET_SPACEBALL;
+ retval = agilent_82357a_write_registers(a_priv, writes, 2);
+ if (retval) {
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return -EIO;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout(usec_to_jiffies(2000)))
+ return -ERESTARTSYS;
+ writes[0].address = AUXCR;
+ writes[0].value = AUX_NBAF;
+ writes[1].address = AUXCR;
+ writes[1].value = AUX_HLDE;
+ writes[2].address = AUXCR;
+ writes[2].value = AUX_TON;
+ writes[3].address = AUXCR;
+ writes[3].value = AUX_LON;
+ writes[4].address = AUXCR;
+ writes[4].value = AUX_RSV2;
+ writes[5].address = AUXCR;
+ writes[5].value = AUX_INVAL;
+ writes[6].address = AUXCR;
+ writes[6].value = AUX_RPP;
+ writes[7].address = AUXCR;
+ writes[7].value = AUX_STDL;
+ writes[8].address = AUXCR;
+ writes[8].value = AUX_VSTDL;
+ writes[9].address = FAST_TALKER_T1;
+ nanosec = board->t1_nano_sec;
+ writes[9].value = nanosec_to_fast_talker_bits(&nanosec);
+ board->t1_nano_sec = nanosec;
+ writes[10].address = ADR;
+ writes[10].value = board->pad & ADDRESS_MASK;
+ writes[11].address = PPR;
+ writes[11].value = 0;
+ writes[12].address = SPMR;
+ writes[12].value = 0;
+ writes[13].address = PROTOCOL_CONTROL;
+ writes[13].value = WRITE_COMPLETE_INTERRUPT_EN;
+ writes[14].address = IMR0;
+ writes[14].value = HR_BOIE | HR_BIIE;
+ writes[15].address = IMR1;
+ writes[15].value = HR_SRQIE;
+ // turn off reset state
+ writes[16].address = AUXCR;
+ writes[16].value = AUX_CHIP_RESET;
+ writes[17].address = LED_CONTROL;
+ writes[17].value = FIRMWARE_LED_CONTROL;
+ retval = agilent_82357a_write_registers(a_priv, writes, INIT_NUM_REG_WRITES);
+ if (retval) {
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return -EIO;
+ }
+ hw_control.address = HW_CONTROL;
+ retval = agilent_82357a_read_registers(a_priv, &hw_control, 1, 1);
+ if (retval) {
+ dev_err(&usb_dev->dev, "read_registers() returned error\n");
+ return -EIO;
+ }
+ a_priv->hw_control_bits = (hw_control.value & ~0x7) | NOT_TI_RESET | NOT_PARALLEL_POLL;
+
+ return 0;
+}
+
+static inline int agilent_82357a_device_match(struct usb_interface *interface,
+ const struct gpib_board_config *config)
+{
+ struct usb_device * const usbdev = interface_to_usbdev(interface);
+
+ if (gpib_match_device_path(&interface->dev, config->device_path) == 0)
+ return 0;
+ if (config->serial_number &&
+ strcmp(usbdev->serial, config->serial_number) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int agilent_82357a_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ int retval;
+ int i;
+ unsigned int product_id;
+ struct agilent_82357a_priv *a_priv;
+ struct usb_device *usb_dev;
+
+ if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock))
+ return -ERESTARTSYS;
+
+ retval = agilent_82357a_allocate_private(board);
+ if (retval < 0) {
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return retval;
+ }
+ a_priv = board->private_data;
+ for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+ if (agilent_82357a_driver_interfaces[i] &&
+ !usb_get_intfdata(agilent_82357a_driver_interfaces[i]) &&
+ agilent_82357a_device_match(agilent_82357a_driver_interfaces[i], config)) {
+ a_priv->bus_interface = agilent_82357a_driver_interfaces[i];
+ usb_set_intfdata(agilent_82357a_driver_interfaces[i], board);
+ usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ break;
+ }
+ }
+ if (i == MAX_NUM_82357A_INTERFACES) {
+ dev_err(board->gpib_dev,
+ "No supported adapters found, have you loaded its firmware?\n");
+ retval = -ENODEV;
+ goto attach_fail;
+ }
+ product_id = le16_to_cpu(interface_to_usbdev(a_priv->bus_interface)->descriptor.idProduct);
+ switch (product_id) {
+ case USB_DEVICE_ID_AGILENT_82357A:
+ a_priv->bulk_out_endpoint = AGILENT_82357A_BULK_OUT_ENDPOINT;
+ a_priv->interrupt_in_endpoint = AGILENT_82357A_INTERRUPT_IN_ENDPOINT;
+ break;
+ case USB_DEVICE_ID_AGILENT_82357B:
+ a_priv->bulk_out_endpoint = AGILENT_82357B_BULK_OUT_ENDPOINT;
+ a_priv->interrupt_in_endpoint = AGILENT_82357B_INTERRUPT_IN_ENDPOINT;
+ break;
+ default:
+ dev_err(&usb_dev->dev, "bug, unhandled product_id in switch?\n");
+ retval = -EIO;
+ goto attach_fail;
+ }
+
+ retval = agilent_82357a_setup_urbs(board);
+ if (retval < 0)
+ goto attach_fail;
+
+ timer_setup(&a_priv->bulk_timer, agilent_82357a_timeout_handler, 0);
+
+ board->t1_nano_sec = 800;
+
+ retval = agilent_82357a_init(board);
+
+ if (retval < 0) {
+ agilent_82357a_cleanup_urbs(a_priv);
+ agilent_82357a_release_urbs(a_priv);
+ goto attach_fail;
+ }
+
+ dev_info(&usb_dev->dev, "bus %d dev num %d attached to gpib%d, interface %i\n",
+ usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return retval;
+
+attach_fail:
+ agilent_82357a_free_private(board);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return retval;
+}
+
+static int agilent_82357a_go_idle(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(a_priv->bus_interface);
+ struct agilent_82357a_register_pairlet writes[0x20];
+ int retval;
+
+ // turn on tms9914 reset state
+ writes[0].address = AUXCR;
+ writes[0].value = AUX_CS | AUX_CHIP_RESET;
+ a_priv->hw_control_bits &= ~NOT_TI_RESET;
+ writes[1].address = HW_CONTROL;
+ writes[1].value = a_priv->hw_control_bits;
+ writes[2].address = PROTOCOL_CONTROL;
+ writes[2].value = 0;
+ writes[3].address = IMR0;
+ writes[3].value = 0;
+ writes[4].address = IMR1;
+ writes[4].value = 0;
+ writes[5].address = LED_CONTROL;
+ writes[5].value = 0;
+ retval = agilent_82357a_write_registers(a_priv, writes, 6);
+ if (retval) {
+ dev_err(&usb_dev->dev, "write_registers() returned error\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void agilent_82357a_detach(struct gpib_board *board)
+{
+ struct agilent_82357a_priv *a_priv;
+
+ mutex_lock(&agilent_82357a_hotplug_lock);
+
+ a_priv = board->private_data;
+ if (a_priv) {
+ if (a_priv->bus_interface) {
+ agilent_82357a_go_idle(board);
+ usb_set_intfdata(a_priv->bus_interface, NULL);
+ }
+ mutex_lock(&a_priv->control_alloc_lock);
+ mutex_lock(&a_priv->bulk_alloc_lock);
+ mutex_lock(&a_priv->interrupt_alloc_lock);
+ agilent_82357a_cleanup_urbs(a_priv);
+ agilent_82357a_release_urbs(a_priv);
+ agilent_82357a_free_private(board);
+ }
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+}
+
+static struct gpib_interface agilent_82357a_gpib_interface = {
+ .name = "agilent_82357a",
+ .attach = agilent_82357a_attach,
+ .detach = agilent_82357a_detach,
+ .read = agilent_82357a_read,
+ .write = agilent_82357a_write,
+ .command = agilent_82357a_command,
+ .take_control = agilent_82357a_take_control,
+ .go_to_standby = agilent_82357a_go_to_standby,
+ .request_system_control = agilent_82357a_request_system_control,
+ .interface_clear = agilent_82357a_interface_clear,
+ .remote_enable = agilent_82357a_remote_enable,
+ .enable_eos = agilent_82357a_enable_eos,
+ .disable_eos = agilent_82357a_disable_eos,
+ .parallel_poll = agilent_82357a_parallel_poll,
+ .parallel_poll_configure = agilent_82357a_parallel_poll_configure,
+ .parallel_poll_response = agilent_82357a_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = agilent_82357a_line_status,
+ .update_status = agilent_82357a_update_status,
+ .primary_address = agilent_82357a_primary_address,
+ .secondary_address = agilent_82357a_secondary_address,
+ .serial_poll_response = agilent_82357a_serial_poll_response,
+ .serial_poll_status = agilent_82357a_serial_poll_status,
+ .t1_delay = agilent_82357a_t1_delay,
+ .return_to_local = agilent_82357a_return_to_local,
+ .no_7_bit_eos = 1,
+ .skip_check_for_command_acceptors = 1
+};
+
+// Table with the USB-devices: just now only testing IDs
+static struct usb_device_id agilent_82357a_driver_device_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357A)},
+ {USB_DEVICE(USB_VENDOR_ID_AGILENT, USB_DEVICE_ID_AGILENT_82357B)},
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, agilent_82357a_driver_device_table);
+
+static int agilent_82357a_driver_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int i;
+ char *path;
+ static const int path_length = 1024;
+ struct usb_device *usb_dev;
+
+ if (mutex_lock_interruptible(&agilent_82357a_hotplug_lock))
+ return -ERESTARTSYS;
+ usb_dev = usb_get_dev(interface_to_usbdev(interface));
+ for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+ if (!agilent_82357a_driver_interfaces[i]) {
+ agilent_82357a_driver_interfaces[i] = interface;
+ usb_set_intfdata(interface, NULL);
+ dev_dbg(&usb_dev->dev, "set bus interface %i to address 0x%p\n",
+ i, interface);
+ break;
+ }
+ }
+ if (i == MAX_NUM_82357A_INTERFACES) {
+ usb_put_dev(usb_dev);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ dev_err(&usb_dev->dev, "out of space in agilent_82357a_driver_interfaces[]\n");
+ return -1;
+ }
+ path = kmalloc(path_length, GFP_KERNEL);
+ if (!path) {
+ usb_put_dev(usb_dev);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return -ENOMEM;
+ }
+ usb_make_path(usb_dev, path, path_length);
+ dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path);
+ kfree(path);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return 0;
+}
+
+static void agilent_82357a_driver_disconnect(struct usb_interface *interface)
+{
+ int i;
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+
+ mutex_lock(&agilent_82357a_hotplug_lock);
+
+ for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+ if (agilent_82357a_driver_interfaces[i] == interface) {
+ struct gpib_board *board = usb_get_intfdata(interface);
+
+ if (board) {
+ struct agilent_82357a_priv *a_priv = board->private_data;
+
+ if (a_priv) {
+ mutex_lock(&a_priv->control_alloc_lock);
+ mutex_lock(&a_priv->bulk_alloc_lock);
+ mutex_lock(&a_priv->interrupt_alloc_lock);
+ agilent_82357a_cleanup_urbs(a_priv);
+ a_priv->bus_interface = NULL;
+ mutex_unlock(&a_priv->interrupt_alloc_lock);
+ mutex_unlock(&a_priv->bulk_alloc_lock);
+ mutex_unlock(&a_priv->control_alloc_lock);
+ }
+ }
+ agilent_82357a_driver_interfaces[i] = NULL;
+ break;
+ }
+ }
+ if (i == MAX_NUM_82357A_INTERFACES)
+ dev_err(&usb_dev->dev, "unable to find interface - bug?\n");
+ usb_put_dev(usb_dev);
+
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+}
+
+static int agilent_82357a_driver_suspend(struct usb_interface *interface, pm_message_t message)
+{
+ int i, retval;
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+
+ mutex_lock(&agilent_82357a_hotplug_lock);
+
+ for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+ if (agilent_82357a_driver_interfaces[i] == interface) {
+ struct gpib_board *board = usb_get_intfdata(interface);
+
+ if (board) {
+ struct agilent_82357a_priv *a_priv = board->private_data;
+
+ if (a_priv) {
+ agilent_82357a_abort(a_priv, 0);
+ agilent_82357a_abort(a_priv, 0);
+ retval = agilent_82357a_go_idle(board);
+ if (retval) {
+ dev_err(&usb_dev->dev, "failed to go idle, retval=%i\n",
+ retval);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return retval;
+ }
+ mutex_lock(&a_priv->interrupt_alloc_lock);
+ agilent_82357a_cleanup_urbs(a_priv);
+ mutex_unlock(&a_priv->interrupt_alloc_lock);
+ dev_dbg(&usb_dev->dev,
+ "bus %d dev num %d gpib %d, interface %i suspended\n",
+ usb_dev->bus->busnum, usb_dev->devnum,
+ board->minor, i);
+ }
+ }
+ break;
+ }
+ }
+
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+
+ return 0;
+}
+
+static int agilent_82357a_driver_resume(struct usb_interface *interface)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+ struct gpib_board *board;
+ int i, retval = 0;
+
+ mutex_lock(&agilent_82357a_hotplug_lock);
+
+ for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i) {
+ if (agilent_82357a_driver_interfaces[i] == interface) {
+ board = usb_get_intfdata(interface);
+ if (board)
+ break;
+ }
+ }
+ if (i == MAX_NUM_82357A_INTERFACES) {
+ retval = -ENOENT;
+ goto resume_exit;
+ }
+
+ struct agilent_82357a_priv *a_priv = board->private_data;
+
+ if (a_priv) {
+ if (a_priv->interrupt_urb) {
+ mutex_lock(&a_priv->interrupt_alloc_lock);
+ retval = usb_submit_urb(a_priv->interrupt_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&usb_dev->dev, "failed to resubmit interrupt urb in resume, retval=%i\n",
+ retval);
+ mutex_unlock(&a_priv->interrupt_alloc_lock);
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return retval;
+ }
+ mutex_unlock(&a_priv->interrupt_alloc_lock);
+ }
+ retval = agilent_82357a_init(board);
+ if (retval < 0) {
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+ return retval;
+ }
+ // set/unset system controller
+ retval = agilent_82357a_request_system_control(board, board->master);
+ // toggle ifc if master
+ if (board->master) {
+ agilent_82357a_interface_clear(board, 1);
+ usleep_range(200, 250);
+ agilent_82357a_interface_clear(board, 0);
+ }
+ // assert/unassert REN
+ agilent_82357a_remote_enable(board, a_priv->ren_state);
+
+ dev_dbg(&usb_dev->dev,
+ "bus %d dev num %d gpib%d, interface %i resumed\n",
+ usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+ }
+
+resume_exit:
+ mutex_unlock(&agilent_82357a_hotplug_lock);
+
+ return retval;
+}
+
+static struct usb_driver agilent_82357a_bus_driver = {
+ .name = DRV_NAME,
+ .probe = agilent_82357a_driver_probe,
+ .disconnect = agilent_82357a_driver_disconnect,
+ .suspend = agilent_82357a_driver_suspend,
+ .resume = agilent_82357a_driver_resume,
+ .id_table = agilent_82357a_driver_device_table,
+};
+
+static int __init agilent_82357a_init_module(void)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < MAX_NUM_82357A_INTERFACES; ++i)
+ agilent_82357a_driver_interfaces[i] = NULL;
+
+ ret = usb_register(&agilent_82357a_bus_driver);
+ if (ret) {
+ pr_err("usb_register failed: error = %d\n", ret);
+ return ret;
+ }
+
+ ret = gpib_register_driver(&agilent_82357a_gpib_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ usb_deregister(&agilent_82357a_bus_driver);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit agilent_82357a_exit_module(void)
+{
+ gpib_unregister_driver(&agilent_82357a_gpib_interface);
+ usb_deregister(&agilent_82357a_bus_driver);
+}
+
+module_init(agilent_82357a_init_module);
+module_exit(agilent_82357a_exit_module);
diff --git a/drivers/gpib/agilent_82357a/agilent_82357a.h b/drivers/gpib/agilent_82357a/agilent_82357a.h
new file mode 100644
index 000000000000..33ac558e5552
--- /dev/null
+++ b/drivers/gpib/agilent_82357a/agilent_82357a.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2004 by Frank Mori Hess *
+ ***************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+#include <linux/compiler_attributes.h>
+#include "gpibP.h"
+#include "tms9914.h"
+
+enum usb_vendor_ids {
+ USB_VENDOR_ID_AGILENT = 0x0957
+};
+
+enum usb_device_ids {
+ USB_DEVICE_ID_AGILENT_82357A = 0x0107,
+ USB_DEVICE_ID_AGILENT_82357A_PREINIT = 0x0007, // device id before firmware is loaded
+ USB_DEVICE_ID_AGILENT_82357B = 0x0718, // device id before firmware is loaded
+ USB_DEVICE_ID_AGILENT_82357B_PREINIT = 0x0518, // device id before firmware is loaded
+};
+
+enum endpoint_addresses {
+ AGILENT_82357_CONTROL_ENDPOINT = 0x0,
+ AGILENT_82357_BULK_IN_ENDPOINT = 0x2,
+ AGILENT_82357A_BULK_OUT_ENDPOINT = 0x4,
+ AGILENT_82357A_INTERRUPT_IN_ENDPOINT = 0x6,
+ AGILENT_82357B_BULK_OUT_ENDPOINT = 0x6,
+ AGILENT_82357B_INTERRUPT_IN_ENDPOINT = 0x8,
+};
+
+enum bulk_commands {
+ DATA_PIPE_CMD_WRITE = 0x1,
+ DATA_PIPE_CMD_READ = 0x3,
+ DATA_PIPE_CMD_WR_REGS = 0x4,
+ DATA_PIPE_CMD_RD_REGS = 0x5
+};
+
+enum agilent_82357a_read_flags {
+ ARF_END_ON_EOI = 0x1,
+ ARF_NO_ADDRESS = 0x2,
+ ARF_END_ON_EOS_CHAR = 0x4,
+ ARF_SPOLL = 0x8
+};
+
+enum agilent_82357a_trailing_read_flags {
+ ATRF_EOI = 0x1,
+ ATRF_ATN = 0x2,
+ ATRF_IFC = 0x4,
+ ATRF_EOS = 0x8,
+ ATRF_ABORT = 0x10,
+ ATRF_COUNT = 0x20,
+ ATRF_DEAD_BUS = 0x40,
+ ATRF_UNADDRESSED = 0x80
+};
+
+enum agilent_82357a_write_flags {
+ AWF_SEND_EOI = 0x1,
+ AWF_NO_FAST_TALKER_FIRST_BYTE = 0x2,
+ AWF_NO_FAST_TALKER = 0x4,
+ AWF_NO_ADDRESS = 0x8,
+ AWF_ATN = 0x10,
+ AWF_SEPARATE_HEADER = 0x80
+};
+
+enum agilent_82357a_interrupt_flag_bit_numbers {
+ AIF_SRQ_BN = 0,
+ AIF_WRITE_COMPLETE_BN = 1,
+ AIF_READ_COMPLETE_BN = 2,
+};
+
+enum agilent_82357_error_codes {
+ UGP_SUCCESS = 0,
+ UGP_ERR_INVALID_CMD = 1,
+ UGP_ERR_INVALID_PARAM = 2,
+ UGP_ERR_INVALID_REG = 3,
+ UGP_ERR_GPIB_READ = 4,
+ UGP_ERR_GPIB_WRITE = 5,
+ UGP_ERR_FLUSHING = 6,
+ UGP_ERR_FLUSHING_ALREADY = 7,
+ UGP_ERR_UNSUPPORTED = 8,
+ UGP_ERR_OTHER = 9
+};
+
+enum agilent_82357_control_values {
+ XFER_ABORT = 0xa0,
+ XFER_STATUS = 0xb0,
+};
+
+enum xfer_status_bits {
+ XS_COMPLETED = 0x1,
+ XS_READ = 0x2,
+};
+
+enum xfer_status_completion_bits {
+ XSC_EOI = 0x1,
+ XSC_ATN = 0x2,
+ XSC_IFC = 0x4,
+ XSC_EOS = 0x8,
+ XSC_ABORT = 0x10,
+ XSC_COUNT = 0x20,
+ XSC_DEAD_BUS = 0x40,
+ XSC_BUS_NOT_ADDRESSED = 0x80
+};
+
+enum xfer_abort_type {
+ XA_FLUSH = 0x1
+};
+
+#define STATUS_DATA_LEN 8
+#define INTERRUPT_BUF_LEN 8
+
+struct agilent_82357a_urb_ctx {
+ struct completion complete;
+ unsigned timed_out : 1;
+};
+
+// struct which defines local data for each 82357 device
+struct agilent_82357a_priv {
+ struct usb_interface *bus_interface;
+ unsigned short eos_char;
+ unsigned short eos_mode;
+ unsigned short hw_control_bits;
+ unsigned long interrupt_flags;
+ struct urb *bulk_urb;
+ struct urb *interrupt_urb;
+ u8 *interrupt_buffer;
+ struct mutex bulk_transfer_lock; // bulk transfer lock
+ struct mutex bulk_alloc_lock; // bulk transfer allocation lock
+ struct mutex interrupt_alloc_lock; // interrupt allocation lock
+ struct mutex control_alloc_lock; // control message allocation lock
+ struct timer_list bulk_timer;
+ struct agilent_82357a_urb_ctx context;
+ unsigned int bulk_out_endpoint;
+ unsigned int interrupt_in_endpoint;
+ unsigned is_cic : 1;
+ unsigned ren_state : 1;
+};
+
+struct agilent_82357a_register_pairlet {
+ short address;
+ unsigned short value;
+};
+
+enum firmware_registers {
+ HW_CONTROL = 0xa,
+ LED_CONTROL = 0xb,
+ RESET_TO_POWERUP = 0xc,
+ PROTOCOL_CONTROL = 0xd,
+ FAST_TALKER_T1 = 0xe
+};
+
+enum hardware_control_bits {
+ NOT_TI_RESET = 0x1,
+ SYSTEM_CONTROLLER = 0x2,
+ NOT_PARALLEL_POLL = 0x4,
+ OSCILLATOR_5V_ON = 0x8,
+ OUTPUT_5V_ON = 0x20,
+ CPLD_3V_ON = 0x80,
+};
+
+enum led_control_bits {
+ FIRMWARE_LED_CONTROL = 0x1,
+ FAIL_LED_ON = 0x20,
+ READY_LED_ON = 0x40,
+ ACCESS_LED_ON = 0x80
+};
+
+enum reset_to_powerup_bits {
+ RESET_SPACEBALL = 0x1, // wait 2 millisec after sending
+};
+
+enum protocol_control_bits {
+ WRITE_COMPLETE_INTERRUPT_EN = 0x1,
+};
+
+static const int agilent_82357a_control_request = 0x4;
+
diff --git a/drivers/gpib/cb7210/Makefile b/drivers/gpib/cb7210/Makefile
new file mode 100644
index 000000000000..d239ae80b415
--- /dev/null
+++ b/drivers/gpib/cb7210/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_GPIB_CB7210) += cb7210.o
+
+
diff --git a/drivers/gpib/cb7210/cb7210.c b/drivers/gpib/cb7210/cb7210.c
new file mode 100644
index 000000000000..24c61b151071
--- /dev/null
+++ b/drivers/gpib/cb7210/cb7210.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * Measurement Computing boards using cb7210.2 and cbi488.2 chips
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "cb7210.h"
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <linux/bitops.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include "gpib_pci_ids.h"
+#include "quancom_pci.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver Measurement Computing boards using cb7210.2 and cbi488.2");
+
+static int cb7210_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read);
+
+ static inline int have_fifo_word(const struct cb7210_priv *cb_priv)
+{
+ if (((cb7210_read_byte(cb_priv, HS_STATUS)) &
+ (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY)) ==
+ (HS_RX_MSB_NOT_EMPTY | HS_RX_LSB_NOT_EMPTY))
+ return 1;
+ else
+ return 0;
+}
+
+static inline void input_fifo_enable(struct gpib_board *board, int enable)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ if (enable) {
+ cb_priv->in_fifo_half_full = 0;
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+
+ cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT |
+ HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits,
+ HS_MODE);
+
+ cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE);
+
+ cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL);
+
+ cb_priv->hs_mode_bits |= HS_RX_ENABLE;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE);
+ } else {
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+
+ cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, nec7210_iobase(cb_priv) +
+ HS_MODE);
+
+ clear_bit(READ_READY_BN, &nec_priv->state);
+ }
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+static int fifo_read(struct gpib_board *board, struct cb7210_priv *cb_priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval = 0;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ int hs_status;
+ u16 word;
+ unsigned long flags;
+
+ *bytes_read = 0;
+ if (cb_priv->fifo_iobase == 0) {
+ dev_err(board->gpib_dev, "fifo iobase is zero!\n");
+ return -EIO;
+ }
+ *end = 0;
+ if (length <= cb7210_fifo_size) {
+ dev_err(board->gpib_dev, " bug! fifo read length < fifo size\n");
+ return -EINVAL;
+ }
+
+ input_fifo_enable(board, 1);
+
+ while (*bytes_read + cb7210_fifo_size < length) {
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI);
+
+ if (wait_event_interruptible(board->wait,
+ (cb_priv->in_fifo_half_full &&
+ have_fifo_word(cb_priv)) ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+ break;
+ }
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+
+ while (have_fifo_word(cb_priv)) {
+ word = inw(cb_priv->fifo_iobase + DIR);
+ buffer[(*bytes_read)++] = word & 0xff;
+ buffer[(*bytes_read)++] = (word >> 8) & 0xff;
+ }
+
+ cb_priv->in_fifo_half_full = 0;
+
+ hs_status = cb7210_read_byte(cb_priv, HS_STATUS);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state)) {
+ *end = 1;
+ break;
+ }
+ if (hs_status & HS_FIFO_FULL)
+ break;
+ if (test_bit(TIMO_NUM, &board->status)) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+ }
+ hs_status = cb7210_read_byte(cb_priv, HS_STATUS);
+ if (hs_status & HS_RX_LSB_NOT_EMPTY) {
+ word = inw(cb_priv->fifo_iobase + DIR);
+ buffer[(*bytes_read)++] = word & 0xff;
+ }
+
+ input_fifo_enable(board, 0);
+
+ if (wait_event_interruptible(board->wait,
+ test_bit(READ_READY_BN, &nec_priv->state) ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ if (test_bit(READ_READY_BN, &nec_priv->state)) {
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDA);
+ buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end);
+ }
+
+ return retval;
+}
+
+static int cb7210_accel_read(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval;
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ size_t num_bytes;
+
+ *bytes_read = 0;
+ // deal with limitations of fifo
+ if (length < cb7210_fifo_size + 3 || (nec_priv->auxa_bits & HR_REOS))
+ return cb7210_read(board, buffer, length, end, bytes_read);
+ *end = 0;
+
+ nec7210_release_rfd_holdoff(board, nec_priv);
+
+ if (wait_event_interruptible(board->wait,
+ test_bit(READ_READY_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ return -ERESTARTSYS;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ return -EINTR;
+
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDE);
+ buffer[(*bytes_read)++] = nec7210_read_data_in(board, nec_priv, end);
+ if (*end)
+ return 0;
+
+ nec7210_release_rfd_holdoff(board, nec_priv);
+
+ retval = fifo_read(board, cb_priv, &buffer[*bytes_read], length - *bytes_read - 1,
+ end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ return retval;
+ if (*end)
+ return 0;
+
+ retval = cb7210_read(board, &buffer[*bytes_read], 1, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+static int output_fifo_empty(const struct cb7210_priv *cb_priv)
+{
+ if ((cb7210_read_byte(cb_priv, HS_STATUS) & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY))
+ == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static inline void output_fifo_enable(struct gpib_board *board, int enable)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ if (enable) {
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO);
+
+ cb7210_write_byte(cb_priv, HS_RX_ENABLE | HS_TX_ENABLE | HS_CLR_SRQ_INT |
+ HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT | cb_priv->hs_mode_bits,
+ HS_MODE);
+
+ cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK;
+ cb_priv->hs_mode_bits |= HS_TX_ENABLE;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE);
+
+ cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL);
+
+ clear_bit(WRITE_READY_BN, &nec_priv->state);
+
+ } else {
+ cb_priv->hs_mode_bits &= ~HS_ENABLE_MASK;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE);
+
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0);
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE);
+ }
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+static int fifo_write(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ size_t count = 0;
+ ssize_t retval = 0;
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ unsigned int num_bytes, i;
+ unsigned long flags;
+
+ *bytes_written = 0;
+ if (cb_priv->fifo_iobase == 0) {
+ dev_err(board->gpib_dev, "fifo iobase is zero!\n");
+ return -EINVAL;
+ }
+ if (length == 0)
+ return 0;
+
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state);
+ clear_bit(BUS_ERROR_BN, &nec_priv->state);
+
+ output_fifo_enable(board, 1);
+
+ while (count < length) {
+ // wait until byte is ready to be sent
+ if (wait_event_interruptible(board->wait,
+ cb_priv->out_fifo_half_empty ||
+ output_fifo_empty(cb_priv) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (test_bit(TIMO_NUM, &board->status) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state))
+ break;
+
+ if (output_fifo_empty(cb_priv))
+ num_bytes = cb7210_fifo_size - cb7210_fifo_width;
+ else
+ num_bytes = cb7210_fifo_size / 2;
+ if (num_bytes + count > length)
+ num_bytes = length - count;
+ if (num_bytes % cb7210_fifo_width) {
+ dev_err(board->gpib_dev, " bug! fifo write with odd number of bytes\n");
+ retval = -EINVAL;
+ break;
+ }
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ for (i = 0; i < num_bytes / cb7210_fifo_width; i++) {
+ u16 word;
+
+ word = buffer[count++] & 0xff;
+ word |= (buffer[count++] << 8) & 0xff00;
+ outw(word, cb_priv->fifo_iobase + CDOR);
+ }
+ cb_priv->out_fifo_half_empty = 0;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits |
+ HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE);
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ }
+ // wait last byte has been sent
+ if (wait_event_interruptible(board->wait,
+ output_fifo_empty(cb_priv) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_bit(BUS_ERROR_BN, &nec_priv->state))
+ retval = -EIO;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+
+ output_fifo_enable(board, 0);
+
+ *bytes_written = count;
+ return retval;
+}
+
+static int cb7210_accel_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ unsigned long fast_chunk_size, leftover;
+ int retval;
+ size_t num_bytes;
+
+ *bytes_written = 0;
+ if (length > cb7210_fifo_width)
+ fast_chunk_size = length - 1;
+ else
+ fast_chunk_size = 0;
+ fast_chunk_size -= fast_chunk_size % cb7210_fifo_width;
+ leftover = length - fast_chunk_size;
+
+ retval = fifo_write(board, buffer, fast_chunk_size, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+
+ retval = nec7210_write(board, nec_priv, buffer + fast_chunk_size, leftover,
+ send_eoi, &num_bytes);
+ *bytes_written += num_bytes;
+ return retval;
+}
+
+static int cb7210_line_status(const struct gpib_board *board)
+{
+ int status = VALID_ALL;
+ int bsr_bits;
+ struct cb7210_priv *cb_priv;
+
+ cb_priv = board->private_data;
+
+ bsr_bits = cb7210_paged_read_byte(cb_priv, BUS_STATUS, BUS_STATUS_PAGE);
+
+ if ((bsr_bits & BSR_REN_BIT) == 0)
+ status |= BUS_REN;
+ if ((bsr_bits & BSR_IFC_BIT) == 0)
+ status |= BUS_IFC;
+ if ((bsr_bits & BSR_SRQ_BIT) == 0)
+ status |= BUS_SRQ;
+ if ((bsr_bits & BSR_EOI_BIT) == 0)
+ status |= BUS_EOI;
+ if ((bsr_bits & BSR_NRFD_BIT) == 0)
+ status |= BUS_NRFD;
+ if ((bsr_bits & BSR_NDAC_BIT) == 0)
+ status |= BUS_NDAC;
+ if ((bsr_bits & BSR_DAV_BIT) == 0)
+ status |= BUS_DAV;
+ if ((bsr_bits & BSR_ATN_BIT) == 0)
+ status |= BUS_ATN;
+
+ return status;
+}
+
+static int cb7210_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ unsigned int retval;
+
+ retval = nec7210_t1_delay(board, nec_priv, nano_sec);
+
+ if (nano_sec <= 350) {
+ write_byte(nec_priv, AUX_HI_SPEED, AUXMR);
+ retval = 350;
+ } else {
+ write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
+ }
+ return retval;
+}
+
+static irqreturn_t cb7210_locked_internal_interrupt(struct gpib_board *board);
+
+/*
+ * GPIB interrupt service routines
+ */
+
+static irqreturn_t cb_pci_interrupt(int irq, void *arg)
+{
+ int bits;
+ struct gpib_board *board = arg;
+ struct cb7210_priv *priv = board->private_data;
+
+ // first task check if this is really our interrupt in a shared irq environment
+ switch (priv->pci_chip) {
+ case PCI_CHIP_AMCC_S5933:
+ if ((inl(priv->amcc_iobase + INTCSR_REG) &
+ (INBOX_INTR_CS_BIT | INTR_ASSERTED_BIT)) == 0)
+ return IRQ_NONE;
+
+ // read incoming mailbox to clear mailbox full flag
+ inl(priv->amcc_iobase + INCOMING_MAILBOX_REG(3));
+ // clear amccs5933 interrupt
+ bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) |
+ INBOX_SELECT_BITS(3) | INBOX_INTR_CS_BIT;
+ outl(bits, priv->amcc_iobase + INTCSR_REG);
+ break;
+ case PCI_CHIP_QUANCOM:
+ if ((inb(nec7210_iobase(priv) + QUANCOM_IRQ_CONTROL_STATUS_REG) &
+ QUANCOM_IRQ_ASSERTED_BIT))
+ outb(QUANCOM_IRQ_ENABLE_BIT, nec7210_iobase(priv) +
+ QUANCOM_IRQ_CONTROL_STATUS_REG);
+ break;
+ default:
+ break;
+ }
+ return cb7210_locked_internal_interrupt(arg);
+}
+
+static irqreturn_t cb7210_internal_interrupt(struct gpib_board *board)
+{
+ int hs_status, status1, status2;
+ struct cb7210_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+ int clear_bits;
+
+ if ((priv->hs_mode_bits & HS_ENABLE_MASK)) {
+ status1 = 0;
+ hs_status = cb7210_read_byte(priv, HS_STATUS);
+ } else {
+ hs_status = 0;
+ status1 = read_byte(nec_priv, ISR1);
+ }
+ status2 = read_byte(nec_priv, ISR2);
+ nec7210_interrupt_have_status(board, nec_priv, status1, status2);
+
+ dev_dbg(board->gpib_dev, "status 0x%x, mode 0x%x\n", hs_status, priv->hs_mode_bits);
+
+ clear_bits = 0;
+
+ if (hs_status & HS_HALF_FULL) {
+ if (priv->hs_mode_bits & HS_TX_ENABLE)
+ priv->out_fifo_half_empty = 1;
+ else if (priv->hs_mode_bits & HS_RX_ENABLE)
+ priv->in_fifo_half_full = 1;
+ clear_bits |= HS_CLR_HF_INT;
+ }
+
+ if (hs_status & HS_SRQ_INT) {
+ set_bit(SRQI_NUM, &board->status);
+ clear_bits |= HS_CLR_SRQ_INT;
+ }
+
+ if ((hs_status & HS_EOI_INT)) {
+ clear_bits |= HS_CLR_EOI_EMPTY_INT;
+ set_bit(RECEIVED_END_BN, &nec_priv->state);
+ if ((nec_priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE)
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ }
+
+ if ((priv->hs_mode_bits & HS_TX_ENABLE) &&
+ (hs_status & (HS_TX_MSB_NOT_EMPTY | HS_TX_LSB_NOT_EMPTY)) == 0)
+ clear_bits |= HS_CLR_EOI_EMPTY_INT;
+
+ if (clear_bits) {
+ cb7210_write_byte(priv, priv->hs_mode_bits | clear_bits, HS_MODE);
+ cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE);
+ wake_up_interruptible(&board->wait);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cb7210_locked_internal_interrupt(struct gpib_board *board)
+{
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = cb7210_internal_interrupt(board);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+static irqreturn_t cb7210_interrupt(int irq, void *arg)
+{
+ return cb7210_internal_interrupt(arg);
+}
+
+static int cb_pci_attach(struct gpib_board *board, const struct gpib_board_config *config);
+static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_config *config);
+
+static void cb_pci_detach(struct gpib_board *board);
+static void cb_isa_detach(struct gpib_board *board);
+
+// wrappers for interface functions
+static int cb7210_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+}
+
+static int cb7210_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int cb7210_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int cb7210_take_control(struct gpib_board *board, int synchronous)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int cb7210_go_to_standby(struct gpib_board *board)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int cb7210_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct cb7210_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ if (request_control)
+ priv->hs_mode_bits |= HS_SYS_CONTROL;
+ else
+ priv->hs_mode_bits &= ~HS_SYS_CONTROL;
+
+ cb7210_write_byte(priv, priv->hs_mode_bits, HS_MODE);
+ return nec7210_request_system_control(board, nec_priv, request_control);
+}
+
+static void cb7210_interface_clear(struct gpib_board *board, int assert)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void cb7210_remote_enable(struct gpib_board *board, int enable)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int cb7210_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void cb7210_disable_eos(struct gpib_board *board)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int cb7210_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
+}
+
+static int cb7210_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int cb7210_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int cb7210_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
+}
+
+static void cb7210_parallel_poll_configure(struct gpib_board *board, u8 configuration)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration);
+}
+
+static void cb7210_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+static void cb7210_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
+}
+
+static u8 cb7210_serial_poll_status(struct gpib_board *board)
+{
+ struct cb7210_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static void cb7210_return_to_local(struct gpib_board *board)
+{
+ struct cb7210_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ write_byte(nec_priv, AUX_RTL2, AUXMR);
+ udelay(1);
+ write_byte(nec_priv, AUX_RTL, AUXMR);
+}
+
+static struct gpib_interface cb_pci_unaccel_interface = {
+ .name = "cbi_pci_unaccel",
+ .attach = cb_pci_attach,
+ .detach = cb_pci_detach,
+ .read = cb7210_read,
+ .write = cb7210_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_pci_accel_interface = {
+ .name = "cbi_pci_accel",
+ .attach = cb_pci_attach,
+ .detach = cb_pci_detach,
+ .read = cb7210_accel_read,
+ .write = cb7210_accel_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_pci_interface = {
+ .name = "cbi_pci",
+ .attach = cb_pci_attach,
+ .detach = cb_pci_detach,
+ .read = cb7210_accel_read,
+ .write = cb7210_accel_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_isa_unaccel_interface = {
+ .name = "cbi_isa_unaccel",
+ .attach = cb_isa_attach,
+ .detach = cb_isa_detach,
+ .read = cb7210_read,
+ .write = cb7210_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_isa_interface = {
+ .name = "cbi_isa",
+ .attach = cb_isa_attach,
+ .detach = cb_isa_detach,
+ .read = cb7210_accel_read,
+ .write = cb7210_accel_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_isa_accel_interface = {
+ .name = "cbi_isa_accel",
+ .attach = cb_isa_attach,
+ .detach = cb_isa_detach,
+ .read = cb7210_accel_read,
+ .write = cb7210_accel_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static int cb7210_allocate_private(struct gpib_board *board)
+{
+ struct cb7210_priv *priv;
+
+ board->private_data = kmalloc(sizeof(struct cb7210_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ priv = board->private_data;
+ memset(priv, 0, sizeof(struct cb7210_priv));
+ init_nec7210_private(&priv->nec7210_priv);
+ return 0;
+}
+
+static void cb7210_generic_detach(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+// generic part of attach functions shared by all cb7210 boards
+static int cb7210_generic_attach(struct gpib_board *board)
+{
+ struct cb7210_priv *cb_priv;
+ struct nec7210_priv *nec_priv;
+
+ board->status = 0;
+
+ if (cb7210_allocate_private(board))
+ return -ENOMEM;
+ cb_priv = board->private_data;
+ nec_priv = &cb_priv->nec7210_priv;
+ nec_priv->read_byte = nec7210_locking_ioport_read_byte;
+ nec_priv->write_byte = nec7210_locking_ioport_write_byte;
+ nec_priv->offset = cb7210_reg_offset;
+ nec_priv->type = CB7210;
+ return 0;
+}
+
+static int cb7210_init(struct cb7210_priv *cb_priv, struct gpib_board *board)
+{
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+
+ cb7210_write_byte(cb_priv, HS_RESET7210, HS_INT_LEVEL);
+ cb7210_write_byte(cb_priv, irq_bits(cb_priv->irq), HS_INT_LEVEL);
+
+ nec7210_board_reset(nec_priv, board);
+ cb7210_write_byte(cb_priv, HS_TX_ENABLE | HS_RX_ENABLE | HS_CLR_SRQ_INT |
+ HS_CLR_EOI_EMPTY_INT | HS_CLR_HF_INT, HS_MODE);
+
+ cb_priv->hs_mode_bits = HS_HF_INT_EN;
+ cb7210_write_byte(cb_priv, cb_priv->hs_mode_bits, HS_MODE);
+
+ write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
+ /*
+ * set clock register for maximum (20 MHz) driving frequency
+ * ICR should be set to clock in megahertz (1-15) and to zero
+ * for clocks faster than 15 MHz (max 20MHz)
+ */
+ write_byte(nec_priv, ICR | 0, AUXMR);
+
+ if (cb_priv->pci_chip == PCI_CHIP_QUANCOM) {
+ /* change interrupt polarity */
+ nec_priv->auxb_bits |= HR_INV;
+ write_byte(nec_priv, nec_priv->auxb_bits, AUXMR);
+ }
+ nec7210_board_online(nec_priv, board);
+
+ /* poll so we can detect assertion of ATN */
+ if (gpib_request_pseudo_irq(board, cb_pci_interrupt)) {
+ pr_err("failed to allocate pseudo_irq\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int cb_pci_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct cb7210_priv *cb_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = 0;
+ int bits;
+ int retval;
+
+ retval = cb7210_generic_attach(board);
+ if (retval)
+ return retval;
+
+ cb_priv = board->private_data;
+ nec_priv = &cb_priv->nec7210_priv;
+
+ cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS,
+ PCI_DEVICE_ID_CBOARDS_PCI_GPIB, NULL);
+ if (cb_priv->pci_device)
+ cb_priv->pci_chip = PCI_CHIP_AMCC_S5933;
+ if (!cb_priv->pci_device) {
+ cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_CBOARDS,
+ PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, NULL);
+ if (cb_priv->pci_device)
+ cb_priv->pci_chip = PCI_CHIP_AMCC_S5933;
+ }
+ if (!cb_priv->pci_device) {
+ cb_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_QUANCOM,
+ PCI_DEVICE_ID_QUANCOM_GPIB, NULL);
+ if (cb_priv->pci_device) {
+ cb_priv->pci_chip = PCI_CHIP_QUANCOM;
+ nec_priv->offset = 4;
+ }
+ }
+ if (!cb_priv->pci_device) {
+ dev_err(board->gpib_dev, "no supported boards found.\n");
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(cb_priv->pci_device)) {
+ dev_err(board->gpib_dev, "error enabling pci device\n");
+ return -EIO;
+ }
+
+ if (pci_request_regions(cb_priv->pci_device, DRV_NAME))
+ return -EBUSY;
+ switch (cb_priv->pci_chip) {
+ case PCI_CHIP_AMCC_S5933:
+ cb_priv->amcc_iobase = pci_resource_start(cb_priv->pci_device, 0);
+ nec_priv->iobase = pci_resource_start(cb_priv->pci_device, 1);
+ cb_priv->fifo_iobase = pci_resource_start(cb_priv->pci_device, 2);
+ break;
+ case PCI_CHIP_QUANCOM:
+ nec_priv->iobase = pci_resource_start(cb_priv->pci_device, 0);
+ cb_priv->fifo_iobase = nec_priv->iobase;
+ break;
+ default:
+ dev_err(board->gpib_dev, "bug! unhandled pci_chip=%i\n", cb_priv->pci_chip);
+ return -EIO;
+ }
+ isr_flags |= IRQF_SHARED;
+ if (request_irq(cb_priv->pci_device->irq, cb_pci_interrupt, isr_flags, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "can't request IRQ %d\n",
+ cb_priv->pci_device->irq);
+ return -EBUSY;
+ }
+ cb_priv->irq = cb_priv->pci_device->irq;
+
+ switch (cb_priv->pci_chip) {
+ case PCI_CHIP_AMCC_S5933:
+ // make sure mailbox flags are clear
+ inl(cb_priv->amcc_iobase + INCOMING_MAILBOX_REG(3));
+ // enable interrupts on amccs5933 chip
+ bits = INBOX_FULL_INTR_BIT | INBOX_BYTE_BITS(3) | INBOX_SELECT_BITS(3) |
+ INBOX_INTR_CS_BIT;
+ outl(bits, cb_priv->amcc_iobase + INTCSR_REG);
+ break;
+ default:
+ break;
+ }
+ return cb7210_init(cb_priv, board);
+}
+
+static void cb_pci_detach(struct gpib_board *board)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (cb_priv) {
+ gpib_free_pseudo_irq(board);
+ nec_priv = &cb_priv->nec7210_priv;
+ if (cb_priv->irq) {
+ // disable amcc interrupts
+ outl(0, cb_priv->amcc_iobase + INTCSR_REG);
+ free_irq(cb_priv->irq, board);
+ }
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ pci_release_regions(cb_priv->pci_device);
+ }
+ if (cb_priv->pci_device)
+ pci_dev_put(cb_priv->pci_device);
+ }
+ cb7210_generic_detach(board);
+}
+
+static int cb_isa_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ int isr_flags = 0;
+ struct cb7210_priv *cb_priv;
+ struct nec7210_priv *nec_priv;
+ unsigned int bits;
+ int retval;
+
+ retval = cb7210_generic_attach(board);
+ if (retval)
+ return retval;
+ cb_priv = board->private_data;
+ nec_priv = &cb_priv->nec7210_priv;
+ if (!request_region(config->ibbase, cb7210_iosize, DRV_NAME)) {
+ dev_err(board->gpib_dev, "ioports starting at 0x%x are already in use\n",
+ config->ibbase);
+ return -EBUSY;
+ }
+ nec_priv->iobase = config->ibbase;
+ cb_priv->fifo_iobase = nec7210_iobase(cb_priv);
+
+ bits = irq_bits(config->ibirq);
+ if (bits == 0)
+ dev_err(board->gpib_dev, "board incapable of using irq %i, try 2-5, 7, 10, or 11\n",
+ config->ibirq);
+
+ // install interrupt handler
+ if (request_irq(config->ibirq, cb7210_interrupt, isr_flags, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", config->ibirq);
+ return -EBUSY;
+ }
+ cb_priv->irq = config->ibirq;
+
+ return cb7210_init(cb_priv, board);
+}
+
+static void cb_isa_detach(struct gpib_board *board)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (cb_priv) {
+ gpib_free_pseudo_irq(board);
+ nec_priv = &cb_priv->nec7210_priv;
+ if (cb_priv->irq)
+ free_irq(cb_priv->irq, board);
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ release_region(nec7210_iobase(cb_priv), cb7210_iosize);
+ }
+ }
+ cb7210_generic_detach(board);
+}
+
+static int cb7210_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return 0;
+}
+
+static const struct pci_device_id cb7210_pci_table[] = {
+ {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_PCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ {PCI_VENDOR_ID_CBOARDS, PCI_DEVICE_ID_CBOARDS_CPCI_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ {PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, cb7210_pci_table);
+
+static struct pci_driver cb7210_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = cb7210_pci_table,
+ .probe = &cb7210_pci_probe
+};
+
+/***************************************************************************
+ * Support for computer boards pcmcia-gpib card
+ *
+ * Based on gpib PCMCIA client driver written by Claus Schroeter
+ * (clausi@chemie.fu-berlin.de), which was adapted from the
+ * pcmcia skeleton example (presumably David Hinds)
+ ***************************************************************************/
+
+#ifdef CONFIG_GPIB_PCMCIA
+
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+/*
+ * The event() function is this driver's Card Services event handler.
+ * It will be called by Card Services when an appropriate card status
+ * event is received. The config() and release() entry points are
+ * used to configure or release a socket, in response to card insertion
+ * and ejection events. They are invoked from the gpib event
+ * handler.
+ */
+
+static int cb_gpib_config(struct pcmcia_device *link);
+static void cb_gpib_release(struct pcmcia_device *link);
+static int cb_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config);
+static void cb_pcmcia_detach(struct gpib_board *board);
+
+/*
+ * A linked list of "instances" of the gpib device. Each actual
+ * PCMCIA card corresponds to one device instance, and is described
+ * by one dev_link_t structure (defined in ds.h).
+ *
+ * You may not want to use a linked list for this -- for example, the
+ * memory card driver uses an array of dev_link_t pointers, where minor
+ * device numbers are used to derive the corresponding array index.
+ */
+
+static struct pcmcia_device *curr_dev;
+
+/*
+ * A dev_link_t structure has fields for most things that are needed
+ * to keep track of a socket, but there will usually be some device
+ * specific information that also needs to be kept track of. The
+ * 'priv' pointer in a dev_link_t structure can be used to point to
+ * a device-specific private data structure, like this.
+ *
+ * A driver needs to provide a dev_node_t structure for each device
+ * on a card. In some cases, there is only one device per card (for
+ * example, ethernet cards, modems). In other cases, there may be
+ * many actual or logical devices (SCSI adapters, memory cards with
+ * multiple partitions). The dev_node_t structures need to be kept
+ * in a linked list starting at the 'dev' field of a dev_link_t
+ * structure. We allocate them in the card's private data structure,
+ * because they generally can't be allocated dynamically.
+ */
+
+struct local_info {
+ struct pcmcia_device *p_dev;
+ struct gpib_board *dev;
+};
+
+/*
+ * gpib_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device. The device is registered
+ * with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+
+static int cb_gpib_probe(struct pcmcia_device *link)
+{
+ struct local_info *info;
+ int ret;
+
+ /* Allocate space for private device-specific data */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ /* The io structure describes IO port mapping */
+ link->resource[0]->end = 16;
+ link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ link->resource[1]->end = 16;
+ link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
+ link->io_lines = 10;
+
+ /* General socket configuration */
+ link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+ link->config_index = 1;
+ link->config_regs = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ curr_dev = link;
+ ret = cb_gpib_config(link);
+ if (ret)
+ goto free_info;
+
+ return 0;
+
+free_info:
+ kfree(info);
+ return ret;
+}
+
+/*
+ * This deletes a driver "instance". The device is de-registered
+ * with Card Services. If it has been released, all local data
+ * structures are freed. Otherwise, the structures will be freed
+ * when the device is released.
+ */
+
+static void cb_gpib_remove(struct pcmcia_device *link)
+{
+ struct local_info *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ if (info->dev)
+ cb_pcmcia_detach(info->dev);
+ cb_gpib_release(link);
+
+ //free_netdev(dev);
+ kfree(info);
+}
+
+static int cb_gpib_config_iteration(struct pcmcia_device *link, void *priv_data)
+{
+ return pcmcia_request_io(link);
+}
+
+/*
+ * gpib_config() is scheduled to run after a CARD_INSERTION event
+ * is received, to configure the PCMCIA socket, and to make the
+ * ethernet device available to the system.
+ */
+
+static int cb_gpib_config(struct pcmcia_device *link)
+{
+ int retval;
+
+ retval = pcmcia_loop_config(link, &cb_gpib_config_iteration, NULL);
+ if (retval) {
+ dev_warn(&link->dev, "no configuration found\n");
+ cb_gpib_release(link);
+ return -ENODEV;
+ }
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping.
+ */
+ retval = pcmcia_enable_device(link);
+ if (retval) {
+ dev_warn(&link->dev, "pcmcia_enable_device failed\n");
+ cb_gpib_release(link);
+ return -ENODEV;
+ }
+
+ return 0;
+} /* gpib_config */
+
+/*
+ * After a card is removed, gpib_release() will unregister the net
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+
+static void cb_gpib_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+}
+
+static int cb_gpib_suspend(struct pcmcia_device *link)
+{
+ if (link->open)
+ dev_warn(&link->dev, "Device still open\n");
+
+ return 0;
+}
+
+static int cb_gpib_resume(struct pcmcia_device *link)
+{
+ return cb_gpib_config(link);
+}
+
+/*====================================================================*/
+
+static struct pcmcia_device_id cb_pcmcia_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0005),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, cb_pcmcia_ids);
+
+static struct pcmcia_driver cb_gpib_cs_driver = {
+ .name = "cb_gpib_cs",
+ .owner = THIS_MODULE,
+ .id_table = cb_pcmcia_ids,
+ .probe = cb_gpib_probe,
+ .remove = cb_gpib_remove,
+ .suspend = cb_gpib_suspend,
+ .resume = cb_gpib_resume,
+};
+
+static void cb_pcmcia_cleanup_module(void)
+{
+ pcmcia_unregister_driver(&cb_gpib_cs_driver);
+}
+
+static struct gpib_interface cb_pcmcia_unaccel_interface = {
+ .name = "cbi_pcmcia_unaccel",
+ .attach = cb_pcmcia_attach,
+ .detach = cb_pcmcia_detach,
+ .read = cb7210_read,
+ .write = cb7210_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_pcmcia_interface = {
+ .name = "cbi_pcmcia",
+ .attach = cb_pcmcia_attach,
+ .detach = cb_pcmcia_detach,
+ .read = cb7210_accel_read,
+ .write = cb7210_accel_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static struct gpib_interface cb_pcmcia_accel_interface = {
+ .name = "cbi_pcmcia_accel",
+ .attach = cb_pcmcia_attach,
+ .detach = cb_pcmcia_detach,
+ .read = cb7210_accel_read,
+ .write = cb7210_accel_write,
+ .command = cb7210_command,
+ .take_control = cb7210_take_control,
+ .go_to_standby = cb7210_go_to_standby,
+ .request_system_control = cb7210_request_system_control,
+ .interface_clear = cb7210_interface_clear,
+ .remote_enable = cb7210_remote_enable,
+ .enable_eos = cb7210_enable_eos,
+ .disable_eos = cb7210_disable_eos,
+ .parallel_poll = cb7210_parallel_poll,
+ .parallel_poll_configure = cb7210_parallel_poll_configure,
+ .parallel_poll_response = cb7210_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = cb7210_line_status,
+ .update_status = cb7210_update_status,
+ .primary_address = cb7210_primary_address,
+ .secondary_address = cb7210_secondary_address,
+ .serial_poll_response = cb7210_serial_poll_response,
+ .serial_poll_status = cb7210_serial_poll_status,
+ .t1_delay = cb7210_t1_delay,
+ .return_to_local = cb7210_return_to_local,
+};
+
+static int cb_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct cb7210_priv *cb_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+
+ if (!curr_dev) {
+ dev_err(board->gpib_dev, "no cb pcmcia cards found\n");
+ return -ENODEV;
+ }
+
+ retval = cb7210_generic_attach(board);
+ if (retval)
+ return retval;
+
+ cb_priv = board->private_data;
+ nec_priv = &cb_priv->nec7210_priv;
+
+ if (!request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]),
+ DRV_NAME)) {
+ dev_err(board->gpib_dev, "ioports starting at 0x%lx are already in use\n",
+ (unsigned long)curr_dev->resource[0]->start);
+ return -EBUSY;
+ }
+ nec_priv->iobase = curr_dev->resource[0]->start;
+ cb_priv->fifo_iobase = curr_dev->resource[0]->start;
+
+ if (request_irq(curr_dev->irq, cb7210_interrupt, IRQF_SHARED, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "failed to request IRQ %d\n", curr_dev->irq);
+ return -EBUSY;
+ }
+ cb_priv->irq = curr_dev->irq;
+
+ return cb7210_init(cb_priv, board);
+}
+
+static void cb_pcmcia_detach(struct gpib_board *board)
+{
+ struct cb7210_priv *cb_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (cb_priv) {
+ nec_priv = &cb_priv->nec7210_priv;
+ gpib_free_pseudo_irq(board);
+ if (cb_priv->irq)
+ free_irq(cb_priv->irq, board);
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ release_region(nec7210_iobase(cb_priv), cb7210_iosize);
+ }
+ }
+ cb7210_generic_detach(board);
+}
+
+#endif /* CONFIG_GPIB_PCMCIA */
+
+static int __init cb7210_init_module(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&cb7210_pci_driver);
+ if (ret) {
+ pr_err("pci_register_driver failed: error = %d\n", ret);
+ return ret;
+ }
+
+ ret = gpib_register_driver(&cb_pci_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pci;
+ }
+
+ ret = gpib_register_driver(&cb_isa_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_isa;
+ }
+
+ ret = gpib_register_driver(&cb_pci_accel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pci_accel;
+ }
+
+ ret = gpib_register_driver(&cb_pci_unaccel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pci_unaccel;
+ }
+
+ ret = gpib_register_driver(&cb_isa_accel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_isa_accel;
+ }
+
+ ret = gpib_register_driver(&cb_isa_unaccel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_isa_unaccel;
+ }
+
+#ifdef CONFIG_GPIB_PCMCIA
+ ret = gpib_register_driver(&cb_pcmcia_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia;
+ }
+
+ ret = gpib_register_driver(&cb_pcmcia_accel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia_accel;
+ }
+
+ ret = gpib_register_driver(&cb_pcmcia_unaccel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia_unaccel;
+ }
+
+ ret = pcmcia_register_driver(&cb_gpib_cs_driver);
+ if (ret) {
+ pr_err("pcmcia_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia_driver;
+ }
+#endif
+
+ return 0;
+
+#ifdef CONFIG_GPIB_PCMCIA
+err_pcmcia_driver:
+ gpib_unregister_driver(&cb_pcmcia_unaccel_interface);
+err_pcmcia_unaccel:
+ gpib_unregister_driver(&cb_pcmcia_accel_interface);
+err_pcmcia_accel:
+ gpib_unregister_driver(&cb_pcmcia_interface);
+err_pcmcia:
+#endif
+ gpib_unregister_driver(&cb_isa_unaccel_interface);
+err_isa_unaccel:
+ gpib_unregister_driver(&cb_isa_accel_interface);
+err_isa_accel:
+ gpib_unregister_driver(&cb_pci_unaccel_interface);
+err_pci_unaccel:
+ gpib_unregister_driver(&cb_pci_accel_interface);
+err_pci_accel:
+ gpib_unregister_driver(&cb_isa_interface);
+err_isa:
+ gpib_unregister_driver(&cb_pci_interface);
+err_pci:
+ pci_unregister_driver(&cb7210_pci_driver);
+
+ return ret;
+}
+
+static void __exit cb7210_exit_module(void)
+{
+ gpib_unregister_driver(&cb_pci_interface);
+ gpib_unregister_driver(&cb_isa_interface);
+ gpib_unregister_driver(&cb_pci_accel_interface);
+ gpib_unregister_driver(&cb_pci_unaccel_interface);
+ gpib_unregister_driver(&cb_isa_accel_interface);
+ gpib_unregister_driver(&cb_isa_unaccel_interface);
+#ifdef CONFIG_GPIB_PCMCIA
+ gpib_unregister_driver(&cb_pcmcia_interface);
+ gpib_unregister_driver(&cb_pcmcia_accel_interface);
+ gpib_unregister_driver(&cb_pcmcia_unaccel_interface);
+ cb_pcmcia_cleanup_module();
+#endif
+
+ pci_unregister_driver(&cb7210_pci_driver);
+}
+
+module_init(cb7210_init_module);
+module_exit(cb7210_exit_module);
diff --git a/drivers/gpib/cb7210/cb7210.h b/drivers/gpib/cb7210/cb7210.h
new file mode 100644
index 000000000000..ddc841ff87ae
--- /dev/null
+++ b/drivers/gpib/cb7210/cb7210.h
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#include "nec7210.h"
+#include "gpibP.h"
+#include "amccs5933.h"
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+enum {
+ PCI_DEVICE_ID_CBOARDS_PCI_GPIB = 0x6,
+ PCI_DEVICE_ID_CBOARDS_CPCI_GPIB = 0xe,
+};
+
+enum pci_chip {
+ PCI_CHIP_NONE = 0,
+ PCI_CHIP_AMCC_S5933,
+ PCI_CHIP_QUANCOM
+};
+
+// struct which defines private_data for cb7210 boards
+struct cb7210_priv {
+ struct nec7210_priv nec7210_priv;
+ struct pci_dev *pci_device;
+ // base address of amccs5933 pci chip
+ unsigned long amcc_iobase;
+ unsigned long fifo_iobase;
+ unsigned int irq;
+ enum pci_chip pci_chip;
+ u8 hs_mode_bits;
+ unsigned out_fifo_half_empty : 1;
+ unsigned in_fifo_half_full : 1;
+};
+
+// pci-gpib register offset
+static const int cb7210_reg_offset = 1;
+
+// uses 10 ioports
+static const int cb7210_iosize = 10;
+
+// fifo size in bytes
+static const int cb7210_fifo_size = 2048;
+static const int cb7210_fifo_width = 2;
+
+// cb7210 specific registers and bits
+enum cb7210_regs {
+ BUS_STATUS = 0x7,
+};
+
+enum cb7210_page_in {
+ BUS_STATUS_PAGE = 1,
+};
+
+enum hs_regs {
+ // write registers
+ HS_MODE = 0x8, /* HS_MODE register */
+ HS_INT_LEVEL = 0x9, /* HS_INT_LEVEL register */
+ // read registers
+ HS_STATUS = 0x8, /* HS_STATUS register */
+};
+
+static inline u32 nec7210_iobase(const struct cb7210_priv *cb_priv)
+{
+ return cb_priv->nec7210_priv.iobase;
+}
+
+static inline int cb7210_page_in_bits(unsigned int page)
+{
+ return 0x50 | (page & 0xf);
+}
+
+static inline u8 cb7210_paged_read_byte(struct cb7210_priv *cb_priv,
+ unsigned int register_num, unsigned int page)
+{
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ u8 retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nec_priv->register_page_lock, flags);
+ outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset);
+ udelay(1);
+ retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
+ spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
+ return retval;
+}
+
+// don't use for register_num < 8, since it doesn't lock
+static inline u8 cb7210_read_byte(const struct cb7210_priv *cb_priv,
+ enum hs_regs register_num)
+{
+ const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ u8 retval;
+
+ retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
+ return retval;
+}
+
+static inline void cb7210_paged_write_byte(struct cb7210_priv *cb_priv, u8 data,
+ unsigned int register_num, unsigned int page)
+{
+ struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nec_priv->register_page_lock, flags);
+ outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset);
+ udelay(1);
+ outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
+ spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
+}
+
+// don't use for register_num < 8, since it doesn't lock
+static inline void cb7210_write_byte(const struct cb7210_priv *cb_priv, u8 data,
+ enum hs_regs register_num)
+{
+ const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
+
+ outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
+}
+
+enum bus_status_bits {
+ BSR_ATN_BIT = 0x1,
+ BSR_EOI_BIT = 0x2,
+ BSR_SRQ_BIT = 0x4,
+ BSR_IFC_BIT = 0x8,
+ BSR_REN_BIT = 0x10,
+ BSR_DAV_BIT = 0x20,
+ BSR_NRFD_BIT = 0x40,
+ BSR_NDAC_BIT = 0x80,
+};
+
+/* CBI 488.2 HS control */
+
+/*
+ * when both bit 0 and 1 are set, it
+ * 1 clears the transmit state machine to an initial condition
+ * 2 clears any residual interrupts left latched on cbi488.2
+ * 3 resets all control bits in HS_MODE to zero
+ * 4 enables TX empty interrupts
+ * when both bit 0 and 1 are zero, then the high speed mode is disabled
+ */
+enum hs_mode_bits {
+ HS_ENABLE_MASK = 0x3,
+ HS_TX_ENABLE = (1 << 0),
+ HS_RX_ENABLE = (1 << 1),
+ HS_HF_INT_EN = (1 << 3),
+ HS_CLR_SRQ_INT = (1 << 4),
+ HS_CLR_EOI_EMPTY_INT = (1 << 5),
+ HS_CLR_HF_INT = (1 << 6),
+ HS_SYS_CONTROL = (1 << 7),
+};
+
+/* CBI 488.2 status */
+enum hs_status_bits {
+ HS_FIFO_FULL = (1 << 0),
+ HS_HALF_FULL = (1 << 1),
+ HS_SRQ_INT = (1 << 2),
+ HS_EOI_INT = (1 << 3),
+ HS_TX_MSB_NOT_EMPTY = (1 << 4),
+ HS_RX_MSB_NOT_EMPTY = (1 << 5),
+ HS_TX_LSB_NOT_EMPTY = (1 << 6),
+ HS_RX_LSB_NOT_EMPTY = (1 << 7),
+};
+
+/* CBI488.2 hs_int_level register */
+enum hs_int_level_bits {
+ HS_RESET7210 = (1 << 7),
+};
+
+static inline unsigned int irq_bits(unsigned int irq)
+{
+ switch (irq) {
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ return irq - 1;
+ case 7:
+ return 0x5;
+ case 10:
+ return 0x6;
+ case 11:
+ return 0x7;
+ default:
+ return 0;
+ }
+}
+
+enum cb7210_aux_cmds {
+/*
+ * AUX_RTL2 is an undocumented aux command which causes cb7210 to assert
+ * (and keep asserted) local rtl message. This is used in conjunction
+ * with the (stupid) cb7210 implementation
+ * of the normal nec7210 AUX_RTL aux command, which
+ * causes the rtl message to toggle between on and off.
+ */
+ AUX_RTL2 = 0xd,
+ AUX_LO_SPEED = 0x40,
+ AUX_HI_SPEED = 0x41,
+};
diff --git a/drivers/gpib/cec/Makefile b/drivers/gpib/cec/Makefile
new file mode 100644
index 000000000000..b7141e23d4e0
--- /dev/null
+++ b/drivers/gpib/cec/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_GPIB_CEC_PCI) += cec_gpib.o
+
diff --git a/drivers/gpib/cec/cec.h b/drivers/gpib/cec/cec.h
new file mode 100644
index 000000000000..3ce2869c7429
--- /dev/null
+++ b/drivers/gpib/cec/cec.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#include "nec7210.h"
+#include "gpibP.h"
+#include "plx9050.h"
+
+struct cec_priv {
+ struct nec7210_priv nec7210_priv;
+ struct pci_dev *pci_device;
+ // base address for plx9052 pci chip
+ unsigned long plx_iobase;
+ unsigned int irq;
+};
+
+// offset between consecutive nec7210 registers
+static const int cec_reg_offset = 1;
diff --git a/drivers/gpib/cec/cec_gpib.c b/drivers/gpib/cec/cec_gpib.c
new file mode 100644
index 000000000000..dbf9b95baabc
--- /dev/null
+++ b/drivers/gpib/cec/cec_gpib.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "cec.h"
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <asm/dma.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for CEC PCI and PCMCIA boards");
+
+/*
+ * GPIB interrupt service routines
+ */
+
+static irqreturn_t cec_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct cec_priv *priv = board->private_data;
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = nec7210_interrupt(board, &priv->nec7210_priv);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+#define CEC_VENDOR_ID 0x12fc
+#define CEC_DEV_ID 0x5cec
+#define CEC_SUBID 0x9050
+
+static int cec_pci_attach(struct gpib_board *board, const struct gpib_board_config *config);
+
+static void cec_pci_detach(struct gpib_board *board);
+
+// wrappers for interface functions
+static int cec_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+}
+
+static int cec_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int cec_command(struct gpib_board *board, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int cec_take_control(struct gpib_board *board, int synchronous)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int cec_go_to_standby(struct gpib_board *board)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int cec_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
+}
+
+static void cec_interface_clear(struct gpib_board *board, int assert)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void cec_remote_enable(struct gpib_board *board, int enable)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int cec_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void cec_disable_eos(struct gpib_board *board)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int cec_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
+}
+
+static int cec_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int cec_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int cec_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
+}
+
+static void cec_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
+}
+
+static void cec_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+static void cec_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
+}
+
+static u8 cec_serial_poll_status(struct gpib_board *board)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static int cec_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct cec_priv *priv = board->private_data;
+
+ return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec);
+}
+
+static void cec_return_to_local(struct gpib_board *board)
+{
+ struct cec_priv *priv = board->private_data;
+
+ nec7210_return_to_local(board, &priv->nec7210_priv);
+}
+
+static struct gpib_interface cec_pci_interface = {
+ .name = "cec_pci",
+ .attach = cec_pci_attach,
+ .detach = cec_pci_detach,
+ .read = cec_read,
+ .write = cec_write,
+ .command = cec_command,
+ .take_control = cec_take_control,
+ .go_to_standby = cec_go_to_standby,
+ .request_system_control = cec_request_system_control,
+ .interface_clear = cec_interface_clear,
+ .remote_enable = cec_remote_enable,
+ .enable_eos = cec_enable_eos,
+ .disable_eos = cec_disable_eos,
+ .parallel_poll = cec_parallel_poll,
+ .parallel_poll_configure = cec_parallel_poll_configure,
+ .parallel_poll_response = cec_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL, // XXX
+ .update_status = cec_update_status,
+ .primary_address = cec_primary_address,
+ .secondary_address = cec_secondary_address,
+ .serial_poll_response = cec_serial_poll_response,
+ .serial_poll_status = cec_serial_poll_status,
+ .t1_delay = cec_t1_delay,
+ .return_to_local = cec_return_to_local,
+};
+
+static int cec_allocate_private(struct gpib_board *board)
+{
+ struct cec_priv *priv;
+
+ board->private_data = kmalloc(sizeof(struct cec_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -1;
+ priv = board->private_data;
+ memset(priv, 0, sizeof(struct cec_priv));
+ init_nec7210_private(&priv->nec7210_priv);
+ return 0;
+}
+
+static void cec_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static int cec_generic_attach(struct gpib_board *board)
+{
+ struct cec_priv *cec_priv;
+ struct nec7210_priv *nec_priv;
+
+ board->status = 0;
+
+ if (cec_allocate_private(board))
+ return -ENOMEM;
+ cec_priv = board->private_data;
+ nec_priv = &cec_priv->nec7210_priv;
+ nec_priv->read_byte = nec7210_ioport_read_byte;
+ nec_priv->write_byte = nec7210_ioport_write_byte;
+ nec_priv->offset = cec_reg_offset;
+ nec_priv->type = NEC7210; // guess
+ return 0;
+}
+
+static void cec_init(struct cec_priv *cec_priv, const struct gpib_board *board)
+{
+ struct nec7210_priv *nec_priv = &cec_priv->nec7210_priv;
+
+ nec7210_board_reset(nec_priv, board);
+
+ /* set internal counter register for 8 MHz input clock */
+ write_byte(nec_priv, ICR | 8, AUXMR);
+
+ nec7210_board_online(nec_priv, board);
+}
+
+static int cec_pci_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct cec_priv *cec_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = 0;
+ int retval;
+
+ retval = cec_generic_attach(board);
+ if (retval)
+ return retval;
+
+ cec_priv = board->private_data;
+ nec_priv = &cec_priv->nec7210_priv;
+
+ // find board
+ cec_priv->pci_device = NULL;
+ while ((cec_priv->pci_device =
+ gpib_pci_get_device(config, CEC_VENDOR_ID,
+ CEC_DEV_ID, cec_priv->pci_device))) {
+ // check for board with plx9050 controller
+ if (cec_priv->pci_device->subsystem_device == CEC_SUBID)
+ break;
+ }
+ if (!cec_priv->pci_device) {
+ dev_err(board->gpib_dev, "no cec PCI board found\n");
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(cec_priv->pci_device)) {
+ dev_err(board->gpib_dev, "error enabling pci device\n");
+ return -EIO;
+ }
+
+ if (pci_request_regions(cec_priv->pci_device, "cec-gpib"))
+ return -EBUSY;
+
+ cec_priv->plx_iobase = pci_resource_start(cec_priv->pci_device, 1);
+ nec_priv->iobase = pci_resource_start(cec_priv->pci_device, 3);
+
+ isr_flags |= IRQF_SHARED;
+ if (request_irq(cec_priv->pci_device->irq, cec_interrupt, isr_flags, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "failed to obtain IRQ %d\n", cec_priv->pci_device->irq);
+ return -EBUSY;
+ }
+ cec_priv->irq = cec_priv->pci_device->irq;
+ if (gpib_request_pseudo_irq(board, cec_interrupt)) {
+ dev_err(board->gpib_dev, "failed to allocate pseudo irq\n");
+ return -1;
+ }
+ cec_init(cec_priv, board);
+
+ // enable interrupts on plx chip
+ outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT,
+ cec_priv->plx_iobase + PLX9050_INTCSR_REG);
+
+ return 0;
+}
+
+static void cec_pci_detach(struct gpib_board *board)
+{
+ struct cec_priv *cec_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (cec_priv) {
+ nec_priv = &cec_priv->nec7210_priv;
+ gpib_free_pseudo_irq(board);
+ if (cec_priv->irq) {
+ // disable plx9050 interrupts
+ outl(0, cec_priv->plx_iobase + PLX9050_INTCSR_REG);
+ free_irq(cec_priv->irq, board);
+ }
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ pci_release_regions(cec_priv->pci_device);
+ }
+ if (cec_priv->pci_device)
+ pci_dev_put(cec_priv->pci_device);
+ }
+ cec_free_private(board);
+}
+
+static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return 0;
+}
+
+static const struct pci_device_id cec_pci_table[] = {
+ {CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 },
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, cec_pci_table);
+
+static struct pci_driver cec_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = cec_pci_table,
+ .probe = &cec_pci_probe
+};
+
+static int __init cec_init_module(void)
+{
+ int result;
+
+ result = pci_register_driver(&cec_pci_driver);
+ if (result) {
+ pr_err("pci_register_driver failed: error = %d\n", result);
+ return result;
+ }
+
+ result = gpib_register_driver(&cec_pci_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void cec_exit_module(void)
+{
+ gpib_unregister_driver(&cec_pci_interface);
+
+ pci_unregister_driver(&cec_pci_driver);
+}
+
+module_init(cec_init_module);
+module_exit(cec_exit_module);
diff --git a/drivers/gpib/common/Makefile b/drivers/gpib/common/Makefile
new file mode 100644
index 000000000000..460586edb574
--- /dev/null
+++ b/drivers/gpib/common/Makefile
@@ -0,0 +1,6 @@
+
+obj-$(CONFIG_GPIB_COMMON) += gpib_common.o
+
+gpib_common-objs := gpib_os.o iblib.o
+
+
diff --git a/drivers/gpib/common/gpib_os.c b/drivers/gpib/common/gpib_os.c
new file mode 100644
index 000000000000..9dbbac8b8436
--- /dev/null
+++ b/drivers/gpib/common/gpib_os.c
@@ -0,0 +1,2271 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2001, 2004 by Frank Mori Hess
+ ***************************************************************************
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+
+#include "ibsys.h"
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/fcntl.h>
+#include <linux/kmod.h>
+#include <linux/uaccess.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB base support");
+MODULE_ALIAS_CHARDEV_MAJOR(GPIB_CODE);
+
+static int board_type_ioctl(struct gpib_file_private *file_priv,
+ struct gpib_board *board, unsigned long arg);
+static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board,
+ unsigned long arg);
+static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board,
+ unsigned long arg);
+static int command_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board,
+ unsigned long arg);
+static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg);
+static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg);
+static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg);
+static int wait_ioctl(struct gpib_file_private *file_priv,
+ struct gpib_board *board, unsigned long arg);
+static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg);
+static int online_ioctl(struct gpib_board *board, unsigned long arg);
+static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg);
+static int take_control_ioctl(struct gpib_board *board, unsigned long arg);
+static int line_status_ioctl(struct gpib_board *board, unsigned long arg);
+static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg);
+static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg);
+static int eos_ioctl(struct gpib_board *board, unsigned long arg);
+static int request_service_ioctl(struct gpib_board *board, unsigned long arg);
+static int request_service2_ioctl(struct gpib_board *board, unsigned long arg);
+static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg);
+static int irq_ioctl(struct gpib_board_config *config, unsigned long arg);
+static int dma_ioctl(struct gpib_board_config *config, unsigned long arg);
+static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg);
+static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg);
+static int timeout_ioctl(struct gpib_board *board, unsigned long arg);
+static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg);
+static int board_info_ioctl(const struct gpib_board *board, unsigned long arg);
+static int ppc_ioctl(struct gpib_board *board, unsigned long arg);
+static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg);
+static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg);
+static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg);
+static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg);
+static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg);
+static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg);
+static int event_ioctl(struct gpib_board *board, unsigned long arg);
+static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg);
+static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg);
+
+static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board);
+
+static int pop_gpib_event_nolock(struct gpib_board *board,
+ struct gpib_event_queue *queue, short *event_type);
+
+/*
+ * Timer functions
+ */
+
+/* Watchdog timeout routine */
+
+static void watchdog_timeout(struct timer_list *t)
+{
+ struct gpib_board *board = timer_container_of(board, t, timer);
+
+ set_bit(TIMO_NUM, &board->status);
+ wake_up_interruptible(&board->wait);
+}
+
+/* install timer interrupt handler */
+void os_start_timer(struct gpib_board *board, unsigned int usec_timeout)
+/* Starts the timeout task */
+{
+ if (timer_pending(&board->timer)) {
+ dev_err(board->gpib_dev, "bug! timer already running?\n");
+ return;
+ }
+ clear_bit(TIMO_NUM, &board->status);
+
+ if (usec_timeout > 0) {
+ board->timer.function = watchdog_timeout;
+ /* set number of ticks */
+ mod_timer(&board->timer, jiffies + usec_to_jiffies(usec_timeout));
+ }
+}
+
+void os_remove_timer(struct gpib_board *board)
+/* Removes the timeout task */
+{
+ if (timer_pending(&board->timer))
+ timer_delete_sync(&board->timer);
+}
+
+int io_timed_out(struct gpib_board *board)
+{
+ if (test_bit(TIMO_NUM, &board->status))
+ return 1;
+ return 0;
+}
+
+/*
+ * this is a function instead of a constant because of Suse
+ * defining HZ to be a function call to get_hz()
+ */
+static inline int pseudo_irq_period(void)
+{
+ return (HZ + 99) / 100;
+}
+
+static void pseudo_irq_handler(struct timer_list *t)
+{
+ struct gpib_pseudo_irq *pseudo_irq = timer_container_of(pseudo_irq, t,
+ timer);
+
+ if (pseudo_irq->handler)
+ pseudo_irq->handler(0, pseudo_irq->board);
+ else
+ pr_err("gpib: bug! pseudo_irq.handler is NULL\n");
+
+ if (atomic_read(&pseudo_irq->active))
+ mod_timer(&pseudo_irq->timer, jiffies + pseudo_irq_period());
+}
+
+int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *))
+{
+ if (timer_pending(&board->pseudo_irq.timer) || board->pseudo_irq.handler) {
+ dev_err(board->gpib_dev, "only one pseudo interrupt per board allowed\n");
+ return -1;
+ }
+
+ board->pseudo_irq.handler = handler;
+ board->pseudo_irq.timer.function = pseudo_irq_handler;
+ board->pseudo_irq.board = board;
+
+ atomic_set(&board->pseudo_irq.active, 1);
+
+ mod_timer(&board->pseudo_irq.timer, jiffies + pseudo_irq_period());
+
+ return 0;
+}
+EXPORT_SYMBOL(gpib_request_pseudo_irq);
+
+void gpib_free_pseudo_irq(struct gpib_board *board)
+{
+ atomic_set(&board->pseudo_irq.active, 0);
+
+ timer_delete_sync(&board->pseudo_irq.timer);
+ board->pseudo_irq.handler = NULL;
+}
+EXPORT_SYMBOL(gpib_free_pseudo_irq);
+
+static const unsigned int serial_timeout = 1000000;
+
+unsigned int num_status_bytes(const struct gpib_status_queue *dev)
+{
+ if (!dev)
+ return 0;
+ return dev->num_status_bytes;
+}
+
+// push status byte onto back of status byte fifo
+int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 poll_byte)
+{
+ struct list_head *head = &device->status_bytes;
+ struct gpib_status_byte *status;
+ static const unsigned int max_num_status_bytes = 1024;
+ int retval;
+
+ if (num_status_bytes(device) >= max_num_status_bytes) {
+ u8 lost_byte;
+
+ device->dropped_byte = 1;
+ retval = pop_status_byte(board, device, &lost_byte);
+ if (retval < 0)
+ return retval;
+ }
+
+ status = kmalloc(sizeof(*status), GFP_KERNEL);
+ if (!status)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&status->list);
+ status->poll_byte = poll_byte;
+
+ list_add_tail(&status->list, head);
+
+ device->num_status_bytes++;
+
+ dev_dbg(board->gpib_dev, "pushed status byte 0x%x, %i in queue\n",
+ (int)poll_byte, num_status_bytes(device));
+
+ return 0;
+}
+
+// pop status byte from front of status byte fifo
+int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 *poll_byte)
+{
+ struct list_head *head = &device->status_bytes;
+ struct list_head *front = head->next;
+ struct gpib_status_byte *status;
+
+ if (num_status_bytes(device) == 0)
+ return -EIO;
+
+ if (front == head)
+ return -EIO;
+
+ if (device->dropped_byte) {
+ device->dropped_byte = 0;
+ return -EPIPE;
+ }
+
+ status = list_entry(front, struct gpib_status_byte, list);
+ *poll_byte = status->poll_byte;
+
+ list_del(front);
+ kfree(status);
+
+ device->num_status_bytes--;
+
+ dev_dbg(board->gpib_dev, "popped status byte 0x%x, %i in queue\n",
+ (int)*poll_byte, num_status_bytes(device));
+
+ return 0;
+}
+
+struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board, unsigned int pad, int sad)
+{
+ struct gpib_status_queue *device;
+ struct list_head *list_ptr;
+ const struct list_head *head = &board->device_list;
+
+ for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) {
+ device = list_entry(list_ptr, struct gpib_status_queue, list);
+ if (gpib_address_equal(device->pad, device->sad, pad, sad))
+ return device;
+ }
+
+ return NULL;
+}
+
+int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad,
+ unsigned int usec_timeout, u8 *poll_byte)
+{
+ struct gpib_status_queue *device;
+
+ device = get_gpib_status_queue(board, pad, sad);
+ if (num_status_bytes(device))
+ return pop_status_byte(board, device, poll_byte);
+ else
+ return dvrsp(board, pad, sad, usec_timeout, poll_byte);
+}
+
+int autopoll_all_devices(struct gpib_board *board)
+{
+ int retval;
+
+ if (mutex_lock_interruptible(&board->user_mutex))
+ return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&board->big_gpib_mutex)) {
+ mutex_unlock(&board->user_mutex);
+ return -ERESTARTSYS;
+ }
+
+ dev_dbg(board->gpib_dev, "autopoll has board lock\n");
+
+ retval = serial_poll_all(board, serial_timeout);
+ if (retval < 0) {
+ mutex_unlock(&board->big_gpib_mutex);
+ mutex_unlock(&board->user_mutex);
+ return retval;
+ }
+
+ dev_dbg(board->gpib_dev, "complete\n");
+ /*
+ * need to wake wait queue in case someone is
+ * waiting on RQS
+ */
+ wake_up_interruptible(&board->wait);
+ mutex_unlock(&board->big_gpib_mutex);
+ mutex_unlock(&board->user_mutex);
+
+ return retval;
+}
+
+static int setup_serial_poll(struct gpib_board *board, unsigned int usec_timeout)
+{
+ u8 cmd_string[8];
+ int i;
+ size_t bytes_written;
+ int ret;
+
+ os_start_timer(board, usec_timeout);
+ ret = ibcac(board, 1, 1);
+ if (ret < 0) {
+ os_remove_timer(board);
+ return ret;
+ }
+
+ i = 0;
+ cmd_string[i++] = UNL;
+ cmd_string[i++] = MLA(board->pad); /* controller's listen address */
+ if (board->sad >= 0)
+ cmd_string[i++] = MSA(board->sad);
+ cmd_string[i++] = SPE; // serial poll enable
+
+ ret = board->interface->command(board, cmd_string, i, &bytes_written);
+ if (ret < 0 || bytes_written < i) {
+ dev_dbg(board->gpib_dev, "failed to setup serial poll\n");
+ os_remove_timer(board);
+ return -EIO;
+ }
+ os_remove_timer(board);
+
+ return 0;
+}
+
+static int read_serial_poll_byte(struct gpib_board *board, unsigned int pad,
+ int sad, unsigned int usec_timeout, u8 *result)
+{
+ u8 cmd_string[8];
+ int end_flag;
+ int ret;
+ int i;
+ size_t nbytes;
+
+ dev_dbg(board->gpib_dev, "entering pad=%i sad=%i\n", pad, sad);
+
+ os_start_timer(board, usec_timeout);
+ ret = ibcac(board, 1, 1);
+ if (ret < 0) {
+ os_remove_timer(board);
+ return ret;
+ }
+
+ i = 0;
+ // send talk address
+ cmd_string[i++] = MTA(pad);
+ if (sad >= 0)
+ cmd_string[i++] = MSA(sad);
+
+ ret = board->interface->command(board, cmd_string, i, &nbytes);
+ if (ret < 0 || nbytes < i) {
+ dev_err(board->gpib_dev, "failed to setup serial poll\n");
+ os_remove_timer(board);
+ return -EIO;
+ }
+
+ ibgts(board);
+
+ // read poll result
+ ret = board->interface->read(board, result, 1, &end_flag, &nbytes);
+ if (ret < 0 || nbytes < 1) {
+ dev_err(board->gpib_dev, "serial poll failed\n");
+ os_remove_timer(board);
+ return -EIO;
+ }
+ os_remove_timer(board);
+
+ return 0;
+}
+
+static int cleanup_serial_poll(struct gpib_board *board, unsigned int usec_timeout)
+{
+ u8 cmd_string[8];
+ int ret;
+ size_t bytes_written;
+
+ os_start_timer(board, usec_timeout);
+ ret = ibcac(board, 1, 1);
+ if (ret < 0) {
+ os_remove_timer(board);
+ return ret;
+ }
+
+ cmd_string[0] = SPD; /* disable serial poll bytes */
+ cmd_string[1] = UNT;
+ ret = board->interface->command(board, cmd_string, 2, &bytes_written);
+ if (ret < 0 || bytes_written < 2) {
+ dev_err(board->gpib_dev, "failed to disable serial poll\n");
+ os_remove_timer(board);
+ return -EIO;
+ }
+ os_remove_timer(board);
+
+ return 0;
+}
+
+static int serial_poll_single(struct gpib_board *board, unsigned int pad, int sad,
+ unsigned int usec_timeout, u8 *result)
+{
+ int retval, cleanup_retval;
+
+ retval = setup_serial_poll(board, usec_timeout);
+ if (retval < 0)
+ return retval;
+ retval = read_serial_poll_byte(board, pad, sad, usec_timeout, result);
+ cleanup_retval = cleanup_serial_poll(board, usec_timeout);
+ if (retval < 0)
+ return retval;
+ if (cleanup_retval < 0)
+ return retval;
+
+ return 0;
+}
+
+int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout)
+{
+ int retval = 0;
+ struct list_head *cur;
+ const struct list_head *head = NULL;
+ struct gpib_status_queue *device;
+ u8 result;
+ unsigned int num_bytes = 0;
+
+ head = &board->device_list;
+ if (head->next == head)
+ return 0;
+
+ retval = setup_serial_poll(board, usec_timeout);
+ if (retval < 0)
+ return retval;
+
+ for (cur = head->next; cur != head; cur = cur->next) {
+ device = list_entry(cur, struct gpib_status_queue, list);
+ retval = read_serial_poll_byte(board,
+ device->pad, device->sad, usec_timeout, &result);
+ if (retval < 0)
+ continue;
+ if (result & request_service_bit) {
+ retval = push_status_byte(board, device, result);
+ if (retval < 0)
+ continue;
+ num_bytes++;
+ }
+ }
+
+ retval = cleanup_serial_poll(board, usec_timeout);
+ if (retval < 0)
+ return retval;
+
+ return num_bytes;
+}
+
+/*
+ * DVRSP
+ * This function performs a serial poll of the device with primary
+ * address pad and secondary address sad. If the device has no
+ * secondary address, pass a negative number in for this argument. At the
+ * end of a successful serial poll the response is returned in result.
+ * SPD and UNT are sent at the completion of the poll.
+ */
+
+int dvrsp(struct gpib_board *board, unsigned int pad, int sad,
+ unsigned int usec_timeout, u8 *result)
+{
+ int status = ibstatus(board);
+ int retval;
+
+ if ((status & CIC) == 0) {
+ dev_err(board->gpib_dev, "not CIC during serial poll\n");
+ return -1;
+ }
+
+ if (pad > MAX_GPIB_PRIMARY_ADDRESS || sad > MAX_GPIB_SECONDARY_ADDRESS || sad < -1) {
+ dev_err(board->gpib_dev, "bad address for serial poll");
+ return -1;
+ }
+
+ retval = serial_poll_single(board, pad, sad, usec_timeout, result);
+ if (io_timed_out(board))
+ retval = -ETIMEDOUT;
+
+ return retval;
+}
+
+static struct gpib_descriptor *handle_to_descriptor(const struct gpib_file_private *file_priv,
+ int handle)
+{
+ if (handle < 0 || handle >= GPIB_MAX_NUM_DESCRIPTORS) {
+ pr_err("gpib: invalid handle %i\n", handle);
+ return NULL;
+ }
+
+ return file_priv->descriptors[handle];
+}
+
+static int init_gpib_file_private(struct gpib_file_private *priv)
+{
+ memset(priv, 0, sizeof(*priv));
+ atomic_set(&priv->holding_mutex, 0);
+ priv->descriptors[0] = kmalloc(sizeof(struct gpib_descriptor), GFP_KERNEL);
+ if (!priv->descriptors[0]) {
+ pr_err("gpib: failed to allocate default board descriptor\n");
+ return -ENOMEM;
+ }
+ init_gpib_descriptor(priv->descriptors[0]);
+ priv->descriptors[0]->is_board = 1;
+ mutex_init(&priv->descriptors_mutex);
+ return 0;
+}
+
+int ibopen(struct inode *inode, struct file *filep)
+{
+ unsigned int minor = iminor(inode);
+ struct gpib_board *board;
+ struct gpib_file_private *priv;
+
+ if (minor >= GPIB_MAX_NUM_BOARDS) {
+ pr_err("gpib: invalid minor number of device file\n");
+ return -ENXIO;
+ }
+
+ board = &board_array[minor];
+
+ filep->private_data = kmalloc(sizeof(struct gpib_file_private), GFP_KERNEL);
+ if (!filep->private_data)
+ return -ENOMEM;
+
+ priv = filep->private_data;
+ init_gpib_file_private((struct gpib_file_private *)filep->private_data);
+
+ if (board->use_count == 0) {
+ int retval;
+
+ retval = request_module("gpib%i", minor);
+ if (retval)
+ dev_dbg(board->gpib_dev, "request module returned %i\n", retval);
+ }
+ if (board->interface) {
+ if (!try_module_get(board->provider_module)) {
+ dev_err(board->gpib_dev, "try_module_get() failed\n");
+ return -EIO;
+ }
+ board->use_count++;
+ priv->got_module = 1;
+ }
+ return 0;
+}
+
+int ibclose(struct inode *inode, struct file *filep)
+{
+ unsigned int minor = iminor(inode);
+ struct gpib_board *board;
+ struct gpib_file_private *priv = filep->private_data;
+ struct gpib_descriptor *desc;
+
+ if (minor >= GPIB_MAX_NUM_BOARDS) {
+ pr_err("gpib: invalid minor number of device file\n");
+ return -ENODEV;
+ }
+
+ board = &board_array[minor];
+
+ if (priv) {
+ desc = handle_to_descriptor(priv, 0);
+ if (desc) {
+ if (desc->autopoll_enabled) {
+ dev_dbg(board->gpib_dev, "decrementing autospollers\n");
+ if (board->autospollers > 0)
+ board->autospollers--;
+ else
+ dev_err(board->gpib_dev,
+ "Attempt to decrement zero autospollers\n");
+ }
+ } else {
+ dev_err(board->gpib_dev, "Unexpected null gpib_descriptor\n");
+ }
+
+ cleanup_open_devices(priv, board);
+
+ if (atomic_read(&priv->holding_mutex))
+ mutex_unlock(&board->user_mutex);
+
+ if (priv->got_module && board->use_count) {
+ module_put(board->provider_module);
+ --board->use_count;
+ }
+
+ kfree(filep->private_data);
+ filep->private_data = NULL;
+ }
+
+ return 0;
+}
+
+long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ unsigned int minor = iminor(file_inode(filep));
+ struct gpib_board *board;
+ struct gpib_file_private *file_priv = filep->private_data;
+ long retval = -ENOTTY;
+
+ if (minor >= GPIB_MAX_NUM_BOARDS) {
+ pr_err("gpib: invalid minor number of device file\n");
+ return -ENODEV;
+ }
+ board = &board_array[minor];
+
+ if (mutex_lock_interruptible(&board->big_gpib_mutex))
+ return -ERESTARTSYS;
+
+ dev_dbg(board->gpib_dev, "ioctl %d, interface=%s, use=%d, onl=%d\n",
+ cmd & 0xff,
+ board->interface ? board->interface->name : "",
+ board->use_count,
+ board->online);
+
+ switch (cmd) {
+ case CFCBOARDTYPE:
+ retval = board_type_ioctl(file_priv, board, arg);
+ goto done;
+ case IBONL:
+ retval = online_ioctl(board, arg);
+ goto done;
+ default:
+ break;
+ }
+ if (!board->interface) {
+ dev_err(board->gpib_dev, "no gpib board configured\n");
+ retval = -ENODEV;
+ goto done;
+ }
+ if (file_priv->got_module == 0) {
+ if (!try_module_get(board->provider_module)) {
+ dev_err(board->gpib_dev, "try_module_get() failed\n");
+ retval = -EIO;
+ goto done;
+ }
+ file_priv->got_module = 1;
+ board->use_count++;
+ }
+ switch (cmd) {
+ case CFCBASE:
+ retval = iobase_ioctl(&board->config, arg);
+ goto done;
+ case CFCIRQ:
+ retval = irq_ioctl(&board->config, arg);
+ goto done;
+ case CFCDMA:
+ retval = dma_ioctl(&board->config, arg);
+ goto done;
+ case IBAUTOSPOLL:
+ retval = autospoll_ioctl(board, file_priv, arg);
+ goto done;
+ case IBBOARD_INFO:
+ retval = board_info_ioctl(board, arg);
+ goto done;
+ case IBMUTEX:
+ /*
+ * Need to unlock board->big_gpib_mutex before potentially locking board->user_mutex
+ * to maintain consistent locking order
+ */
+ mutex_unlock(&board->big_gpib_mutex);
+ return mutex_ioctl(board, file_priv, arg);
+ case IBPAD:
+ retval = pad_ioctl(board, file_priv, arg);
+ goto done;
+ case IBSAD:
+ retval = sad_ioctl(board, file_priv, arg);
+ goto done;
+ case IBSELECT_PCI:
+ retval = select_pci_ioctl(&board->config, arg);
+ goto done;
+ case IBSELECT_DEVICE_PATH:
+ retval = select_device_path_ioctl(&board->config, arg);
+ goto done;
+ default:
+ break;
+ }
+
+ if (!board->online) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ switch (cmd) {
+ case IBEVENT:
+ retval = event_ioctl(board, arg);
+ goto done;
+ case IBCLOSEDEV:
+ retval = close_dev_ioctl(filep, board, arg);
+ goto done;
+ case IBOPENDEV:
+ retval = open_dev_ioctl(filep, board, arg);
+ goto done;
+ case IBSPOLL_BYTES:
+ retval = status_bytes_ioctl(board, arg);
+ goto done;
+ case IBWAIT:
+ retval = wait_ioctl(file_priv, board, arg);
+ if (retval == -ERESTARTSYS)
+ return retval;
+ goto done;
+ case IBLINES:
+ retval = line_status_ioctl(board, arg);
+ goto done;
+ case IBLOC:
+ board->interface->return_to_local(board);
+ retval = 0;
+ goto done;
+ default:
+ break;
+ }
+
+ spin_lock(&board->locking_pid_spinlock);
+ if (current->pid != board->locking_pid) {
+ spin_unlock(&board->locking_pid_spinlock);
+ retval = -EPERM;
+ goto done;
+ }
+ spin_unlock(&board->locking_pid_spinlock);
+
+ switch (cmd) {
+ case IB_T1_DELAY:
+ retval = t1_delay_ioctl(board, arg);
+ goto done;
+ case IBCAC:
+ retval = take_control_ioctl(board, arg);
+ goto done;
+ case IBCMD:
+ /*
+ * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex
+ * before we call them.
+ */
+ mutex_unlock(&board->big_gpib_mutex);
+ return command_ioctl(file_priv, board, arg);
+ case IBEOS:
+ retval = eos_ioctl(board, arg);
+ goto done;
+ case IBGTS:
+ retval = ibgts(board);
+ goto done;
+ case IBPPC:
+ retval = ppc_ioctl(board, arg);
+ goto done;
+ case IBPP2_SET:
+ retval = set_local_ppoll_mode_ioctl(board, arg);
+ goto done;
+ case IBPP2_GET:
+ retval = get_local_ppoll_mode_ioctl(board, arg);
+ goto done;
+ case IBQUERY_BOARD_RSV:
+ retval = query_board_rsv_ioctl(board, arg);
+ goto done;
+ case IBRD:
+ /*
+ * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex
+ * before we call them.
+ */
+ mutex_unlock(&board->big_gpib_mutex);
+ return read_ioctl(file_priv, board, arg);
+ case IBRPP:
+ retval = parallel_poll_ioctl(board, arg);
+ goto done;
+ case IBRSC:
+ retval = request_system_control_ioctl(board, arg);
+ goto done;
+ case IBRSP:
+ retval = serial_poll_ioctl(board, arg);
+ goto done;
+ case IBRSV:
+ retval = request_service_ioctl(board, arg);
+ goto done;
+ case IBRSV2:
+ retval = request_service2_ioctl(board, arg);
+ goto done;
+ case IBSIC:
+ retval = interface_clear_ioctl(board, arg);
+ goto done;
+ case IBSRE:
+ retval = remote_enable_ioctl(board, arg);
+ goto done;
+ case IBTMO:
+ retval = timeout_ioctl(board, arg);
+ goto done;
+ case IBWRT:
+ /*
+ * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex
+ * before we call them.
+ */
+ mutex_unlock(&board->big_gpib_mutex);
+ return write_ioctl(file_priv, board, arg);
+ default:
+ retval = -ENOTTY;
+ goto done;
+ }
+
+done:
+ mutex_unlock(&board->big_gpib_mutex);
+ dev_dbg(board->gpib_dev, "ioctl done status = 0x%lx\n", board->status);
+ return retval;
+}
+
+static int board_type_ioctl(struct gpib_file_private *file_priv,
+ struct gpib_board *board, unsigned long arg)
+{
+ struct list_head *list_ptr;
+ struct gpib_board_type_ioctl cmd;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (board->online)
+ return -EBUSY;
+
+ retval = copy_from_user(&cmd, (void __user *)arg,
+ sizeof(struct gpib_board_type_ioctl));
+ if (retval)
+ return -EFAULT;
+
+ for (list_ptr = registered_drivers.next; list_ptr != &registered_drivers;
+ list_ptr = list_ptr->next) {
+ struct gpib_interface_list *entry;
+
+ entry = list_entry(list_ptr, struct gpib_interface_list, list);
+ if (strcmp(entry->interface->name, cmd.name) == 0) {
+ int i;
+ int had_module = file_priv->got_module;
+
+ if (board->use_count) {
+ for (i = 0; i < board->use_count; ++i)
+ module_put(board->provider_module);
+ board->interface = NULL;
+ file_priv->got_module = 0;
+ }
+ board->interface = entry->interface;
+ board->provider_module = entry->module;
+ for (i = 0; i < board->use_count; ++i) {
+ if (!try_module_get(entry->module)) {
+ board->use_count = i;
+ return -EIO;
+ }
+ }
+ if (had_module == 0) {
+ if (!try_module_get(entry->module))
+ return -EIO;
+ ++board->use_count;
+ }
+ file_priv->got_module = 1;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board,
+ unsigned long arg)
+{
+ struct gpib_read_write_ioctl read_cmd;
+ u8 __user *userbuf;
+ unsigned long remain;
+ int end_flag = 0;
+ int retval;
+ ssize_t read_ret = 0;
+ struct gpib_descriptor *desc;
+ size_t nbytes;
+
+ retval = copy_from_user(&read_cmd, (void __user *)arg, sizeof(read_cmd));
+ if (retval)
+ return -EFAULT;
+
+ if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count)
+ return -EINVAL;
+
+ desc = handle_to_descriptor(file_priv, read_cmd.handle);
+ if (!desc)
+ return -EINVAL;
+
+ if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr)))
+ return -EFAULT;
+
+ userbuf = (u8 __user *)(unsigned long)read_cmd.buffer_ptr;
+ userbuf += read_cmd.completed_transfer_count;
+
+ remain = read_cmd.requested_transfer_count - read_cmd.completed_transfer_count;
+
+ /* Check write access to buffer */
+ if (!access_ok(userbuf, remain))
+ return -EFAULT;
+
+ atomic_set(&desc->io_in_progress, 1);
+
+ /* Read buffer loads till we fill the user supplied buffer */
+ while (remain > 0 && end_flag == 0) {
+ nbytes = 0;
+ read_ret = ibrd(board, board->buffer, (board->buffer_length < remain) ?
+ board->buffer_length : remain, &end_flag, &nbytes);
+ if (nbytes == 0)
+ break;
+ retval = copy_to_user(userbuf, board->buffer, nbytes);
+ if (retval) {
+ retval = -EFAULT;
+ break;
+ }
+ remain -= nbytes;
+ userbuf += nbytes;
+ if (read_ret < 0)
+ break;
+ }
+ read_cmd.completed_transfer_count = read_cmd.requested_transfer_count - remain;
+ read_cmd.end = end_flag;
+ /*
+ * suppress errors (for example due to timeout or interruption by device clear)
+ * if all bytes got sent. This prevents races that can occur in the various drivers
+ * if a device receives a device clear immediately after a transfer completes and
+ * the driver code wasn't careful enough to handle that case.
+ */
+ if (remain == 0 || end_flag)
+ read_ret = 0;
+ if (retval == 0)
+ retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd));
+
+ atomic_set(&desc->io_in_progress, 0);
+
+ wake_up_interruptible(&board->wait);
+ if (retval)
+ return -EFAULT;
+
+ return read_ret;
+}
+
+static int command_ioctl(struct gpib_file_private *file_priv,
+ struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_read_write_ioctl cmd;
+ u8 __user *userbuf;
+ unsigned long remain;
+ int retval;
+ int fault = 0;
+ struct gpib_descriptor *desc;
+ size_t bytes_written;
+ int no_clear_io_in_prog;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ if (cmd.completed_transfer_count > cmd.requested_transfer_count)
+ return -EINVAL;
+
+ desc = handle_to_descriptor(file_priv, cmd.handle);
+ if (!desc)
+ return -EINVAL;
+
+ userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr;
+ userbuf += cmd.completed_transfer_count;
+
+ no_clear_io_in_prog = cmd.end;
+ cmd.end = 0;
+
+ remain = cmd.requested_transfer_count - cmd.completed_transfer_count;
+
+ /* Check read access to buffer */
+ if (!access_ok(userbuf, remain))
+ return -EFAULT;
+
+ /*
+ * Write buffer loads till we empty the user supplied buffer.
+ * Call drivers at least once, even if remain is zero, in
+ * order to allow them to insure previous commands were
+ * completely finished, in the case of a restarted ioctl.
+ */
+
+ atomic_set(&desc->io_in_progress, 1);
+
+ do {
+ fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ?
+ board->buffer_length : remain);
+ if (fault) {
+ retval = -EFAULT;
+ bytes_written = 0;
+ } else {
+ retval = ibcmd(board, board->buffer, (board->buffer_length < remain) ?
+ board->buffer_length : remain, &bytes_written);
+ }
+ remain -= bytes_written;
+ userbuf += bytes_written;
+ if (retval < 0) {
+ atomic_set(&desc->io_in_progress, 0);
+
+ wake_up_interruptible(&board->wait);
+ break;
+ }
+ } while (remain > 0);
+
+ cmd.completed_transfer_count = cmd.requested_transfer_count - remain;
+
+ if (fault == 0)
+ fault = copy_to_user((void __user *)arg, &cmd, sizeof(cmd));
+
+ /*
+ * no_clear_io_in_prog (cmd.end) is true when io_in_progress should
+ * not be set to zero because the cmd in progress is the address setup
+ * operation for an async read or write. This causes CMPL not to be set
+ * in general_ibstatus until the async read or write completes.
+ */
+ if (!no_clear_io_in_prog || fault)
+ atomic_set(&desc->io_in_progress, 0);
+
+ wake_up_interruptible(&board->wait);
+ if (fault)
+ return -EFAULT;
+
+ return retval;
+}
+
+static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board,
+ unsigned long arg)
+{
+ struct gpib_read_write_ioctl write_cmd;
+ u8 __user *userbuf;
+ unsigned long remain;
+ int retval = 0;
+ int fault;
+ struct gpib_descriptor *desc;
+
+ fault = copy_from_user(&write_cmd, (void __user *)arg, sizeof(write_cmd));
+ if (fault)
+ return -EFAULT;
+
+ if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count)
+ return -EINVAL;
+
+ desc = handle_to_descriptor(file_priv, write_cmd.handle);
+ if (!desc)
+ return -EINVAL;
+
+ userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr;
+ userbuf += write_cmd.completed_transfer_count;
+
+ remain = write_cmd.requested_transfer_count - write_cmd.completed_transfer_count;
+
+ /* Check read access to buffer */
+ if (!access_ok(userbuf, remain))
+ return -EFAULT;
+
+ atomic_set(&desc->io_in_progress, 1);
+
+ /* Write buffer loads till we empty the user supplied buffer */
+ while (remain > 0) {
+ int send_eoi;
+ size_t bytes_written = 0;
+
+ send_eoi = remain <= board->buffer_length && write_cmd.end;
+ fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ?
+ board->buffer_length : remain);
+ if (fault) {
+ retval = -EFAULT;
+ break;
+ }
+ retval = ibwrt(board, board->buffer, (board->buffer_length < remain) ?
+ board->buffer_length : remain, send_eoi, &bytes_written);
+ remain -= bytes_written;
+ userbuf += bytes_written;
+ if (retval < 0)
+ break;
+ }
+ write_cmd.completed_transfer_count = write_cmd.requested_transfer_count - remain;
+ /*
+ * suppress errors (for example due to timeout or interruption by device clear)
+ * if all bytes got sent. This prevents races that can occur in the various drivers
+ * if a device receives a device clear immediately after a transfer completes and
+ * the driver code wasn't careful enough to handle that case.
+ */
+ if (remain == 0)
+ retval = 0;
+ if (fault == 0)
+ fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd));
+
+ atomic_set(&desc->io_in_progress, 0);
+
+ wake_up_interruptible(&board->wait);
+ if (fault)
+ return -EFAULT;
+
+ return retval;
+}
+
+static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_status_queue *device;
+ struct gpib_spoll_bytes_ioctl cmd;
+ int retval;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ device = get_gpib_status_queue(board, cmd.pad, cmd.sad);
+ if (!device)
+ cmd.num_bytes = 0;
+ else
+ cmd.num_bytes = num_status_bytes(device);
+
+ retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int increment_open_device_count(struct gpib_board *board, struct list_head *head,
+ unsigned int pad, int sad)
+{
+ struct list_head *list_ptr;
+ struct gpib_status_queue *device;
+
+ /*
+ * first see if address has already been opened, then increment
+ * open count
+ */
+ for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) {
+ device = list_entry(list_ptr, struct gpib_status_queue, list);
+ if (gpib_address_equal(device->pad, device->sad, pad, sad)) {
+ dev_dbg(board->gpib_dev, "incrementing open count for pad %i, sad %i\n",
+ device->pad, device->sad);
+ device->reference_count++;
+ return 0;
+ }
+ }
+
+ /* otherwise we need to allocate a new struct gpib_status_queue */
+ device = kmalloc(sizeof(struct gpib_status_queue), GFP_ATOMIC);
+ if (!device)
+ return -ENOMEM;
+ init_gpib_status_queue(device);
+ device->pad = pad;
+ device->sad = sad;
+ device->reference_count = 1;
+
+ list_add(&device->list, head);
+
+ dev_dbg(board->gpib_dev, "opened pad %i, sad %i\n", device->pad, device->sad);
+
+ return 0;
+}
+
+static int subtract_open_device_count(struct gpib_board *board, struct list_head *head,
+ unsigned int pad, int sad, unsigned int count)
+{
+ struct gpib_status_queue *device;
+ struct list_head *list_ptr;
+
+ for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) {
+ device = list_entry(list_ptr, struct gpib_status_queue, list);
+ if (gpib_address_equal(device->pad, device->sad, pad, sad)) {
+ dev_dbg(board->gpib_dev, "decrementing open count for pad %i, sad %i\n",
+ device->pad, device->sad);
+ if (count > device->reference_count) {
+ dev_err(board->gpib_dev, "bug! in %s()\n", __func__);
+ return -EINVAL;
+ }
+ device->reference_count -= count;
+ if (device->reference_count == 0) {
+ dev_dbg(board->gpib_dev, "closing pad %i, sad %i\n",
+ device->pad, device->sad);
+ list_del(list_ptr);
+ kfree(device);
+ }
+ return 0;
+ }
+ }
+ dev_err(board->gpib_dev, "bug! tried to close address that was never opened!\n");
+ return -EINVAL;
+}
+
+static inline int decrement_open_device_count(struct gpib_board *board, struct list_head *head,
+ unsigned int pad, int sad)
+{
+ return subtract_open_device_count(board, head, pad, sad, 1);
+}
+
+static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board)
+{
+ int retval = 0;
+ int i;
+
+ for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) {
+ struct gpib_descriptor *desc;
+
+ desc = file_priv->descriptors[i];
+ if (!desc)
+ continue;
+
+ if (desc->is_board == 0) {
+ retval = decrement_open_device_count(board, &board->device_list, desc->pad,
+ desc->sad);
+ if (retval < 0)
+ return retval;
+ }
+ kfree(desc);
+ file_priv->descriptors[i] = NULL;
+ }
+
+ return 0;
+}
+
+static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_open_dev_ioctl open_dev_cmd;
+ int retval;
+ struct gpib_file_private *file_priv = filep->private_data;
+ int i;
+
+ retval = copy_from_user(&open_dev_cmd, (void __user *)arg, sizeof(open_dev_cmd));
+ if (retval)
+ return -EFAULT;
+
+ if (mutex_lock_interruptible(&file_priv->descriptors_mutex))
+ return -ERESTARTSYS;
+ for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++)
+ if (!file_priv->descriptors[i])
+ break;
+ if (i == GPIB_MAX_NUM_DESCRIPTORS) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -ERANGE;
+ }
+ file_priv->descriptors[i] = kmalloc(sizeof(struct gpib_descriptor), GFP_KERNEL);
+ if (!file_priv->descriptors[i]) {
+ mutex_unlock(&file_priv->descriptors_mutex);
+ return -ENOMEM;
+ }
+ init_gpib_descriptor(file_priv->descriptors[i]);
+
+ file_priv->descriptors[i]->pad = open_dev_cmd.pad;
+ file_priv->descriptors[i]->sad = open_dev_cmd.sad;
+ file_priv->descriptors[i]->is_board = open_dev_cmd.is_board;
+ mutex_unlock(&file_priv->descriptors_mutex);
+
+ retval = increment_open_device_count(board, &board->device_list, open_dev_cmd.pad,
+ open_dev_cmd.sad);
+ if (retval < 0)
+ return retval;
+
+ /*
+ * clear stuck srq state, since we may be able to find service request on
+ * the new device
+ */
+ atomic_set(&board->stuck_srq, 0);
+
+ open_dev_cmd.handle = i;
+ retval = copy_to_user((void __user *)arg, &open_dev_cmd, sizeof(open_dev_cmd));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_close_dev_ioctl cmd;
+ struct gpib_file_private *file_priv = filep->private_data;
+ int retval;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS)
+ return -EINVAL;
+ if (!file_priv->descriptors[cmd.handle])
+ return -EINVAL;
+
+ retval = decrement_open_device_count(board, &board->device_list,
+ file_priv->descriptors[cmd.handle]->pad,
+ file_priv->descriptors[cmd.handle]->sad);
+ if (retval < 0)
+ return retval;
+
+ kfree(file_priv->descriptors[cmd.handle]);
+ file_priv->descriptors[cmd.handle] = NULL;
+
+ return 0;
+}
+
+static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_serial_poll_ioctl serial_cmd;
+ int retval;
+
+ retval = copy_from_user(&serial_cmd, (void __user *)arg, sizeof(serial_cmd));
+ if (retval)
+ return -EFAULT;
+
+ retval = get_serial_poll_byte(board, serial_cmd.pad, serial_cmd.sad, board->usec_timeout,
+ &serial_cmd.status_byte);
+ if (retval < 0)
+ return retval;
+
+ retval = copy_to_user((void __user *)arg, &serial_cmd, sizeof(serial_cmd));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board,
+ unsigned long arg)
+{
+ struct gpib_wait_ioctl wait_cmd;
+ int retval;
+ struct gpib_descriptor *desc;
+
+ retval = copy_from_user(&wait_cmd, (void __user *)arg, sizeof(wait_cmd));
+ if (retval)
+ return -EFAULT;
+
+ desc = handle_to_descriptor(file_priv, wait_cmd.handle);
+ if (!desc)
+ return -EINVAL;
+
+ retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask,
+ wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc);
+ if (retval < 0)
+ return retval;
+
+ retval = copy_to_user((void __user *)arg, &wait_cmd, sizeof(wait_cmd));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ u8 poll_byte;
+ int retval;
+
+ retval = ibrpp(board, &poll_byte);
+ if (retval < 0)
+ return retval;
+
+ retval = copy_to_user((void __user *)arg, &poll_byte, sizeof(poll_byte));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int online_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_online_ioctl online_cmd;
+ int retval;
+ void __user *init_data = NULL;
+
+ board->config.init_data = NULL;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ retval = copy_from_user(&online_cmd, (void __user *)arg, sizeof(online_cmd));
+ if (retval)
+ return -EFAULT;
+ if (online_cmd.init_data_length > 0) {
+ board->config.init_data = vmalloc(online_cmd.init_data_length);
+ if (!board->config.init_data)
+ return -ENOMEM;
+ if (WARN_ON_ONCE(sizeof(init_data) > sizeof(online_cmd.init_data_ptr)))
+ return -EFAULT;
+ init_data = (void __user *)(unsigned long)(online_cmd.init_data_ptr);
+ retval = copy_from_user(board->config.init_data, init_data,
+ online_cmd.init_data_length);
+ if (retval) {
+ vfree(board->config.init_data);
+ return -EFAULT;
+ }
+ board->config.init_data_length = online_cmd.init_data_length;
+ } else {
+ board->config.init_data = NULL;
+ board->config.init_data_length = 0;
+ }
+ if (online_cmd.online)
+ retval = ibonline(board);
+ else
+ retval = iboffline(board);
+ if (board->config.init_data) {
+ vfree(board->config.init_data);
+ board->config.init_data = NULL;
+ board->config.init_data_length = 0;
+ }
+ return retval;
+}
+
+static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ int enable;
+ int retval;
+
+ retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable));
+ if (retval)
+ return -EFAULT;
+
+ return ibsre(board, enable);
+}
+
+static int take_control_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ int synchronous;
+ int retval;
+
+ retval = copy_from_user(&synchronous, (void __user *)arg, sizeof(synchronous));
+ if (retval)
+ return -EFAULT;
+
+ return ibcac(board, synchronous, 1);
+}
+
+static int line_status_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ short lines;
+ int retval;
+
+ retval = iblines(board, &lines);
+ if (retval < 0)
+ return retval;
+
+ retval = copy_to_user((void __user *)arg, &lines, sizeof(lines));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg)
+{
+ struct gpib_pad_ioctl cmd;
+ int retval;
+ struct gpib_descriptor *desc;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ desc = handle_to_descriptor(file_priv, cmd.handle);
+ if (!desc)
+ return -EINVAL;
+
+ if (desc->is_board) {
+ retval = ibpad(board, cmd.pad);
+ if (retval < 0)
+ return retval;
+ } else {
+ retval = decrement_open_device_count(board, &board->device_list, desc->pad,
+ desc->sad);
+ if (retval < 0)
+ return retval;
+
+ desc->pad = cmd.pad;
+
+ retval = increment_open_device_count(board, &board->device_list, desc->pad,
+ desc->sad);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg)
+{
+ struct gpib_sad_ioctl cmd;
+ int retval;
+ struct gpib_descriptor *desc;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ desc = handle_to_descriptor(file_priv, cmd.handle);
+ if (!desc)
+ return -EINVAL;
+
+ if (desc->is_board) {
+ retval = ibsad(board, cmd.sad);
+ if (retval < 0)
+ return retval;
+ } else {
+ retval = decrement_open_device_count(board, &board->device_list, desc->pad,
+ desc->sad);
+ if (retval < 0)
+ return retval;
+
+ desc->sad = cmd.sad;
+
+ retval = increment_open_device_count(board, &board->device_list, desc->pad,
+ desc->sad);
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static int eos_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_eos_ioctl eos_cmd;
+ int retval;
+
+ retval = copy_from_user(&eos_cmd, (void __user *)arg, sizeof(eos_cmd));
+ if (retval)
+ return -EFAULT;
+
+ return ibeos(board, eos_cmd.eos, eos_cmd.eos_flags);
+}
+
+static int request_service_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ u8 status_byte;
+ int retval;
+
+ retval = copy_from_user(&status_byte, (void __user *)arg, sizeof(status_byte));
+ if (retval)
+ return -EFAULT;
+
+ return ibrsv2(board, status_byte, status_byte & request_service_bit);
+}
+
+static int request_service2_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_request_service2 request_service2_cmd;
+ int retval;
+
+ retval = copy_from_user(&request_service2_cmd, (void __user *)arg,
+ sizeof(struct gpib_request_service2));
+ if (retval)
+ return -EFAULT;
+
+ return ibrsv2(board, request_service2_cmd.status_byte,
+ request_service2_cmd.new_reason_for_service);
+}
+
+static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg)
+{
+ u64 base_addr;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ retval = copy_from_user(&base_addr, (void __user *)arg, sizeof(base_addr));
+ if (retval)
+ return -EFAULT;
+
+ if (WARN_ON_ONCE(sizeof(void *) > sizeof(base_addr)))
+ return -EFAULT;
+ config->ibbase = base_addr;
+
+ return 0;
+}
+
+static int irq_ioctl(struct gpib_board_config *config, unsigned long arg)
+{
+ unsigned int irq;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ retval = copy_from_user(&irq, (void __user *)arg, sizeof(irq));
+ if (retval)
+ return -EFAULT;
+
+ config->ibirq = irq;
+
+ return 0;
+}
+
+static int dma_ioctl(struct gpib_board_config *config, unsigned long arg)
+{
+ unsigned int dma_channel;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ retval = copy_from_user(&dma_channel, (void __user *)arg, sizeof(dma_channel));
+ if (retval)
+ return -EFAULT;
+
+ config->ibdma = dma_channel;
+
+ return 0;
+}
+
+static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg)
+{
+ short enable;
+ int retval;
+ struct gpib_descriptor *desc;
+
+ retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable));
+ if (retval)
+ return -EFAULT;
+
+ desc = handle_to_descriptor(file_priv, 0); /* board handle is 0 */
+
+ if (enable) {
+ if (!desc->autopoll_enabled) {
+ board->autospollers++;
+ desc->autopoll_enabled = 1;
+ }
+ retval = 0;
+ } else {
+ if (desc->autopoll_enabled) {
+ desc->autopoll_enabled = 0;
+ if (board->autospollers > 0) {
+ board->autospollers--;
+ retval = 0;
+ } else {
+ dev_err(board->gpib_dev,
+ "tried to set number of autospollers negative\n");
+ retval = -EINVAL;
+ }
+ } else {
+ dev_err(board->gpib_dev, "autopoll disable requested before enable\n");
+ retval = -EINVAL;
+ }
+ }
+ return retval;
+}
+
+static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv,
+ unsigned long arg)
+{
+ int retval, lock_mutex;
+
+ retval = copy_from_user(&lock_mutex, (void __user *)arg, sizeof(lock_mutex));
+ if (retval)
+ return -EFAULT;
+
+ if (lock_mutex) {
+ retval = mutex_lock_interruptible(&board->user_mutex);
+ if (retval)
+ return -ERESTARTSYS;
+
+ spin_lock(&board->locking_pid_spinlock);
+ board->locking_pid = current->pid;
+ spin_unlock(&board->locking_pid_spinlock);
+
+ atomic_set(&file_priv->holding_mutex, 1);
+
+ dev_dbg(board->gpib_dev, "locked board mutex\n");
+ } else {
+ spin_lock(&board->locking_pid_spinlock);
+ if (current->pid != board->locking_pid) {
+ dev_err(board->gpib_dev, "bug! pid %i tried to release mutex held by pid %i\n",
+ current->pid, board->locking_pid);
+ spin_unlock(&board->locking_pid_spinlock);
+ return -EPERM;
+ }
+ board->locking_pid = 0;
+ spin_unlock(&board->locking_pid_spinlock);
+
+ atomic_set(&file_priv->holding_mutex, 0);
+
+ mutex_unlock(&board->user_mutex);
+ dev_dbg(board->gpib_dev, "unlocked board mutex\n");
+ }
+ return 0;
+}
+
+static int timeout_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ unsigned int timeout;
+ int retval;
+
+ retval = copy_from_user(&timeout, (void __user *)arg, sizeof(timeout));
+ if (retval)
+ return -EFAULT;
+
+ board->usec_timeout = timeout;
+ dev_dbg(board->gpib_dev, "timeout set to %i usec\n", timeout);
+
+ return 0;
+}
+
+static int ppc_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_ppoll_config_ioctl cmd;
+ int retval;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ if (cmd.set_ist) {
+ board->ist = 1;
+ board->interface->parallel_poll_response(board, board->ist);
+ } else if (cmd.clear_ist) {
+ board->ist = 0;
+ board->interface->parallel_poll_response(board, board->ist);
+ }
+
+ if (cmd.config) {
+ retval = ibppc(board, cmd.config);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ short cmd;
+ int retval;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ if (!board->interface->local_parallel_poll_mode)
+ return -ENOENT;
+ board->local_ppoll_mode = cmd != 0;
+ board->interface->local_parallel_poll_mode(board, board->local_ppoll_mode);
+
+ return 0;
+}
+
+static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ short cmd;
+ int retval;
+
+ cmd = board->local_ppoll_mode;
+ retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ int status;
+ int retval;
+
+ status = board->interface->serial_poll_status(board);
+
+ retval = copy_to_user((void __user *)arg, &status, sizeof(status));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int board_info_ioctl(const struct gpib_board *board, unsigned long arg)
+{
+ struct gpib_board_info_ioctl info = { };
+ int retval;
+
+ info.pad = board->pad;
+ info.sad = board->sad;
+ info.parallel_poll_configuration = board->parallel_poll_configuration;
+ info.is_system_controller = board->master;
+ if (board->autospollers)
+ info.autopolling = 1;
+ else
+ info.autopolling = 0;
+ info.t1_delay = board->t1_nano_sec;
+ info.ist = board->ist;
+ info.no_7_bit_eos = board->interface->no_7_bit_eos;
+ retval = copy_to_user((void __user *)arg, &info, sizeof(info));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ unsigned int usec_duration;
+ int retval;
+
+ retval = copy_from_user(&usec_duration, (void __user *)arg, sizeof(usec_duration));
+ if (retval)
+ return -EFAULT;
+
+ return ibsic(board, usec_duration);
+}
+
+static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg)
+{
+ struct gpib_select_pci_ioctl selection;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ retval = copy_from_user(&selection, (void __user *)arg, sizeof(selection));
+ if (retval)
+ return -EFAULT;
+
+ config->pci_bus = selection.pci_bus;
+ config->pci_slot = selection.pci_slot;
+
+ return 0;
+}
+
+static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg)
+{
+ struct gpib_select_device_path_ioctl *selection;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ selection = vmalloc(sizeof(struct gpib_select_device_path_ioctl));
+ if (!selection)
+ return -ENOMEM;
+
+ retval = copy_from_user(selection, (void __user *)arg,
+ sizeof(struct gpib_select_device_path_ioctl));
+ if (retval) {
+ vfree(selection);
+ return -EFAULT;
+ }
+
+ selection->device_path[sizeof(selection->device_path) - 1] = '\0';
+ kfree(config->device_path);
+ config->device_path = NULL;
+ if (strlen(selection->device_path) > 0)
+ config->device_path = kstrdup(selection->device_path, GFP_KERNEL);
+
+ vfree(selection);
+ return 0;
+}
+
+unsigned int num_gpib_events(const struct gpib_event_queue *queue)
+{
+ return queue->num_events;
+}
+
+static int push_gpib_event_nolock(struct gpib_board *board, short event_type)
+{
+ struct gpib_event_queue *queue = &board->event_queue;
+ struct list_head *head = &queue->event_head;
+ struct gpib_event *event;
+ static const unsigned int max_num_events = 1024;
+ int retval;
+
+ if (num_gpib_events(queue) >= max_num_events) {
+ short lost_event;
+
+ queue->dropped_event = 1;
+ retval = pop_gpib_event_nolock(board, queue, &lost_event);
+ if (retval < 0)
+ return retval;
+ }
+
+ event = kmalloc(sizeof(struct gpib_event), GFP_ATOMIC);
+ if (!event) {
+ queue->dropped_event = 1;
+ dev_err(board->gpib_dev, "failed to allocate memory for event\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&event->list);
+ event->event_type = event_type;
+
+ list_add_tail(&event->list, head);
+
+ queue->num_events++;
+
+ dev_dbg(board->gpib_dev, "pushed event %i, %i in queue\n",
+ (int)event_type, num_gpib_events(queue));
+
+ return 0;
+}
+
+// push event onto back of event queue
+int push_gpib_event(struct gpib_board *board, short event_type)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&board->event_queue.lock, flags);
+ retval = push_gpib_event_nolock(board, event_type);
+ spin_unlock_irqrestore(&board->event_queue.lock, flags);
+
+ if (event_type == EVENT_DEV_TRG)
+ board->status |= DTAS;
+ if (event_type == EVENT_DEV_CLR)
+ board->status |= DCAS;
+
+ return retval;
+}
+EXPORT_SYMBOL(push_gpib_event);
+
+static int pop_gpib_event_nolock(struct gpib_board *board,
+ struct gpib_event_queue *queue, short *event_type)
+{
+ struct list_head *head = &queue->event_head;
+ struct list_head *front = head->next;
+ struct gpib_event *event;
+
+ if (num_gpib_events(queue) == 0) {
+ *event_type = EVENT_NONE;
+ return 0;
+ }
+
+ if (front == head)
+ return -EIO;
+
+ if (queue->dropped_event) {
+ queue->dropped_event = 0;
+ return -EPIPE;
+ }
+
+ event = list_entry(front, struct gpib_event, list);
+ *event_type = event->event_type;
+
+ list_del(front);
+ kfree(event);
+
+ queue->num_events--;
+
+ dev_dbg(board->gpib_dev, "popped event %i, %i in queue\n",
+ (int)*event_type, num_gpib_events(queue));
+
+ return 0;
+}
+
+// pop event from front of event queue
+int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ retval = pop_gpib_event_nolock(board, queue, event_type);
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return retval;
+}
+
+static int event_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ short user_event;
+ int retval;
+ short event;
+
+ retval = pop_gpib_event(board, &board->event_queue, &event);
+ if (retval < 0)
+ return retval;
+
+ user_event = event;
+
+ retval = copy_to_user((void __user *)arg, &user_event, sizeof(user_event));
+ if (retval)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ int request_control;
+ int retval;
+
+ retval = copy_from_user(&request_control, (void __user *)arg, sizeof(request_control));
+ if (retval)
+ return -EFAULT;
+
+ return ibrsc(board, request_control);
+}
+
+static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg)
+{
+ unsigned int cmd;
+ unsigned int delay;
+ int retval;
+
+ if (!board->interface->t1_delay)
+ return -ENOENT;
+
+ retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd));
+ if (retval)
+ return -EFAULT;
+
+ delay = cmd;
+
+ retval = board->interface->t1_delay(board, delay);
+ if (retval < 0)
+ return retval;
+
+ board->t1_nano_sec = retval;
+ return 0;
+}
+
+static const struct file_operations ib_fops = {
+ .owner = THIS_MODULE,
+ .llseek = NULL,
+ .unlocked_ioctl = &ibioctl,
+ .compat_ioctl = &ibioctl,
+ .open = &ibopen,
+ .release = &ibclose,
+};
+
+struct gpib_board board_array[GPIB_MAX_NUM_BOARDS];
+
+LIST_HEAD(registered_drivers);
+
+void init_gpib_descriptor(struct gpib_descriptor *desc)
+{
+ desc->pad = 0;
+ desc->sad = -1;
+ desc->is_board = 0;
+ desc->autopoll_enabled = 0;
+ atomic_set(&desc->io_in_progress, 0);
+}
+
+int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module)
+{
+ struct gpib_interface_list *entry;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->interface = interface;
+ entry->module = provider_module;
+ list_add(&entry->list, &registered_drivers);
+
+ return 0;
+}
+EXPORT_SYMBOL(gpib_register_driver);
+
+void gpib_unregister_driver(struct gpib_interface *interface)
+{
+ int i;
+ struct list_head *list_ptr;
+
+ for (i = 0; i < GPIB_MAX_NUM_BOARDS; i++) {
+ struct gpib_board *board = &board_array[i];
+
+ if (board->interface == interface) {
+ if (board->use_count > 0)
+ pr_warn("gpib: Warning: deregistered interface %s in use\n",
+ interface->name);
+ iboffline(board);
+ board->interface = NULL;
+ }
+ }
+ for (list_ptr = registered_drivers.next; list_ptr != &registered_drivers;) {
+ struct gpib_interface_list *entry;
+
+ entry = list_entry(list_ptr, struct gpib_interface_list, list);
+ list_ptr = list_ptr->next;
+ if (entry->interface == interface) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+}
+EXPORT_SYMBOL(gpib_unregister_driver);
+
+static void init_gpib_board_config(struct gpib_board_config *config)
+{
+ memset(config, 0, sizeof(struct gpib_board_config));
+ config->pci_bus = -1;
+ config->pci_slot = -1;
+}
+
+void init_gpib_board(struct gpib_board *board)
+{
+ board->interface = NULL;
+ board->provider_module = NULL;
+ board->buffer = NULL;
+ board->buffer_length = 0;
+ board->status = 0;
+ init_waitqueue_head(&board->wait);
+ mutex_init(&board->user_mutex);
+ mutex_init(&board->big_gpib_mutex);
+ board->locking_pid = 0;
+ spin_lock_init(&board->locking_pid_spinlock);
+ spin_lock_init(&board->spinlock);
+ timer_setup(&board->timer, NULL, 0);
+ board->dev = NULL;
+ board->gpib_dev = NULL;
+ init_gpib_board_config(&board->config);
+ board->private_data = NULL;
+ board->use_count = 0;
+ INIT_LIST_HEAD(&board->device_list);
+ board->pad = 0;
+ board->sad = -1;
+ board->usec_timeout = 3000000;
+ board->parallel_poll_configuration = 0;
+ board->online = 0;
+ board->autospollers = 0;
+ board->autospoll_task = NULL;
+ init_event_queue(&board->event_queue);
+ board->minor = -1;
+ init_gpib_pseudo_irq(&board->pseudo_irq);
+ board->master = 1;
+ atomic_set(&board->stuck_srq, 0);
+ board->local_ppoll_mode = 0;
+}
+
+int gpib_allocate_board(struct gpib_board *board)
+{
+ if (!board->buffer) {
+ board->buffer_length = 0x4000;
+ board->buffer = vmalloc(board->buffer_length);
+ if (!board->buffer) {
+ board->buffer_length = 0;
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+void gpib_deallocate_board(struct gpib_board *board)
+{
+ short dummy;
+
+ if (board->buffer) {
+ vfree(board->buffer);
+ board->buffer = NULL;
+ board->buffer_length = 0;
+ }
+ while (num_gpib_events(&board->event_queue))
+ pop_gpib_event(board, &board->event_queue, &dummy);
+}
+
+static void init_board_array(struct gpib_board *board_array, unsigned int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ init_gpib_board(&board_array[i]);
+ board_array[i].minor = i;
+ }
+}
+
+void init_gpib_status_queue(struct gpib_status_queue *device)
+{
+ INIT_LIST_HEAD(&device->list);
+ INIT_LIST_HEAD(&device->status_bytes);
+ device->num_status_bytes = 0;
+ device->reference_count = 0;
+ device->dropped_byte = 0;
+}
+
+static struct class *gpib_class;
+
+static int __init gpib_common_init_module(void)
+{
+ int i;
+
+ pr_info("GPIB core driver\n");
+ init_board_array(board_array, GPIB_MAX_NUM_BOARDS);
+ if (register_chrdev(GPIB_CODE, "gpib", &ib_fops)) {
+ pr_err("gpib: can't get major %d\n", GPIB_CODE);
+ return -EIO;
+ }
+ gpib_class = class_create("gpib_common");
+ if (IS_ERR(gpib_class)) {
+ pr_err("gpib: failed to create gpib class\n");
+ unregister_chrdev(GPIB_CODE, "gpib");
+ return PTR_ERR(gpib_class);
+ }
+ for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i)
+ board_array[i].gpib_dev = device_create(gpib_class, NULL,
+ MKDEV(GPIB_CODE, i), NULL, "gpib%i", i);
+
+ return 0;
+}
+
+static void __exit gpib_common_exit_module(void)
+{
+ int i;
+
+ for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i)
+ device_destroy(gpib_class, MKDEV(GPIB_CODE, i));
+
+ class_destroy(gpib_class);
+ unregister_chrdev(GPIB_CODE, "gpib");
+}
+
+int gpib_match_device_path(struct device *dev, const char *device_path_in)
+{
+ if (device_path_in) {
+ char *device_path;
+
+ device_path = kobject_get_path(&dev->kobj, GFP_KERNEL);
+ if (!device_path) {
+ dev_err(dev, "kobject_get_path returned NULL.");
+ return 0;
+ }
+ if (strcmp(device_path_in, device_path) != 0) {
+ kfree(device_path);
+ return 0;
+ }
+ kfree(device_path);
+ }
+ return 1;
+}
+EXPORT_SYMBOL(gpib_match_device_path);
+
+struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id,
+ unsigned int device_id, struct pci_dev *from)
+{
+ struct pci_dev *pci_device = from;
+
+ while ((pci_device = pci_get_device(vendor_id, device_id, pci_device))) {
+ if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number)
+ continue;
+ if (config->pci_slot >= 0 && config->pci_slot !=
+ PCI_SLOT(pci_device->devfn))
+ continue;
+ if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0)
+ continue;
+ return pci_device;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(gpib_pci_get_device);
+
+struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id,
+ unsigned int device_id, unsigned int ss_vendor,
+ unsigned int ss_device,
+ struct pci_dev *from)
+{
+ struct pci_dev *pci_device = from;
+
+ while ((pci_device = pci_get_subsys(vendor_id, device_id,
+ ss_vendor, ss_device, pci_device))) {
+ if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number)
+ continue;
+ if (config->pci_slot >= 0 && config->pci_slot !=
+ PCI_SLOT(pci_device->devfn))
+ continue;
+ if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0)
+ continue;
+ return pci_device;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(gpib_pci_get_subsys);
+
+module_init(gpib_common_init_module);
+module_exit(gpib_common_exit_module);
+
diff --git a/drivers/gpib/common/iblib.c b/drivers/gpib/common/iblib.c
new file mode 100644
index 000000000000..7cbb6a467177
--- /dev/null
+++ b/drivers/gpib/common/iblib.c
@@ -0,0 +1,717 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "ibsys.h"
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+
+/*
+ * IBCAC
+ * Return to the controller active state from the
+ * controller standby state, i.e., turn ATN on. Note
+ * that in order to enter the controller active state
+ * from the controller idle state, ibsic must be called.
+ * If sync is non-zero, attempt to take control synchronously.
+ * If fallback_to_async is non-zero, try to take control asynchronously
+ * if synchronous attempt fails.
+ */
+int ibcac(struct gpib_board *board, int sync, int fallback_to_async)
+{
+ int status = ibstatus(board);
+ int retval;
+
+ if ((status & CIC) == 0)
+ return -EINVAL;
+
+ if (status & ATN)
+ return 0;
+
+ if (sync && (status & LACS) == 0)
+ /*
+ * tcs (take control synchronously) can only possibly work when
+ * controller is listener. Error code also needs to be -ETIMEDOUT
+ * or it will giveout without doing fallback.
+ */
+ retval = -ETIMEDOUT;
+ else
+ retval = board->interface->take_control(board, sync);
+
+ if (retval < 0 && fallback_to_async) {
+ if (sync && retval == -ETIMEDOUT)
+ retval = board->interface->take_control(board, 0);
+ }
+ board->interface->update_status(board, 0);
+
+ return retval;
+}
+
+/*
+ * After ATN is asserted, it should cause any connected devices
+ * to start listening for command bytes and leave acceptor idle state.
+ * So if ATN is asserted and neither NDAC or NRFD are asserted,
+ * then there are no devices and ibcmd should error out immediately.
+ * Some gpib hardware sees itself asserting NDAC/NRFD when it
+ * is controller in charge, in which case this check will
+ * do nothing useful (but shouldn't cause any harm either).
+ * Drivers that don't need this check (ni_usb for example) may
+ * set the skip_check_for_command_acceptors flag in their
+ * gpib_interface_struct to avoid useless overhead.
+ */
+static int check_for_command_acceptors(struct gpib_board *board)
+{
+ int lines;
+
+ if (board->interface->skip_check_for_command_acceptors)
+ return 0;
+ if (!board->interface->line_status)
+ return 0;
+
+ udelay(2); // allow time for devices to respond to ATN if it was just asserted
+
+ lines = board->interface->line_status(board);
+ if (lines < 0)
+ return lines;
+
+ if ((lines & VALID_NRFD) && (lines & VALID_NDAC)) {
+ if ((lines & BUS_NRFD) == 0 && (lines & BUS_NDAC) == 0)
+ return -ENOTCONN;
+ }
+
+ return 0;
+}
+
+/*
+ * IBCMD
+ * Write cnt command bytes from buf to the GPIB. The
+ * command operation terminates only on I/O complete.
+ *
+ * NOTE:
+ * 1. Prior to beginning the command, the interface is
+ * placed in the controller active state.
+ * 2. Before calling ibcmd for the first time, ibsic
+ * must be called to initialize the GPIB and enable
+ * the interface to leave the controller idle state.
+ */
+int ibcmd(struct gpib_board *board, u8 *buf, size_t length, size_t *bytes_written)
+{
+ ssize_t ret = 0;
+ int status;
+
+ *bytes_written = 0;
+
+ status = ibstatus(board);
+
+ if ((status & CIC) == 0)
+ return -EINVAL;
+
+ os_start_timer(board, board->usec_timeout);
+
+ ret = ibcac(board, 1, 1);
+ if (ret == 0) {
+ ret = check_for_command_acceptors(board);
+ if (ret == 0)
+ ret = board->interface->command(board, buf, length, bytes_written);
+ }
+
+ os_remove_timer(board);
+
+ if (io_timed_out(board))
+ ret = -ETIMEDOUT;
+
+ return ret;
+}
+
+/*
+ * IBGTS
+ * Go to the controller standby state from the controller
+ * active state, i.e., turn ATN off.
+ */
+
+int ibgts(struct gpib_board *board)
+{
+ int status = ibstatus(board);
+ int retval;
+
+ if ((status & CIC) == 0)
+ return -EINVAL;
+
+ retval = board->interface->go_to_standby(board); /* go to standby */
+
+ board->interface->update_status(board, 0);
+
+ return retval;
+}
+
+static int autospoll_wait_should_wake_up(struct gpib_board *board)
+{
+ int retval;
+
+ mutex_lock(&board->big_gpib_mutex);
+
+ retval = board->master && board->autospollers > 0 &&
+ !atomic_read(&board->stuck_srq) &&
+ test_and_clear_bit(SRQI_NUM, &board->status);
+
+ mutex_unlock(&board->big_gpib_mutex);
+ return retval;
+}
+
+static int autospoll_thread(void *board_void)
+{
+ struct gpib_board *board = board_void;
+ int retval = 0;
+
+ dev_dbg(board->gpib_dev, "entering autospoll thread\n");
+
+ while (1) {
+ wait_event_interruptible(board->wait,
+ kthread_should_stop() ||
+ autospoll_wait_should_wake_up(board));
+ dev_dbg(board->gpib_dev, "autospoll wait satisfied\n");
+ if (kthread_should_stop())
+ break;
+
+ mutex_lock(&board->big_gpib_mutex);
+ /* make sure we are still good after we have lock */
+ if (board->autospollers <= 0 || board->master == 0) {
+ mutex_unlock(&board->big_gpib_mutex);
+ continue;
+ }
+ mutex_unlock(&board->big_gpib_mutex);
+
+ if (try_module_get(board->provider_module)) {
+ retval = autopoll_all_devices(board);
+ module_put(board->provider_module);
+ } else {
+ dev_err(board->gpib_dev, "try_module_get() failed!\n");
+ }
+ if (retval <= 0) {
+ dev_err(board->gpib_dev, "stuck SRQ\n");
+
+ atomic_set(&board->stuck_srq, 1); // XXX could be better
+ set_bit(SRQI_NUM, &board->status);
+ }
+ }
+ return retval;
+}
+
+int ibonline(struct gpib_board *board)
+{
+ int retval;
+
+ if (board->online)
+ return -EBUSY;
+ if (!board->interface)
+ return -ENODEV;
+ retval = gpib_allocate_board(board);
+ if (retval < 0)
+ return retval;
+
+ board->dev = NULL;
+ board->local_ppoll_mode = 0;
+ retval = board->interface->attach(board, &board->config);
+ if (retval < 0) {
+ board->interface->detach(board);
+ return retval;
+ }
+ /*
+ * nios2nommu on 2.6.11 uclinux kernel has weird problems
+ * with autospoll thread causing huge slowdowns
+ */
+#ifndef CONFIG_NIOS2
+ board->autospoll_task = kthread_run(&autospoll_thread, board,
+ "gpib%d_autospoll_kthread", board->minor);
+ retval = IS_ERR(board->autospoll_task);
+ if (retval) {
+ dev_err(board->gpib_dev, "failed to create autospoll thread\n");
+ board->interface->detach(board);
+ return retval;
+ }
+#endif
+ board->online = 1;
+ dev_dbg(board->gpib_dev, "board online\n");
+
+ return 0;
+}
+
+/* XXX need to make sure board is generally not in use (grab board lock?) */
+int iboffline(struct gpib_board *board)
+{
+ int retval;
+
+ if (board->online == 0)
+ return 0;
+ if (!board->interface)
+ return -ENODEV;
+
+ if (board->autospoll_task && !IS_ERR(board->autospoll_task)) {
+ retval = kthread_stop(board->autospoll_task);
+ if (retval)
+ dev_err(board->gpib_dev, "kthread_stop returned %i\n", retval);
+ board->autospoll_task = NULL;
+ }
+
+ board->interface->detach(board);
+ gpib_deallocate_board(board);
+ board->online = 0;
+ dev_dbg(board->gpib_dev, "board offline\n");
+
+ return 0;
+}
+
+/*
+ * IBLINES
+ * Poll the GPIB control lines and return their status in buf.
+ *
+ * LSB (bits 0-7) - VALID lines mask (lines that can be monitored).
+ * Next LSB (bits 8-15) - STATUS lines mask (lines that are currently set).
+ *
+ */
+int iblines(const struct gpib_board *board, short *lines)
+{
+ int retval;
+
+ *lines = 0;
+ if (!board->interface->line_status)
+ return 0;
+ retval = board->interface->line_status(board);
+ if (retval < 0)
+ return retval;
+ *lines = retval;
+ return 0;
+}
+
+/*
+ * IBRD
+ * Read up to 'length' bytes of data from the GPIB into buf. End
+ * on detection of END (EOI and or EOS) and set 'end_flag'.
+ *
+ * NOTE:
+ * 1. The interface is placed in the controller standby
+ * state prior to beginning the read.
+ * 2. Prior to calling ibrd, the intended devices as well
+ * as the interface board itself must be addressed by
+ * calling ibcmd.
+ */
+
+int ibrd(struct gpib_board *board, u8 *buf, size_t length, int *end_flag, size_t *nbytes)
+{
+ ssize_t ret = 0;
+ int retval;
+ size_t bytes_read;
+
+ *nbytes = 0;
+ *end_flag = 0;
+ if (length == 0)
+ return 0;
+
+ if (board->master) {
+ retval = ibgts(board);
+ if (retval < 0)
+ return retval;
+ }
+ /*
+ * XXX resetting timer here could cause timeouts take longer than they should,
+ * since read_ioctl calls this
+ * function in a loop, there is probably a similar problem with writes/commands
+ */
+ os_start_timer(board, board->usec_timeout);
+
+ do {
+ ret = board->interface->read(board, buf, length - *nbytes, end_flag, &bytes_read);
+ if (ret < 0)
+ goto ibrd_out;
+
+ buf += bytes_read;
+ *nbytes += bytes_read;
+ if (need_resched())
+ schedule();
+ } while (ret == 0 && *nbytes > 0 && *nbytes < length && *end_flag == 0);
+ibrd_out:
+ os_remove_timer(board);
+
+ return ret;
+}
+
+/*
+ * IBRPP
+ * Conduct a parallel poll and return the byte in buf.
+ *
+ * NOTE:
+ * 1. Prior to conducting the poll the interface is placed
+ * in the controller active state.
+ */
+int ibrpp(struct gpib_board *board, u8 *result)
+{
+ int retval = 0;
+
+ os_start_timer(board, board->usec_timeout);
+ retval = ibcac(board, 1, 1);
+ if (retval)
+ return -1;
+
+ retval = board->interface->parallel_poll(board, result);
+
+ os_remove_timer(board);
+ return retval;
+}
+
+int ibppc(struct gpib_board *board, u8 configuration)
+{
+ configuration &= 0x1f;
+ board->interface->parallel_poll_configure(board, configuration);
+ board->parallel_poll_configuration = configuration;
+
+ return 0;
+}
+
+int ibrsv2(struct gpib_board *board, u8 status_byte, int new_reason_for_service)
+{
+ int board_status = ibstatus(board);
+ const unsigned int MSS = status_byte & request_service_bit;
+
+ if ((board_status & CIC))
+ return -EINVAL;
+
+ if (MSS == 0 && new_reason_for_service)
+ return -EINVAL;
+
+ if (board->interface->serial_poll_response2) {
+ board->interface->serial_poll_response2(board, status_byte, new_reason_for_service);
+ // fall back on simpler serial_poll_response if the behavior would be the same
+ } else if (board->interface->serial_poll_response &&
+ (MSS == 0 || (MSS && new_reason_for_service))) {
+ board->interface->serial_poll_response(board, status_byte);
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+ * IBSIC
+ * Send IFC for at least 100 microseconds.
+ *
+ * NOTE:
+ * 1. Ibsic must be called prior to the first call to
+ * ibcmd in order to initialize the bus and enable the
+ * interface to leave the controller idle state.
+ */
+int ibsic(struct gpib_board *board, unsigned int usec_duration)
+{
+ if (board->master == 0)
+ return -EINVAL;
+
+ if (usec_duration < 100)
+ usec_duration = 100;
+ if (usec_duration > 1000)
+ usec_duration = 1000;
+
+ dev_dbg(board->gpib_dev, "sending interface clear, delay = %ius\n", usec_duration);
+ board->interface->interface_clear(board, 1);
+ udelay(usec_duration);
+ board->interface->interface_clear(board, 0);
+
+ return 0;
+}
+
+int ibrsc(struct gpib_board *board, int request_control)
+{
+ int retval;
+
+ if (!board->interface->request_system_control)
+ return -EPERM;
+
+ retval = board->interface->request_system_control(board, request_control);
+
+ if (retval)
+ return retval;
+
+ board->master = request_control != 0;
+
+ return 0;
+}
+
+/*
+ * IBSRE
+ * Send REN true if v is non-zero or false if v is zero.
+ */
+int ibsre(struct gpib_board *board, int enable)
+{
+ if (board->master == 0)
+ return -EINVAL;
+
+ board->interface->remote_enable(board, enable); /* set or clear REN */
+ if (!enable)
+ usleep_range(100, 150);
+
+ return 0;
+}
+
+/*
+ * IBPAD
+ * change the GPIB address of the interface board. The address
+ * must be 0 through 30. ibonl resets the address to PAD.
+ */
+int ibpad(struct gpib_board *board, unsigned int addr)
+{
+ if (addr > MAX_GPIB_PRIMARY_ADDRESS)
+ return -EINVAL;
+
+ board->pad = addr;
+ if (board->online)
+ board->interface->primary_address(board, board->pad);
+ dev_dbg(board->gpib_dev, "set primary addr to %i\n", board->pad);
+ return 0;
+}
+
+/*
+ * IBSAD
+ * change the secondary GPIB address of the interface board.
+ * The address must be 0 through 30, or negative disables. ibonl resets the
+ * address to SAD.
+ */
+int ibsad(struct gpib_board *board, int addr)
+{
+ if (addr > MAX_GPIB_SECONDARY_ADDRESS)
+ return -EINVAL;
+ board->sad = addr;
+ if (board->online) {
+ if (board->sad >= 0)
+ board->interface->secondary_address(board, board->sad, 1);
+ else
+ board->interface->secondary_address(board, 0, 0);
+ }
+ dev_dbg(board->gpib_dev, "set secondary addr to %i\n", board->sad);
+
+ return 0;
+}
+
+/*
+ * IBEOS
+ * Set the end-of-string modes for I/O operations to v.
+ *
+ */
+int ibeos(struct gpib_board *board, int eos, int eosflags)
+{
+ int retval;
+
+ if (eosflags & ~EOS_MASK)
+ return -EINVAL;
+ if (eosflags & REOS) {
+ retval = board->interface->enable_eos(board, eos, eosflags & BIN);
+ } else {
+ board->interface->disable_eos(board);
+ retval = 0;
+ }
+ return retval;
+}
+
+int ibstatus(struct gpib_board *board)
+{
+ return general_ibstatus(board, NULL, 0, 0, NULL);
+}
+
+int general_ibstatus(struct gpib_board *board, const struct gpib_status_queue *device,
+ int clear_mask, int set_mask, struct gpib_descriptor *desc)
+{
+ int status = 0;
+ short line_status;
+
+ if (board->private_data) {
+ status = board->interface->update_status(board, clear_mask);
+ /*
+ * XXX should probably stop having drivers use TIMO bit in
+ * board->status to avoid confusion
+ */
+ status &= ~TIMO;
+ /* get real SRQI status if we can */
+ if (iblines(board, &line_status) == 0) {
+ if ((line_status & VALID_SRQ)) {
+ if ((line_status & BUS_SRQ))
+ status |= SRQI;
+ else
+ status &= ~SRQI;
+ }
+ }
+ }
+ if (device)
+ if (num_status_bytes(device))
+ status |= RQS;
+
+ if (desc) {
+ if (set_mask & CMPL)
+ atomic_set(&desc->io_in_progress, 0);
+ else if (clear_mask & CMPL)
+ atomic_set(&desc->io_in_progress, 1);
+
+ if (atomic_read(&desc->io_in_progress))
+ status &= ~CMPL;
+ else
+ status |= CMPL;
+ }
+ if (num_gpib_events(&board->event_queue))
+ status |= EVENT;
+ else
+ status &= ~EVENT;
+
+ return status;
+}
+
+struct wait_info {
+ struct gpib_board *board;
+ struct timer_list timer;
+ int timed_out;
+ unsigned long usec_timeout;
+};
+
+static void wait_timeout(struct timer_list *t)
+{
+ struct wait_info *winfo = timer_container_of(winfo, t, timer);
+
+ winfo->timed_out = 1;
+ wake_up_interruptible(&winfo->board->wait);
+}
+
+static void init_wait_info(struct wait_info *winfo)
+{
+ winfo->board = NULL;
+ winfo->timed_out = 0;
+ timer_setup_on_stack(&winfo->timer, wait_timeout, 0);
+}
+
+static int wait_satisfied(struct wait_info *winfo, struct gpib_status_queue *status_queue,
+ int wait_mask, int *status, struct gpib_descriptor *desc)
+{
+ struct gpib_board *board = winfo->board;
+ int temp_status;
+
+ if (mutex_lock_interruptible(&board->big_gpib_mutex))
+ return -ERESTARTSYS;
+
+ temp_status = general_ibstatus(board, status_queue, 0, 0, desc);
+
+ mutex_unlock(&board->big_gpib_mutex);
+
+ if (winfo->timed_out)
+ temp_status |= TIMO;
+ else
+ temp_status &= ~TIMO;
+ if (wait_mask & temp_status) {
+ *status = temp_status;
+ return 1;
+ }
+// XXX does wait for END work?
+ return 0;
+}
+
+/* install timer interrupt handler */
+static void start_wait_timer(struct wait_info *winfo)
+/* Starts the timeout task */
+{
+ winfo->timed_out = 0;
+
+ if (winfo->usec_timeout > 0)
+ mod_timer(&winfo->timer, jiffies + usec_to_jiffies(winfo->usec_timeout));
+}
+
+static void remove_wait_timer(struct wait_info *winfo)
+{
+ timer_delete_sync(&winfo->timer);
+ timer_destroy_on_stack(&winfo->timer);
+}
+
+/*
+ * IBWAIT
+ * Check or wait for a GPIB event to occur. The mask argument
+ * is a bit vector corresponding to the status bit vector. It
+ * has a bit set for each condition which can terminate the wait
+ * If the mask is 0 then
+ * no condition is waited for.
+ */
+int ibwait(struct gpib_board *board, int wait_mask, int clear_mask, int set_mask,
+ int *status, unsigned long usec_timeout, struct gpib_descriptor *desc)
+{
+ int retval = 0;
+ struct gpib_status_queue *status_queue;
+ struct wait_info winfo;
+
+ if (desc->is_board)
+ status_queue = NULL;
+ else
+ status_queue = get_gpib_status_queue(board, desc->pad, desc->sad);
+
+ if (wait_mask == 0) {
+ *status = general_ibstatus(board, status_queue, clear_mask, set_mask, desc);
+ return 0;
+ }
+
+ mutex_unlock(&board->big_gpib_mutex);
+
+ init_wait_info(&winfo);
+ winfo.board = board;
+ winfo.usec_timeout = usec_timeout;
+ start_wait_timer(&winfo);
+
+ if (wait_event_interruptible(board->wait, wait_satisfied(&winfo, status_queue,
+ wait_mask, status, desc))) {
+ dev_dbg(board->gpib_dev, "wait interrupted\n");
+ retval = -ERESTARTSYS;
+ }
+ remove_wait_timer(&winfo);
+
+ if (retval)
+ return retval;
+ if (mutex_lock_interruptible(&board->big_gpib_mutex))
+ return -ERESTARTSYS;
+
+ /* make sure we only clear status bits that we are reporting */
+ if (*status & clear_mask || set_mask)
+ general_ibstatus(board, status_queue, *status & clear_mask, set_mask, NULL);
+
+ return 0;
+}
+
+/*
+ * IBWRT
+ * Write cnt bytes of data from buf to the GPIB. The write
+ * operation terminates only on I/O complete.
+ *
+ * NOTE:
+ * 1. Prior to beginning the write, the interface is
+ * placed in the controller standby state.
+ * 2. Prior to calling ibwrt, the intended devices as
+ * well as the interface board itself must be
+ * addressed by calling ibcmd.
+ */
+int ibwrt(struct gpib_board *board, u8 *buf, size_t cnt, int send_eoi, size_t *bytes_written)
+{
+ int ret = 0;
+ int retval;
+
+ if (cnt == 0)
+ return 0;
+
+ if (board->master) {
+ retval = ibgts(board);
+ if (retval < 0)
+ return retval;
+ }
+ os_start_timer(board, board->usec_timeout);
+ ret = board->interface->write(board, buf, cnt, send_eoi, bytes_written);
+
+ if (io_timed_out(board))
+ ret = -ETIMEDOUT;
+
+ os_remove_timer(board);
+
+ return ret;
+}
+
diff --git a/drivers/gpib/common/ibsys.h b/drivers/gpib/common/ibsys.h
new file mode 100644
index 000000000000..e5a148f513a8
--- /dev/null
+++ b/drivers/gpib/common/ibsys.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include "gpibP.h"
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#define MAX_GPIB_PRIMARY_ADDRESS 30
+#define MAX_GPIB_SECONDARY_ADDRESS 31
+
+int gpib_allocate_board(struct gpib_board *board);
+void gpib_deallocate_board(struct gpib_board *board);
+
+unsigned int num_status_bytes(const struct gpib_status_queue *dev);
+int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device,
+ u8 poll_byte);
+int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device,
+ u8 *poll_byte);
+struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board,
+ unsigned int pad, int sad);
+int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad,
+ unsigned int usec_timeout, u8 *poll_byte);
+int autopoll_all_devices(struct gpib_board *board);
diff --git a/drivers/gpib/eastwood/Makefile b/drivers/gpib/eastwood/Makefile
new file mode 100644
index 000000000000..384825195f77
--- /dev/null
+++ b/drivers/gpib/eastwood/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_GPIB_FLUKE) += fluke_gpib.o
+
diff --git a/drivers/gpib/eastwood/fluke_gpib.c b/drivers/gpib/eastwood/fluke_gpib.c
new file mode 100644
index 000000000000..3ae848e3f738
--- /dev/null
+++ b/drivers/gpib/eastwood/fluke_gpib.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * GPIB Driver for Fluke cda devices. Basically, its a driver for a (bugfixed)
+ * cb7210 connected to channel 0 of a pl330 dma controller.
+ * Author: Frank Mori Hess <fmh6jj@gmail.com>
+ * copyright: (C) 2006, 2010, 2015 Fluke Corporation
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "fluke_gpib.h"
+
+#include "gpibP.h"
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB Driver for Fluke cda devices");
+
+static int fluke_attach_holdoff_all(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static int fluke_attach_holdoff_end(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static void fluke_detach(struct gpib_board *board);
+static int fluke_config_dma(struct gpib_board *board, int output);
+static irqreturn_t fluke_gpib_internal_interrupt(struct gpib_board *board);
+
+static struct platform_device *fluke_gpib_pdev;
+
+static u8 fluke_locking_read_byte(struct nec7210_priv *nec_priv, unsigned int register_number)
+{
+ u8 retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nec_priv->register_page_lock, flags);
+ retval = fluke_read_byte_nolock(nec_priv, register_number);
+ spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
+ return retval;
+}
+
+static void fluke_locking_write_byte(struct nec7210_priv *nec_priv, u8 byte,
+ unsigned int register_number)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&nec_priv->register_page_lock, flags);
+ fluke_write_byte_nolock(nec_priv, byte, register_number);
+ spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
+}
+
+// wrappers for interface functions
+static int fluke_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+}
+
+static int fluke_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int fluke_command(struct gpib_board *board, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int fluke_take_control(struct gpib_board *board, int synchronous)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int fluke_go_to_standby(struct gpib_board *board)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int fluke_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct fluke_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ return nec7210_request_system_control(board, nec_priv, request_control);
+}
+
+static void fluke_interface_clear(struct gpib_board *board, int assert)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void fluke_remote_enable(struct gpib_board *board, int enable)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int fluke_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void fluke_disable_eos(struct gpib_board *board)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int fluke_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
+}
+
+static int fluke_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int fluke_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int fluke_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
+}
+
+static void fluke_parallel_poll_configure(struct gpib_board *board, u8 configuration)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration);
+}
+
+static void fluke_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+static void fluke_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
+}
+
+static u8 fluke_serial_poll_status(struct gpib_board *board)
+{
+ struct fluke_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static void fluke_return_to_local(struct gpib_board *board)
+{
+ struct fluke_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ write_byte(nec_priv, AUX_RTL2, AUXMR);
+ udelay(1);
+ write_byte(nec_priv, AUX_RTL, AUXMR);
+}
+
+static int fluke_line_status(const struct gpib_board *board)
+{
+ int status = VALID_ALL;
+ int bsr_bits;
+ struct fluke_priv *e_priv;
+
+ e_priv = board->private_data;
+
+ bsr_bits = fluke_paged_read_byte(e_priv, BUS_STATUS, BUS_STATUS_PAGE);
+
+ if ((bsr_bits & BSR_REN_BIT) == 0)
+ status |= BUS_REN;
+ if ((bsr_bits & BSR_IFC_BIT) == 0)
+ status |= BUS_IFC;
+ if ((bsr_bits & BSR_SRQ_BIT) == 0)
+ status |= BUS_SRQ;
+ if ((bsr_bits & BSR_EOI_BIT) == 0)
+ status |= BUS_EOI;
+ if ((bsr_bits & BSR_NRFD_BIT) == 0)
+ status |= BUS_NRFD;
+ if ((bsr_bits & BSR_NDAC_BIT) == 0)
+ status |= BUS_NDAC;
+ if ((bsr_bits & BSR_DAV_BIT) == 0)
+ status |= BUS_DAV;
+ if ((bsr_bits & BSR_ATN_BIT) == 0)
+ status |= BUS_ATN;
+
+ return status;
+}
+
+static int fluke_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned int retval;
+
+ retval = nec7210_t1_delay(board, nec_priv, nano_sec);
+
+ if (nano_sec <= 350) {
+ write_byte(nec_priv, AUX_HI_SPEED, AUXMR);
+ retval = 350;
+ } else {
+ write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
+ }
+ return retval;
+}
+
+static int lacs_or_read_ready(struct gpib_board *board)
+{
+ const struct fluke_priv *e_priv = board->private_data;
+ const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = test_bit(LACS_NUM, &board->status) || test_bit(READ_READY_BN, &nec_priv->state);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+/*
+ * Wait until it is possible for a read to do something useful. This
+ * is not essential, it only exists to prevent RFD holdoff from being released pointlessly.
+ */
+static int wait_for_read(struct gpib_board *board)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ lacs_or_read_ready(board) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ return retval;
+}
+
+/*
+ * Check if the SH state machine is in SGNS. We check twice since there is a very small chance
+ * we could be blowing through SGNS from SIDS to SDYS if there is already a
+ * byte available in the handshake state machine. We are interested
+ * in the case where the handshake is stuck in SGNS due to no byte being
+ * available to the chip (and thus we can be confident a dma transfer will
+ * result in at least one byte making it into the chip). This matters
+ * because we want to be confident before sending a "send eoi" auxilary
+ * command that we will be able to also put the associated data byte
+ * in the chip before any potential timeout.
+ */
+static int source_handshake_is_sgns(struct fluke_priv *e_priv)
+{
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ if ((fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) &
+ SOURCE_HANDSHAKE_MASK) != SOURCE_HANDSHAKE_SGNS_BITS) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int source_handshake_is_sids_or_sgns(struct fluke_priv *e_priv)
+{
+ unsigned int source_handshake_bits;
+
+ source_handshake_bits = fluke_paged_read_byte(e_priv, STATE1_REG, STATE1_PAGE) &
+ SOURCE_HANDSHAKE_MASK;
+
+ return (source_handshake_bits == SOURCE_HANDSHAKE_SGNS_BITS) ||
+ (source_handshake_bits == SOURCE_HANDSHAKE_SIDS_BITS);
+}
+
+/*
+ * Wait until the gpib chip is ready to accept a data out byte.
+ * If the chip is SGNS it is probably waiting for a a byte to
+ * be written to it.
+ */
+static int wait_for_data_out_ready(struct gpib_board *board)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ (test_bit(TACS_NUM, &board->status) &&
+ source_handshake_is_sgns(e_priv)) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ return retval;
+}
+
+static int wait_for_sids_or_sgns(struct gpib_board *board)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ source_handshake_is_sids_or_sgns(e_priv) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ return retval;
+}
+
+static void fluke_dma_callback(void *arg)
+{
+ struct gpib_board *board = arg;
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE);
+ wake_up_interruptible(&board->wait);
+
+ fluke_gpib_internal_interrupt(board);
+ clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state);
+ clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+static int fluke_dma_write(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+ int retval = 0;
+ dma_addr_t address;
+ struct dma_async_tx_descriptor *tx_desc;
+
+ *bytes_written = 0;
+
+ if (WARN_ON_ONCE(length > e_priv->dma_buffer_size))
+ return -EFAULT;
+ dmaengine_terminate_all(e_priv->dma_channel);
+ // write-clear counter
+ writel(0x0, e_priv->write_transfer_counter);
+
+ memcpy(e_priv->dma_buffer, buffer, length);
+ address = dma_map_single(board->dev, e_priv->dma_buffer,
+ length, DMA_TO_DEVICE);
+ /* program dma controller */
+ retval = fluke_config_dma(board, 1);
+ if (retval)
+ goto cleanup;
+
+ tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx_desc) {
+ dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n");
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ tx_desc->callback = fluke_dma_callback;
+ tx_desc->callback_param = board;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO);
+ dmaengine_submit(tx_desc);
+ dma_async_issue_pending(e_priv->dma_channel);
+
+ clear_bit(WRITE_READY_BN, &nec_priv->state);
+ set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ // suspend until message is sent
+ if (wait_event_interruptible(board->wait,
+ ((readl(e_priv->write_transfer_counter) &
+ write_transfer_counter_mask) == length) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state))
+ retval = -EIO;
+ // disable board's dma
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0);
+
+ dmaengine_terminate_all(e_priv->dma_channel);
+ // make sure fluke_dma_callback got called
+ if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state))
+ fluke_dma_callback(board);
+
+ /*
+ * if everything went fine, try to wait until last byte is actually
+ * transmitted across gpib (but don't try _too_ hard)
+ */
+ if (retval == 0)
+ retval = wait_for_sids_or_sgns(board);
+
+ *bytes_written = readl(e_priv->write_transfer_counter) & write_transfer_counter_mask;
+ if (WARN_ON_ONCE(*bytes_written > length))
+ return -EFAULT;
+
+cleanup:
+ dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE);
+ return retval;
+}
+
+static int fluke_accel_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ size_t remainder = length;
+ size_t transfer_size;
+ ssize_t retval = 0;
+ size_t dma_remainder = remainder;
+
+ if (!e_priv->dma_channel) {
+ dev_err(board->gpib_dev, "No dma channel available, cannot do accel write.");
+ return -ENXIO;
+ }
+
+ *bytes_written = 0;
+ if (length < 1)
+ return 0;
+
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME
+
+ if (send_eoi)
+ --dma_remainder;
+
+ while (dma_remainder > 0) {
+ size_t num_bytes;
+
+ retval = wait_for_data_out_ready(board);
+ if (retval < 0)
+ break;
+
+ transfer_size = (e_priv->dma_buffer_size < dma_remainder) ?
+ e_priv->dma_buffer_size : dma_remainder;
+ retval = fluke_dma_write(board, buffer, transfer_size, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ break;
+ dma_remainder -= num_bytes;
+ remainder -= num_bytes;
+ buffer += num_bytes;
+ if (need_resched())
+ schedule();
+ }
+ if (retval < 0)
+ return retval;
+ // handle sending of last byte with eoi
+ if (send_eoi) {
+ size_t num_bytes;
+
+ if (WARN_ON_ONCE(remainder != 1))
+ return -EFAULT;
+
+ /*
+ * wait until we are sure we will be able to write the data byte
+ * into the chip before we send AUX_SEOI. This prevents a timeout
+ * scenerio where we send AUX_SEOI but then timeout without getting
+ * any bytes into the gpib chip. This will result in the first byte
+ * of the next write having a spurious EOI set on the first byte.
+ */
+ retval = wait_for_data_out_ready(board);
+ if (retval < 0)
+ return retval;
+
+ write_byte(nec_priv, AUX_SEOI, AUXMR);
+ retval = fluke_dma_write(board, buffer, remainder, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ remainder -= num_bytes;
+ }
+ return 0;
+}
+
+static int fluke_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie)
+{
+ struct dma_tx_state state;
+ int result;
+
+ result = dmaengine_pause(chan);
+ if (result < 0) {
+ pr_err("dma pause failed?\n");
+ return result;
+ }
+ dmaengine_tx_status(chan, cookie, &state);
+ /*
+ * hardware doesn't support resume, so dont call this
+ * method unless the dma transfer is done.
+ */
+ return state.residue;
+}
+
+static int fluke_dma_read(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+ unsigned long flags;
+ int residue;
+ dma_addr_t bus_address;
+ struct dma_async_tx_descriptor *tx_desc;
+ dma_cookie_t dma_cookie;
+ int i;
+ static const int timeout = 10;
+
+ *bytes_read = 0;
+ *end = 0;
+ if (length == 0)
+ return 0;
+
+ bus_address = dma_map_single(board->dev, e_priv->dma_buffer,
+ length, DMA_FROM_DEVICE);
+
+ /* program dma controller */
+ retval = fluke_config_dma(board, 0);
+ if (retval) {
+ dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE);
+ return retval;
+ }
+ tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel,
+ bus_address, length, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx_desc) {
+ dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n");
+ dma_unmap_single(NULL, bus_address, length, DMA_FROM_DEVICE);
+ return -EIO;
+ }
+ tx_desc->callback = fluke_dma_callback;
+ tx_desc->callback_param = board;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ // enable nec7210 dma
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI);
+
+ dma_cookie = dmaengine_submit(tx_desc);
+ dma_async_issue_pending(e_priv->dma_channel);
+
+ set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state);
+ clear_bit(READ_READY_BN, &nec_priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ // wait for data to transfer
+ if (wait_event_interruptible(board->wait,
+ test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0 ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+
+ /*
+ * If we woke up because of end, wait until the dma transfer has pulled
+ * the data byte associated with the end before we cancel the dma transfer.
+ */
+ if (test_bit(RECEIVED_END_BN, &nec_priv->state)) {
+ for (i = 0; i < timeout; ++i) {
+ if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state) == 0)
+ break;
+ if ((read_byte(nec_priv, ADR0) & DATA_IN_STATUS) == 0)
+ break;
+ usleep_range(10, 15);
+ }
+ if (i == timeout)
+ pr_warn("fluke_gpib: timeout waiting for dma to transfer end data byte.\n");
+ }
+
+ // stop the dma transfer
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+ /*
+ * delay a little just to make sure any bytes in dma controller's fifo get
+ * written to memory before we disable it
+ */
+ usleep_range(10, 15);
+ residue = fluke_get_dma_residue(e_priv->dma_channel, dma_cookie);
+ if (WARN_ON_ONCE(residue > length || residue < 0))
+ return -EFAULT;
+ *bytes_read += length - residue;
+ dmaengine_terminate_all(e_priv->dma_channel);
+ // make sure fluke_dma_callback got called
+ if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state))
+ fluke_dma_callback(board);
+
+ dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE);
+ memcpy(buffer, e_priv->dma_buffer, *bytes_read);
+
+ /*
+ * If we got an end interrupt, figure out if it was
+ * associated with the last byte we dma'd or with a
+ * byte still sitting on the cb7210.
+ */
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (test_bit(READ_READY_BN, &nec_priv->state) == 0) {
+ /*
+ * There is no byte sitting on the cb7210. If we
+ * saw an end interrupt, we need to deal with it now
+ */
+ if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state))
+ *end = 1;
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return retval;
+}
+
+static int fluke_accel_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ size_t remain = length;
+ size_t transfer_size;
+ int retval = 0;
+ size_t dma_nbytes;
+
+ *end = 0;
+ *bytes_read = 0;
+
+ smp_mb__before_atomic();
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME
+ smp_mb__after_atomic();
+
+ retval = wait_for_read(board);
+ if (retval < 0)
+ return retval;
+
+ nec7210_release_rfd_holdoff(board, nec_priv);
+
+ while (remain > 0) {
+ transfer_size = (e_priv->dma_buffer_size < remain) ?
+ e_priv->dma_buffer_size : remain;
+ retval = fluke_dma_read(board, buffer, transfer_size, end, &dma_nbytes);
+ remain -= dma_nbytes;
+ buffer += dma_nbytes;
+ *bytes_read += dma_nbytes;
+ if (*end)
+ break;
+ if (retval < 0)
+ return retval;
+ if (need_resched())
+ schedule();
+ }
+
+ return retval;
+}
+
+static struct gpib_interface fluke_unaccel_interface = {
+ .name = "fluke_unaccel",
+ .attach = fluke_attach_holdoff_all,
+ .detach = fluke_detach,
+ .read = fluke_read,
+ .write = fluke_write,
+ .command = fluke_command,
+ .take_control = fluke_take_control,
+ .go_to_standby = fluke_go_to_standby,
+ .request_system_control = fluke_request_system_control,
+ .interface_clear = fluke_interface_clear,
+ .remote_enable = fluke_remote_enable,
+ .enable_eos = fluke_enable_eos,
+ .disable_eos = fluke_disable_eos,
+ .parallel_poll = fluke_parallel_poll,
+ .parallel_poll_configure = fluke_parallel_poll_configure,
+ .parallel_poll_response = fluke_parallel_poll_response,
+ .line_status = fluke_line_status,
+ .update_status = fluke_update_status,
+ .primary_address = fluke_primary_address,
+ .secondary_address = fluke_secondary_address,
+ .serial_poll_response = fluke_serial_poll_response,
+ .serial_poll_status = fluke_serial_poll_status,
+ .t1_delay = fluke_t1_delay,
+ .return_to_local = fluke_return_to_local,
+};
+
+/*
+ * fluke_hybrid uses dma for writes but not for reads. Added
+ * to deal with occasional corruption of bytes seen when doing dma
+ * reads. From looking at the cb7210 vhdl, I believe the corruption
+ * is due to a hardware bug triggered by the cpu reading a cb7210
+ * }
+ * register just as the dma controller is also doing a read.
+ */
+
+static struct gpib_interface fluke_hybrid_interface = {
+ .name = "fluke_hybrid",
+ .attach = fluke_attach_holdoff_all,
+ .detach = fluke_detach,
+ .read = fluke_read,
+ .write = fluke_accel_write,
+ .command = fluke_command,
+ .take_control = fluke_take_control,
+ .go_to_standby = fluke_go_to_standby,
+ .request_system_control = fluke_request_system_control,
+ .interface_clear = fluke_interface_clear,
+ .remote_enable = fluke_remote_enable,
+ .enable_eos = fluke_enable_eos,
+ .disable_eos = fluke_disable_eos,
+ .parallel_poll = fluke_parallel_poll,
+ .parallel_poll_configure = fluke_parallel_poll_configure,
+ .parallel_poll_response = fluke_parallel_poll_response,
+ .line_status = fluke_line_status,
+ .update_status = fluke_update_status,
+ .primary_address = fluke_primary_address,
+ .secondary_address = fluke_secondary_address,
+ .serial_poll_response = fluke_serial_poll_response,
+ .serial_poll_status = fluke_serial_poll_status,
+ .t1_delay = fluke_t1_delay,
+ .return_to_local = fluke_return_to_local,
+};
+
+static struct gpib_interface fluke_interface = {
+ .name = "fluke",
+ .attach = fluke_attach_holdoff_end,
+ .detach = fluke_detach,
+ .read = fluke_accel_read,
+ .write = fluke_accel_write,
+ .command = fluke_command,
+ .take_control = fluke_take_control,
+ .go_to_standby = fluke_go_to_standby,
+ .request_system_control = fluke_request_system_control,
+ .interface_clear = fluke_interface_clear,
+ .remote_enable = fluke_remote_enable,
+ .enable_eos = fluke_enable_eos,
+ .disable_eos = fluke_disable_eos,
+ .parallel_poll = fluke_parallel_poll,
+ .parallel_poll_configure = fluke_parallel_poll_configure,
+ .parallel_poll_response = fluke_parallel_poll_response,
+ .line_status = fluke_line_status,
+ .update_status = fluke_update_status,
+ .primary_address = fluke_primary_address,
+ .secondary_address = fluke_secondary_address,
+ .serial_poll_response = fluke_serial_poll_response,
+ .serial_poll_status = fluke_serial_poll_status,
+ .t1_delay = fluke_t1_delay,
+ .return_to_local = fluke_return_to_local,
+};
+
+irqreturn_t fluke_gpib_internal_interrupt(struct gpib_board *board)
+{
+ int status0, status1, status2;
+ struct fluke_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+ int retval = IRQ_NONE;
+
+ if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS)
+ set_bit(READ_READY_BN, &nec_priv->state);
+
+ status0 = fluke_paged_read_byte(priv, ISR0_IMR0, ISR0_IMR0_PAGE);
+ status1 = read_byte(nec_priv, ISR1);
+ status2 = read_byte(nec_priv, ISR2);
+
+ if (status0 & FLUKE_IFCI_BIT) {
+ push_gpib_event(board, EVENT_IFC);
+ retval = IRQ_HANDLED;
+ }
+
+ if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED)
+ retval = IRQ_HANDLED;
+
+ if (read_byte(nec_priv, ADR0) & DATA_IN_STATUS) {
+ if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state))
+ set_bit(READ_READY_BN, &nec_priv->state);
+ else
+ clear_bit(READ_READY_BN, &nec_priv->state);
+ }
+
+ if (retval == IRQ_HANDLED)
+ wake_up_interruptible(&board->wait);
+
+ return retval;
+}
+
+static irqreturn_t fluke_gpib_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = fluke_gpib_internal_interrupt(board);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+static int fluke_allocate_private(struct gpib_board *board)
+{
+ struct fluke_priv *priv;
+
+ board->private_data = kmalloc(sizeof(struct fluke_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ priv = board->private_data;
+ memset(priv, 0, sizeof(struct fluke_priv));
+ init_nec7210_private(&priv->nec7210_priv);
+ priv->dma_buffer_size = 0x7ff;
+ priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL);
+ if (!priv->dma_buffer)
+ return -ENOMEM;
+ return 0;
+}
+
+static void fluke_generic_detach(struct gpib_board *board)
+{
+ if (board->private_data) {
+ struct fluke_priv *e_priv = board->private_data;
+
+ kfree(e_priv->dma_buffer);
+ kfree(board->private_data);
+ board->private_data = NULL;
+ }
+}
+
+// generic part of attach functions shared by all cb7210 boards
+static int fluke_generic_attach(struct gpib_board *board)
+{
+ struct fluke_priv *e_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+
+ board->status = 0;
+
+ retval = fluke_allocate_private(board);
+ if (retval < 0)
+ return retval;
+ e_priv = board->private_data;
+ nec_priv = &e_priv->nec7210_priv;
+ nec_priv->read_byte = fluke_locking_read_byte;
+ nec_priv->write_byte = fluke_locking_write_byte;
+ nec_priv->offset = fluke_reg_offset;
+ nec_priv->type = CB7210;
+ return 0;
+}
+
+static int fluke_config_dma(struct gpib_board *board, int output)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct dma_slave_config config;
+
+ config.src_maxburst = 1;
+ config.dst_maxburst = 1;
+ config.device_fc = true;
+
+ if (output) {
+ config.direction = DMA_MEM_TO_DEV;
+ config.src_addr = 0;
+ config.dst_addr = e_priv->dma_port_res->start;
+ config.src_addr_width = 1;
+ config.dst_addr_width = 1;
+ } else {
+ config.direction = DMA_DEV_TO_MEM;
+ config.src_addr = e_priv->dma_port_res->start;
+ config.dst_addr = 0;
+ config.src_addr_width = 1;
+ config.dst_addr_width = 1;
+ }
+ return dmaengine_slave_config(e_priv->dma_channel, &config);
+}
+
+static int fluke_init(struct fluke_priv *e_priv, struct gpib_board *board, int handshake_mode)
+{
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+
+ nec7210_board_reset(nec_priv, board);
+ write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
+ /*
+ * set clock register for driving frequency
+ * ICR should be set to clock in megahertz (1-15) and to zero
+ * for clocks faster than 15 MHz (max 20MHz)
+ */
+ write_byte(nec_priv, ICR | 10, AUXMR);
+ nec7210_set_handshake_mode(board, nec_priv, handshake_mode);
+
+ nec7210_board_online(nec_priv, board);
+
+ /* poll so we can detect ATN changes */
+ if (gpib_request_pseudo_irq(board, fluke_gpib_interrupt)) {
+ dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n");
+ return -EINVAL;
+ }
+
+ fluke_paged_write_byte(e_priv, FLUKE_IFCIE_BIT, ISR0_IMR0, ISR0_IMR0_PAGE);
+ return 0;
+}
+
+/*
+ * This function is passed to dma_request_channel() in order to
+ * select the pl330 dma channel which has been hardwired to
+ * the gpib controller.
+ */
+static bool gpib_dma_channel_filter(struct dma_chan *chan, void *filter_param)
+{
+ // select the channel which is wired to the gpib chip
+ return chan->chan_id == 0;
+}
+
+static int fluke_attach_impl(struct gpib_board *board, const struct gpib_board_config *config,
+ unsigned int handshake_mode)
+{
+ struct fluke_priv *e_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = 0;
+ int retval;
+ int irq;
+ struct resource *res;
+ dma_cap_mask_t dma_cap;
+
+ if (!fluke_gpib_pdev) {
+ dev_err(board->gpib_dev, "No fluke device was found, attach failed.\n");
+ return -ENODEV;
+ }
+
+ retval = fluke_generic_attach(board);
+ if (retval)
+ return retval;
+
+ e_priv = board->private_data;
+ nec_priv = &e_priv->nec7210_priv;
+ nec_priv->offset = fluke_reg_offset;
+ board->dev = &fluke_gpib_pdev->dev;
+
+ res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource\n");
+ return -ENODEV;
+ }
+
+ if (request_mem_region(res->start,
+ resource_size(res),
+ fluke_gpib_pdev->name) == NULL) {
+ dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n");
+ return -ENXIO;
+ }
+ e_priv->gpib_iomem_res = res;
+
+ nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start,
+ resource_size(e_priv->gpib_iomem_res));
+ if (!nec_priv->mmiobase) {
+ dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n");
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for gpib dma port\n");
+ return -ENODEV;
+ }
+ if (request_mem_region(res->start,
+ resource_size(res),
+ fluke_gpib_pdev->name) == NULL) {
+ dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n");
+ return -ENXIO;
+ }
+ e_priv->dma_port_res = res;
+
+ res = platform_get_resource(fluke_gpib_pdev, IORESOURCE_MEM, 2);
+ if (!res) {
+ dev_err(&fluke_gpib_pdev->dev, "Unable to locate mmio resource for write transfer counter\n");
+ return -ENODEV;
+ }
+
+ if (request_mem_region(res->start,
+ resource_size(res),
+ fluke_gpib_pdev->name) == NULL) {
+ dev_err(&fluke_gpib_pdev->dev, "cannot claim registers\n");
+ return -ENXIO;
+ }
+ e_priv->write_transfer_counter_res = res;
+
+ e_priv->write_transfer_counter = ioremap(e_priv->write_transfer_counter_res->start,
+ resource_size(e_priv->write_transfer_counter_res));
+ if (!e_priv->write_transfer_counter) {
+ dev_err(&fluke_gpib_pdev->dev, "Could not map I/O memory\n");
+ return -ENOMEM;
+ }
+
+ irq = platform_get_irq(fluke_gpib_pdev, 0);
+ if (irq < 0)
+ return -EBUSY;
+ retval = request_irq(irq, fluke_gpib_interrupt, isr_flags, fluke_gpib_pdev->name, board);
+ if (retval) {
+ dev_err(&fluke_gpib_pdev->dev,
+ "cannot register interrupt handler err=%d\n",
+ retval);
+ return retval;
+ }
+ e_priv->irq = irq;
+
+ dma_cap_zero(dma_cap);
+ dma_cap_set(DMA_SLAVE, dma_cap);
+ e_priv->dma_channel = dma_request_channel(dma_cap, gpib_dma_channel_filter, NULL);
+ if (!e_priv->dma_channel) {
+ dev_err(board->gpib_dev, "failed to allocate a dma channel.\n");
+ /*
+ * we don't error out here because unaccel interface will still
+ * work without dma
+ */
+ }
+
+ return fluke_init(e_priv, board, handshake_mode);
+}
+
+int fluke_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return fluke_attach_impl(board, config, HR_HLDA);
+}
+
+int fluke_attach_holdoff_end(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return fluke_attach_impl(board, config, HR_HLDE);
+}
+
+void fluke_detach(struct gpib_board *board)
+{
+ struct fluke_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (e_priv) {
+ if (e_priv->dma_channel)
+ dma_release_channel(e_priv->dma_channel);
+ gpib_free_pseudo_irq(board);
+ nec_priv = &e_priv->nec7210_priv;
+
+ if (nec_priv->mmiobase) {
+ fluke_paged_write_byte(e_priv, 0, ISR0_IMR0, ISR0_IMR0_PAGE);
+ nec7210_board_reset(nec_priv, board);
+ }
+ if (e_priv->irq)
+ free_irq(e_priv->irq, board);
+ if (e_priv->write_transfer_counter_res) {
+ release_mem_region(e_priv->write_transfer_counter_res->start,
+ resource_size(e_priv->write_transfer_counter_res));
+ }
+ if (e_priv->dma_port_res) {
+ release_mem_region(e_priv->dma_port_res->start,
+ resource_size(e_priv->dma_port_res));
+ }
+ if (e_priv->gpib_iomem_res)
+ release_mem_region(e_priv->gpib_iomem_res->start,
+ resource_size(e_priv->gpib_iomem_res));
+ }
+ fluke_generic_detach(board);
+}
+
+static int fluke_gpib_probe(struct platform_device *pdev)
+{
+ fluke_gpib_pdev = pdev;
+ return 0;
+}
+
+static const struct of_device_id fluke_gpib_of_match[] = {
+ { .compatible = "flk,fgpib-4.0"},
+ { {0} }
+};
+MODULE_DEVICE_TABLE(of, fluke_gpib_of_match);
+
+static struct platform_driver fluke_gpib_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = fluke_gpib_of_match,
+ },
+ .probe = &fluke_gpib_probe
+};
+
+static int __init fluke_init_module(void)
+{
+ int result;
+
+ result = platform_driver_register(&fluke_gpib_platform_driver);
+ if (result) {
+ pr_err("platform_driver_register failed: error = %d\n", result);
+ return result;
+ }
+
+ result = gpib_register_driver(&fluke_unaccel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_unaccel;
+ }
+
+ result = gpib_register_driver(&fluke_hybrid_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_hybrid;
+ }
+
+ result = gpib_register_driver(&fluke_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_interface;
+ }
+
+ return 0;
+
+err_interface:
+ gpib_unregister_driver(&fluke_hybrid_interface);
+err_hybrid:
+ gpib_unregister_driver(&fluke_unaccel_interface);
+err_unaccel:
+ platform_driver_unregister(&fluke_gpib_platform_driver);
+
+ return result;
+}
+
+static void __exit fluke_exit_module(void)
+{
+ gpib_unregister_driver(&fluke_unaccel_interface);
+ gpib_unregister_driver(&fluke_hybrid_interface);
+ gpib_unregister_driver(&fluke_interface);
+ platform_driver_unregister(&fluke_gpib_platform_driver);
+}
+
+module_init(fluke_init_module);
+module_exit(fluke_exit_module);
diff --git a/drivers/gpib/eastwood/fluke_gpib.h b/drivers/gpib/eastwood/fluke_gpib.h
new file mode 100644
index 000000000000..493c200d0bbf
--- /dev/null
+++ b/drivers/gpib/eastwood/fluke_gpib.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Author: Frank Mori Hess <fmh6jj@gmail.com>
+ * copyright: (C) 2006, 2010, 2015 Fluke Corporation
+ ***************************************************************************/
+
+#include <linux/compiler.h>
+#include <linux/dmaengine.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "nec7210.h"
+
+struct fluke_priv {
+ struct nec7210_priv nec7210_priv;
+ struct resource *gpib_iomem_res;
+ struct resource *write_transfer_counter_res;
+ struct resource *dma_port_res;
+ int irq;
+ struct dma_chan *dma_channel;
+ u8 *dma_buffer;
+ int dma_buffer_size;
+ void __iomem *write_transfer_counter;
+};
+
+// cb7210 specific registers and bits
+enum cb7210_regs {
+ STATE1_REG = 0x4,
+ ISR0_IMR0 = 0x6,
+ BUS_STATUS = 0x7
+};
+
+enum cb7210_page_in {
+ ISR0_IMR0_PAGE = 1,
+ BUS_STATUS_PAGE = 1,
+ STATE1_PAGE = 1
+};
+
+/* IMR0 -- Interrupt Mode Register 0 */
+enum imr0_bits {
+ FLUKE_IFCIE_BIT = 0x8, /* interface clear interrupt */
+};
+
+/* ISR0 -- Interrupt Status Register 0 */
+enum isr0_bits {
+ FLUKE_IFCI_BIT = 0x8, /* interface clear interrupt */
+};
+
+enum state1_bits {
+ SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */
+ SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */
+ SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */
+ SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */
+ SOURCE_HANDSHAKE_MASK = 0x7
+};
+
+/*
+ * we customized the cb7210 vhdl to give the "data in" status
+ * on the unused bit 7 of the address0 register.
+ */
+enum cb7210_address0 {
+ DATA_IN_STATUS = 0x80
+};
+
+static inline int cb7210_page_in_bits(unsigned int page)
+{
+ return 0x50 | (page & 0xf);
+}
+
+// don't use without locking nec_priv->register_page_lock
+static inline u8 fluke_read_byte_nolock(struct nec7210_priv *nec_priv,
+ int register_num)
+{
+ u8 retval;
+
+ retval = readl(nec_priv->mmiobase + register_num * nec_priv->offset);
+ return retval;
+}
+
+// don't use without locking nec_priv->register_page_lock
+static inline void fluke_write_byte_nolock(struct nec7210_priv *nec_priv, u8 data,
+ int register_num)
+{
+ writel(data, nec_priv->mmiobase + register_num * nec_priv->offset);
+}
+
+static inline u8 fluke_paged_read_byte(struct fluke_priv *e_priv,
+ unsigned int register_num, unsigned int page)
+{
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ u8 retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nec_priv->register_page_lock, flags);
+ fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR);
+ udelay(1);
+ /* chip auto clears the page after a read */
+ retval = fluke_read_byte_nolock(nec_priv, register_num);
+ spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
+ return retval;
+}
+
+static inline void fluke_paged_write_byte(struct fluke_priv *e_priv, u8 data,
+ unsigned int register_num, unsigned int page)
+{
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nec_priv->register_page_lock, flags);
+ fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR);
+ udelay(1);
+ fluke_write_byte_nolock(nec_priv, data, register_num);
+ spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
+}
+
+enum bus_status_bits {
+ BSR_ATN_BIT = 0x1,
+ BSR_EOI_BIT = 0x2,
+ BSR_SRQ_BIT = 0x4,
+ BSR_IFC_BIT = 0x8,
+ BSR_REN_BIT = 0x10,
+ BSR_DAV_BIT = 0x20,
+ BSR_NRFD_BIT = 0x40,
+ BSR_NDAC_BIT = 0x80,
+};
+
+enum cb7210_aux_cmds {
+/*
+ * AUX_RTL2 is an undocumented aux command which causes cb7210 to assert
+ * (and keep asserted) local rtl message. This is used in conjunction
+ * with the (stupid) cb7210 implementation
+ * of the normal nec7210 AUX_RTL aux command, which
+ * causes the rtl message to toggle between on and off.
+ */
+ AUX_RTL2 = 0xd,
+ AUX_NBAF = 0xe, // new byte available false (also clears seoi)
+ AUX_LO_SPEED = 0x40,
+ AUX_HI_SPEED = 0x41,
+};
+
+enum {
+ fluke_reg_offset = 4,
+ fluke_num_regs = 8,
+ write_transfer_counter_mask = 0x7ff,
+};
diff --git a/drivers/gpib/fmh_gpib/Makefile b/drivers/gpib/fmh_gpib/Makefile
new file mode 100644
index 000000000000..cc4d9e7cd5cd
--- /dev/null
+++ b/drivers/gpib/fmh_gpib/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_GPIB_FMH) += fmh_gpib.o
diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c
new file mode 100644
index 000000000000..f7bfb4a8e553
--- /dev/null
+++ b/drivers/gpib/fmh_gpib/fmh_gpib.c
@@ -0,0 +1,1754 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * GPIB Driver for fmh_gpib_core, see
+ * https://github.com/fmhess/fmh_gpib_core
+ *
+ * More specifically, it is a driver for the hardware arrangement described by
+ * src/examples/fmh_gpib_top.vhd in the fmh_gpib_core repository.
+ *
+ * Author: Frank Mori Hess <fmh6jj@gmail.com>
+ * Copyright: (C) 2006, 2010, 2015 Fluke Corporation
+ * (C) 2017 Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "fmh_gpib.h"
+
+#include "gpibP.h"
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB Driver for fmh_gpib_core");
+MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
+
+static irqreturn_t fmh_gpib_interrupt(int irq, void *arg);
+static int fmh_gpib_attach_holdoff_all(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static int fmh_gpib_attach_holdoff_end(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static void fmh_gpib_detach(struct gpib_board *board);
+static int fmh_gpib_pci_attach_holdoff_all(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static int fmh_gpib_pci_attach_holdoff_end(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static void fmh_gpib_pci_detach(struct gpib_board *board);
+static int fmh_gpib_config_dma(struct gpib_board *board, int output);
+static irqreturn_t fmh_gpib_internal_interrupt(struct gpib_board *board);
+static struct platform_driver fmh_gpib_platform_driver;
+static struct pci_driver fmh_gpib_pci_driver;
+
+// wrappers for interface functions
+static int fmh_gpib_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+}
+
+static int fmh_gpib_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int fmh_gpib_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int fmh_gpib_take_control(struct gpib_board *board, int synchronous)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int fmh_gpib_go_to_standby(struct gpib_board *board)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int fmh_gpib_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct fmh_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ return nec7210_request_system_control(board, nec_priv, request_control);
+}
+
+static void fmh_gpib_interface_clear(struct gpib_board *board, int assert)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void fmh_gpib_remote_enable(struct gpib_board *board, int enable)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int fmh_gpib_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void fmh_gpib_disable_eos(struct gpib_board *board)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int fmh_gpib_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
+}
+
+static int fmh_gpib_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int fmh_gpib_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int fmh_gpib_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
+}
+
+static void fmh_gpib_parallel_poll_configure(struct gpib_board *board, u8 configuration)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, configuration);
+}
+
+static void fmh_gpib_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+static void fmh_gpib_local_parallel_poll_mode(struct gpib_board *board, int local)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ if (local) {
+ write_byte(&priv->nec7210_priv, AUX_I_REG | LOCAL_PPOLL_MODE_BIT, AUXMR);
+ } else {
+ /*
+ * For fmh_gpib_core, remote parallel poll config mode is unaffected by the
+ * state of the disable bit of the parallel poll register (unlike the tnt4882).
+ * So, we don't need to worry about that.
+ */
+ write_byte(&priv->nec7210_priv, AUX_I_REG | 0x0, AUXMR);
+ }
+}
+
+static void fmh_gpib_serial_poll_response2(struct gpib_board *board, u8 status,
+ int new_reason_for_service)
+{
+ struct fmh_priv *priv = board->private_data;
+ unsigned long flags;
+ const int MSS = status & request_service_bit;
+ const int reqt = MSS && new_reason_for_service;
+ const int reqf = MSS == 0;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (reqt) {
+ priv->nec7210_priv.srq_pending = 1;
+ clear_bit(SPOLL_NUM, &board->status);
+ } else if (reqf) {
+ priv->nec7210_priv.srq_pending = 0;
+ }
+
+ if (reqt) {
+ /*
+ * It may seem like a race to issue reqt before updating
+ * the status byte, but it is not. The chip does not
+ * issue the reqt until the SPMR is written to at
+ * a later time.
+ */
+ write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR);
+ } else if (reqf) {
+ write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR);
+ }
+ /*
+ * We need to always zero bit 6 of the status byte before writing it to
+ * the SPMR to insure we are using
+ * serial poll mode SP1, and not accidentally triggering mode SP3.
+ */
+ write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+static u8 fmh_gpib_serial_poll_status(struct gpib_board *board)
+{
+ struct fmh_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static void fmh_gpib_return_to_local(struct gpib_board *board)
+{
+ struct fmh_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ write_byte(nec_priv, AUX_RTL2, AUXMR);
+ udelay(1);
+ write_byte(nec_priv, AUX_RTL, AUXMR);
+}
+
+static int fmh_gpib_line_status(const struct gpib_board *board)
+{
+ int status = VALID_ALL;
+ int bsr_bits;
+ struct fmh_priv *e_priv;
+ struct nec7210_priv *nec_priv;
+
+ e_priv = board->private_data;
+ nec_priv = &e_priv->nec7210_priv;
+
+ bsr_bits = read_byte(nec_priv, BUS_STATUS_REG);
+
+ if ((bsr_bits & BSR_REN_BIT) == 0)
+ status |= BUS_REN;
+ if ((bsr_bits & BSR_IFC_BIT) == 0)
+ status |= BUS_IFC;
+ if ((bsr_bits & BSR_SRQ_BIT) == 0)
+ status |= BUS_SRQ;
+ if ((bsr_bits & BSR_EOI_BIT) == 0)
+ status |= BUS_EOI;
+ if ((bsr_bits & BSR_NRFD_BIT) == 0)
+ status |= BUS_NRFD;
+ if ((bsr_bits & BSR_NDAC_BIT) == 0)
+ status |= BUS_NDAC;
+ if ((bsr_bits & BSR_DAV_BIT) == 0)
+ status |= BUS_DAV;
+ if ((bsr_bits & BSR_ATN_BIT) == 0)
+ status |= BUS_ATN;
+
+ return status;
+}
+
+static int fmh_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned int retval;
+
+ retval = nec7210_t1_delay(board, nec_priv, nano_sec);
+
+ if (nano_sec <= 350) {
+ write_byte(nec_priv, AUX_HI_SPEED, AUXMR);
+ retval = 350;
+ } else {
+ write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
+ }
+ return retval;
+}
+
+static int lacs_or_read_ready(struct gpib_board *board)
+{
+ const struct fmh_priv *e_priv = board->private_data;
+ const struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = test_bit(LACS_NUM, &board->status) ||
+ test_bit(READ_READY_BN, &nec_priv->state);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return retval;
+}
+
+static int wait_for_read(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ lacs_or_read_ready(board) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ return retval;
+}
+
+static int wait_for_rx_fifo_half_full_or_end(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) &
+ RX_FIFO_HALF_FULL) ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ return retval;
+}
+
+/*
+ * Wait until the gpib chip is ready to accept a data out byte.
+ */
+static int wait_for_data_out_ready(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ (test_bit(TACS_NUM, &board->status) &&
+ (read_byte(nec_priv, EXT_STATUS_1_REG) &
+ DATA_OUT_STATUS_BIT)) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+
+ return retval;
+}
+
+static void fmh_gpib_dma_callback(void *arg)
+{
+ struct gpib_board *board = arg;
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, HR_DOIE | HR_DIIE);
+ wake_up_interruptible(&board->wait);
+
+ fmh_gpib_internal_interrupt(board);
+
+ clear_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state);
+ clear_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+/*
+ * returns true when all the bytes of a write have been transferred to
+ * the chip and successfully transferred out over the gpib bus.
+ */
+static int fmh_gpib_all_bytes_are_sent(struct fmh_priv *e_priv)
+{
+ if (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) & fifo_xfer_counter_mask)
+ return 0;
+
+ if ((read_byte(&e_priv->nec7210_priv, EXT_STATUS_1_REG) & DATA_OUT_STATUS_BIT) == 0)
+ return 0;
+
+ return 1;
+}
+
+static int fmh_gpib_dma_write(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+ int retval = 0;
+ dma_addr_t address;
+ struct dma_async_tx_descriptor *tx_desc;
+
+ *bytes_written = 0;
+ if (WARN_ON_ONCE(length > e_priv->dma_buffer_size))
+ return -EFAULT;
+ dmaengine_terminate_all(e_priv->dma_channel);
+ memcpy(e_priv->dma_buffer, buffer, length);
+ address = dma_map_single(board->dev, e_priv->dma_buffer, length, DMA_TO_DEVICE);
+ if (dma_mapping_error(board->dev, address))
+ dev_err(board->gpib_dev, "dma mapping error in dma write!\n");
+ /* program dma controller */
+ retval = fmh_gpib_config_dma(board, 1);
+ if (retval)
+ goto cleanup;
+
+ tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, address, length, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx_desc) {
+ dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n");
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ tx_desc->callback = fmh_gpib_dma_callback;
+ tx_desc->callback_param = board;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG);
+ fifos_write(e_priv, TX_FIFO_DMA_REQUEST_ENABLE | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG);
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO);
+
+ dmaengine_submit(tx_desc);
+ dma_async_issue_pending(e_priv->dma_channel);
+ clear_bit(WRITE_READY_BN, &nec_priv->state);
+ set_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ // suspend until message is sent
+ if (wait_event_interruptible(board->wait,
+ fmh_gpib_all_bytes_are_sent(e_priv) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state))
+ retval = -EIO;
+ // disable board's dma
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0);
+ fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG);
+
+ dmaengine_terminate_all(e_priv->dma_channel);
+ // make sure fmh_gpib_dma_callback got called
+ if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &nec_priv->state))
+ fmh_gpib_dma_callback(board);
+
+ *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) &
+ fifo_xfer_counter_mask);
+ if (WARN_ON_ONCE(*bytes_written > length))
+ return -EFAULT;
+cleanup:
+ dma_unmap_single(board->dev, address, length, DMA_TO_DEVICE);
+ return retval;
+}
+
+static int fmh_gpib_accel_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ size_t remainder = length;
+ size_t transfer_size;
+ ssize_t retval = 0;
+ size_t dma_remainder = remainder;
+
+ if (!e_priv->dma_channel) {
+ dev_err(board->gpib_dev, "No dma channel available, cannot do accel write.");
+ return -ENXIO;
+ }
+
+ *bytes_written = 0;
+ if (length < 1)
+ return 0;
+
+ smp_mb__before_atomic();
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME
+ smp_mb__after_atomic();
+
+ if (send_eoi)
+ --dma_remainder;
+
+ while (dma_remainder > 0) {
+ size_t num_bytes;
+
+ retval = wait_for_data_out_ready(board);
+ if (retval < 0)
+ break;
+
+ transfer_size = (e_priv->dma_buffer_size < dma_remainder) ?
+ e_priv->dma_buffer_size : dma_remainder;
+ retval = fmh_gpib_dma_write(board, buffer, transfer_size, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ break;
+ dma_remainder -= num_bytes;
+ remainder -= num_bytes;
+ buffer += num_bytes;
+ if (need_resched())
+ schedule();
+ }
+ if (retval < 0)
+ return retval;
+ // handle sending of last byte with eoi
+ if (send_eoi) {
+ size_t num_bytes;
+
+ if (WARN_ON_ONCE(remainder != 1))
+ return -EFAULT;
+
+ /*
+ * wait until we are sure we will be able to write the data byte
+ * into the chip before we send AUX_SEOI. This prevents a timeout
+ * scenario where we send AUX_SEOI but then timeout without getting
+ * any bytes into the gpib chip. This will result in the first byte
+ * of the next write having a spurious EOI set on the first byte.
+ */
+ retval = wait_for_data_out_ready(board);
+ if (retval < 0)
+ return retval;
+
+ write_byte(nec_priv, AUX_SEOI, AUXMR);
+ retval = fmh_gpib_dma_write(board, buffer, remainder, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ remainder -= num_bytes;
+ }
+ return 0;
+}
+
+static int fmh_gpib_get_dma_residue(struct dma_chan *chan, dma_cookie_t cookie)
+{
+ struct dma_tx_state state;
+ int result;
+
+ result = dmaengine_pause(chan);
+ if (result < 0) {
+ pr_err("dma pause failed?\n");
+ return result;
+ }
+ dmaengine_tx_status(chan, cookie, &state);
+ /*
+ * dma330 hardware doesn't support resume, so dont call this
+ * method unless the dma transfer is done.
+ */
+ return state.residue;
+}
+
+static int wait_for_tx_fifo_half_empty(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ if (wait_event_interruptible(board->wait,
+ (test_bit(TACS_NUM, &board->status) &&
+ (fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) &
+ TX_FIFO_HALF_EMPTY)) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+
+ return retval;
+}
+
+/*
+ * supports writing a chunk of data whose length must fit into the hardware'd xfer counter,
+ * called in a loop by fmh_gpib_fifo_write()
+ */
+static int fmh_gpib_fifo_write_countable(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+ unsigned int remainder;
+
+ *bytes_written = 0;
+ if (WARN_ON_ONCE(length > fifo_xfer_counter_mask))
+ return -EFAULT;
+
+ fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG);
+ fifos_write(e_priv, TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG);
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO);
+
+ remainder = length;
+ while (remainder > 0) {
+ int i;
+
+ fifos_write(e_priv, TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG);
+ retval = wait_for_tx_fifo_half_empty(board);
+ if (retval < 0)
+ goto cleanup;
+
+ for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && remainder > 0; ++i) {
+ unsigned int data_value = *buffer;
+
+ if (send_eoi && remainder == 1)
+ data_value |= FIFO_DATA_EOI_FLAG;
+ fifos_write(e_priv, data_value, FIFO_DATA_REG);
+ ++buffer;
+ --remainder;
+ }
+ }
+
+ // suspend until last byte is sent
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, HR_DOIE);
+ if (wait_event_interruptible(board->wait,
+ fmh_gpib_all_bytes_are_sent(e_priv) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state))
+ retval = -EIO;
+
+cleanup:
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0);
+ fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG);
+
+ *bytes_written = length - (fifos_read(e_priv, FIFO_XFER_COUNTER_REG) &
+ fifo_xfer_counter_mask);
+ if (WARN_ON_ONCE(*bytes_written > length))
+ return -EFAULT;
+
+ return retval;
+}
+
+static int fmh_gpib_fifo_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ size_t remainder = length;
+ size_t transfer_size;
+ ssize_t retval = 0;
+
+ *bytes_written = 0;
+ if (length < 1)
+ return 0;
+
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME
+
+ while (remainder > 0) {
+ size_t num_bytes;
+ int last_pass;
+
+ retval = wait_for_data_out_ready(board);
+ if (retval < 0)
+ break;
+
+ if (fifo_xfer_counter_mask < remainder) {
+ // round transfer size to a multiple of half fifo size
+ transfer_size = (fifo_xfer_counter_mask /
+ fmh_gpib_half_fifo_size(e_priv)) *
+ fmh_gpib_half_fifo_size(e_priv);
+ last_pass = 0;
+ } else {
+ transfer_size = remainder;
+ last_pass = 1;
+ }
+ retval = fmh_gpib_fifo_write_countable(board, buffer, transfer_size,
+ last_pass && send_eoi, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ break;
+ remainder -= num_bytes;
+ buffer += num_bytes;
+ if (need_resched())
+ schedule();
+ }
+
+ return retval;
+}
+
+static int fmh_gpib_dma_read(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+ unsigned long flags;
+ int residue;
+ int wait_retval;
+ dma_addr_t bus_address;
+ struct dma_async_tx_descriptor *tx_desc;
+ dma_cookie_t dma_cookie;
+
+ *bytes_read = 0;
+ *end = 0;
+ if (length == 0)
+ return 0;
+
+ bus_address = dma_map_single(board->dev, e_priv->dma_buffer,
+ length, DMA_FROM_DEVICE);
+ if (dma_mapping_error(board->dev, bus_address))
+ dev_err(board->gpib_dev, "dma mapping error in dma read!");
+
+ /* program dma controller */
+ retval = fmh_gpib_config_dma(board, 0);
+ if (retval) {
+ dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE);
+ return retval;
+ }
+ tx_desc = dmaengine_prep_slave_single(e_priv->dma_channel, bus_address,
+ length, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx_desc) {
+ dev_err(board->gpib_dev, "failed to allocate dma transmit descriptor\n");
+ dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE);
+ return -EIO;
+ }
+ tx_desc->callback = fmh_gpib_dma_callback;
+ tx_desc->callback_param = board;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ // enable nec7210 dma
+ fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG);
+ fifos_write(e_priv, RX_FIFO_DMA_REQUEST_ENABLE | RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG);
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI);
+
+ dma_cookie = dmaengine_submit(tx_desc);
+ dma_async_issue_pending(e_priv->dma_channel);
+
+ set_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ // wait for data to transfer
+ wait_retval = wait_event_interruptible(board->wait,
+ test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state)
+ == 0 ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status));
+ if (wait_retval)
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ // stop the dma transfer
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+ fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG);
+ /*
+ * give time for pl330 to transfer any in-flight data, since
+ * pl330 will throw it away when dmaengine_pause is called.
+ */
+ usleep_range(10, 15);
+ residue = fmh_gpib_get_dma_residue(e_priv->dma_channel, dma_cookie);
+ if (WARN_ON_ONCE(residue > length || residue < 0))
+ return -EFAULT;
+ *bytes_read += length - residue;
+ dmaengine_terminate_all(e_priv->dma_channel);
+ // make sure fmh_gpib_dma_callback got called
+ if (test_bit(DMA_READ_IN_PROGRESS_BN, &nec_priv->state))
+ fmh_gpib_dma_callback(board);
+
+ dma_unmap_single(board->dev, bus_address, length, DMA_FROM_DEVICE);
+ memcpy(buffer, e_priv->dma_buffer, *bytes_read);
+
+ /* Manually read any dregs out of fifo. */
+ while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) {
+ if ((*bytes_read) >= length) {
+ dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d residue=%d\n",
+ (int)(*bytes_read), (int)length, (int)residue);
+ break;
+ }
+ buffer[(*bytes_read)++] = fifos_read(e_priv, FIFO_DATA_REG) & fifo_data_mask;
+ }
+
+ /*
+ * If we got an end interrupt, figure out if it was
+ * associated with the last byte we dma'd or with a
+ * byte still sitting on the cb7210.
+ */
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (*bytes_read > 0 && test_bit(READ_READY_BN, &nec_priv->state) == 0) {
+ /*
+ * If there is no byte sitting on the cb7210 and we
+ * saw an end, we need to deal with it now
+ */
+ if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state))
+ *end = 1;
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return retval;
+}
+
+static void fmh_gpib_release_rfd_holdoff(struct gpib_board *board, struct fmh_priv *e_priv)
+{
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned int ext_status_1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG);
+
+ /*
+ * if there is an end byte sitting on the chip, don't release
+ * holdoff. We want it left set after we read out the end
+ * byte.
+ */
+ if ((ext_status_1 & (DATA_IN_STATUS_BIT | END_STATUS_BIT)) !=
+ (DATA_IN_STATUS_BIT | END_STATUS_BIT)) {
+ if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT)
+ write_byte(nec_priv, AUX_FH, AUXMR);
+
+ /*
+ * Check if an end byte raced in before we executed the AUX_FH command.
+ * If it did, we want to make sure the rfd holdoff is in effect. The end
+ * byte can arrive since
+ * AUX_RFD_HOLDOFF_ASAP doesn't immediately force the acceptor handshake
+ * to leave ACRS.
+ */
+ if ((read_byte(nec_priv, EXT_STATUS_1_REG) &
+ (RFD_HOLDOFF_STATUS_BIT | DATA_IN_STATUS_BIT | END_STATUS_BIT)) ==
+ (DATA_IN_STATUS_BIT | END_STATUS_BIT)) {
+ write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR);
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ } else {
+ clear_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ }
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+static int fmh_gpib_accel_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ size_t remain = length;
+ size_t transfer_size;
+ int retval = 0;
+ size_t dma_nbytes;
+ unsigned long flags;
+
+ smp_mb__before_atomic();
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME
+ smp_mb__after_atomic();
+ *end = 0;
+ *bytes_read = 0;
+
+ retval = wait_for_read(board);
+ if (retval < 0)
+ return retval;
+
+ fmh_gpib_release_rfd_holdoff(board, e_priv);
+ while (remain > 0) {
+ transfer_size = (e_priv->dma_buffer_size < remain) ?
+ e_priv->dma_buffer_size : remain;
+ retval = fmh_gpib_dma_read(board, buffer, transfer_size, end, &dma_nbytes);
+ remain -= dma_nbytes;
+ buffer += dma_nbytes;
+ *bytes_read += dma_nbytes;
+ if (*end)
+ break;
+ if (retval < 0)
+ break;
+ if (need_resched())
+ schedule();
+ }
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (test_bit(RFD_HOLDOFF_BN, &nec_priv->state) == 0) {
+ write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR);
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return retval;
+}
+
+/*
+ * Read a chunk of data whose length is within the limits of the hardware's
+ * xfer counter. Called in a loop from fmh_gpib_fifo_read().
+ */
+static int fmh_gpib_fifo_read_countable(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ int retval = 0;
+
+ *bytes_read = 0;
+ *end = 0;
+ if (length == 0)
+ return 0;
+
+ fifos_write(e_priv, length & fifo_xfer_counter_mask, FIFO_XFER_COUNTER_REG);
+ fifos_write(e_priv, RX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG);
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI);
+
+ while (*bytes_read < length && *end == 0) {
+ int i;
+
+ fifos_write(e_priv, RX_FIFO_HALF_FULL_INTERRUPT_ENABLE, FIFO_CONTROL_STATUS_REG);
+ retval = wait_for_rx_fifo_half_full_or_end(board);
+ if (retval < 0)
+ goto cleanup;
+
+ for (i = 0; i < fmh_gpib_half_fifo_size(e_priv) && *end == 0; ++i) {
+ unsigned int data_value;
+
+ data_value = fifos_read(e_priv, FIFO_DATA_REG);
+ buffer[(*bytes_read)++] = data_value & fifo_data_mask;
+ if (data_value & FIFO_DATA_EOI_FLAG)
+ *end = 1;
+ }
+ }
+
+cleanup:
+ // stop the transfer
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+ fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG);
+
+ /* Manually read any dregs out of fifo. */
+ while ((fifos_read(e_priv, FIFO_CONTROL_STATUS_REG) & RX_FIFO_EMPTY) == 0) {
+ unsigned int data_value;
+
+ if ((*bytes_read) >= length) {
+ dev_err(board->dev, "unexpected extra bytes in rx fifo, discarding! bytes_read=%d length=%d\n",
+ (int)(*bytes_read), (int)length);
+ break;
+ }
+ data_value = fifos_read(e_priv, FIFO_DATA_REG);
+ buffer[(*bytes_read)++] = data_value & fifo_data_mask;
+ if (data_value & FIFO_DATA_EOI_FLAG)
+ *end = 1;
+ }
+
+ return retval;
+}
+
+static int fmh_gpib_fifo_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ size_t remain = length;
+ size_t transfer_size;
+ int retval = 0;
+ size_t nbytes;
+ unsigned long flags;
+
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state); // XXX FIXME
+ *end = 0;
+ *bytes_read = 0;
+
+ /*
+ * Do a little prep with data in interrupt so that following wait_for_read()
+ * will wake up if a data byte is received.
+ */
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DIIE, HR_DIIE);
+ fmh_gpib_interrupt(0, board);
+
+ retval = wait_for_read(board);
+ if (retval < 0)
+ return retval;
+
+ fmh_gpib_release_rfd_holdoff(board, e_priv);
+ while (remain > 0) {
+ if (fifo_xfer_counter_mask < remain) {
+ // round transfer size to a multiple of half fifo size
+ transfer_size = (fifo_xfer_counter_mask /
+ fmh_gpib_half_fifo_size(e_priv)) *
+ fmh_gpib_half_fifo_size(e_priv);
+ } else {
+ transfer_size = remain;
+ }
+ retval = fmh_gpib_fifo_read_countable(board, buffer, transfer_size, end, &nbytes);
+ remain -= nbytes;
+ buffer += nbytes;
+ *bytes_read += nbytes;
+ if (*end)
+ break;
+ if (retval < 0)
+ break;
+ if (need_resched())
+ schedule();
+ }
+
+ if (*end == 0) {
+ spin_lock_irqsave(&board->spinlock, flags);
+ write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR);
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ }
+
+ return retval;
+}
+
+static struct gpib_interface fmh_gpib_unaccel_interface = {
+ .name = "fmh_gpib_unaccel",
+ .attach = fmh_gpib_attach_holdoff_all,
+ .detach = fmh_gpib_detach,
+ .read = fmh_gpib_read,
+ .write = fmh_gpib_write,
+ .command = fmh_gpib_command,
+ .take_control = fmh_gpib_take_control,
+ .go_to_standby = fmh_gpib_go_to_standby,
+ .request_system_control = fmh_gpib_request_system_control,
+ .interface_clear = fmh_gpib_interface_clear,
+ .remote_enable = fmh_gpib_remote_enable,
+ .enable_eos = fmh_gpib_enable_eos,
+ .disable_eos = fmh_gpib_disable_eos,
+ .parallel_poll = fmh_gpib_parallel_poll,
+ .parallel_poll_configure = fmh_gpib_parallel_poll_configure,
+ .parallel_poll_response = fmh_gpib_parallel_poll_response,
+ .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode,
+ .line_status = fmh_gpib_line_status,
+ .update_status = fmh_gpib_update_status,
+ .primary_address = fmh_gpib_primary_address,
+ .secondary_address = fmh_gpib_secondary_address,
+ .serial_poll_response2 = fmh_gpib_serial_poll_response2,
+ .serial_poll_status = fmh_gpib_serial_poll_status,
+ .t1_delay = fmh_gpib_t1_delay,
+ .return_to_local = fmh_gpib_return_to_local,
+};
+
+static struct gpib_interface fmh_gpib_interface = {
+ .name = "fmh_gpib",
+ .attach = fmh_gpib_attach_holdoff_end,
+ .detach = fmh_gpib_detach,
+ .read = fmh_gpib_accel_read,
+ .write = fmh_gpib_accel_write,
+ .command = fmh_gpib_command,
+ .take_control = fmh_gpib_take_control,
+ .go_to_standby = fmh_gpib_go_to_standby,
+ .request_system_control = fmh_gpib_request_system_control,
+ .interface_clear = fmh_gpib_interface_clear,
+ .remote_enable = fmh_gpib_remote_enable,
+ .enable_eos = fmh_gpib_enable_eos,
+ .disable_eos = fmh_gpib_disable_eos,
+ .parallel_poll = fmh_gpib_parallel_poll,
+ .parallel_poll_configure = fmh_gpib_parallel_poll_configure,
+ .parallel_poll_response = fmh_gpib_parallel_poll_response,
+ .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode,
+ .line_status = fmh_gpib_line_status,
+ .update_status = fmh_gpib_update_status,
+ .primary_address = fmh_gpib_primary_address,
+ .secondary_address = fmh_gpib_secondary_address,
+ .serial_poll_response2 = fmh_gpib_serial_poll_response2,
+ .serial_poll_status = fmh_gpib_serial_poll_status,
+ .t1_delay = fmh_gpib_t1_delay,
+ .return_to_local = fmh_gpib_return_to_local,
+};
+
+static struct gpib_interface fmh_gpib_pci_interface = {
+ .name = "fmh_gpib_pci",
+ .attach = fmh_gpib_pci_attach_holdoff_end,
+ .detach = fmh_gpib_pci_detach,
+ .read = fmh_gpib_fifo_read,
+ .write = fmh_gpib_fifo_write,
+ .command = fmh_gpib_command,
+ .take_control = fmh_gpib_take_control,
+ .go_to_standby = fmh_gpib_go_to_standby,
+ .request_system_control = fmh_gpib_request_system_control,
+ .interface_clear = fmh_gpib_interface_clear,
+ .remote_enable = fmh_gpib_remote_enable,
+ .enable_eos = fmh_gpib_enable_eos,
+ .disable_eos = fmh_gpib_disable_eos,
+ .parallel_poll = fmh_gpib_parallel_poll,
+ .parallel_poll_configure = fmh_gpib_parallel_poll_configure,
+ .parallel_poll_response = fmh_gpib_parallel_poll_response,
+ .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode,
+ .line_status = fmh_gpib_line_status,
+ .update_status = fmh_gpib_update_status,
+ .primary_address = fmh_gpib_primary_address,
+ .secondary_address = fmh_gpib_secondary_address,
+ .serial_poll_response2 = fmh_gpib_serial_poll_response2,
+ .serial_poll_status = fmh_gpib_serial_poll_status,
+ .t1_delay = fmh_gpib_t1_delay,
+ .return_to_local = fmh_gpib_return_to_local,
+};
+
+static struct gpib_interface fmh_gpib_pci_unaccel_interface = {
+ .name = "fmh_gpib_pci_unaccel",
+ .attach = fmh_gpib_pci_attach_holdoff_all,
+ .detach = fmh_gpib_pci_detach,
+ .read = fmh_gpib_read,
+ .write = fmh_gpib_write,
+ .command = fmh_gpib_command,
+ .take_control = fmh_gpib_take_control,
+ .go_to_standby = fmh_gpib_go_to_standby,
+ .request_system_control = fmh_gpib_request_system_control,
+ .interface_clear = fmh_gpib_interface_clear,
+ .remote_enable = fmh_gpib_remote_enable,
+ .enable_eos = fmh_gpib_enable_eos,
+ .disable_eos = fmh_gpib_disable_eos,
+ .parallel_poll = fmh_gpib_parallel_poll,
+ .parallel_poll_configure = fmh_gpib_parallel_poll_configure,
+ .parallel_poll_response = fmh_gpib_parallel_poll_response,
+ .local_parallel_poll_mode = fmh_gpib_local_parallel_poll_mode,
+ .line_status = fmh_gpib_line_status,
+ .update_status = fmh_gpib_update_status,
+ .primary_address = fmh_gpib_primary_address,
+ .secondary_address = fmh_gpib_secondary_address,
+ .serial_poll_response2 = fmh_gpib_serial_poll_response2,
+ .serial_poll_status = fmh_gpib_serial_poll_status,
+ .t1_delay = fmh_gpib_t1_delay,
+ .return_to_local = fmh_gpib_return_to_local,
+};
+
+irqreturn_t fmh_gpib_internal_interrupt(struct gpib_board *board)
+{
+ unsigned int status0, status1, status2, ext_status_1, fifo_status;
+ struct fmh_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+ int retval = IRQ_NONE;
+
+ status0 = read_byte(nec_priv, ISR0_IMR0_REG);
+ status1 = read_byte(nec_priv, ISR1);
+ status2 = read_byte(nec_priv, ISR2);
+ fifo_status = fifos_read(priv, FIFO_CONTROL_STATUS_REG);
+
+ if (status0 & IFC_INTERRUPT_BIT) {
+ push_gpib_event(board, EVENT_IFC);
+ retval = IRQ_HANDLED;
+ }
+
+ if (nec7210_interrupt_have_status(board, nec_priv, status1, status2) == IRQ_HANDLED)
+ retval = IRQ_HANDLED;
+
+ ext_status_1 = read_byte(nec_priv, EXT_STATUS_1_REG);
+
+ if (ext_status_1 & DATA_IN_STATUS_BIT)
+ set_bit(READ_READY_BN, &nec_priv->state);
+ else
+ clear_bit(READ_READY_BN, &nec_priv->state);
+
+ if (ext_status_1 & DATA_OUT_STATUS_BIT)
+ set_bit(WRITE_READY_BN, &nec_priv->state);
+ else
+ clear_bit(WRITE_READY_BN, &nec_priv->state);
+
+ if (ext_status_1 & COMMAND_OUT_STATUS_BIT)
+ set_bit(COMMAND_READY_BN, &nec_priv->state);
+ else
+ clear_bit(COMMAND_READY_BN, &nec_priv->state);
+
+ if (ext_status_1 & RFD_HOLDOFF_STATUS_BIT)
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ else
+ clear_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+
+ if (ext_status_1 & END_STATUS_BIT) {
+ /*
+ * only set RECEIVED_END while there is still a data
+ * byte sitting in the chip, to avoid spuriously
+ * setting it multiple times after it has been cleared
+ * during a read.
+ */
+ if (ext_status_1 & DATA_IN_STATUS_BIT)
+ set_bit(RECEIVED_END_BN, &nec_priv->state);
+ } else {
+ clear_bit(RECEIVED_END_BN, &nec_priv->state);
+ }
+
+ if ((fifo_status & TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED) &&
+ (fifo_status & TX_FIFO_HALF_EMPTY)) {
+ /*
+ * We really only want to clear the
+ * TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE bit in the
+ * FIFO_CONTROL_STATUS_REG. Since we are not being
+ * careful, this also has a side effect of disabling
+ * DMA requests and the RX fifo interrupt. That is
+ * fine though, since they should never be in use at
+ * the same time as the TX fifo interrupt.
+ */
+ fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG);
+ retval = IRQ_HANDLED;
+ }
+
+ if ((fifo_status & RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED) &&
+ (fifo_status & RX_FIFO_HALF_FULL)) {
+ /*
+ * We really only want to clear the
+ * RX_FIFO_HALF_FULL_INTERRUPT_ENABLE bit in the
+ * FIFO_CONTROL_STATUS_REG. Since we are not being
+ * careful, this also has a side effect of disabling
+ * DMA requests and the TX fifo interrupt. That is
+ * fine though, since they should never be in use at
+ * the same time as the RX fifo interrupt.
+ */
+ fifos_write(priv, 0x0, FIFO_CONTROL_STATUS_REG);
+ retval = IRQ_HANDLED;
+ }
+
+ if (retval == IRQ_HANDLED)
+ wake_up_interruptible(&board->wait);
+
+ return retval;
+}
+
+irqreturn_t fmh_gpib_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = fmh_gpib_internal_interrupt(board);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+static int fmh_gpib_allocate_private(struct gpib_board *board)
+{
+ struct fmh_priv *priv;
+
+ board->private_data = kmalloc(sizeof(struct fmh_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ priv = board->private_data;
+ memset(priv, 0, sizeof(struct fmh_priv));
+ init_nec7210_private(&priv->nec7210_priv);
+ priv->dma_buffer_size = 0x800;
+ priv->dma_buffer = kmalloc(priv->dma_buffer_size, GFP_KERNEL);
+ if (!priv->dma_buffer)
+ return -ENOMEM;
+ return 0;
+}
+
+static void fmh_gpib_generic_detach(struct gpib_board *board)
+{
+ if (board->private_data) {
+ struct fmh_priv *e_priv = board->private_data;
+
+ kfree(e_priv->dma_buffer);
+ kfree(board->private_data);
+ board->private_data = NULL;
+ }
+ if (board->dev)
+ dev_set_drvdata(board->dev, NULL);
+}
+
+// generic part of attach functions
+static int fmh_gpib_generic_attach(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+
+ board->status = 0;
+
+ retval = fmh_gpib_allocate_private(board);
+ if (retval < 0)
+ return retval;
+ e_priv = board->private_data;
+ nec_priv = &e_priv->nec7210_priv;
+ nec_priv->read_byte = gpib_cs_read_byte;
+ nec_priv->write_byte = gpib_cs_write_byte;
+ nec_priv->offset = 1;
+ nec_priv->type = CB7210;
+ return 0;
+}
+
+static int fmh_gpib_config_dma(struct gpib_board *board, int output)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct dma_slave_config config;
+
+ config.device_fc = true;
+
+ if (e_priv->dma_burst_length < 1) {
+ config.src_maxburst = 1;
+ config.dst_maxburst = 1;
+ } else {
+ config.src_maxburst = e_priv->dma_burst_length;
+ config.dst_maxburst = e_priv->dma_burst_length;
+ }
+
+ config.src_addr_width = 1;
+ config.dst_addr_width = 1;
+
+ if (output) {
+ config.direction = DMA_MEM_TO_DEV;
+ config.src_addr = 0;
+ config.dst_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset;
+ } else {
+ config.direction = DMA_DEV_TO_MEM;
+ config.src_addr = e_priv->dma_port_res->start + FIFO_DATA_REG * fifo_reg_offset;
+ config.dst_addr = 0;
+ }
+ return dmaengine_slave_config(e_priv->dma_channel, &config);
+}
+
+static int fmh_gpib_init(struct fmh_priv *e_priv, struct gpib_board *board, int handshake_mode)
+{
+ struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
+ unsigned long flags;
+ unsigned int fifo_status_bits;
+
+ fifos_write(e_priv, RX_FIFO_CLEAR | TX_FIFO_CLEAR, FIFO_CONTROL_STATUS_REG);
+
+ nec7210_board_reset(nec_priv, board);
+ write_byte(nec_priv, AUX_LO_SPEED, AUXMR);
+ nec7210_set_handshake_mode(board, nec_priv, handshake_mode);
+
+ /* Hueristically check if hardware supports fifo half full/empty interrupts */
+ fifo_status_bits = fifos_read(e_priv, FIFO_CONTROL_STATUS_REG);
+ e_priv->supports_fifo_interrupts = (fifo_status_bits & TX_FIFO_EMPTY) &&
+ (fifo_status_bits & TX_FIFO_HALF_EMPTY);
+
+ nec7210_board_online(nec_priv, board);
+
+ write_byte(nec_priv, IFC_INTERRUPT_ENABLE_BIT | ATN_INTERRUPT_ENABLE_BIT, ISR0_IMR0_REG);
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ write_byte(nec_priv, AUX_RFD_HOLDOFF_ASAP, AUXMR);
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return 0;
+}
+
+/* Match callback for driver_find_device */
+static int fmh_gpib_device_match(struct device *dev, const void *data)
+{
+ const struct gpib_board_config *config = data;
+
+ if (dev_get_drvdata(dev))
+ return 0;
+
+ if (gpib_match_device_path(dev, config->device_path) == 0)
+ return 0;
+
+ // driver doesn't support selection by serial number
+ if (config->serial_number)
+ return 0;
+
+ dev_dbg(dev, "matched: %s\n", of_node_full_name(dev_of_node((dev))));
+ return 1;
+}
+
+static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_board_config *config,
+ unsigned int handshake_mode, int acquire_dma)
+{
+ struct fmh_priv *e_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+ int irq;
+ struct resource *res;
+ struct platform_device *pdev;
+
+ board->dev = driver_find_device(&fmh_gpib_platform_driver.driver,
+ NULL, (const void *)config, &fmh_gpib_device_match);
+ if (!board->dev) {
+ dev_err(board->gpib_dev, "No matching fmh_gpib_core device was found, attach failed.");
+ return -ENODEV;
+ }
+ // currently only used to mark the device as already attached
+ dev_set_drvdata(board->dev, board);
+ pdev = to_platform_device(board->dev);
+
+ retval = fmh_gpib_generic_attach(board);
+ if (retval)
+ return retval;
+
+ e_priv = board->private_data;
+ nec_priv = &e_priv->nec7210_priv;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpib_control_status");
+ if (!res) {
+ dev_err(board->dev, "Unable to locate mmio resource\n");
+ return -ENODEV;
+ }
+
+ if (request_mem_region(res->start,
+ resource_size(res),
+ pdev->name) == NULL) {
+ dev_err(board->dev, "cannot claim registers\n");
+ return -ENXIO;
+ }
+ e_priv->gpib_iomem_res = res;
+
+ nec_priv->mmiobase = ioremap(e_priv->gpib_iomem_res->start,
+ resource_size(e_priv->gpib_iomem_res));
+ if (!nec_priv->mmiobase) {
+ dev_err(board->dev, "Could not map I/O memory\n");
+ return -ENOMEM;
+ }
+ dev_dbg(board->dev, "iobase %pr remapped to %p\n",
+ e_priv->gpib_iomem_res, nec_priv->mmiobase);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma_fifos");
+ if (!res) {
+ dev_err(board->dev, "Unable to locate mmio resource for gpib dma port\n");
+ return -ENODEV;
+ }
+ if (request_mem_region(res->start,
+ resource_size(res),
+ pdev->name) == NULL) {
+ dev_err(board->dev, "cannot claim registers\n");
+ return -ENXIO;
+ }
+ e_priv->dma_port_res = res;
+ e_priv->fifo_base = ioremap(e_priv->dma_port_res->start,
+ resource_size(e_priv->dma_port_res));
+ if (!e_priv->fifo_base) {
+ dev_err(board->dev, "Could not map I/O memory for fifos\n");
+ return -ENOMEM;
+ }
+ dev_dbg(board->dev, "dma fifos 0x%lx remapped to %p, length=%ld\n",
+ (unsigned long)e_priv->dma_port_res->start, e_priv->fifo_base,
+ (unsigned long)resource_size(e_priv->dma_port_res));
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -EBUSY;
+ retval = request_irq(irq, fmh_gpib_interrupt, IRQF_SHARED, pdev->name, board);
+ if (retval) {
+ dev_err(board->dev,
+ "cannot register interrupt handler err=%d\n",
+ retval);
+ return retval;
+ }
+ e_priv->irq = irq;
+
+ if (acquire_dma) {
+ e_priv->dma_channel = dma_request_slave_channel(board->dev, "rxtx");
+ if (!e_priv->dma_channel) {
+ dev_err(board->dev, "failed to acquire dma channel \"rxtx\".\n");
+ return -EIO;
+ }
+ }
+ /*
+ * in the future we might want to know the half-fifo size
+ * (dma_burst_length) even when not using dma, so go ahead an
+ * initialize it unconditionally.
+ */
+ e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) &
+ fifo_max_burst_length_mask;
+
+ return fmh_gpib_init(e_priv, board, handshake_mode);
+}
+
+int fmh_gpib_attach_holdoff_all(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return fmh_gpib_attach_impl(board, config, HR_HLDA, 0);
+}
+
+int fmh_gpib_attach_holdoff_end(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return fmh_gpib_attach_impl(board, config, HR_HLDE, 1);
+}
+
+void fmh_gpib_detach(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (e_priv) {
+ if (e_priv->dma_channel)
+ dma_release_channel(e_priv->dma_channel);
+ nec_priv = &e_priv->nec7210_priv;
+
+ if (e_priv->irq)
+ free_irq(e_priv->irq, board);
+ if (e_priv->fifo_base)
+ fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG);
+ if (nec_priv->mmiobase) {
+ write_byte(nec_priv, 0, ISR0_IMR0_REG);
+ nec7210_board_reset(nec_priv, board);
+ }
+ if (e_priv->fifo_base)
+ iounmap(e_priv->fifo_base);
+ if (nec_priv->mmiobase)
+ iounmap(nec_priv->mmiobase);
+ if (e_priv->dma_port_res) {
+ release_mem_region(e_priv->dma_port_res->start,
+ resource_size(e_priv->dma_port_res));
+ }
+ if (e_priv->gpib_iomem_res)
+ release_mem_region(e_priv->gpib_iomem_res->start,
+ resource_size(e_priv->gpib_iomem_res));
+ }
+ fmh_gpib_generic_detach(board);
+
+ if (board->dev) {
+ put_device(board->dev);
+ board->dev = NULL;
+ }
+}
+
+static int fmh_gpib_pci_attach_impl(struct gpib_board *board,
+ const struct gpib_board_config *config,
+ unsigned int handshake_mode)
+{
+ struct fmh_priv *e_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+ struct pci_dev *pci_device;
+
+ retval = fmh_gpib_generic_attach(board);
+ if (retval)
+ return retval;
+
+ e_priv = board->private_data;
+ nec_priv = &e_priv->nec7210_priv;
+
+ // find board
+ pci_device = gpib_pci_get_device(config, BOGUS_PCI_VENDOR_ID_FLUKE,
+ BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, NULL);
+ if (!pci_device) {
+ dev_err(board->gpib_dev, "No matching fmh_gpib_core pci device was found, attach failed.");
+ return -ENODEV;
+ }
+ board->dev = &pci_device->dev;
+
+ // bladerunner prototype has offset of 4 between gpib control/status registers
+ nec_priv->offset = 4;
+
+ if (pci_enable_device(pci_device)) {
+ dev_err(board->dev, "error enabling pci device\n");
+ return -EIO;
+ }
+ if (pci_request_regions(pci_device, KBUILD_MODNAME)) {
+ dev_err(board->dev, "pci_request_regions failed\n");
+ return -EIO;
+ }
+ e_priv->gpib_iomem_res = &pci_device->resource[gpib_control_status_pci_resource_index];
+ e_priv->dma_port_res = &pci_device->resource[gpib_fifo_pci_resource_index];
+
+ nec_priv->mmiobase = ioremap(pci_resource_start(pci_device,
+ gpib_control_status_pci_resource_index),
+ pci_resource_len(pci_device,
+ gpib_control_status_pci_resource_index));
+ dev_dbg(board->dev, "base address for gpib control/status registers remapped to 0x%p\n",
+ nec_priv->mmiobase);
+
+ if (e_priv->dma_port_res->flags & IORESOURCE_MEM) {
+ e_priv->fifo_base = ioremap(pci_resource_start(pci_device,
+ gpib_fifo_pci_resource_index),
+ pci_resource_len(pci_device,
+ gpib_fifo_pci_resource_index));
+ dev_dbg(board->dev, "base address for gpib fifo registers remapped to 0x%p\n",
+ e_priv->fifo_base);
+ } else {
+ e_priv->fifo_base = NULL;
+ dev_dbg(board->dev, "hardware has no gpib fifo registers.\n");
+ }
+
+ if (pci_device->irq) {
+ retval = request_irq(pci_device->irq, fmh_gpib_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, board);
+ if (retval) {
+ dev_err(board->dev, "cannot register interrupt handler err=%d\n", retval);
+ return retval;
+ }
+ }
+ e_priv->irq = pci_device->irq;
+
+ e_priv->dma_burst_length = fifos_read(e_priv, FIFO_MAX_BURST_LENGTH_REG) &
+ fifo_max_burst_length_mask;
+
+ return fmh_gpib_init(e_priv, board, handshake_mode);
+}
+
+int fmh_gpib_pci_attach_holdoff_all(struct gpib_board *board,
+ const struct gpib_board_config *config)
+{
+ return fmh_gpib_pci_attach_impl(board, config, HR_HLDA);
+}
+
+int fmh_gpib_pci_attach_holdoff_end(struct gpib_board *board,
+ const struct gpib_board_config *config)
+{
+ int retval;
+ struct fmh_priv *e_priv;
+
+ retval = fmh_gpib_pci_attach_impl(board, config, HR_HLDE);
+ e_priv = board->private_data;
+ if (retval == 0 && e_priv && e_priv->supports_fifo_interrupts == 0) {
+ dev_err(board->gpib_dev, "your fmh_gpib_core does not appear to support fifo interrupts. Try the fmh_gpib_pci_unaccel board type instead.");
+ return -EIO;
+ }
+ return retval;
+}
+
+void fmh_gpib_pci_detach(struct gpib_board *board)
+{
+ struct fmh_priv *e_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (e_priv) {
+ nec_priv = &e_priv->nec7210_priv;
+
+ if (e_priv->irq)
+ free_irq(e_priv->irq, board);
+ if (e_priv->fifo_base)
+ fifos_write(e_priv, 0, FIFO_CONTROL_STATUS_REG);
+ if (nec_priv->mmiobase) {
+ write_byte(nec_priv, 0, ISR0_IMR0_REG);
+ nec7210_board_reset(nec_priv, board);
+ }
+ if (e_priv->fifo_base)
+ iounmap(e_priv->fifo_base);
+ if (nec_priv->mmiobase)
+ iounmap(nec_priv->mmiobase);
+ if (e_priv->dma_port_res || e_priv->gpib_iomem_res)
+ pci_release_regions(to_pci_dev(board->dev));
+ if (board->dev)
+ pci_dev_put(to_pci_dev(board->dev));
+ }
+ fmh_gpib_generic_detach(board);
+}
+
+static int fmh_gpib_platform_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static const struct of_device_id fmh_gpib_of_match[] = {
+ { .compatible = "fmhess,fmh_gpib_core"},
+ { {0} }
+};
+MODULE_DEVICE_TABLE(of, fmh_gpib_of_match);
+
+static struct platform_driver fmh_gpib_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = fmh_gpib_of_match,
+ },
+ .probe = &fmh_gpib_platform_probe
+};
+
+static int fmh_gpib_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return 0;
+}
+
+static const struct pci_device_id fmh_gpib_pci_match[] = {
+ { BOGUS_PCI_VENDOR_ID_FLUKE, BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, fmh_gpib_pci_match);
+
+static struct pci_driver fmh_gpib_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = fmh_gpib_pci_match,
+ .probe = &fmh_gpib_pci_probe
+};
+
+static int __init fmh_gpib_init_module(void)
+{
+ int result;
+
+ result = platform_driver_register(&fmh_gpib_platform_driver);
+ if (result) {
+ pr_err("platform_driver_register failed: error = %d\n", result);
+ return result;
+ }
+
+ result = pci_register_driver(&fmh_gpib_pci_driver);
+ if (result) {
+ pr_err("pci_register_driver failed: error = %d\n", result);
+ goto err_pci_driver;
+ }
+
+ result = gpib_register_driver(&fmh_gpib_unaccel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_unaccel;
+ }
+
+ result = gpib_register_driver(&fmh_gpib_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_interface;
+ }
+
+ result = gpib_register_driver(&fmh_gpib_pci_unaccel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_pci_unaccel;
+ }
+
+ result = gpib_register_driver(&fmh_gpib_pci_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_pci;
+ }
+
+ return 0;
+
+err_pci:
+ gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface);
+err_pci_unaccel:
+ gpib_unregister_driver(&fmh_gpib_interface);
+err_interface:
+ gpib_unregister_driver(&fmh_gpib_unaccel_interface);
+err_unaccel:
+ pci_unregister_driver(&fmh_gpib_pci_driver);
+err_pci_driver:
+ platform_driver_unregister(&fmh_gpib_platform_driver);
+
+ return result;
+}
+
+static void __exit fmh_gpib_exit_module(void)
+{
+ gpib_unregister_driver(&fmh_gpib_pci_interface);
+ gpib_unregister_driver(&fmh_gpib_pci_unaccel_interface);
+ gpib_unregister_driver(&fmh_gpib_unaccel_interface);
+ gpib_unregister_driver(&fmh_gpib_interface);
+
+ pci_unregister_driver(&fmh_gpib_pci_driver);
+ platform_driver_unregister(&fmh_gpib_platform_driver);
+}
+
+module_init(fmh_gpib_init_module);
+module_exit(fmh_gpib_exit_module);
diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.h b/drivers/gpib/fmh_gpib/fmh_gpib.h
new file mode 100644
index 000000000000..e7602d7e1401
--- /dev/null
+++ b/drivers/gpib/fmh_gpib/fmh_gpib.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Author: Frank Mori Hess <fmh6jj@gmail.com>
+ * Copyright: (C) 2006, 2010, 2015 Fluke Corporation
+ * (C) 2017 Frank Mori Hess
+ ***************************************************************************/
+
+#include <linux/dmaengine.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include "nec7210.h"
+
+static const int fifo_reg_offset = 2;
+
+static const int gpib_control_status_pci_resource_index;
+static const int gpib_fifo_pci_resource_index = 1;
+
+/* We don't have a real pci vendor/device id, the following will need to be
+ * patched to match prototype hardware.
+ */
+#define BOGUS_PCI_VENDOR_ID_FLUKE 0xffff
+#define BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER 0x0
+
+struct fmh_priv {
+ struct nec7210_priv nec7210_priv;
+ struct resource *gpib_iomem_res;
+ struct resource *write_transfer_counter_res;
+ struct resource *dma_port_res;
+ int irq;
+ struct dma_chan *dma_channel;
+ u8 *dma_buffer;
+ int dma_buffer_size;
+ int dma_burst_length;
+ void __iomem *fifo_base;
+ unsigned supports_fifo_interrupts : 1;
+};
+
+static inline int fmh_gpib_half_fifo_size(struct fmh_priv *priv)
+{
+ return priv->dma_burst_length;
+}
+
+// registers beyond the nec7210 register set
+enum fmh_gpib_regs {
+ EXT_STATUS_1_REG = 0x9,
+ STATE1_REG = 0xc,
+ ISR0_IMR0_REG = 0xe,
+ BUS_STATUS_REG = 0xf
+};
+
+/* IMR0 -- Interrupt Mode Register 0 */
+enum imr0_bits {
+ ATN_INTERRUPT_ENABLE_BIT = 0x4,
+ IFC_INTERRUPT_ENABLE_BIT = 0x8
+};
+
+/* ISR0 -- Interrupt Status Register 0 */
+enum isr0_bits {
+ ATN_INTERRUPT_BIT = 0x4,
+ IFC_INTERRUPT_BIT = 0x8
+};
+
+enum state1_bits {
+ SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */
+ SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */
+ SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */
+ SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */
+ SOURCE_HANDSHAKE_MASK = 0x7
+};
+
+enum fmh_gpib_auxmr_bits {
+ AUX_I_REG = 0xe0,
+};
+
+enum aux_reg_i_bits {
+ LOCAL_PPOLL_MODE_BIT = 0x4
+};
+
+enum ext_status_1_bits {
+ DATA_IN_STATUS_BIT = 0x01,
+ DATA_OUT_STATUS_BIT = 0x02,
+ COMMAND_OUT_STATUS_BIT = 0x04,
+ RFD_HOLDOFF_STATUS_BIT = 0x08,
+ END_STATUS_BIT = 0x10
+};
+
+/* dma fifo reg and bits */
+enum dma_fifo_regs {
+ FIFO_DATA_REG = 0x0,
+ FIFO_CONTROL_STATUS_REG = 0x1,
+ FIFO_XFER_COUNTER_REG = 0x2,
+ FIFO_MAX_BURST_LENGTH_REG = 0x3
+};
+
+enum fifo_data_bits {
+ FIFO_DATA_EOI_FLAG = 0x100
+};
+
+enum fifo_control_bits {
+ TX_FIFO_DMA_REQUEST_ENABLE = 0x0001,
+ TX_FIFO_CLEAR = 0x0002,
+ TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE = 0x0008,
+ RX_FIFO_DMA_REQUEST_ENABLE = 0x0100,
+ RX_FIFO_CLEAR = 0x0200,
+ RX_FIFO_HALF_FULL_INTERRUPT_ENABLE = 0x0800
+};
+
+enum fifo_status_bits {
+ TX_FIFO_EMPTY = 0x0001,
+ TX_FIFO_FULL = 0x0002,
+ TX_FIFO_HALF_EMPTY = 0x0004,
+ TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED = 0x0008,
+ TX_FIFO_DMA_REQUEST_IS_ENABLED = 0x0010,
+ RX_FIFO_EMPTY = 0x0100,
+ RX_FIFO_FULL = 0x0200,
+ RX_FIFO_HALF_FULL = 0x0400,
+ RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED = 0x0800,
+ RX_FIFO_DMA_REQUEST_IS_ENABLED = 0x1000
+};
+
+static const unsigned int fifo_data_mask = 0x00ff;
+static const unsigned int fifo_xfer_counter_mask = 0x0fff;
+static const unsigned int fifo_max_burst_length_mask = 0x00ff;
+
+static inline u8 gpib_cs_read_byte(struct nec7210_priv *nec_priv,
+ unsigned int register_num)
+{
+ return readb(nec_priv->mmiobase + register_num * nec_priv->offset);
+}
+
+static inline void gpib_cs_write_byte(struct nec7210_priv *nec_priv, u8 data,
+ unsigned int register_num)
+{
+ writeb(data, nec_priv->mmiobase + register_num * nec_priv->offset);
+}
+
+static inline uint16_t fifos_read(struct fmh_priv *fmh_priv, int register_num)
+{
+ if (!fmh_priv->fifo_base)
+ return 0;
+ return readw(fmh_priv->fifo_base + register_num * fifo_reg_offset);
+}
+
+static inline void fifos_write(struct fmh_priv *fmh_priv, uint16_t data, int register_num)
+{
+ if (!fmh_priv->fifo_base)
+ return;
+ writew(data, fmh_priv->fifo_base + register_num * fifo_reg_offset);
+}
+
+enum bus_status_bits {
+ BSR_ATN_BIT = 0x01,
+ BSR_EOI_BIT = 0x02,
+ BSR_SRQ_BIT = 0x04,
+ BSR_IFC_BIT = 0x08,
+ BSR_REN_BIT = 0x10,
+ BSR_DAV_BIT = 0x20,
+ BSR_NRFD_BIT = 0x40,
+ BSR_NDAC_BIT = 0x80,
+};
+
+enum fmh_gpib_aux_cmds {
+ /* AUX_RTL2 is an auxiliary command which causes the cb7210 to assert
+ * (and keep asserted) the local rtl message. This is used in conjunction
+ * with the normal nec7210 AUX_RTL command, which
+ * pulses the rtl message, having the effect of clearing rtl if it was left
+ * asserted by AUX_RTL2.
+ */
+ AUX_RTL2 = 0x0d,
+ AUX_RFD_HOLDOFF_ASAP = 0x15,
+ AUX_REQT = 0x18,
+ AUX_REQF = 0x19,
+ AUX_LO_SPEED = 0x40,
+ AUX_HI_SPEED = 0x41
+};
diff --git a/drivers/gpib/gpio/Makefile b/drivers/gpib/gpio/Makefile
new file mode 100644
index 000000000000..00ea52abdda7
--- /dev/null
+++ b/drivers/gpib/gpio/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_GPIB_GPIO) += gpib_bitbang.o
+
+
diff --git a/drivers/gpib/gpio/gpib_bitbang.c b/drivers/gpib/gpio/gpib_bitbang.c
new file mode 100644
index 000000000000..374cd61355e9
--- /dev/null
+++ b/drivers/gpib/gpio/gpib_bitbang.c
@@ -0,0 +1,1469 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*************************************************************************
+ * This code has been developed at the Institute of Sensor and Actuator *
+ * Systems (Technical University of Vienna, Austria) to enable the GPIO *
+ * lines (e.g. of a raspberry pi) to function as a GPIO master device *
+ * *
+ * authors : Thomas Klima *
+ * Marcello Carla' *
+ * Dave Penkler *
+ * *
+ * copyright : (C) 2016 Thomas Klima *
+ * *
+ *************************************************************************/
+
+/*
+ * limitations:
+ * works only on RPi
+ * cannot function as non-CIC system controller with SN7516x because
+ * SN75161B cannot simultaneously make ATN input with IFC and REN as
+ * outputs.
+ * not implemented:
+ * parallel poll
+ * return2local
+ * device support (non master operation)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define NAME KBUILD_MODNAME
+
+#define ENABLE_IRQ(IRQ, TYPE) irq_set_irq_type(IRQ, TYPE)
+#define DISABLE_IRQ(IRQ) irq_set_irq_type(IRQ, IRQ_TYPE_NONE)
+
+/*
+ * Debug print levels:
+ * 0 = load/unload info and errors that make the driver fail;
+ * 1 = + warnings for unforeseen events that may break the current
+ * operation and lead to a timeout, but do not affect the
+ * driver integrity (mainly unexpected interrupts);
+ * 2 = + trace of function calls;
+ * 3 = + trace of protocol codes;
+ * 4 = + trace of interrupt operation.
+ */
+#define dbg_printk(level, frm, ...) \
+ do { if (debug >= (level)) \
+ dev_dbg(board->gpib_dev, frm, ## __VA_ARGS__); } \
+ while (0)
+
+#define LINVAL gpiod_get_value(DAV), \
+ gpiod_get_value(NRFD), \
+ gpiod_get_value(NDAC), \
+ gpiod_get_value(SRQ)
+#define LINFMT "DAV: %d NRFD:%d NDAC: %d SRQ: %d"
+
+#include "gpibP.h"
+#include "gpib_state_machines.h"
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+
+static int sn7516x_used = 1, sn7516x;
+module_param(sn7516x_used, int, 0660);
+
+#define PINMAP_0 "elektronomikon"
+#define PINMAP_1 "gpib4pi-1.1"
+#define PINMAP_2 "yoga"
+static char *pin_map = PINMAP_0;
+module_param(pin_map, charp, 0660);
+MODULE_PARM_DESC(pin_map, " valid values: " PINMAP_0 " " PINMAP_1 " " PINMAP_2);
+
+/**********************************************
+ * Signal pairing and pin wiring between the *
+ * Raspberry-Pi connector and the GPIB bus *
+ * *
+ * signal pin wiring *
+ * GPIB Pi-gpio GPIB -> RPi *
+ **********************************************
+ */
+enum lines_t {
+ D01_pin_nr = 20, /* 1 -> 38 */
+ D02_pin_nr = 26, /* 2 -> 37 */
+ D03_pin_nr = 16, /* 3 -> 36 */
+ D04_pin_nr = 19, /* 4 -> 35 */
+ D05_pin_nr = 13, /* 13 -> 33 */
+ D06_pin_nr = 12, /* 14 -> 32 */
+ D07_pin_nr = 6, /* 15 -> 31 */
+ D08_pin_nr = 5, /* 16 -> 29 */
+ EOI_pin_nr = 9, /* 5 -> 21 */
+ DAV_pin_nr = 10, /* 6 -> 19 */
+ NRFD_pin_nr = 24, /* 7 -> 18 */
+ NDAC_pin_nr = 23, /* 8 -> 16 */
+ IFC_pin_nr = 22, /* 9 -> 15 */
+ SRQ_pin_nr = 11, /* 10 -> 23 */
+ _ATN_pin_nr = 25, /* 11 -> 22 */
+ REN_pin_nr = 27, /* 17 -> 13 */
+/*
+ * GROUND PINS
+ * 12,18,19,20,21,22,23,24 => 14,20,25,30,34,39
+ */
+
+/*
+ * These lines are used to control the external
+ * SN75160/161 driver chips when used.
+ * When not used there is reduced fan out;
+ * currently tested with up to 4 devices.
+ */
+
+/* Pi GPIO RPI 75161B 75160B Description */
+ PE_pin_nr = 7, /* 26 -> nc 11 Pullup Enable */
+ DC_pin_nr = 8, /* 24 -> 12 nc Direction control */
+ TE_pin_nr = 18, /* 12 -> 2 1 Talk Enable */
+ ACT_LED_pin_nr = 4, /* 7 -> LED */
+
+/* YOGA adapter uses different pinout to ease layout */
+ YOGA_D03_pin_nr = 13,
+ YOGA_D04_pin_nr = 12,
+ YOGA_D05_pin_nr = 21,
+ YOGA_D06_pin_nr = 19,
+};
+
+/*
+ * GPIO descriptors and pins - WARNING: STRICTLY KEEP ITEMS ORDER
+ */
+
+#define GPIB_PINS 16
+#define SN7516X_PINS 4
+#define NUM_PINS (GPIB_PINS + SN7516X_PINS)
+
+#define ACT_LED_ON do { \
+ if (ACT_LED) \
+ gpiod_direction_output(ACT_LED, 1); \
+ } while (0)
+#define ACT_LED_OFF do { \
+ if (ACT_LED) \
+ gpiod_direction_output(ACT_LED, 0); \
+ } while (0)
+
+static struct gpio_desc *all_descriptors[GPIB_PINS + SN7516X_PINS];
+
+#define D01 all_descriptors[0]
+#define D02 all_descriptors[1]
+#define D03 all_descriptors[2]
+#define D04 all_descriptors[3]
+#define D05 all_descriptors[4]
+#define D06 all_descriptors[5]
+#define D07 all_descriptors[6]
+#define D08 all_descriptors[7]
+
+#define EOI all_descriptors[8]
+#define NRFD all_descriptors[9]
+#define IFC all_descriptors[10]
+#define _ATN all_descriptors[11]
+#define REN all_descriptors[12]
+#define DAV all_descriptors[13]
+#define NDAC all_descriptors[14]
+#define SRQ all_descriptors[15]
+
+#define PE all_descriptors[16]
+#define DC all_descriptors[17]
+#define TE all_descriptors[18]
+#define ACT_LED all_descriptors[19]
+
+/* YOGA adapter uses a global enable for the buffer chips, re-using the TE pin */
+#define YOGA_ENABLE TE
+
+static int gpios_vector[] = {
+ D01_pin_nr,
+ D02_pin_nr,
+ D03_pin_nr,
+ D04_pin_nr,
+ D05_pin_nr,
+ D06_pin_nr,
+ D07_pin_nr,
+ D08_pin_nr,
+
+ EOI_pin_nr,
+ NRFD_pin_nr,
+ IFC_pin_nr,
+ _ATN_pin_nr,
+ REN_pin_nr,
+ DAV_pin_nr,
+ NDAC_pin_nr,
+ SRQ_pin_nr,
+
+ PE_pin_nr,
+ DC_pin_nr,
+ TE_pin_nr,
+ ACT_LED_pin_nr
+};
+
+/* Lookup table for general GPIOs */
+
+static struct gpiod_lookup_table gpib_gpio_table_1 = {
+ // for bcm2835/6
+ .dev_id = "", // device id of board device
+ .table = {
+ GPIO_LOOKUP_IDX("GPIO_GCLK", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("SPI_CE1_N", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("SPI_CE0_N", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("SPI_MISO", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("SPI_MOSI", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("SPI_SCLK", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH),
+ { }
+ },
+};
+
+static struct gpiod_lookup_table gpib_gpio_table_0 = {
+ .dev_id = "", // device id of board device
+ .table = {
+ // for bcm27xx based pis (b b+ 2b 3b 3b+ 4 5)
+ GPIO_LOOKUP_IDX("GPIO4", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO7", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO8", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO9", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO10", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO11", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH),
+ GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH),
+ { }
+ },
+};
+
+static struct gpiod_lookup_table *lookup_tables[] = {
+ &gpib_gpio_table_0,
+ &gpib_gpio_table_1,
+ NULL
+};
+
+/* struct which defines private_data for gpio driver */
+
+struct bb_priv {
+ int irq_NRFD;
+ int irq_NDAC;
+ int irq_DAV;
+ int irq_SRQ;
+ int dav_mode; /* dav interrupt mode 0/1 -> edge/levels */
+ int nrfd_mode; /* nrfd interrupt mode 0/1 -> edge/levels */
+ int ndac_mode; /* nrfd interrupt mode 0/1 -> edge/levels */
+ int dav_tx; /* keep trace of DAV status while sending */
+ int dav_rx; /* keep trace of DAV status while receiving */
+ u8 eos; /* eos character */
+ short eos_flags; /* eos mode */
+ short eos_check; /* eos check required in current operation ... */
+ short eos_check_8; /* ... with byte comparison */
+ short eos_mask_7; /* ... with 7 bit masked character */
+ short int end;
+ int request;
+ int count;
+ int direction;
+ int t1_delay;
+ u8 *rbuf;
+ u8 *wbuf;
+ int end_flag;
+ int r_busy; /* 0==idle 1==busy */
+ int w_busy;
+ int write_done;
+ int cmd; /* 1 = cmd write in progress */
+ size_t w_cnt;
+ size_t length;
+ u8 *w_buf;
+ spinlock_t rw_lock; /* protect mods to rw_lock */
+ int phase;
+ int ndac_idle;
+ int ndac_seq;
+ int nrfd_idle;
+ int nrfd_seq;
+ int dav_seq;
+ long all_irqs;
+ int dav_idle;
+
+ enum talker_function_state talker_state;
+ enum listener_function_state listener_state;
+};
+
+static inline long usec_diff(struct timespec64 *a, struct timespec64 *b);
+static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length,
+ int cmd, int eoi);
+static void set_data_lines(u8 byte);
+static u8 get_data_lines(void);
+static void set_data_lines_input(void);
+static void set_data_lines_output(void);
+static inline int check_for_eos(struct bb_priv *priv, u8 byte);
+static void set_atn(struct gpib_board *board, int atn_asserted);
+
+static inline void SET_DIR_WRITE(struct bb_priv *priv);
+static inline void SET_DIR_READ(struct bb_priv *priv);
+
+#define DIR_READ 0
+#define DIR_WRITE 1
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB helper functions for bitbanging I/O");
+
+/**** global variables ****/
+static int debug;
+module_param(debug, int, 0644);
+
+static char printable(char x)
+{
+ if (x < 32 || x > 126)
+ return ' ';
+ return x;
+}
+
+/***************************************************************************
+ * *
+ * READ *
+ * *
+ ***************************************************************************/
+
+static int bb_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct bb_priv *priv = board->private_data;
+ unsigned long flags;
+ int retval = 0;
+
+ ACT_LED_ON;
+ SET_DIR_READ(priv);
+
+ dbg_printk(2, "board: %p lock %d length: %zu\n",
+ board, mutex_is_locked(&board->user_mutex), length);
+
+ priv->end = 0;
+ priv->count = 0;
+ priv->rbuf = buffer;
+ if (length == 0)
+ goto read_end;
+ priv->request = length;
+ priv->eos_check = (priv->eos_flags & REOS) == 0; /* do eos check */
+ priv->eos_check_8 = priv->eos_flags & BIN; /* over 8 bits */
+ priv->eos_mask_7 = priv->eos & 0x7f; /* with this 7 bit eos */
+
+ dbg_printk(3, ".........." LINFMT "\n", LINVAL);
+
+ spin_lock_irqsave(&priv->rw_lock, flags);
+ priv->dav_mode = 1;
+ priv->dav_rx = 1;
+ ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_LEVEL_LOW);
+ priv->end_flag = 0;
+ gpiod_set_value(NRFD, 1); // ready for data
+ priv->r_busy = 1;
+ priv->phase = 100;
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+
+ /* wait for the interrupt routines finish their work */
+
+ retval = wait_event_interruptible(board->wait,
+ (priv->end_flag || board->status & TIMO));
+
+ dbg_printk(3, "awake from wait queue: %d\n", retval);
+
+ if (retval == 0 && board->status & TIMO) {
+ retval = -ETIMEDOUT;
+ dbg_printk(1, "timeout\n");
+ } else if (retval) {
+ retval = -ERESTARTSYS;
+ }
+
+ DISABLE_IRQ(priv->irq_DAV);
+ spin_lock_irqsave(&priv->rw_lock, flags);
+ gpiod_set_value(NRFD, 0); // DIR_READ line state
+ priv->r_busy = 0;
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+
+read_end:
+ ACT_LED_OFF;
+ *bytes_read = priv->count;
+ *end = priv->end;
+ priv->r_busy = 0;
+ dbg_printk(2, "return: %d eoi|eos: %d count: %d\n\n", retval, priv->end, priv->count);
+ return retval;
+}
+
+/***************************************************************************
+ * *
+ * READ interrupt routine (DAV line) *
+ * *
+ ***************************************************************************/
+
+static irqreturn_t bb_DAV_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct bb_priv *priv = board->private_data;
+ int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->rw_lock, flags);
+
+ priv->all_irqs++;
+
+ if (priv->dav_mode) {
+ ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_EDGE_BOTH);
+ priv->dav_mode = 0;
+ }
+
+ if (priv->r_busy == 0) {
+ dbg_printk(1, "interrupt while idle after %d at %d\n",
+ priv->count, priv->phase);
+ priv->dav_idle++;
+ priv->phase = 200;
+ goto dav_exit; /* idle */
+ }
+
+ val = gpiod_get_value(DAV);
+ if (val == priv->dav_rx) {
+ dbg_printk(1, "out of order DAV interrupt %d/%d after %zu/%zu at %d cmd %d "
+ LINFMT ".\n", val, priv->dav_rx, priv->w_cnt, priv->length,
+ priv->phase, priv->cmd, LINVAL);
+ priv->dav_seq++;
+ }
+ priv->dav_rx = val;
+
+ dbg_printk(3, "> irq: %d DAV: %d st: %4lx dir: %d busy: %d:%d\n",
+ irq, val, board->status, priv->direction, priv->r_busy, priv->w_busy);
+
+ if (val == 0) {
+ gpiod_set_value(NRFD, 0); // not ready for data
+ priv->rbuf[priv->count++] = get_data_lines();
+ priv->end = !gpiod_get_value(EOI);
+ gpiod_set_value(NDAC, 1); // data accepted
+ priv->end |= check_for_eos(priv, priv->rbuf[priv->count - 1]);
+ priv->end_flag = ((priv->count >= priv->request) || priv->end);
+ priv->phase = 210;
+ } else {
+ gpiod_set_value(NDAC, 0); // data not accepted
+ if (priv->end_flag) {
+ priv->r_busy = 0;
+ wake_up_interruptible(&board->wait);
+ priv->phase = 220;
+ } else {
+ gpiod_set_value(NRFD, 1); // ready for data
+ priv->phase = 230;
+ }
+ }
+
+dav_exit:
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+ dbg_printk(3, "< irq: %d count %d\n", irq, priv->count);
+ return IRQ_HANDLED;
+}
+
+/***************************************************************************
+ * *
+ * WRITE *
+ * *
+ ***************************************************************************/
+
+static int bb_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ unsigned long flags;
+ int retval = 0;
+
+ struct bb_priv *priv = board->private_data;
+
+ ACT_LED_ON;
+
+ priv->w_cnt = 0;
+ priv->w_buf = buffer;
+ dbg_printk(2, "board %p lock %d length: %zu\n",
+ board, mutex_is_locked(&board->user_mutex), length);
+
+ if (debug > 1)
+ bb_buffer_print(board, buffer, length, priv->cmd, send_eoi);
+ priv->count = 0;
+ priv->phase = 300;
+
+ if (length == 0)
+ goto write_end;
+ priv->end = send_eoi;
+ priv->length = length;
+
+ SET_DIR_WRITE(priv);
+
+ dbg_printk(2, "Enabling interrupts - NRFD: %d NDAC: %d\n",
+ gpiod_get_value(NRFD), gpiod_get_value(NDAC));
+
+ if (gpiod_get_value(NRFD) && gpiod_get_value(NDAC)) { /* check for listener */
+ retval = -ENOTCONN;
+ goto write_end;
+ }
+
+ spin_lock_irqsave(&priv->rw_lock, flags);
+ priv->w_busy = 1; /* make the interrupt routines active */
+ priv->write_done = 0;
+ priv->nrfd_mode = 1;
+ priv->ndac_mode = 1;
+ priv->dav_tx = 1;
+ ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_LEVEL_HIGH);
+ ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_LEVEL_HIGH);
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+
+ /* wait for the interrupt routines finish their work */
+
+ retval = wait_event_interruptible(board->wait,
+ priv->write_done || (board->status & TIMO));
+
+ dbg_printk(3, "awake from wait queue: %d\n", retval);
+
+ if (retval == 0) {
+ if (board->status & TIMO) {
+ retval = -ETIMEDOUT;
+ dbg_printk(1, "timeout after %zu/%zu at %d " LINFMT " eoi: %d\n",
+ priv->w_cnt, length, priv->phase, LINVAL, send_eoi);
+ } else {
+ retval = priv->w_cnt;
+ }
+ } else {
+ retval = -ERESTARTSYS;
+ }
+
+ DISABLE_IRQ(priv->irq_NRFD);
+ DISABLE_IRQ(priv->irq_NDAC);
+
+ spin_lock_irqsave(&priv->rw_lock, flags);
+ priv->w_busy = 0;
+ gpiod_set_value(DAV, 1); // DIR_WRITE line state
+ gpiod_set_value(EOI, 1); // De-assert EOI (in case)
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+
+write_end:
+ *bytes_written = priv->w_cnt;
+ ACT_LED_OFF;
+ dbg_printk(2, "sent %zu bytes\r\n\r\n", *bytes_written);
+ priv->phase = 310;
+ return retval;
+}
+
+/***************************************************************************
+ * *
+ * WRITE interrupt routine (NRFD line) *
+ * *
+ ***************************************************************************/
+
+static irqreturn_t bb_NRFD_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct bb_priv *priv = board->private_data;
+ unsigned long flags;
+ int nrfd;
+
+ spin_lock_irqsave(&priv->rw_lock, flags);
+
+ nrfd = gpiod_get_value(NRFD);
+ priv->all_irqs++;
+
+ dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n",
+ irq, nrfd, gpiod_get_value(NDAC), board->status, priv->direction,
+ priv->w_busy, priv->r_busy);
+
+ if (priv->nrfd_mode) {
+ ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_EDGE_RISING);
+ priv->nrfd_mode = 0;
+ }
+
+ if (priv->w_busy == 0) {
+ dbg_printk(1, "interrupt while idle after %zu/%zu at %d\n",
+ priv->w_cnt, priv->length, priv->phase);
+ priv->nrfd_idle++;
+ goto nrfd_exit; /* idle */
+ }
+ if (nrfd == 0) {
+ dbg_printk(1, "out of order interrupt after %zu/%zu at %d cmd %d " LINFMT ".\n",
+ priv->w_cnt, priv->length, priv->phase, priv->cmd, LINVAL);
+ priv->phase = 400;
+ priv->nrfd_seq++;
+ goto nrfd_exit;
+ }
+ if (!priv->dav_tx) {
+ dbg_printk(1, "DAV low after %zu/%zu cmd %d " LINFMT ". No action.\n",
+ priv->w_cnt, priv->length, priv->cmd, LINVAL);
+ priv->dav_seq++;
+ goto nrfd_exit;
+ }
+
+ if (priv->w_cnt >= priv->length) { // test for missed NDAC end of transfer
+ dev_err(board->gpib_dev, "Unexpected NRFD exit\n");
+ priv->write_done = 1;
+ priv->w_busy = 0;
+ wake_up_interruptible(&board->wait);
+ goto nrfd_exit;
+ }
+
+ dbg_printk(3, "sending %zu\n", priv->w_cnt);
+
+ set_data_lines(priv->w_buf[priv->w_cnt++]); // put the data on the lines
+
+ if (priv->w_cnt == priv->length && priv->end) {
+ dbg_printk(3, "Asserting EOI\n");
+ gpiod_set_value(EOI, 0); // Assert EOI
+ }
+
+ gpiod_set_value(DAV, 0); // Data available
+ priv->dav_tx = 0;
+ priv->phase = 410;
+
+nrfd_exit:
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/***************************************************************************
+ * *
+ * WRITE interrupt routine (NDAC line) *
+ * *
+ ***************************************************************************/
+
+static irqreturn_t bb_NDAC_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct bb_priv *priv = board->private_data;
+ unsigned long flags;
+ int ndac;
+
+ spin_lock_irqsave(&priv->rw_lock, flags);
+
+ ndac = gpiod_get_value(NDAC);
+ priv->all_irqs++;
+ dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n",
+ irq, gpiod_get_value(NRFD), ndac, board->status, priv->direction,
+ priv->w_busy, priv->r_busy);
+
+ if (priv->ndac_mode) {
+ ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_EDGE_RISING);
+ priv->ndac_mode = 0;
+ }
+
+ if (priv->w_busy == 0) {
+ dbg_printk(1, "interrupt while idle.\n");
+ priv->ndac_idle++;
+ goto ndac_exit;
+ }
+ if (ndac == 0) {
+ dbg_printk(1, "out of order interrupt at %zu:%d.\n", priv->w_cnt, priv->phase);
+ priv->phase = 500;
+ priv->ndac_seq++;
+ goto ndac_exit;
+ }
+ if (priv->dav_tx) {
+ dbg_printk(1, "DAV high after %zu/%zu cmd %d " LINFMT ". No action.\n",
+ priv->w_cnt, priv->length, priv->cmd, LINVAL);
+ priv->dav_seq++;
+ goto ndac_exit;
+ }
+
+ dbg_printk(3, "accepted %zu\n", priv->w_cnt - 1);
+
+ gpiod_set_value(DAV, 1); // Data not available
+ priv->dav_tx = 1;
+ priv->phase = 510;
+
+ if (priv->w_cnt >= priv->length) { // test for end of transfer
+ priv->write_done = 1;
+ priv->w_busy = 0;
+ wake_up_interruptible(&board->wait);
+ }
+
+ndac_exit:
+ spin_unlock_irqrestore(&priv->rw_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/***************************************************************************
+ * *
+ * interrupt routine for SRQ line *
+ * *
+ ***************************************************************************/
+
+static irqreturn_t bb_SRQ_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+
+ int val = gpiod_get_value(SRQ);
+
+ dbg_printk(3, "> %d st: %4lx\n", val, board->status);
+
+ if (!val)
+ set_bit(SRQI_NUM, &board->status); /* set_bit() is atomic */
+
+ wake_up_interruptible(&board->wait);
+
+ return IRQ_HANDLED;
+}
+
+static int bb_command(struct gpib_board *board, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ int ret;
+ struct bb_priv *priv = board->private_data;
+ int i;
+
+ dbg_printk(2, "%p %p\n", buffer, board->buffer);
+
+ /* the _ATN line has already been asserted by bb_take_control() */
+
+ priv->cmd = 1;
+
+ ret = bb_write(board, buffer, length, 0, bytes_written); // no eoi
+
+ for (i = 0; i < length; i++) {
+ if (buffer[i] == UNT) {
+ priv->talker_state = talker_idle;
+ } else {
+ if (buffer[i] == UNL) {
+ priv->listener_state = listener_idle;
+ } else {
+ if (buffer[i] == (MTA(board->pad))) {
+ priv->talker_state = talker_addressed;
+ priv->listener_state = listener_idle;
+ } else if (buffer[i] == (MLA(board->pad))) {
+ priv->listener_state = listener_addressed;
+ priv->talker_state = talker_idle;
+ }
+ }
+ }
+ }
+
+ /* the _ATN line will be released by bb_go_to_stby */
+
+ priv->cmd = 0;
+
+ return ret;
+}
+
+/***************************************************************************
+ * *
+ * Buffer print with decode for debug/trace *
+ * *
+ ***************************************************************************/
+
+static char *cmd_string[32] = {
+ "", // 0x00
+ "GTL", // 0x01
+ "", // 0x02
+ "", // 0x03
+ "SDC", // 0x04
+ "PPC", // 0x05
+ "", // 0x06
+ "", // 0x07
+ "GET", // 0x08
+ "TCT", // 0x09
+ "", // 0x0a
+ "", // 0x0b
+ "", // 0x0c
+ "", // 0x0d
+ "", // 0x0e
+ "", // 0x0f
+ "", // 0x10
+ "LLO", // 0x11
+ "", // 0x12
+ "", // 0x13
+ "DCL", // 0x14
+ "PPU", // 0x15
+ "", // 0x16
+ "", // 0x17
+ "SPE", // 0x18
+ "SPD", // 0x19
+ "", // 0x1a
+ "", // 0x1b
+ "", // 0x1c
+ "", // 0x1d
+ "", // 0x1e
+ "CFE" // 0x1f
+};
+
+static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length,
+ int cmd, int eoi)
+{
+ int i;
+
+ if (cmd) {
+ dbg_printk(2, "<cmd len %zu>\n", length);
+ for (i = 0; i < length; i++) {
+ if (buffer[i] < 0x20) {
+ dbg_printk(3, "0x%x=%s\n", buffer[i], cmd_string[buffer[i]]);
+ } else if (buffer[i] == 0x3f) {
+ dbg_printk(3, "0x%x=%s\n", buffer[i], "UNL");
+ } else if (buffer[i] == 0x5f) {
+ dbg_printk(3, "0x%x=%s\n", buffer[i], "UNT");
+ } else if (buffer[i] < 0x60) {
+ dbg_printk(3, "0x%x=%s%d\n", buffer[i],
+ (buffer[i] & 0x40) ? "TLK" : "LSN", buffer[i] & 0x1F);
+ } else {
+ dbg_printk(3, "0x%x\n", buffer[i]);
+ }
+ }
+ } else {
+ dbg_printk(2, "<data len %zu %s>\n", length, (eoi) ? "w.EOI" : " ");
+ for (i = 0; i < length; i++)
+ dbg_printk(2, "%3d 0x%x->%c\n", i, buffer[i], printable(buffer[i]));
+ }
+}
+
+/***************************************************************************
+ * *
+ * STATUS Management *
+ * *
+ ***************************************************************************/
+static void set_atn(struct gpib_board *board, int atn_asserted)
+{
+ struct bb_priv *priv = board->private_data;
+
+ if (priv->listener_state != listener_idle &&
+ priv->talker_state != talker_idle) {
+ dev_err(board->gpib_dev, "listener/talker state machine conflict\n");
+ }
+ if (atn_asserted) {
+ if (priv->listener_state == listener_active)
+ priv->listener_state = listener_addressed;
+ if (priv->talker_state == talker_active)
+ priv->talker_state = talker_addressed;
+ SET_DIR_WRITE(priv); // need to be able to read bus NRFD/NDAC
+ } else {
+ if (priv->listener_state == listener_addressed) {
+ priv->listener_state = listener_active;
+ SET_DIR_READ(priv); // make sure holdoff is active when we unassert ATN
+ }
+ if (priv->talker_state == talker_addressed)
+ priv->talker_state = talker_active;
+ }
+ gpiod_direction_output(_ATN, !atn_asserted);
+}
+
+static int bb_take_control(struct gpib_board *board, int synchronous)
+{
+ dbg_printk(2, "%d\n", synchronous);
+ set_atn(board, 1);
+ return 0;
+}
+
+static int bb_go_to_standby(struct gpib_board *board)
+{
+ dbg_printk(2, "\n");
+ set_atn(board, 0);
+ return 0;
+}
+
+static int bb_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct bb_priv *priv = board->private_data;
+
+ dbg_printk(2, "%d\n", request_control);
+ if (!request_control)
+ return -EINVAL;
+
+ gpiod_direction_output(REN, 1); /* user space must enable REN if needed */
+ gpiod_direction_output(IFC, 1); /* user space must toggle IFC if needed */
+ if (sn7516x)
+ gpiod_direction_output(DC, 0); /* enable ATN as output on SN75161/2 */
+
+ gpiod_direction_input(SRQ);
+
+ ENABLE_IRQ(priv->irq_SRQ, IRQ_TYPE_EDGE_FALLING);
+
+ return 0;
+}
+
+static void bb_interface_clear(struct gpib_board *board, int assert)
+{
+ struct bb_priv *priv = board->private_data;
+
+ dbg_printk(2, "%d\n", assert);
+ if (assert) {
+ gpiod_direction_output(IFC, 0);
+ priv->talker_state = talker_idle;
+ priv->listener_state = listener_idle;
+ set_bit(CIC_NUM, &board->status);
+ } else {
+ gpiod_direction_output(IFC, 1);
+ }
+}
+
+static void bb_remote_enable(struct gpib_board *board, int enable)
+{
+ dbg_printk(2, "%d\n", enable);
+ if (enable) {
+ set_bit(REM_NUM, &board->status);
+ gpiod_direction_output(REN, 0);
+ } else {
+ clear_bit(REM_NUM, &board->status);
+ gpiod_direction_output(REN, 1);
+ }
+}
+
+static int bb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct bb_priv *priv = board->private_data;
+
+ dbg_printk(2, "%s\n", "EOS_en");
+ priv->eos = eos_byte;
+ priv->eos_flags = REOS;
+ if (compare_8_bits)
+ priv->eos_flags |= BIN;
+
+ return 0;
+}
+
+static void bb_disable_eos(struct gpib_board *board)
+{
+ struct bb_priv *priv = board->private_data;
+
+ dbg_printk(2, "\n");
+ priv->eos_flags &= ~REOS;
+}
+
+static unsigned int bb_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct bb_priv *priv = board->private_data;
+
+ board->status &= ~clear_mask;
+
+ if (gpiod_get_value(SRQ)) /* SRQ asserted low */
+ clear_bit(SRQI_NUM, &board->status);
+ else
+ set_bit(SRQI_NUM, &board->status);
+ if (gpiod_get_value(_ATN)) /* ATN asserted low */
+ clear_bit(ATN_NUM, &board->status);
+ else
+ set_bit(ATN_NUM, &board->status);
+ if (priv->talker_state == talker_active ||
+ priv->talker_state == talker_addressed)
+ set_bit(TACS_NUM, &board->status);
+ else
+ clear_bit(TACS_NUM, &board->status);
+
+ if (priv->listener_state == listener_active ||
+ priv->listener_state == listener_addressed)
+ set_bit(LACS_NUM, &board->status);
+ else
+ clear_bit(LACS_NUM, &board->status);
+
+ dbg_printk(2, "0x%lx mask 0x%x\n", board->status, clear_mask);
+
+ return board->status;
+}
+
+static int bb_primary_address(struct gpib_board *board, unsigned int address)
+{
+ dbg_printk(2, "%d\n", address);
+ board->pad = address;
+ return 0;
+}
+
+static int bb_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ dbg_printk(2, "%d %d\n", address, enable);
+ if (enable)
+ board->sad = address;
+ return 0;
+}
+
+static int bb_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ return -ENOENT;
+}
+
+static void bb_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+}
+
+static void bb_parallel_poll_response(struct gpib_board *board, int ist)
+{
+}
+
+static void bb_serial_poll_response(struct gpib_board *board, u8 status)
+{
+}
+
+static u8 bb_serial_poll_status(struct gpib_board *board)
+{
+ return 0; // -ENOENT;
+}
+
+static int bb_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct bb_priv *priv = board->private_data;
+
+ if (nano_sec <= 350)
+ priv->t1_delay = 350;
+ else if (nano_sec <= 1100)
+ priv->t1_delay = 1100;
+ else
+ priv->t1_delay = 2000;
+
+ dbg_printk(2, "t1 delay set to %d nanosec\n", priv->t1_delay);
+
+ return priv->t1_delay;
+}
+
+static void bb_return_to_local(struct gpib_board *board)
+{
+}
+
+static int bb_line_status(const struct gpib_board *board)
+{
+ int line_status = VALID_ALL;
+
+ if (gpiod_get_value(REN) == 0)
+ line_status |= BUS_REN;
+ if (gpiod_get_value(IFC) == 0)
+ line_status |= BUS_IFC;
+ if (gpiod_get_value(NDAC) == 0)
+ line_status |= BUS_NDAC;
+ if (gpiod_get_value(NRFD) == 0)
+ line_status |= BUS_NRFD;
+ if (gpiod_get_value(DAV) == 0)
+ line_status |= BUS_DAV;
+ if (gpiod_get_value(EOI) == 0)
+ line_status |= BUS_EOI;
+ if (gpiod_get_value(_ATN) == 0)
+ line_status |= BUS_ATN;
+ if (gpiod_get_value(SRQ) == 0)
+ line_status |= BUS_SRQ;
+
+ dbg_printk(2, "status lines: %4x\n", line_status);
+
+ return line_status;
+}
+
+/***************************************************************************
+ * *
+ * Module Management *
+ * *
+ ***************************************************************************/
+
+static int allocate_private(struct gpib_board *board)
+{
+ board->private_data = kzalloc(sizeof(struct bb_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -1;
+ return 0;
+}
+
+static void free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static int bb_get_irq(struct gpib_board *board, char *name,
+ struct gpio_desc *gpio, int *irq,
+ irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags)
+{
+ if (!gpio)
+ return -1;
+ gpiod_direction_input(gpio);
+ *irq = gpiod_to_irq(gpio);
+ dbg_printk(2, "IRQ %s: %d\n", name, *irq);
+ if (*irq < 0) {
+ dev_err(board->gpib_dev, "can't get IRQ for %s\n", name);
+ return -1;
+ }
+ if (request_threaded_irq(*irq, handler, thread_fn, flags, name, board)) {
+ dev_err(board->gpib_dev, "can't request IRQ for %s %d\n", name, *irq);
+ *irq = 0;
+ return -1;
+ }
+ DISABLE_IRQ(*irq);
+ return 0;
+}
+
+static void bb_free_irq(struct gpib_board *board, int *irq, char *name)
+{
+ if (*irq) {
+ free_irq(*irq, board);
+ dbg_printk(2, "IRQ %d(%s) freed\n", *irq, name);
+ *irq = 0;
+ }
+}
+
+static void release_gpios(void)
+{
+ int j;
+
+ for (j = 0 ; j < NUM_PINS ; j++) {
+ if (all_descriptors[j]) {
+ gpiod_put(all_descriptors[j]);
+ all_descriptors[j] = NULL;
+ }
+ }
+}
+
+static int allocate_gpios(struct gpib_board *board)
+{
+ int j;
+ int table_index = 0;
+ char name[256];
+ struct gpio_desc *desc;
+ struct gpiod_lookup_table *lookup_table;
+
+ if (!board->gpib_dev) {
+ pr_err("NULL gpib dev for board\n");
+ return -ENOENT;
+ }
+
+ lookup_table = lookup_tables[table_index];
+ lookup_table->dev_id = dev_name(board->gpib_dev);
+ gpiod_add_lookup_table(lookup_table);
+ dbg_printk(1, "Allocating gpios using table index %d\n", table_index);
+
+ for (j = 0 ; j < NUM_PINS ; j++) {
+ if (gpios_vector[j] < 0)
+ continue;
+ /* name not really used in gpiod_get_index() */
+ sprintf(name, "GPIO%d", gpios_vector[j]);
+try_again:
+ dbg_printk(1, "Allocating gpio %s pin no %d\n", name, gpios_vector[j]);
+ desc = gpiod_get_index(board->gpib_dev, name, gpios_vector[j], GPIOD_IN);
+
+ if (IS_ERR(desc)) {
+ gpiod_remove_lookup_table(lookup_table);
+ table_index++;
+ lookup_table = lookup_tables[table_index];
+ if (!lookup_table) {
+ dev_err(board->gpib_dev, "Unable to obtain gpio descriptor for pin %d error %ld\n",
+ gpios_vector[j], PTR_ERR(desc));
+ goto alloc_gpios_fail;
+ }
+ dbg_printk(1, "Allocation failed, now using table_index %d\n", table_index);
+ lookup_table->dev_id = dev_name(board->gpib_dev);
+ gpiod_add_lookup_table(lookup_table);
+ goto try_again;
+ }
+ all_descriptors[j] = desc;
+ }
+
+ gpiod_remove_lookup_table(lookup_table);
+
+ return 0;
+
+alloc_gpios_fail:
+ release_gpios();
+ return -1;
+}
+
+static void bb_detach(struct gpib_board *board)
+{
+ struct bb_priv *priv = board->private_data;
+
+ dbg_printk(2, "Enter with data %p\n", board->private_data);
+ if (!board->private_data)
+ return;
+
+ bb_free_irq(board, &priv->irq_DAV, NAME "_DAV");
+ bb_free_irq(board, &priv->irq_NRFD, NAME "_NRFD");
+ bb_free_irq(board, &priv->irq_NDAC, NAME "_NDAC");
+ bb_free_irq(board, &priv->irq_SRQ, NAME "_SRQ");
+
+ if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */
+ gpiod_set_value(YOGA_ENABLE, 0);
+ }
+
+ release_gpios();
+
+ dbg_printk(2, "detached board: %d\n", board->minor);
+ dbg_printk(0, "NRFD: idle %d, seq %d, NDAC: idle %d, seq %d DAV: idle %d seq: %d all: %ld",
+ priv->nrfd_idle, priv->nrfd_seq,
+ priv->ndac_idle, priv->ndac_seq,
+ priv->dav_idle, priv->dav_seq, priv->all_irqs);
+
+ free_private(board);
+}
+
+static int bb_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct bb_priv *priv;
+ int retval = 0;
+
+ dbg_printk(2, "%s\n", "Enter ...");
+
+ board->status = 0;
+
+ if (allocate_private(board))
+ return -ENOMEM;
+ priv = board->private_data;
+ priv->direction = -1;
+ priv->t1_delay = 2000;
+ priv->listener_state = listener_idle;
+ priv->talker_state = talker_idle;
+
+ sn7516x = sn7516x_used;
+ if (strcmp(PINMAP_0, pin_map) == 0) {
+ if (!sn7516x) {
+ gpios_vector[&(PE) - &all_descriptors[0]] = -1;
+ gpios_vector[&(DC) - &all_descriptors[0]] = -1;
+ gpios_vector[&(TE) - &all_descriptors[0]] = -1;
+ }
+ } else if (strcmp(PINMAP_1, pin_map) == 0) {
+ if (!sn7516x) {
+ gpios_vector[&(PE) - &all_descriptors[0]] = -1;
+ gpios_vector[&(DC) - &all_descriptors[0]] = -1;
+ gpios_vector[&(TE) - &all_descriptors[0]] = -1;
+ }
+ gpios_vector[&(REN) - &all_descriptors[0]] = 0; /* 27 -> 0 REN on GPIB pin 0 */
+ } else if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */
+ sn7516x = 0;
+ gpios_vector[&(D03) - &all_descriptors[0]] = YOGA_D03_pin_nr;
+ gpios_vector[&(D04) - &all_descriptors[0]] = YOGA_D04_pin_nr;
+ gpios_vector[&(D05) - &all_descriptors[0]] = YOGA_D05_pin_nr;
+ gpios_vector[&(D06) - &all_descriptors[0]] = YOGA_D06_pin_nr;
+ gpios_vector[&(PE) - &all_descriptors[0]] = -1;
+ gpios_vector[&(DC) - &all_descriptors[0]] = -1;
+ } else {
+ dev_err(board->gpib_dev, "Unrecognized pin map %s\n", pin_map);
+ goto bb_attach_fail;
+ }
+ dbg_printk(0, "Using pin map \"%s\" %s\n", pin_map, (sn7516x) ?
+ " with SN7516x driver support" : "");
+
+ if (allocate_gpios(board))
+ goto bb_attach_fail;
+
+/*
+ * Configure SN7516X control lines.
+ * drive ATN, IFC and REN as outputs only when master
+ * i.e. system controller. In this mode can only be the CIC
+ * When not master then enable device mode ATN, IFC & REN as inputs
+ */
+ if (sn7516x) {
+ gpiod_direction_output(DC, 0);
+ gpiod_direction_output(TE, 1);
+ gpiod_direction_output(PE, 1);
+ }
+/* Set main control lines to a known state */
+ gpiod_direction_output(IFC, 1);
+ gpiod_direction_output(REN, 1);
+ gpiod_direction_output(_ATN, 1);
+
+ if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA: enable level shifters */
+ gpiod_direction_output(YOGA_ENABLE, 1);
+ }
+
+ spin_lock_init(&priv->rw_lock);
+
+ /* request DAV interrupt for read */
+ if (bb_get_irq(board, NAME "_DAV", DAV, &priv->irq_DAV, bb_DAV_interrupt, NULL,
+ IRQF_TRIGGER_NONE))
+ goto bb_attach_fail_r;
+
+ /* request NRFD interrupt for write */
+ if (bb_get_irq(board, NAME "_NRFD", NRFD, &priv->irq_NRFD, bb_NRFD_interrupt, NULL,
+ IRQF_TRIGGER_NONE))
+ goto bb_attach_fail_r;
+
+ /* request NDAC interrupt for write */
+ if (bb_get_irq(board, NAME "_NDAC", NDAC, &priv->irq_NDAC, bb_NDAC_interrupt, NULL,
+ IRQF_TRIGGER_NONE))
+ goto bb_attach_fail_r;
+
+ /* request SRQ interrupt for Service Request */
+ if (bb_get_irq(board, NAME "_SRQ", SRQ, &priv->irq_SRQ, bb_SRQ_interrupt, NULL,
+ IRQF_TRIGGER_NONE))
+ goto bb_attach_fail_r;
+
+ dbg_printk(0, "attached board %d\n", board->minor);
+ goto bb_attach_out;
+
+bb_attach_fail_r:
+ release_gpios();
+bb_attach_fail:
+ retval = -1;
+bb_attach_out:
+ return retval;
+}
+
+static struct gpib_interface bb_interface = {
+ .name = NAME,
+ .attach = bb_attach,
+ .detach = bb_detach,
+ .read = bb_read,
+ .write = bb_write,
+ .command = bb_command,
+ .take_control = bb_take_control,
+ .go_to_standby = bb_go_to_standby,
+ .request_system_control = bb_request_system_control,
+ .interface_clear = bb_interface_clear,
+ .remote_enable = bb_remote_enable,
+ .enable_eos = bb_enable_eos,
+ .disable_eos = bb_disable_eos,
+ .parallel_poll = bb_parallel_poll,
+ .parallel_poll_configure = bb_parallel_poll_configure,
+ .parallel_poll_response = bb_parallel_poll_response,
+ .line_status = bb_line_status,
+ .update_status = bb_update_status,
+ .primary_address = bb_primary_address,
+ .secondary_address = bb_secondary_address,
+ .serial_poll_response = bb_serial_poll_response,
+ .serial_poll_status = bb_serial_poll_status,
+ .t1_delay = bb_t1_delay,
+ .return_to_local = bb_return_to_local,
+};
+
+static int __init bb_init_module(void)
+{
+ int result = gpib_register_driver(&bb_interface, THIS_MODULE);
+
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit bb_exit_module(void)
+{
+ gpib_unregister_driver(&bb_interface);
+}
+
+module_init(bb_init_module);
+module_exit(bb_exit_module);
+
+/***************************************************************************
+ * *
+ * UTILITY Functions *
+ * *
+ ***************************************************************************/
+inline long usec_diff(struct timespec64 *a, struct timespec64 *b)
+{
+ return ((a->tv_sec - b->tv_sec) * 1000000 +
+ (a->tv_nsec - b->tv_nsec) / 1000);
+}
+
+static inline int check_for_eos(struct bb_priv *priv, u8 byte)
+{
+ if (priv->eos_check)
+ return 0;
+
+ if (priv->eos_check_8) {
+ if (priv->eos == byte)
+ return 1;
+ } else {
+ if (priv->eos_mask_7 == (byte & 0x7f))
+ return 1;
+ }
+ return 0;
+}
+
+static void set_data_lines_output(void)
+{
+ gpiod_direction_output(D01, 1);
+ gpiod_direction_output(D02, 1);
+ gpiod_direction_output(D03, 1);
+ gpiod_direction_output(D04, 1);
+ gpiod_direction_output(D05, 1);
+ gpiod_direction_output(D06, 1);
+ gpiod_direction_output(D07, 1);
+ gpiod_direction_output(D08, 1);
+}
+
+static void set_data_lines(u8 byte)
+{
+ gpiod_set_value(D01, !(byte & 0x01));
+ gpiod_set_value(D02, !(byte & 0x02));
+ gpiod_set_value(D03, !(byte & 0x04));
+ gpiod_set_value(D04, !(byte & 0x08));
+ gpiod_set_value(D05, !(byte & 0x10));
+ gpiod_set_value(D06, !(byte & 0x20));
+ gpiod_set_value(D07, !(byte & 0x40));
+ gpiod_set_value(D08, !(byte & 0x80));
+}
+
+static u8 get_data_lines(void)
+{
+ u8 ret;
+
+ ret = gpiod_get_value(D01);
+ ret |= gpiod_get_value(D02) << 1;
+ ret |= gpiod_get_value(D03) << 2;
+ ret |= gpiod_get_value(D04) << 3;
+ ret |= gpiod_get_value(D05) << 4;
+ ret |= gpiod_get_value(D06) << 5;
+ ret |= gpiod_get_value(D07) << 6;
+ ret |= gpiod_get_value(D08) << 7;
+ return ~ret;
+}
+
+static void set_data_lines_input(void)
+{
+ gpiod_direction_input(D01);
+ gpiod_direction_input(D02);
+ gpiod_direction_input(D03);
+ gpiod_direction_input(D04);
+ gpiod_direction_input(D05);
+ gpiod_direction_input(D06);
+ gpiod_direction_input(D07);
+ gpiod_direction_input(D08);
+}
+
+static inline void SET_DIR_WRITE(struct bb_priv *priv)
+{
+ if (priv->direction == DIR_WRITE)
+ return;
+
+ gpiod_direction_input(NRFD);
+ gpiod_direction_input(NDAC);
+ set_data_lines_output();
+ gpiod_direction_output(DAV, 1);
+ gpiod_direction_output(EOI, 1);
+
+ if (sn7516x) {
+ gpiod_set_value(PE, 1); /* set data lines to transmit on sn75160b */
+ gpiod_set_value(TE, 1); /* set NDAC and NRFD to receive and DAV to transmit */
+ }
+
+ priv->direction = DIR_WRITE;
+}
+
+static inline void SET_DIR_READ(struct bb_priv *priv)
+{
+ if (priv->direction == DIR_READ)
+ return;
+
+ gpiod_direction_input(DAV);
+ gpiod_direction_input(EOI);
+
+ set_data_lines_input();
+
+ if (sn7516x) {
+ gpiod_set_value(PE, 0); /* set data lines to receive on sn75160b */
+ gpiod_set_value(TE, 0); /* set NDAC and NRFD to transmit and DAV to receive */
+ }
+
+ gpiod_direction_output(NRFD, 0); /* hold off the talker */
+ gpiod_direction_output(NDAC, 0); /* data not accepted */
+
+ priv->direction = DIR_READ;
+}
diff --git a/drivers/gpib/hp_82335/Makefile b/drivers/gpib/hp_82335/Makefile
new file mode 100644
index 000000000000..305ce44ee48a
--- /dev/null
+++ b/drivers/gpib/hp_82335/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_GPIB_HP82335) += hp82335.o
+
+
diff --git a/drivers/gpib/hp_82335/hp82335.c b/drivers/gpib/hp_82335/hp82335.c
new file mode 100644
index 000000000000..d0e47ef77c87
--- /dev/null
+++ b/drivers/gpib/hp_82335/hp82335.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess *
+ ***************************************************************************/
+
+/*
+ * should enable ATN interrupts (and update board->status on occurrence),
+ * implement recovery from bus errors (if necessary)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "hp82335.h"
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for HP 82335 interface cards");
+
+static int hp82335_attach(struct gpib_board *board, const struct gpib_board_config *config);
+static void hp82335_detach(struct gpib_board *board);
+static irqreturn_t hp82335_interrupt(int irq, void *arg);
+
+// wrappers for interface functions
+static int hp82335_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
+}
+
+static int hp82335_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int hp82335_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
+}
+
+static int hp82335_take_control(struct gpib_board *board, int synchronous)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
+}
+
+static int hp82335_go_to_standby(struct gpib_board *board)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_go_to_standby(board, &priv->tms9914_priv);
+}
+
+static int hp82335_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
+}
+
+static void hp82335_interface_clear(struct gpib_board *board, int assert)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_interface_clear(board, &priv->tms9914_priv, assert);
+}
+
+static void hp82335_remote_enable(struct gpib_board *board, int enable)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_remote_enable(board, &priv->tms9914_priv, enable);
+}
+
+static int hp82335_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
+}
+
+static void hp82335_disable_eos(struct gpib_board *board)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_disable_eos(board, &priv->tms9914_priv);
+}
+
+static unsigned int hp82335_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
+}
+
+static int hp82335_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_primary_address(board, &priv->tms9914_priv, address);
+}
+
+static int hp82335_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
+}
+
+static int hp82335_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
+}
+
+static void hp82335_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
+}
+
+static void hp82335_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
+}
+
+static void hp82335_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
+}
+
+static u8 hp82335_serial_poll_status(struct gpib_board *board)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_serial_poll_status(board, &priv->tms9914_priv);
+}
+
+static int hp82335_line_status(const struct gpib_board *board)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_line_status(board, &priv->tms9914_priv);
+}
+
+static int hp82335_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
+}
+
+static void hp82335_return_to_local(struct gpib_board *board)
+{
+ struct hp82335_priv *priv = board->private_data;
+
+ tms9914_return_to_local(board, &priv->tms9914_priv);
+}
+
+static struct gpib_interface hp82335_interface = {
+ .name = "hp82335",
+ .attach = hp82335_attach,
+ .detach = hp82335_detach,
+ .read = hp82335_read,
+ .write = hp82335_write,
+ .command = hp82335_command,
+ .request_system_control = hp82335_request_system_control,
+ .take_control = hp82335_take_control,
+ .go_to_standby = hp82335_go_to_standby,
+ .interface_clear = hp82335_interface_clear,
+ .remote_enable = hp82335_remote_enable,
+ .enable_eos = hp82335_enable_eos,
+ .disable_eos = hp82335_disable_eos,
+ .parallel_poll = hp82335_parallel_poll,
+ .parallel_poll_configure = hp82335_parallel_poll_configure,
+ .parallel_poll_response = hp82335_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = hp82335_line_status,
+ .update_status = hp82335_update_status,
+ .primary_address = hp82335_primary_address,
+ .secondary_address = hp82335_secondary_address,
+ .serial_poll_response = hp82335_serial_poll_response,
+ .serial_poll_status = hp82335_serial_poll_status,
+ .t1_delay = hp82335_t1_delay,
+ .return_to_local = hp82335_return_to_local,
+};
+
+static int hp82335_allocate_private(struct gpib_board *board)
+{
+ board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -1;
+ return 0;
+}
+
+static void hp82335_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static inline unsigned int tms9914_to_hp82335_offset(unsigned int register_num)
+{
+ return 0x1ff8 + register_num;
+}
+
+static u8 hp82335_read_byte(struct tms9914_priv *priv, unsigned int register_num)
+{
+ return tms9914_iomem_read_byte(priv, tms9914_to_hp82335_offset(register_num));
+}
+
+static void hp82335_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num)
+{
+ tms9914_iomem_write_byte(priv, data, tms9914_to_hp82335_offset(register_num));
+}
+
+static void hp82335_clear_interrupt(struct hp82335_priv *hp_priv)
+{
+ struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
+
+ writeb(0, tms_priv->mmiobase + HPREG_INTR_CLEAR);
+}
+
+static int hp82335_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct hp82335_priv *hp_priv;
+ struct tms9914_priv *tms_priv;
+ int retval;
+ const unsigned long upper_iomem_base = config->ibbase + hp82335_rom_size;
+
+ board->status = 0;
+
+ if (hp82335_allocate_private(board))
+ return -ENOMEM;
+ hp_priv = board->private_data;
+ tms_priv = &hp_priv->tms9914_priv;
+ tms_priv->read_byte = hp82335_read_byte;
+ tms_priv->write_byte = hp82335_write_byte;
+ tms_priv->offset = 1;
+
+ switch (config->ibbase) {
+ case 0xc4000:
+ case 0xc8000:
+ case 0xcc000:
+ case 0xd0000:
+ case 0xd4000:
+ case 0xd8000:
+ case 0xdc000:
+ case 0xe0000:
+ case 0xe4000:
+ case 0xe8000:
+ case 0xec000:
+ case 0xf0000:
+ case 0xf4000:
+ case 0xf8000:
+ case 0xfc000:
+ break;
+ default:
+ dev_err(board->gpib_dev, "invalid base io address 0x%x\n", config->ibbase);
+ return -EINVAL;
+ }
+ if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) {
+ dev_err(board->gpib_dev, "failed to allocate io memory region 0x%lx-0x%lx\n",
+ upper_iomem_base, upper_iomem_base + hp82335_upper_iomem_size - 1);
+ return -EBUSY;
+ }
+ hp_priv->raw_iobase = upper_iomem_base;
+ tms_priv->mmiobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size);
+
+ retval = request_irq(config->ibirq, hp82335_interrupt, 0, DRV_NAME, board);
+ if (retval) {
+ dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq);
+ return retval;
+ }
+ hp_priv->irq = config->ibirq;
+
+ tms9914_board_reset(tms_priv);
+
+ hp82335_clear_interrupt(hp_priv);
+
+ writeb(INTR_ENABLE, tms_priv->mmiobase + HPREG_CCR);
+
+ tms9914_online(board, tms_priv);
+
+ return 0;
+}
+
+static void hp82335_detach(struct gpib_board *board)
+{
+ struct hp82335_priv *hp_priv = board->private_data;
+ struct tms9914_priv *tms_priv;
+
+ if (hp_priv) {
+ tms_priv = &hp_priv->tms9914_priv;
+ if (hp_priv->irq)
+ free_irq(hp_priv->irq, board);
+ if (tms_priv->mmiobase) {
+ writeb(0, tms_priv->mmiobase + HPREG_CCR);
+ tms9914_board_reset(tms_priv);
+ iounmap(tms_priv->mmiobase);
+ }
+ if (hp_priv->raw_iobase)
+ release_mem_region(hp_priv->raw_iobase, hp82335_upper_iomem_size);
+ }
+ hp82335_free_private(board);
+}
+
+static int __init hp82335_init_module(void)
+{
+ int result = gpib_register_driver(&hp82335_interface, THIS_MODULE);
+
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit hp82335_exit_module(void)
+{
+ gpib_unregister_driver(&hp82335_interface);
+}
+
+module_init(hp82335_init_module);
+module_exit(hp82335_exit_module);
+
+/*
+ * GPIB interrupt service routines
+ */
+
+static irqreturn_t hp82335_interrupt(int irq, void *arg)
+{
+ int status1, status2;
+ struct gpib_board *board = arg;
+ struct hp82335_priv *priv = board->private_data;
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ status1 = read_byte(&priv->tms9914_priv, ISR0);
+ status2 = read_byte(&priv->tms9914_priv, ISR1);
+ hp82335_clear_interrupt(priv);
+ retval = tms9914_interrupt_have_status(board, &priv->tms9914_priv, status1, status2);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
diff --git a/drivers/gpib/hp_82335/hp82335.h b/drivers/gpib/hp_82335/hp82335.h
new file mode 100644
index 000000000000..0c252a712ec9
--- /dev/null
+++ b/drivers/gpib/hp_82335/hp82335.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess *
+ ***************************************************************************/
+
+#ifndef _HP82335_H
+#define _HP82335_H
+
+#include "tms9914.h"
+#include "gpibP.h"
+
+// struct which defines private_data for board
+struct hp82335_priv {
+ struct tms9914_priv tms9914_priv;
+ unsigned int irq;
+ unsigned long raw_iobase;
+};
+
+// size of io memory region used
+static const int hp82335_rom_size = 0x2000;
+static const int hp82335_upper_iomem_size = 0x2000;
+
+// hp82335 register offsets
+enum hp_read_regs {
+ HPREG_CSR = 0x17f8,
+ HPREG_STATUS = 0x1ffc,
+};
+
+enum hp_write_regs {
+ HPREG_INTR_CLEAR = 0x17f7,
+ HPREG_CCR = HPREG_CSR,
+};
+
+enum ccr_bits {
+ DMA_ENABLE = (1 << 0), /* DMA enable */
+ DMA_CHAN_SELECT = (1 << 1), /* DMA channel select O=3,1=2 */
+ INTR_ENABLE = (1 << 2), /* interrupt enable */
+ SYS_DISABLE = (1 << 3), /* system controller disable */
+};
+
+enum csr_bits {
+ SWITCH6 = (1 << 0), /* switch 6 position */
+ SWITCH5 = (1 << 1), /* switch 5 position */
+ SYS_CONTROLLER = (1 << 2), /* system controller bit */
+ DMA_ENABLE_STATUS = (1 << 4), /* DMA enabled */
+ DMA_CHAN_STATUS = (1 << 5), /* DMA channel 0=3,1=2 */
+ INTR_ENABLE_STATUS = (1 << 6), /* Interrupt enable */
+ INTR_PENDING = (1 << 7), /* Interrupt Pending */
+};
+
+#endif // _HP82335_H
diff --git a/drivers/gpib/hp_82341/Makefile b/drivers/gpib/hp_82341/Makefile
new file mode 100644
index 000000000000..21367310a17e
--- /dev/null
+++ b/drivers/gpib/hp_82341/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_GPIB_HP82341) += hp_82341.o
diff --git a/drivers/gpib/hp_82341/hp_82341.c b/drivers/gpib/hp_82341/hp_82341.c
new file mode 100644
index 000000000000..1a2ad0560e14
--- /dev/null
+++ b/drivers/gpib/hp_82341/hp_82341.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * Driver for hp 82341a/b/c/d boards. *
+ * Might be worth merging with Agilent 82350b driver. *
+ * copyright : (C) 2002, 2005 by Frank Mori Hess *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "hp_82341.h"
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/isapnp.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for hp 82341a/b/c/d boards");
+
+static unsigned short read_and_clear_event_status(struct gpib_board *board);
+static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count);
+static int read_transfer_counter(struct hp_82341_priv *hp_priv);
+static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written);
+static irqreturn_t hp_82341_interrupt(int irq, void *arg);
+
+static int hp_82341_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ struct hp_82341_priv *hp_priv = board->private_data;
+ struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
+ int retval = 0;
+ unsigned short event_status;
+ int i;
+ int num_fifo_bytes;
+ // hardware doesn't support checking for end-of-string character when using fifo
+ if (tms_priv->eos_flags & REOS)
+ return tms9914_read(board, tms_priv, buffer, length, end, bytes_read);
+
+ clear_bit(DEV_CLEAR_BN, &tms_priv->state);
+
+ read_and_clear_event_status(board);
+ *end = 0;
+ *bytes_read = 0;
+ if (length == 0)
+ return 0;
+ // disable fifo for the moment
+ outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
+ /*
+ * Handle corner case of board not in holdoff and one byte has slipped in already.
+ * Also, board sometimes has problems (spurious 1 byte reads) when read fifo is
+ * started up with board in TACS under certain data holdoff conditions.
+ * Doing a 1 byte tms9914-style read avoids these problems.
+ */
+ if (/*tms_priv->holdoff_active == 0 && */length > 1) {
+ size_t num_bytes;
+
+ retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ dev_err(board->gpib_dev, "tms9914_read failed retval=%i\n", retval);
+ if (retval < 0 || *end)
+ return retval;
+ ++buffer;
+ --length;
+ }
+ tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI);
+ tms9914_release_holdoff(tms_priv);
+ outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG);
+ i = 0;
+ num_fifo_bytes = length - 1;
+ while (i < num_fifo_bytes && *end == 0) {
+ int block_size;
+ int j;
+ int count;
+
+ block_size = min(num_fifo_bytes - i, hp_82341_fifo_size);
+ set_transfer_counter(hp_priv, block_size);
+ outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] +
+ BUFFER_CONTROL_REG);
+ if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT)
+ outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG);
+
+ clear_bit(READ_READY_BN, &tms_priv->state);
+
+ retval = wait_event_interruptible(board->wait,
+ ((event_status =
+ read_and_clear_event_status(board)) &
+ (TERMINAL_COUNT_EVENT_BIT |
+ BUFFER_END_EVENT_BIT)) ||
+ test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
+ test_bit(TIMO_NUM, &board->status));
+ if (retval) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ // have to disable buffer before we can read from buffer port
+ outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
+ count = block_size - read_transfer_counter(hp_priv);
+ j = 0;
+ while (j < count && i < num_fifo_bytes) {
+ unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG);
+
+ buffer[i++] = data_word & 0xff;
+ ++j;
+ if (j < count && i < num_fifo_bytes) {
+ buffer[i++] = (data_word >> 8) & 0xff;
+ ++j;
+ }
+ }
+ if (event_status & BUFFER_END_EVENT_BIT) {
+ clear_bit(RECEIVED_END_BN, &tms_priv->state);
+
+ *end = 1;
+ tms_priv->holdoff_active = 1;
+ }
+ if (test_bit(TIMO_NUM, &board->status)) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+ }
+ *bytes_read += i;
+ buffer += i;
+ length -= i;
+ if (retval < 0)
+ return retval;
+ // read last byte if we havn't received an END yet
+ if (*end == 0) {
+ size_t num_bytes;
+ // try to make sure we holdoff after last byte read
+ retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static int restart_write_fifo(struct gpib_board *board, struct hp_82341_priv *hp_priv)
+{
+ struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
+
+ if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0)
+ return 0;
+ while (1) {
+ int status;
+
+ // restart doesn't work if data holdoff is in effect
+ status = tms9914_line_status(board, tms_priv);
+ if ((status & BUS_NRFD) == 0) {
+ outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG);
+ return 0;
+ }
+ if (test_bit(DEV_CLEAR_BN, &tms_priv->state))
+ return -EINTR;
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+ if (msleep_interruptible(1))
+ return -EINTR;
+ }
+ return 0;
+}
+
+static int hp_82341_accel_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ struct hp_82341_priv *hp_priv = board->private_data;
+ struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
+ int i, j;
+ unsigned short event_status;
+ int retval = 0;
+ int fifo_xfer_len = length;
+
+ *bytes_written = 0;
+ if (send_eoi)
+ --fifo_xfer_len;
+
+ clear_bit(DEV_CLEAR_BN, &tms_priv->state);
+
+ read_and_clear_event_status(board);
+ outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
+ outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG);
+ for (i = 0; i < fifo_xfer_len;) {
+ int block_size;
+
+ block_size = min(fifo_xfer_len - i, hp_82341_fifo_size);
+ set_transfer_counter(hp_priv, block_size);
+ // load data into board's fifo
+ for (j = 0; j < block_size;) {
+ unsigned short data_word = buffer[i++];
+ ++j;
+ if (j < block_size) {
+ data_word |= buffer[i++] << 8;
+ ++j;
+ }
+ outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG);
+ }
+ clear_bit(WRITE_READY_BN, &tms_priv->state);
+ outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
+ retval = restart_write_fifo(board, hp_priv);
+ if (retval < 0) {
+ dev_err(board->gpib_dev, "failed to restart write stream\n");
+ break;
+ }
+ retval = wait_event_interruptible(board->wait,
+ ((event_status =
+ read_and_clear_event_status(board)) &
+ TERMINAL_COUNT_EVENT_BIT) ||
+ test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
+ test_bit(TIMO_NUM, &board->status));
+ outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
+ *bytes_written += block_size - read_transfer_counter(hp_priv);
+ if (retval) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (test_bit(TIMO_NUM, &board->status)) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+ }
+ if (retval)
+ return retval;
+ if (send_eoi) {
+ size_t num_bytes;
+
+ retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config);
+
+static void hp_82341_detach(struct gpib_board *board);
+
+// wrappers for interface functions
+static int hp_82341_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
+}
+
+static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int hp_82341_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
+}
+
+static int hp_82341_take_control(struct gpib_board *board, int synchronous)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
+}
+
+static int hp_82341_go_to_standby(struct gpib_board *board)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_go_to_standby(board, &priv->tms9914_priv);
+}
+
+static int hp_82341_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ if (request_control)
+ priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT;
+ else
+ priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT;
+ outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG);
+ return tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
+}
+
+static void hp_82341_interface_clear(struct gpib_board *board, int assert)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_interface_clear(board, &priv->tms9914_priv, assert);
+}
+
+static void hp_82341_remote_enable(struct gpib_board *board, int enable)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_remote_enable(board, &priv->tms9914_priv, enable);
+}
+
+static int hp_82341_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
+}
+
+static void hp_82341_disable_eos(struct gpib_board *board)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_disable_eos(board, &priv->tms9914_priv);
+}
+
+static unsigned int hp_82341_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
+}
+
+static int hp_82341_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_primary_address(board, &priv->tms9914_priv, address);
+}
+
+static int hp_82341_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
+}
+
+static int hp_82341_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
+}
+
+static void hp_82341_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
+}
+
+static void hp_82341_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
+}
+
+static void hp_82341_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
+}
+
+static u8 hp_82341_serial_poll_status(struct gpib_board *board)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_serial_poll_status(board, &priv->tms9914_priv);
+}
+
+static int hp_82341_line_status(const struct gpib_board *board)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_line_status(board, &priv->tms9914_priv);
+}
+
+static int hp_82341_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
+}
+
+static void hp_82341_return_to_local(struct gpib_board *board)
+{
+ struct hp_82341_priv *priv = board->private_data;
+
+ tms9914_return_to_local(board, &priv->tms9914_priv);
+}
+
+static struct gpib_interface hp_82341_unaccel_interface = {
+ .name = "hp_82341_unaccel",
+ .attach = hp_82341_attach,
+ .detach = hp_82341_detach,
+ .read = hp_82341_read,
+ .write = hp_82341_write,
+ .command = hp_82341_command,
+ .request_system_control = hp_82341_request_system_control,
+ .take_control = hp_82341_take_control,
+ .go_to_standby = hp_82341_go_to_standby,
+ .interface_clear = hp_82341_interface_clear,
+ .remote_enable = hp_82341_remote_enable,
+ .enable_eos = hp_82341_enable_eos,
+ .disable_eos = hp_82341_disable_eos,
+ .parallel_poll = hp_82341_parallel_poll,
+ .parallel_poll_configure = hp_82341_parallel_poll_configure,
+ .parallel_poll_response = hp_82341_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = hp_82341_line_status,
+ .update_status = hp_82341_update_status,
+ .primary_address = hp_82341_primary_address,
+ .secondary_address = hp_82341_secondary_address,
+ .serial_poll_response = hp_82341_serial_poll_response,
+ .serial_poll_status = hp_82341_serial_poll_status,
+ .t1_delay = hp_82341_t1_delay,
+ .return_to_local = hp_82341_return_to_local,
+};
+
+static struct gpib_interface hp_82341_interface = {
+ .name = "hp_82341",
+ .attach = hp_82341_attach,
+ .detach = hp_82341_detach,
+ .read = hp_82341_accel_read,
+ .write = hp_82341_accel_write,
+ .command = hp_82341_command,
+ .request_system_control = hp_82341_request_system_control,
+ .take_control = hp_82341_take_control,
+ .go_to_standby = hp_82341_go_to_standby,
+ .interface_clear = hp_82341_interface_clear,
+ .remote_enable = hp_82341_remote_enable,
+ .enable_eos = hp_82341_enable_eos,
+ .disable_eos = hp_82341_disable_eos,
+ .parallel_poll = hp_82341_parallel_poll,
+ .parallel_poll_configure = hp_82341_parallel_poll_configure,
+ .parallel_poll_response = hp_82341_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = hp_82341_line_status,
+ .update_status = hp_82341_update_status,
+ .primary_address = hp_82341_primary_address,
+ .secondary_address = hp_82341_secondary_address,
+ .serial_poll_response = hp_82341_serial_poll_response,
+ .t1_delay = hp_82341_t1_delay,
+ .return_to_local = hp_82341_return_to_local,
+};
+
+static int hp_82341_allocate_private(struct gpib_board *board)
+{
+ board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ return 0;
+}
+
+static void hp_82341_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static u8 hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num)
+{
+ return inb(priv->iobase + register_num);
+}
+
+static void hp_82341_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num)
+{
+ outb(data, priv->iobase + register_num);
+}
+
+static int hp_82341_find_isapnp_board(struct pnp_dev **dev)
+{
+ *dev = pnp_find_dev(NULL, ISAPNP_VENDOR('H', 'W', 'P'),
+ ISAPNP_FUNCTION(0x1411), NULL);
+ if (!*dev || !(*dev)->card) {
+ pr_err("failed to find isapnp board\n");
+ return -ENODEV;
+ }
+ if (pnp_device_attach(*dev) < 0) {
+ pr_err("board already active, skipping\n");
+ return -EBUSY;
+ }
+ if (pnp_activate_dev(*dev) < 0) {
+ pnp_device_detach(*dev);
+ pr_err("failed to activate(), aborting\n");
+ return -EAGAIN;
+ }
+ if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) {
+ pnp_device_detach(*dev);
+ pr_err("invalid port or irq, aborting\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int xilinx_ready(struct hp_82341_priv *hp_priv)
+{
+ switch (hp_priv->hw_version) {
+ case HW_VERSION_82341C:
+ if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & XILINX_READY_BIT)
+ return 1;
+ else
+ return 0;
+ break;
+ case HW_VERSION_82341D:
+ if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_READY_BIT)
+ return 1;
+ else
+ return 0;
+ default:
+ pr_err("bug! unknown hw_version\n");
+ break;
+ }
+ return 0;
+}
+
+static int xilinx_done(struct hp_82341_priv *hp_priv)
+{
+ switch (hp_priv->hw_version) {
+ case HW_VERSION_82341C:
+ if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & DONE_PGL_BIT)
+ return 1;
+ else
+ return 0;
+ case HW_VERSION_82341D:
+ if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_DONE_BIT)
+ return 1;
+ else
+ return 0;
+ default:
+ pr_err("bug! unknown hw_version\n");
+ break;
+ }
+ return 0;
+}
+
+static int irq_valid(struct hp_82341_priv *hp_priv, int irq)
+{
+ switch (hp_priv->hw_version) {
+ case HW_VERSION_82341C:
+ switch (irq) {
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 15:
+ return 1;
+ default:
+ pr_err("invalid irq=%i for 82341C, irq must be 3, 5, 7, 9, 10, 11, 12, or 15.\n",
+ irq);
+ return 0;
+ }
+ break;
+ case HW_VERSION_82341D:
+ return 1;
+ default:
+ pr_err("bug! unknown hw_version\n");
+ break;
+ }
+ return 0;
+}
+
+static int hp_82341_load_firmware_array(struct hp_82341_priv *hp_priv,
+ const unsigned char *firmware_data,
+ unsigned int firmware_length)
+{
+ int i, j;
+ static const int timeout = 100;
+
+ for (i = 0; i < firmware_length; ++i) {
+ for (j = 0; j < timeout; ++j) {
+ if (need_resched())
+ schedule();
+ if (xilinx_ready(hp_priv))
+ break;
+ usleep_range(10, 15);
+ }
+ if (j == timeout) {
+ pr_err("timed out waiting for Xilinx ready.\n");
+ return -ETIMEDOUT;
+ }
+ outb(firmware_data[i], hp_priv->iobase[0] + XILINX_DATA_REG);
+ }
+ for (j = 0; j < timeout; ++j) {
+ if (xilinx_done(hp_priv))
+ break;
+ if (need_resched())
+ schedule();
+ usleep_range(10, 15);
+ }
+ if (j == timeout) {
+ pr_err("timed out waiting for Xilinx done.\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv,
+ const struct gpib_board_config *config)
+{
+ if (config->init_data_length == 0) {
+ if (xilinx_done(hp_priv))
+ return 0;
+ pr_err("board needs be initialized with firmware upload.\n"
+ "\tUse the --init-data option of gpib_config.\n");
+ return -EINVAL;
+ }
+ switch (hp_priv->hw_version) {
+ case HW_VERSION_82341C:
+ if (config->init_data_length != hp_82341c_firmware_length) {
+ pr_err("bad firmware length=%i for 82341c (expected %i).\n",
+ config->init_data_length, hp_82341c_firmware_length);
+ return -EINVAL;
+ }
+ break;
+ case HW_VERSION_82341D:
+ if (config->init_data_length != hp_82341d_firmware_length) {
+ pr_err("bad firmware length=%i for 82341d (expected %i).\n",
+ config->init_data_length, hp_82341d_firmware_length);
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_err("bug! unknown hw_version\n");
+ break;
+ }
+ return hp_82341_load_firmware_array(hp_priv, config->init_data, config->init_data_length);
+}
+
+static void set_xilinx_not_prog(struct hp_82341_priv *hp_priv, int assert)
+{
+ switch (hp_priv->hw_version) {
+ case HW_VERSION_82341C:
+ if (assert)
+ hp_priv->config_control_bits |= DONE_PGL_BIT;
+ else
+ hp_priv->config_control_bits &= ~DONE_PGL_BIT;
+ outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG);
+ break;
+ case HW_VERSION_82341D:
+ if (assert)
+ isapnp_write_byte(PIO_DATA_REG, HP_82341D_NOT_PROG_BIT);
+ else
+ isapnp_write_byte(PIO_DATA_REG, 0x0);
+ break;
+ default:
+ break;
+ }
+}
+
+// clear xilinx firmware
+static int clear_xilinx(struct hp_82341_priv *hp_priv)
+{
+ set_xilinx_not_prog(hp_priv, 1);
+ if (msleep_interruptible(1))
+ return -EINTR;
+ set_xilinx_not_prog(hp_priv, 0);
+ if (msleep_interruptible(1))
+ return -EINTR;
+ set_xilinx_not_prog(hp_priv, 1);
+ if (msleep_interruptible(1))
+ return -EINTR;
+ return 0;
+}
+
+static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct hp_82341_priv *hp_priv;
+ struct tms9914_priv *tms_priv;
+ u32 start_addr;
+ u32 iobase;
+ int irq;
+ int i;
+ int retval;
+
+ board->status = 0;
+ if (hp_82341_allocate_private(board))
+ return -ENOMEM;
+ hp_priv = board->private_data;
+ tms_priv = &hp_priv->tms9914_priv;
+ tms_priv->read_byte = hp_82341_read_byte;
+ tms_priv->write_byte = hp_82341_write_byte;
+ tms_priv->offset = 1;
+
+ if (config->ibbase == 0) {
+ struct pnp_dev *dev;
+ int retval = hp_82341_find_isapnp_board(&dev);
+
+ if (retval < 0)
+ return retval;
+ hp_priv->pnp_dev = dev;
+ iobase = pnp_port_start(dev, 0);
+ irq = pnp_irq(dev, 0);
+ hp_priv->hw_version = HW_VERSION_82341D;
+ hp_priv->io_region_offset = 0x8;
+ } else {
+ iobase = config->ibbase;
+ irq = config->ibirq;
+ hp_priv->hw_version = HW_VERSION_82341C;
+ hp_priv->io_region_offset = 0x400;
+ }
+ for (i = 0; i < hp_82341_num_io_regions; ++i) {
+ start_addr = iobase + i * hp_priv->io_region_offset;
+ if (!request_region(start_addr, hp_82341_region_iosize, DRV_NAME)) {
+ dev_err(board->gpib_dev, "failed to allocate io ports 0x%x-0x%x\n",
+ start_addr,
+ start_addr + hp_82341_region_iosize - 1);
+ return -EIO;
+ }
+ hp_priv->iobase[i] = start_addr;
+ }
+ tms_priv->iobase = hp_priv->iobase[2];
+ if (hp_priv->hw_version == HW_VERSION_82341D) {
+ retval = isapnp_cfg_begin(hp_priv->pnp_dev->card->number,
+ hp_priv->pnp_dev->number);
+ if (retval < 0) {
+ dev_err(board->gpib_dev, "isapnp_cfg_begin returned error\n");
+ return retval;
+ }
+ isapnp_write_byte(PIO_DIRECTION_REG, HP_82341D_XILINX_READY_BIT |
+ HP_82341D_XILINX_DONE_BIT);
+ }
+ retval = clear_xilinx(hp_priv);
+ if (retval < 0)
+ return retval;
+ retval = hp_82341_load_firmware(hp_priv, config);
+ if (hp_priv->hw_version == HW_VERSION_82341D)
+ isapnp_cfg_end();
+ if (retval < 0)
+ return retval;
+ if (irq_valid(hp_priv, irq) == 0)
+ return -EINVAL;
+ if (request_irq(irq, hp_82341_interrupt, 0, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", irq);
+ return -EIO;
+ }
+ hp_priv->irq = irq;
+ hp_priv->config_control_bits &= ~IRQ_SELECT_MASK;
+ hp_priv->config_control_bits |= IRQ_SELECT_BITS(irq);
+ outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG);
+ hp_priv->mode_control_bits |= ENABLE_IRQ_CONFIG_BIT;
+ outb(hp_priv->mode_control_bits, hp_priv->iobase[0] + MODE_CONTROL_STATUS_REG);
+ tms9914_board_reset(tms_priv);
+ outb(ENABLE_BUFFER_END_EVENT_BIT | ENABLE_TERMINAL_COUNT_EVENT_BIT |
+ ENABLE_TI_INTERRUPT_EVENT_BIT, hp_priv->iobase[0] + EVENT_ENABLE_REG);
+ outb(ENABLE_BUFFER_END_INTERRUPT_BIT | ENABLE_TERMINAL_COUNT_INTERRUPT_BIT |
+ ENABLE_TI_INTERRUPT_BIT, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG);
+ // write clear event register
+ outb((TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT |
+ BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT),
+ hp_priv->iobase[0] + EVENT_STATUS_REG);
+
+ tms9914_online(board, tms_priv);
+
+ return 0;
+}
+
+static void hp_82341_detach(struct gpib_board *board)
+{
+ struct hp_82341_priv *hp_priv = board->private_data;
+ struct tms9914_priv *tms_priv;
+ int i;
+
+ if (hp_priv) {
+ tms_priv = &hp_priv->tms9914_priv;
+ if (hp_priv->iobase[0]) {
+ outb(0, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG);
+ if (tms_priv->iobase)
+ tms9914_board_reset(tms_priv);
+ if (hp_priv->irq)
+ free_irq(hp_priv->irq, board);
+ }
+ for (i = 0; i < hp_82341_num_io_regions; ++i) {
+ if (hp_priv->iobase[i])
+ release_region(hp_priv->iobase[i], hp_82341_region_iosize);
+ }
+ if (hp_priv->pnp_dev)
+ pnp_device_detach(hp_priv->pnp_dev);
+ }
+ hp_82341_free_private(board);
+}
+
+#if 0
+/* unused, will be needed when the driver is turned into a pnp_driver */
+static const struct pnp_device_id hp_82341_pnp_table[] = {
+ {.id = "HWP1411"},
+ {.id = ""}
+};
+MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table);
+#endif
+
+static int __init hp_82341_init_module(void)
+{
+ int ret;
+
+ ret = gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ return ret;
+ }
+
+ ret = gpib_register_driver(&hp_82341_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ gpib_unregister_driver(&hp_82341_unaccel_interface);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit hp_82341_exit_module(void)
+{
+ gpib_unregister_driver(&hp_82341_interface);
+ gpib_unregister_driver(&hp_82341_unaccel_interface);
+}
+
+module_init(hp_82341_init_module);
+module_exit(hp_82341_exit_module);
+
+/*
+ * GPIB interrupt service routines
+ */
+static unsigned short read_and_clear_event_status(struct gpib_board *board)
+{
+ struct hp_82341_priv *hp_priv = board->private_data;
+ unsigned long flags;
+ unsigned short status;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ status = hp_priv->event_status_bits;
+ hp_priv->event_status_bits = 0;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return status;
+}
+
+static irqreturn_t hp_82341_interrupt(int irq, void *arg)
+{
+ int status1, status2;
+ struct gpib_board *board = arg;
+ struct hp_82341_priv *hp_priv = board->private_data;
+ struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE;
+ int event_status;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ event_status = inb(hp_priv->iobase[0] + EVENT_STATUS_REG);
+ if (event_status & INTERRUPT_PENDING_EVENT_BIT)
+ retval = IRQ_HANDLED;
+ // write-clear status bits
+ if (event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT |
+ BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT)) {
+ outb(event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT |
+ BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT),
+ hp_priv->iobase[0] + EVENT_STATUS_REG);
+ hp_priv->event_status_bits |= event_status;
+ }
+ if (event_status & TI_INTERRUPT_EVENT_BIT) {
+ status1 = read_byte(tms_priv, ISR0);
+ status2 = read_byte(tms_priv, ISR1);
+ tms9914_interrupt_have_status(board, tms_priv, status1, status2);
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+static int read_transfer_counter(struct hp_82341_priv *hp_priv)
+{
+ int lo, mid, value;
+
+ lo = inb(hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG);
+ mid = inb(hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG);
+ value = (lo & 0xff) | ((mid << 8) & 0x7f00);
+ value = ~(value - 1) & 0x7fff;
+ return value;
+}
+
+static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count)
+{
+ int complement = -count;
+
+ outb(complement & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG);
+ outb((complement >> 8) & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG);
+ // I don't think the hi count reg is even used, but oh well
+ outb((complement >> 16) & 0xf, hp_priv->iobase[1] + TRANSFER_COUNT_HIGH_REG);
+}
+
diff --git a/drivers/gpib/hp_82341/hp_82341.h b/drivers/gpib/hp_82341/hp_82341.h
new file mode 100644
index 000000000000..859ef2899acb
--- /dev/null
+++ b/drivers/gpib/hp_82341/hp_82341.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002, 2005 by Frank Mori Hess *
+ ***************************************************************************/
+
+#include "tms9914.h"
+#include "gpibP.h"
+
+enum hp_82341_hardware_version {
+ HW_VERSION_UNKNOWN,
+ HW_VERSION_82341C,
+ HW_VERSION_82341D,
+};
+
+// struct which defines private_data for board
+struct hp_82341_priv {
+ struct tms9914_priv tms9914_priv;
+ unsigned int irq;
+ unsigned short config_control_bits;
+ unsigned short mode_control_bits;
+ unsigned short event_status_bits;
+ struct pnp_dev *pnp_dev;
+ unsigned long iobase[4];
+ unsigned long io_region_offset;
+ enum hp_82341_hardware_version hw_version;
+};
+
+static const int hp_82341_region_iosize = 0x8;
+static const int hp_82341_num_io_regions = 4;
+static const int hp_82341_fifo_size = 0xffe;
+static const int hp_82341c_firmware_length = 5764;
+static const int hp_82341d_firmware_length = 5302;
+
+// hp 82341 register offsets
+enum hp_82341_region_0_registers {
+ CONFIG_CONTROL_STATUS_REG = 0x0,
+ MODE_CONTROL_STATUS_REG = 0x1,
+ MONITOR_REG = 0x2, // after initialization
+ XILINX_DATA_REG = 0x2, // before initialization, write only
+ INTERRUPT_ENABLE_REG = 0x3,
+ EVENT_STATUS_REG = 0x4,
+ EVENT_ENABLE_REG = 0x5,
+ STREAM_STATUS_REG = 0x7,
+};
+
+enum hp_82341_region_1_registers {
+ ID0_REG = 0x2,
+ ID1_REG = 0x3,
+ TRANSFER_COUNT_LOW_REG = 0x4,
+ TRANSFER_COUNT_MID_REG = 0x5,
+ TRANSFER_COUNT_HIGH_REG = 0x6,
+};
+
+enum hp_82341_region_3_registers {
+ BUFFER_PORT_LOW_REG = 0x0,
+ BUFFER_PORT_HIGH_REG = 0x1,
+ ID2_REG = 0x2,
+ ID3_REG = 0x3,
+ BUFFER_FLUSH_REG = 0x4,
+ BUFFER_CONTROL_REG = 0x7
+};
+
+enum config_control_status_bits {
+ IRQ_SELECT_MASK = 0x7,
+ DMA_CONFIG_MASK = 0x18,
+ ENABLE_DMA_CONFIG_BIT = 0x20,
+ XILINX_READY_BIT = 0x40, // read only
+ DONE_PGL_BIT = 0x80
+};
+
+static inline unsigned int IRQ_SELECT_BITS(int irq)
+{
+ switch (irq) {
+ case 3:
+ return 0x3;
+ case 5:
+ return 0x2;
+ case 7:
+ return 0x1;
+ case 9:
+ return 0x0;
+ case 10:
+ return 0x7;
+ case 11:
+ return 0x6;
+ case 12:
+ return 0x5;
+ case 15:
+ return 0x4;
+ default:
+ return 0x0;
+ }
+};
+
+enum mode_control_status_bits {
+ SLOT8_BIT = 0x1, // read only
+ ACTIVE_CONTROLLER_BIT = 0x2, // read only
+ ENABLE_DMA_BIT = 0x4,
+ SYSTEM_CONTROLLER_BIT = 0x8,
+ MONITOR_BIT = 0x10,
+ ENABLE_IRQ_CONFIG_BIT = 0x20,
+ ENABLE_TI_STREAM_BIT = 0x40
+};
+
+enum monitor_bits {
+ MONITOR_INTERRUPT_PENDING_BIT = 0x1, // read only
+ MONITOR_CLEAR_HOLDOFF_BIT = 0x2, // write only
+ MONITOR_PPOLL_BIT = 0x4, // write clear
+ MONITOR_SRQ_BIT = 0x8, // write clear
+ MONITOR_IFC_BIT = 0x10, // write clear
+ MONITOR_REN_BIT = 0x20, // write clear
+ MONITOR_END_BIT = 0x40, // write clear
+ MONITOR_DAV_BIT = 0x80 // write clear
+};
+
+enum interrupt_enable_bits {
+ ENABLE_TI_INTERRUPT_BIT = 0x1,
+ ENABLE_POINTERS_EQUAL_INTERRUPT_BIT = 0x4,
+ ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10,
+ ENABLE_TERMINAL_COUNT_INTERRUPT_BIT = 0x20,
+ ENABLE_DMA_TERMINAL_COUNT_INTERRUPT_BIT = 0x80,
+};
+
+enum event_status_bits {
+ TI_INTERRUPT_EVENT_BIT = 0x1, // write clear
+ INTERRUPT_PENDING_EVENT_BIT = 0x2, // read only
+ POINTERS_EQUAL_EVENT_BIT = 0x4, // write clear
+ BUFFER_END_EVENT_BIT = 0x10, // write clear
+ TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear
+ DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear
+};
+
+enum event_enable_bits {
+ ENABLE_TI_INTERRUPT_EVENT_BIT = 0x1, // write clear
+ ENABLE_POINTERS_EQUAL_EVENT_BIT = 0x4, // write clear
+ ENABLE_BUFFER_END_EVENT_BIT = 0x10, // write clear
+ ENABLE_TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear
+ ENABLE_DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear
+};
+
+enum stream_status_bits {
+ HALTED_STATUS_BIT = 0x1, // read
+ RESTART_STREAM_BIT = 0x1 // write
+};
+
+enum buffer_control_bits {
+ DIRECTION_GPIB_TO_HOST_BIT = 0x20, // transfer direction (set for gpib to host)
+ ENABLE_TI_BUFFER_BIT = 0x40, // enable fifo
+ FAST_WR_EN_BIT = 0x80, // 350 ns t1 delay?
+};
+
+// registers accessible through isapnp chip on 82341d
+enum hp_82341d_pnp_registers {
+ PIO_DATA_REG = 0x20, // read/write pio data lines
+ PIO_DIRECTION_REG = 0x21, // set pio data line directions (set for input)
+};
+
+enum hp_82341d_pnp_pio_bits {
+ HP_82341D_XILINX_READY_BIT = 0x1,
+ HP_82341D_XILINX_DONE_BIT = 0x2,
+ // use register layout compatible with C and older versions instead of 32 contiguous ioports
+ HP_82341D_LEGACY_MODE_BIT = 0x4,
+ HP_82341D_NOT_PROG_BIT = 0x8, // clear to reinitialize xilinx
+};
diff --git a/drivers/gpib/include/amcc5920.h b/drivers/gpib/include/amcc5920.h
new file mode 100644
index 000000000000..7a88bd282feb
--- /dev/null
+++ b/drivers/gpib/include/amcc5920.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Header for amcc5920 pci chip
+ *
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+// plx pci chip registers and bits
+enum amcc_registers {
+ AMCC_INTCS_REG = 0x38,
+ AMCC_PASS_THRU_REG = 0x60,
+};
+
+enum amcc_incsr_bits {
+ AMCC_ADDON_INTR_ENABLE_BIT = 0x2000,
+ AMCC_ADDON_INTR_ACTIVE_BIT = 0x400000,
+ AMCC_INTR_ACTIVE_BIT = 0x800000,
+};
+
+static const int bits_per_region = 8;
+
+static inline uint32_t amcc_wait_state_bits(unsigned int region, unsigned int num_wait_states)
+{
+ return (num_wait_states & 0x7) << (--region * bits_per_region);
+};
+
+enum amcc_prefetch_bits {
+ PREFETCH_DISABLED = 0x0,
+ PREFETCH_SMALL = 0x8,
+ PREFETCH_MEDIUM = 0x10,
+ PREFETCH_LARGE = 0x18,
+};
+
+static inline uint32_t amcc_prefetch_bits(unsigned int region, enum amcc_prefetch_bits prefetch)
+{
+ return prefetch << (--region * bits_per_region);
+};
+
+static inline uint32_t amcc_PTADR_mode_bit(unsigned int region)
+{
+ return 0x80 << (--region * bits_per_region);
+};
+
+static inline uint32_t amcc_disable_write_fifo_bit(unsigned int region)
+{
+ return 0x20 << (--region * bits_per_region);
+};
+
diff --git a/drivers/gpib/include/amccs5933.h b/drivers/gpib/include/amccs5933.h
new file mode 100644
index 000000000000..d7f63c795096
--- /dev/null
+++ b/drivers/gpib/include/amccs5933.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Registers and bits for amccs5933 pci chip
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+// register offsets
+enum {
+ MBEF_REG = 0x34, // mailbux empty/full
+ INTCSR_REG = 0x38, // interrupt control and status
+ BMCSR_REG = 0x3c, // bus master control and status
+};
+
+// incoming mailbox 0-3 register offsets
+extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox)
+{
+ return (0x10 + 4 * mailbox);
+};
+
+// bit definitions
+
+// INTCSR bits
+enum {
+ OUTBOX_EMPTY_INTR_BIT = 0x10, // enable outbox empty interrupt
+ INBOX_FULL_INTR_BIT = 0x1000, // enable inbox full interrupt
+ INBOX_INTR_CS_BIT = 0x20000, // read, or write clear inbox full interrupt
+ INTR_ASSERTED_BIT = 0x800000, // read only, interrupt asserted
+};
+
+// select byte 0 to 3 of incoming mailbox
+extern inline int INBOX_BYTE_BITS(unsigned int byte)
+{
+ return (byte & 0x3) << 8;
+};
+
+// select incoming mailbox 0 to 3
+extern inline int INBOX_SELECT_BITS(unsigned int mailbox)
+{
+ return (mailbox & 0x3) << 10;
+};
+
+// select byte 0 to 3 of outgoing mailbox
+extern inline int OUTBOX_BYTE_BITS(unsigned int byte)
+{
+ return (byte & 0x3);
+};
+
+// select outgoing mailbox 0 to 3
+extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox)
+{
+ return (mailbox & 0x3) << 2;
+};
+
+// BMCSR bits
+enum {
+ MBOX_FLAGS_RESET_BIT = 0x08000000, // resets mailbox empty/full flags
+};
+
diff --git a/drivers/gpib/include/gpibP.h b/drivers/gpib/include/gpibP.h
new file mode 100644
index 000000000000..e3938ada3e0d
--- /dev/null
+++ b/drivers/gpib/include/gpibP.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002,2003 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _GPIB_P_H
+#define _GPIB_P_H
+
+#include <linux/types.h>
+
+#include "gpib_types.h"
+#include "gpib_proto.h"
+#include "gpib_cmd.h"
+#include <linux/gpib.h>
+#include <linux/gpib_ioctl.h>
+
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+int gpib_register_driver(struct gpib_interface *interface, struct module *mod);
+void gpib_unregister_driver(struct gpib_interface *interface);
+struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id,
+ unsigned int device_id, struct pci_dev *from);
+struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id,
+ unsigned int device_id, unsigned int ss_vendor,
+ unsigned int ss_device, struct pci_dev *from);
+unsigned int num_gpib_events(const struct gpib_event_queue *queue);
+int push_gpib_event(struct gpib_board *board, short event_type);
+int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type);
+int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *));
+void gpib_free_pseudo_irq(struct gpib_board *board);
+int gpib_match_device_path(struct device *dev, const char *device_path_in);
+
+extern struct gpib_board board_array[GPIB_MAX_NUM_BOARDS];
+
+extern struct list_head registered_drivers;
+
+#endif // _GPIB_P_H
+
diff --git a/drivers/gpib/include/gpib_cmd.h b/drivers/gpib/include/gpib_cmd.h
new file mode 100644
index 000000000000..9e96a3bfa22d
--- /dev/null
+++ b/drivers/gpib/include/gpib_cmd.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _GPIB_CMD_H
+#define _GPIB_CMD_H
+
+#include <linux/types.h>
+
+/* Command byte definitions tests and functions */
+
+/* mask of bits that actually matter in a command byte */
+enum {
+ gpib_command_mask = 0x7f,
+};
+
+/* Possible GPIB command messages */
+
+enum cmd_byte {
+ GTL = 0x1, /* go to local */
+ SDC = 0x4, /* selected device clear */
+ PP_CONFIG = 0x5,
+ GET = 0x8, /* group execute trigger */
+ TCT = 0x9, /* take control */
+ LLO = 0x11, /* local lockout */
+ DCL = 0x14, /* device clear */
+ PPU = 0x15, /* parallel poll unconfigure */
+ SPE = 0x18, /* serial poll enable */
+ SPD = 0x19, /* serial poll disable */
+ CFE = 0x1f, /* configure enable */
+ LAD = 0x20, /* value to be 'ored' in to obtain listen address */
+ UNL = 0x3F, /* unlisten */
+ TAD = 0x40, /* value to be 'ored' in to obtain talk address */
+ UNT = 0x5F, /* untalk */
+ SAD = 0x60, /* my secondary address (base) */
+ PPE = 0x60, /* parallel poll enable (base) */
+ PPD = 0x70 /* parallel poll disable */
+};
+
+/* confine address to range 0 to 30. */
+static inline unsigned int gpib_address_restrict(u32 addr)
+{
+ addr &= 0x1f;
+ if (addr == 0x1f)
+ addr = 0;
+ return addr;
+}
+
+static inline u8 MLA(u32 addr)
+{
+ return gpib_address_restrict(addr) | LAD;
+}
+
+static inline u8 MTA(u32 addr)
+{
+ return gpib_address_restrict(addr) | TAD;
+}
+
+static inline u8 MSA(u32 addr)
+{
+ return (addr & 0x1f) | SAD;
+}
+
+static inline s32 gpib_address_equal(u32 pad1, s32 sad1, u32 pad2, s32 sad2)
+{
+ if (pad1 == pad2) {
+ if (sad1 == sad2)
+ return 1;
+ if (sad1 < 0 && sad2 < 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline s32 is_PPE(u8 command)
+{
+ return (command & 0x70) == 0x60;
+}
+
+static inline s32 is_PPD(u8 command)
+{
+ return (command & 0x70) == 0x70;
+}
+
+static inline s32 in_addressed_command_group(u8 command)
+{
+ return (command & 0x70) == 0x0;
+}
+
+static inline s32 in_universal_command_group(u8 command)
+{
+ return (command & 0x70) == 0x10;
+}
+
+static inline s32 in_listen_address_group(u8 command)
+{
+ return (command & 0x60) == 0x20;
+}
+
+static inline s32 in_talk_address_group(u8 command)
+{
+ return (command & 0x60) == 0x40;
+}
+
+static inline s32 in_primary_command_group(u8 command)
+{
+ return in_addressed_command_group(command) ||
+ in_universal_command_group(command) ||
+ in_listen_address_group(command) ||
+ in_talk_address_group(command);
+}
+
+#endif /* _GPIB_CMD_H */
diff --git a/drivers/gpib/include/gpib_pci_ids.h b/drivers/gpib/include/gpib_pci_ids.h
new file mode 100644
index 000000000000..52dcab07a7d1
--- /dev/null
+++ b/drivers/gpib/include/gpib_pci_ids.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __GPIB_PCI_IDS_H
+#define __GPIB_PCI_IDS_H
+
+#ifndef PCI_VENDOR_ID_AMCC
+#define PCI_VENDOR_ID_AMCC 0x10e8
+#endif
+
+#ifndef PCI_VENDOR_ID_CBOARDS
+#define PCI_VENDOR_ID_CBOARDS 0x1307
+#endif
+
+#ifndef PCI_VENDOR_ID_QUANCOM
+#define PCI_VENDOR_ID_QUANCOM 0x8008
+#endif
+
+#ifndef PCI_DEVICE_ID_QUANCOM_GPIB
+#define PCI_DEVICE_ID_QUANCOM_GPIB 0x3302
+#endif
+
+#endif // __GPIB_PCI_IDS_H
+
diff --git a/drivers/gpib/include/gpib_proto.h b/drivers/gpib/include/gpib_proto.h
new file mode 100644
index 000000000000..42e736e3b7cd
--- /dev/null
+++ b/drivers/gpib/include/gpib_proto.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef GPIB_PROTO_INCLUDED
+#define GPIB_PROTO_INCLUDED
+
+#include <linux/fs.h>
+
+int ibopen(struct inode *inode, struct file *filep);
+int ibclose(struct inode *inode, struct file *file);
+long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+void os_start_timer(struct gpib_board *board, unsigned int usec_timeout);
+void os_remove_timer(struct gpib_board *board);
+void init_gpib_board(struct gpib_board *board);
+static inline unsigned long usec_to_jiffies(unsigned int usec)
+{
+ unsigned long usec_per_jiffy = 1000000 / HZ;
+
+ return 1 + (usec + usec_per_jiffy - 1) / usec_per_jiffy;
+};
+
+int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout);
+void init_gpib_descriptor(struct gpib_descriptor *desc);
+int dvrsp(struct gpib_board *board, unsigned int pad, int sad,
+ unsigned int usec_timeout, u8 *result);
+int ibcac(struct gpib_board *board, int sync, int fallback_to_async);
+int ibcmd(struct gpib_board *board, u8 *buf, size_t length, size_t *bytes_written);
+int ibgts(struct gpib_board *board);
+int ibonline(struct gpib_board *board);
+int iboffline(struct gpib_board *board);
+int iblines(const struct gpib_board *board, short *lines);
+int ibrd(struct gpib_board *board, u8 *buf, size_t length, int *end_flag, size_t *bytes_read);
+int ibrpp(struct gpib_board *board, u8 *buf);
+int ibrsv2(struct gpib_board *board, u8 status_byte, int new_reason_for_service);
+int ibrsc(struct gpib_board *board, int request_control);
+int ibsic(struct gpib_board *board, unsigned int usec_duration);
+int ibsre(struct gpib_board *board, int enable);
+int ibpad(struct gpib_board *board, unsigned int addr);
+int ibsad(struct gpib_board *board, int addr);
+int ibeos(struct gpib_board *board, int eos, int eosflags);
+int ibwait(struct gpib_board *board, int wait_mask, int clear_mask, int set_mask,
+ int *status, unsigned long usec_timeout, struct gpib_descriptor *desc);
+int ibwrt(struct gpib_board *board, u8 *buf, size_t cnt, int send_eoi, size_t *bytes_written);
+int ibstatus(struct gpib_board *board);
+int general_ibstatus(struct gpib_board *board, const struct gpib_status_queue *device,
+ int clear_mask, int set_mask, struct gpib_descriptor *desc);
+int io_timed_out(struct gpib_board *board);
+int ibppc(struct gpib_board *board, u8 configuration);
+
+#endif /* GPIB_PROTO_INCLUDED */
diff --git a/drivers/gpib/include/gpib_state_machines.h b/drivers/gpib/include/gpib_state_machines.h
new file mode 100644
index 000000000000..7488c00f191e
--- /dev/null
+++ b/drivers/gpib/include/gpib_state_machines.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2006 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _GPIB_STATE_MACHINES_H
+#define _GPIB_STATE_MACHINES_H
+
+enum talker_function_state {
+ talker_idle,
+ talker_addressed,
+ talker_active,
+ serial_poll_active
+};
+
+enum listener_function_state {
+ listener_idle,
+ listener_addressed,
+ listener_active
+};
+
+#endif // _GPIB_STATE_MACHINES_H
diff --git a/drivers/gpib/include/gpib_types.h b/drivers/gpib/include/gpib_types.h
new file mode 100644
index 000000000000..5a0978ae27e7
--- /dev/null
+++ b/drivers/gpib/include/gpib_types.h
@@ -0,0 +1,381 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _GPIB_TYPES_H
+#define _GPIB_TYPES_H
+
+#ifdef __KERNEL__
+#include <linux/gpib.h>
+#include <linux/atomic.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+
+struct gpib_board;
+
+/* config parameters that are only used by driver attach functions */
+struct gpib_board_config {
+ /* firmware blob */
+ void *init_data;
+ int init_data_length;
+ /* IO base address to use for non-pnp cards (set by core, driver should make local copy) */
+ u32 ibbase;
+ void __iomem *mmibbase;
+ /* IRQ to use for non-pnp cards (set by core, driver should make local copy) */
+ unsigned int ibirq;
+ /* dma channel to use for non-pnp cards (set by core, driver should make local copy) */
+ unsigned int ibdma;
+ /*
+ * pci bus of card, useful for distinguishing multiple identical pci cards
+ * (negative means don't care)
+ */
+ int pci_bus;
+ /*
+ * pci slot of card, useful for distinguishing multiple identical pci cards
+ * (negative means don't care)
+ */
+ int pci_slot;
+ /* sysfs device path of hardware to attach */
+ char *device_path;
+ /* serial number of hardware to attach */
+ char *serial_number;
+};
+
+/*
+ * struct gpib_interface defines the interface
+ * between the board-specific details dealt with in the drivers
+ * and generic interface provided by gpib-common.
+ * This really should be in a different header file.
+ */
+struct gpib_interface {
+ /* name of board */
+ char *name;
+ /* attach() initializes board and allocates resources */
+ int (*attach)(struct gpib_board *board, const struct gpib_board_config *config);
+ /* detach() shuts down board and frees resources */
+ void (*detach)(struct gpib_board *board);
+ /*
+ * read() should read at most 'length' bytes from the bus into
+ * 'buffer'. It should return when it fills the buffer or
+ * encounters an END (EOI and or EOS if appropriate). It should set 'end'
+ * to be nonzero if the read was terminated by an END, otherwise 'end'
+ * should be zero.
+ * Ultimately, this will be changed into or replaced by an asynchronous
+ * read. Zero return value for success, negative
+ * return indicates error.
+ * nbytes returns number of bytes read
+ */
+ int (*read)(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read);
+ /*
+ * write() should write 'length' bytes from buffer to the bus.
+ * If the boolean value send_eoi is nonzero, then EOI should
+ * be sent along with the last byte. Returns number of bytes
+ * written or negative value on error.
+ */
+ int (*write)(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written);
+ /*
+ * command() writes the command bytes in 'buffer' to the bus
+ * Returns zero on success or negative value on error.
+ */
+ int (*command)(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written);
+ /*
+ * Take control (assert ATN). If 'asyncronous' is nonzero, take
+ * control asyncronously (assert ATN immediately without waiting
+ * for other processes to complete first). Should not return
+ * until board becomes controller in charge. Returns zero no success,
+ * nonzero on error.
+ */
+ int (*take_control)(struct gpib_board *board, int asyncronous);
+ /*
+ * De-assert ATN. Returns zero on success, nonzer on error.
+ */
+ int (*go_to_standby)(struct gpib_board *board);
+ /* request/release control of the IFC and REN lines (system controller) */
+ int (*request_system_control)(struct gpib_board *board, int request_control);
+ /*
+ * Asserts or de-asserts 'interface clear' (IFC) depending on
+ * boolean value of 'assert'
+ */
+ void (*interface_clear)(struct gpib_board *board, int assert);
+ /*
+ * Sends remote enable command if 'enable' is nonzero, disables remote mode
+ * if 'enable' is zero
+ */
+ void (*remote_enable)(struct gpib_board *board, int enable);
+ /*
+ * enable END for reads, when byte 'eos' is received. If
+ * 'compare_8_bits' is nonzero, then all 8 bits are compared
+ * with the eos bytes. Otherwise only the 7 least significant
+ * bits are compared.
+ */
+ int (*enable_eos)(struct gpib_board *board, u8 eos, int compare_8_bits);
+ /* disable END on eos byte (END on EOI only)*/
+ void (*disable_eos)(struct gpib_board *board);
+ /* configure parallel poll */
+ void (*parallel_poll_configure)(struct gpib_board *board, u8 configuration);
+ /* conduct parallel poll */
+ int (*parallel_poll)(struct gpib_board *board, u8 *result);
+ /* set/clear ist (individual status bit) */
+ void (*parallel_poll_response)(struct gpib_board *board, int ist);
+ /* select local parallel poll configuration mode PP2 versus remote PP1 */
+ void (*local_parallel_poll_mode)(struct gpib_board *board, int local);
+ /*
+ * Returns current status of the bus lines. Should be set to
+ * NULL if your board does not have the ability to query the
+ * state of the bus lines.
+ */
+ int (*line_status)(const struct gpib_board *board);
+ /*
+ * updates and returns the board's current status.
+ * The meaning of the bits are specified in gpib_user.h
+ * in the IBSTA section. The driver does not need to
+ * worry about setting the CMPL, END, TIMO, or ERR bits.
+ */
+ unsigned int (*update_status)(struct gpib_board *board, unsigned int clear_mask);
+ /*
+ * Sets primary address 0-30 for gpib interface card.
+ */
+ int (*primary_address)(struct gpib_board *board, unsigned int address);
+ /*
+ * Sets and enables, or disables secondary address 0-30
+ * for gpib interface card.
+ */
+ int (*secondary_address)(struct gpib_board *board, unsigned int address,
+ int enable);
+ /*
+ * Sets the byte the board should send in response to a serial poll.
+ * This function should also start or stop requests for service via
+ * IEEE 488.2 reqt/reqf, based on MSS (bit 6 of the status_byte).
+ * If the more flexible serial_poll_response2 is implemented by the
+ * driver, then this method should be left NULL since it will not
+ * be used. This method can generate spurious service requests
+ * which are allowed by IEEE 488.2, but not ideal.
+ *
+ * This method should implement the serial poll response method described
+ * by IEEE 488.2 section 11.3.3.4.3 "Allowed Coupled Control of
+ * STB, reqt, and reqf".
+ */
+ void (*serial_poll_response)(struct gpib_board *board, u8 status_byte);
+ /*
+ * Sets the byte the board should send in response to a serial poll.
+ * This function should also request service via IEEE 488.2 reqt/reqf
+ * based on MSS (bit 6 of the status_byte) and new_reason_for_service.
+ * reqt should be set true if new_reason_for_service is true,
+ * and reqf should be set true if MSS is false. This function
+ * will never be called with MSS false and new_reason_for_service
+ * true simultaneously, so don't worry about that case.
+ *
+ * This method implements the serial poll response method described
+ * by IEEE 488.2 section 11.3.3.4.1 "Preferred Implementation".
+ *
+ * If this method is left NULL by the driver, then the user library
+ * function ibrsv2 will not work.
+ */
+ void (*serial_poll_response2)(struct gpib_board *board, u8 status_byte,
+ int new_reason_for_service);
+ /*
+ * returns the byte the board will send in response to a serial poll.
+ */
+ u8 (*serial_poll_status)(struct gpib_board *board);
+ /* adjust T1 delay */
+ int (*t1_delay)(struct gpib_board *board, unsigned int nano_sec);
+ /* go to local mode */
+ void (*return_to_local)(struct gpib_board *board);
+ /* board does not support 7 bit eos comparisons */
+ unsigned no_7_bit_eos : 1;
+ /* skip check for listeners before trying to send command bytes */
+ unsigned skip_check_for_command_acceptors : 1;
+};
+
+struct gpib_event_queue {
+ struct list_head event_head;
+ spinlock_t lock; // for access to event list
+ unsigned int num_events;
+ unsigned dropped_event : 1;
+};
+
+static inline void init_event_queue(struct gpib_event_queue *queue)
+{
+ INIT_LIST_HEAD(&queue->event_head);
+ queue->num_events = 0;
+ queue->dropped_event = 0;
+ spin_lock_init(&queue->lock);
+}
+
+/* struct for supporting polling operation when irq is not available */
+struct gpib_pseudo_irq {
+ struct timer_list timer;
+ irqreturn_t (*handler)(int irq, void *arg);
+ struct gpib_board *board;
+ atomic_t active;
+};
+
+static inline void init_gpib_pseudo_irq(struct gpib_pseudo_irq *pseudo_irq)
+{
+ pseudo_irq->handler = NULL;
+ timer_setup(&pseudo_irq->timer, NULL, 0);
+ atomic_set(&pseudo_irq->active, 0);
+}
+
+/* list so we can make a linked list of drivers */
+struct gpib_interface_list {
+ struct list_head list;
+ struct gpib_interface *interface;
+ struct module *module;
+};
+
+/*
+ * One struct gpib_board is allocated for each physical board in the computer.
+ * It provides storage for variables local to each board, and interface
+ * functions for performing operations on the board
+ */
+struct gpib_board {
+ /* functions used by this board */
+ struct gpib_interface *interface;
+ /*
+ * Pointer to module whose use count we should increment when
+ * interface is in use
+ */
+ struct module *provider_module;
+ /* buffer used to store read/write data for this board */
+ u8 *buffer;
+ /* length of buffer */
+ unsigned int buffer_length;
+ /*
+ * Used to hold the board's current status (see update_status() above)
+ */
+ unsigned long status;
+ /*
+ * Driver should only sleep on this wait queue. It is special in that the
+ * core will wake this queue and set the TIMO bit in 'status' when the
+ * watchdog timer times out.
+ */
+ wait_queue_head_t wait;
+ /*
+ * Lock that only allows one process to access this board at a time.
+ * Has to be first in any locking order, since it can be locked over
+ * multiple ioctls.
+ */
+ struct mutex user_mutex;
+ /*
+ * Mutex which compensates for removal of "big kernel lock" from kernel.
+ * Should not be held for extended waits.
+ */
+ struct mutex big_gpib_mutex;
+ /* pid of last process to lock the board mutex */
+ pid_t locking_pid;
+ /* lock for setting locking pid */
+ spinlock_t locking_pid_spinlock;
+ /* Spin lock for dealing with races with the interrupt handler */
+ spinlock_t spinlock;
+ /* Watchdog timer to enable timeouts */
+ struct timer_list timer;
+ /* device of attached driver if any */
+ struct device *dev;
+ /* gpib_common device gpibN */
+ struct device *gpib_dev;
+ /*
+ * 'private_data' can be used as seen fit by the driver to
+ * store additional variables for this board
+ */
+ void *private_data;
+ /* Number of open file descriptors using this board */
+ unsigned int use_count;
+ /* list of open devices connected to this board */
+ struct list_head device_list;
+ /* primary address */
+ unsigned int pad;
+ /* secondary address */
+ int sad;
+ /* timeout for io operations, in microseconds */
+ unsigned int usec_timeout;
+ /* board's parallel poll configuration byte */
+ u8 parallel_poll_configuration;
+ /* t1 delay we are using */
+ unsigned int t1_nano_sec;
+ /* Count that keeps track of whether board is up and running or not */
+ unsigned int online;
+ /* number of processes trying to autopoll */
+ int autospollers;
+ /* autospoll kernel thread */
+ struct task_struct *autospoll_task;
+ /* queue for recording received trigger/clear/ifc events */
+ struct gpib_event_queue event_queue;
+ /* minor number for this board's device file */
+ int minor;
+ /* struct to deal with polling mode*/
+ struct gpib_pseudo_irq pseudo_irq;
+ /* error dong autopoll */
+ atomic_t stuck_srq;
+ struct gpib_board_config config;
+ /* Flag that indicates whether board is system controller of the bus */
+ unsigned master : 1;
+ /* individual status bit */
+ unsigned ist : 1;
+ /*
+ * one means local parallel poll mode ieee 488.1 PP2 (or no parallel poll PP0),
+ * zero means remote parallel poll configuration mode ieee 488.1 PP1
+ */
+ unsigned local_ppoll_mode : 1;
+};
+
+/* element of event queue */
+struct gpib_event {
+ struct list_head list;
+ short event_type;
+};
+
+/*
+ * Each board has a list of gpib_status_queue to keep track of all open devices
+ * on the bus, so we know what address to poll when we get a service request
+ */
+struct gpib_status_queue {
+ /* list_head so we can make a linked list of devices */
+ struct list_head list;
+ unsigned int pad; /* primary gpib address */
+ int sad; /* secondary gpib address (negative means disabled) */
+ /* stores serial poll bytes for this device */
+ struct list_head status_bytes;
+ unsigned int num_status_bytes;
+ /* number of times this address is opened */
+ unsigned int reference_count;
+ /* flags loss of status byte error due to limit on size of queue */
+ unsigned dropped_byte : 1;
+};
+
+struct gpib_status_byte {
+ struct list_head list;
+ u8 poll_byte;
+};
+
+void init_gpib_status_queue(struct gpib_status_queue *device);
+
+/* Used to store device-descriptor-specific information */
+struct gpib_descriptor {
+ unsigned int pad; /* primary gpib address */
+ int sad; /* secondary gpib address (negative means disabled) */
+ atomic_t io_in_progress;
+ unsigned is_board : 1;
+ unsigned autopoll_enabled : 1;
+};
+
+struct gpib_file_private {
+ atomic_t holding_mutex;
+ struct gpib_descriptor *descriptors[GPIB_MAX_NUM_DESCRIPTORS];
+ /* locked while descriptors are being allocated/deallocated */
+ struct mutex descriptors_mutex;
+ unsigned got_module : 1;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _GPIB_TYPES_H */
diff --git a/drivers/gpib/include/nec7210.h b/drivers/gpib/include/nec7210.h
new file mode 100644
index 000000000000..9835aa5ef4ff
--- /dev/null
+++ b/drivers/gpib/include/nec7210.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _NEC7210_H
+#define _NEC7210_H
+
+#include "gpib_state_machines.h"
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+
+#include "gpib_types.h"
+#include "nec7210_registers.h"
+
+/* struct used to provide variables local to a nec7210 chip */
+struct nec7210_priv {
+#ifdef CONFIG_HAS_IOPORT
+ u32 iobase;
+#endif
+ void __iomem *mmiobase;
+ unsigned int offset; // offset between successive nec7210 io addresses
+ unsigned int dma_channel;
+ u8 *dma_buffer;
+ unsigned int dma_buffer_length; // length of dma buffer
+ dma_addr_t dma_buffer_addr; // bus address of board->buffer for use with dma
+ // software copy of bits written to registers
+ u8 reg_bits[8];
+ u8 auxa_bits; // bits written to auxiliary register A
+ u8 auxb_bits; // bits written to auxiliary register B
+ // used to keep track of board's state, bit definitions given below
+ unsigned long state;
+ // lock for chips that extend the nec7210 registers by paging in alternate regs
+ spinlock_t register_page_lock;
+ // wrappers for outb, inb, readb, or writeb
+ u8 (*read_byte)(struct nec7210_priv *priv, unsigned int register_number);
+ void (*write_byte)(struct nec7210_priv *priv, u8 byte, unsigned int register_number);
+ enum nec7210_chipset type;
+ enum talker_function_state talker_state;
+ enum listener_function_state listener_state;
+ void *private;
+ unsigned srq_pending : 1;
+};
+
+static inline void init_nec7210_private(struct nec7210_priv *priv)
+{
+ memset(priv, 0, sizeof(struct nec7210_priv));
+ spin_lock_init(&priv->register_page_lock);
+}
+
+// slightly shorter way to access read_byte and write_byte
+static inline u8 read_byte(struct nec7210_priv *priv, unsigned int register_number)
+{
+ return priv->read_byte(priv, register_number);
+}
+
+static inline void write_byte(struct nec7210_priv *priv, u8 byte, unsigned int register_number)
+{
+ priv->write_byte(priv, byte, register_number);
+}
+
+// struct nec7210_priv.state bit numbers
+enum {
+ PIO_IN_PROGRESS_BN, // pio transfer in progress
+ DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress
+ DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress
+ READ_READY_BN, // board has data byte available to read
+ WRITE_READY_BN, // board is ready to send a data byte
+ COMMAND_READY_BN, // board is ready to send a command byte
+ RECEIVED_END_BN, // received END
+ BUS_ERROR_BN, // output error has occurred
+ RFD_HOLDOFF_BN, // rfd holdoff in effect
+ DEV_CLEAR_BN, // device clear received
+ ADR_CHANGE_BN, // address state change occurred
+};
+
+// interface functions
+int nec7210_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read);
+int nec7210_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written);
+int nec7210_command(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length, size_t *bytes_written);
+int nec7210_take_control(struct gpib_board *board, struct nec7210_priv *priv, int syncronous);
+int nec7210_go_to_standby(struct gpib_board *board, struct nec7210_priv *priv);
+int nec7210_request_system_control(struct gpib_board *board,
+ struct nec7210_priv *priv, int request_control);
+void nec7210_interface_clear(struct gpib_board *board, struct nec7210_priv *priv, int assert);
+void nec7210_remote_enable(struct gpib_board *board, struct nec7210_priv *priv, int enable);
+int nec7210_enable_eos(struct gpib_board *board, struct nec7210_priv *priv, u8 eos_bytes,
+ int compare_8_bits);
+void nec7210_disable_eos(struct gpib_board *board, struct nec7210_priv *priv);
+unsigned int nec7210_update_status(struct gpib_board *board, struct nec7210_priv *priv,
+ unsigned int clear_mask);
+unsigned int nec7210_update_status_nolock(struct gpib_board *board, struct nec7210_priv *priv);
+int nec7210_primary_address(const struct gpib_board *board,
+ struct nec7210_priv *priv, unsigned int address);
+int nec7210_secondary_address(const struct gpib_board *board, struct nec7210_priv *priv,
+ unsigned int address, int enable);
+int nec7210_parallel_poll(struct gpib_board *board, struct nec7210_priv *priv, u8 *result);
+void nec7210_serial_poll_response(struct gpib_board *board,
+ struct nec7210_priv *priv, u8 status);
+void nec7210_parallel_poll_configure(struct gpib_board *board,
+ struct nec7210_priv *priv, unsigned int configuration);
+void nec7210_parallel_poll_response(struct gpib_board *board,
+ struct nec7210_priv *priv, int ist);
+u8 nec7210_serial_poll_status(struct gpib_board *board, struct nec7210_priv *priv);
+int nec7210_t1_delay(struct gpib_board *board,
+ struct nec7210_priv *priv, unsigned int nano_sec);
+void nec7210_return_to_local(const struct gpib_board *board, struct nec7210_priv *priv);
+
+// utility functions
+void nec7210_board_reset(struct nec7210_priv *priv, const struct gpib_board *board);
+void nec7210_board_online(struct nec7210_priv *priv, const struct gpib_board *board);
+unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg,
+ unsigned int mask, unsigned int bits);
+void nec7210_set_handshake_mode(struct gpib_board *board, struct nec7210_priv *priv, int mode);
+void nec7210_release_rfd_holdoff(struct gpib_board *board, struct nec7210_priv *priv);
+u8 nec7210_read_data_in(struct gpib_board *board, struct nec7210_priv *priv, int *end);
+
+// wrappers for io functions
+u8 nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num);
+void nec7210_ioport_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num);
+u8 nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num);
+void nec7210_iomem_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num);
+u8 nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num);
+void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, u8 data,
+ unsigned int register_num);
+u8 nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num);
+void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data,
+ unsigned int register_num);
+
+// interrupt service routine
+irqreturn_t nec7210_interrupt(struct gpib_board *board, struct nec7210_priv *priv);
+irqreturn_t nec7210_interrupt_have_status(struct gpib_board *board,
+ struct nec7210_priv *priv, int status1, int status2);
+
+#endif //_NEC7210_H
diff --git a/drivers/gpib/include/nec7210_registers.h b/drivers/gpib/include/nec7210_registers.h
new file mode 100644
index 000000000000..067983d7a07f
--- /dev/null
+++ b/drivers/gpib/include/nec7210_registers.h
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _NEC7210_REGISTERS_H
+#define _NEC7210_REGISTERS_H
+
+enum nec7210_chipset {
+ NEC7210, // The original
+ TNT4882, // NI
+ NAT4882, // NI
+ CB7210, // measurement computing
+ IOT7210, // iotech
+ IGPIB7210, // Ines
+ TNT5004, // NI (minor differences to TNT4882)
+};
+
+/*
+ * nec7210 register numbers (might need to be multiplied by
+ * a board-dependent offset to get actually io address offset)
+ */
+// write registers
+enum nec7210_write_regs {
+ CDOR, // command/data out
+ IMR1, // interrupt mask 1
+ IMR2, // interrupt mask 2
+ SPMR, // serial poll mode
+ ADMR, // address mode
+ AUXMR, // auxiliary mode
+ ADR, // address
+ EOSR, // end-of-string
+
+ // nec7210 has 8 registers
+ nec7210_num_registers = 8,
+};
+
+// read registers
+enum nec7210_read_regs {
+ DIR, // data in
+ ISR1, // interrupt status 1
+ ISR2, // interrupt status 2
+ SPSR, // serial poll status
+ ADSR, // address status
+ CPTR, // command pass though
+ ADR0, // address 1
+ ADR1, // address 2
+};
+
+// bit definitions common to nec-7210 compatible registers
+
+// ISR1: interrupt status register 1
+enum isr1_bits {
+ HR_DI = (1 << 0),
+ HR_DO = (1 << 1),
+ HR_ERR = (1 << 2),
+ HR_DEC = (1 << 3),
+ HR_END = (1 << 4),
+ HR_DET = (1 << 5),
+ HR_APT = (1 << 6),
+ HR_CPT = (1 << 7),
+};
+
+// IMR1: interrupt mask register 1
+enum imr1_bits {
+ HR_DIIE = (1 << 0),
+ HR_DOIE = (1 << 1),
+ HR_ERRIE = (1 << 2),
+ HR_DECIE = (1 << 3),
+ HR_ENDIE = (1 << 4),
+ HR_DETIE = (1 << 5),
+ HR_APTIE = (1 << 6),
+ HR_CPTIE = (1 << 7),
+};
+
+// ISR2, interrupt status register 2
+enum isr2_bits {
+ HR_ADSC = (1 << 0),
+ HR_REMC = (1 << 1),
+ HR_LOKC = (1 << 2),
+ HR_CO = (1 << 3),
+ HR_REM = (1 << 4),
+ HR_LOK = (1 << 5),
+ HR_SRQI = (1 << 6),
+ HR_INT = (1 << 7),
+};
+
+// IMR2, interrupt mask register 2
+enum imr2_bits {
+ // all the bits in this register that enable interrupts
+ IMR2_ENABLE_INTR_MASK = 0x4f,
+ HR_ACIE = (1 << 0),
+ HR_REMIE = (1 << 1),
+ HR_LOKIE = (1 << 2),
+ HR_COIE = (1 << 3),
+ HR_DMAI = (1 << 4),
+ HR_DMAO = (1 << 5),
+ HR_SRQIE = (1 << 6),
+};
+
+// SPSR, serial poll status register
+enum spsr_bits {
+ HR_PEND = (1 << 6),
+};
+
+// SPMR, serial poll mode register
+enum spmr_bits {
+ HR_RSV = (1 << 6),
+};
+
+// ADSR, address status register
+enum adsr_bits {
+ HR_MJMN = (1 << 0),
+ HR_TA = (1 << 1),
+ HR_LA = (1 << 2),
+ HR_TPAS = (1 << 3),
+ HR_LPAS = (1 << 4),
+ HR_SPMS = (1 << 5),
+ HR_NATN = (1 << 6),
+ HR_CIC = (1 << 7),
+};
+
+// ADMR, address mode register
+enum admr_bits {
+ HR_ADM0 = (1 << 0),
+ HR_ADM1 = (1 << 1),
+ HR_TRM0 = (1 << 4),
+ HR_TRM1 = (1 << 5),
+ HR_TRM_EOIOE_TRIG = 0,
+ HR_TRM_CIC_TRIG = HR_TRM0,
+ HR_TRM_CIC_EOIOE = HR_TRM1,
+ HR_TRM_CIC_PE = HR_TRM0 | HR_TRM1,
+ HR_LON = (1 << 6),
+ HR_TON = (1 << 7),
+};
+
+// ADR, bits used in address0, address1 and address0/1 registers
+enum adr_bits {
+ ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits */
+ HR_DL = (1 << 5),
+ HR_DT = (1 << 6),
+ HR_ARS = (1 << 7),
+};
+
+// ADR1, address1 register
+enum adr1_bits {
+ HR_EOI = (1 << 7),
+};
+
+// AUXMR, auxiliary mode register
+enum auxmr_bits {
+ ICR = 0x20,
+ PPR = 0x60,
+ AUXRA = 0x80,
+ AUXRB = 0xa0,
+ AUXRE = 0xc0,
+};
+
+// auxra, auxiliary register A
+enum auxra_bits {
+ HR_HANDSHAKE_MASK = 0x3,
+ HR_HLDA = 0x1,
+ HR_HLDE = 0x2,
+ HR_LCM = 0x3, /* auxra listen continuous */
+ HR_REOS = 0x4,
+ HR_XEOS = 0x8,
+ HR_BIN = 0x10,
+};
+
+// auxrb, auxiliary register B
+enum auxrb_bits {
+ HR_CPTE = (1 << 0),
+ HR_SPEOI = (1 << 1),
+ HR_TRI = (1 << 2),
+ HR_INV = (1 << 3),
+ HR_ISS = (1 << 4),
+};
+
+enum auxre_bits {
+ HR_DAC_HLD_DCAS = 0x1, /* perform DAC holdoff on receiving clear */
+ HR_DAC_HLD_DTAS = 0x2, /* perform DAC holdoff on receiving trigger */
+};
+
+// parallel poll register
+enum ppr_bits {
+ HR_PPS = (1 << 3),
+ HR_PPU = (1 << 4),
+};
+
+/* 7210 Auxiliary Commands */
+enum aux_cmds {
+ AUX_PON = 0x0, /* Immediate Execute pon */
+ AUX_CPPF = 0x1, /* Clear Parallel Poll Flag */
+ AUX_CR = 0x2, /* Chip Reset */
+ AUX_FH = 0x3, /* Finish Handshake */
+ AUX_TRIG = 0x4, /* Trigger */
+ AUX_RTL = 0x5, /* Return to local */
+ AUX_SEOI = 0x6, /* Send EOI */
+ AUX_NVAL = 0x7, /* Non-Valid Secondary Command or Address */
+ AUX_SPPF = 0x9, /* Set Parallel Poll Flag */
+ AUX_VAL = 0xf, /* Valid Secondary Command or Address */
+ AUX_GTS = 0x10, /* Go To Standby */
+ AUX_TCA = 0x11, /* Take Control Asynchronously */
+ AUX_TCS = 0x12, /* Take Control Synchronously */
+ AUX_LTN = 0x13, /* Listen */
+ AUX_DSC = 0x14, /* Disable System Control */
+ AUX_CIFC = 0x16, /* Clear IFC */
+ AUX_CREN = 0x17, /* Clear REN */
+ AUX_TCSE = 0x1a, /* Take Control Synchronously on End */
+ AUX_LTNC = 0x1b, /* Listen in Continuous Mode */
+ AUX_LUN = 0x1c, /* Local Unlisten */
+ AUX_EPP = 0x1d, /* Execute Parallel Poll */
+ AUX_SIFC = 0x1e, /* Set IFC */
+ AUX_SREN = 0x1f, /* Set REN */
+};
+
+#endif //_NEC7210_REGISTERS_H
diff --git a/drivers/gpib/include/plx9050.h b/drivers/gpib/include/plx9050.h
new file mode 100644
index 000000000000..c911b285a0ca
--- /dev/null
+++ b/drivers/gpib/include/plx9050.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Header for plx9050 pci chip
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _PLX9050_GPIB_H
+#define _PLX9050_GPIB_H
+
+// plx pci chip registers and bits
+enum {
+ PLX9050_INTCSR_REG = 0x4c,
+ PLX9050_CNTRL_REG = 0x50
+};
+
+enum plx9050_intcsr_bits {
+ PLX9050_LINTR1_EN_BIT = 0x1,
+ PLX9050_LINTR1_POLARITY_BIT = 0x2,
+ PLX9050_LINTR1_STATUS_BIT = 0x4,
+ PLX9050_LINTR2_EN_BIT = 0x8,
+ PLX9050_LINTR2_POLARITY_BIT = 0x10,
+ PLX9050_LINTR2_STATUS_BIT = 0x20,
+ PLX9050_PCI_INTR_EN_BIT = 0x40,
+ PLX9050_SOFT_INTR_BIT = 0x80,
+ PLX9050_LINTR1_SELECT_ENABLE_BIT = 0x100, // 9052 extension
+ PLX9050_LINTR2_SELECT_ENABLE_BIT = 0x200, // 9052 extension
+ PLX9050_LINTR1_EDGE_CLEAR_BIT = 0x400, // 9052 extension
+ PLX9050_LINTR2_EDGE_CLEAR_BIT = 0x800, // 9052 extension
+};
+
+enum plx9050_cntrl_bits {
+ PLX9050_WAITO_NOT_USER0_SELECT_BIT = 0x1,
+ PLX9050_USER0_OUTPUT_BIT = 0x2,
+ PLX9050_USER0_DATA_BIT = 0x4,
+ PLX9050_LLOCK_NOT_USER1_SELECT_BIT = 0x8,
+ PLX9050_USER1_OUTPUT_BIT = 0x10,
+ PLX9050_USER1_DATA_BIT = 0x20,
+ PLX9050_CS2_NOT_USER2_SELECT_BIT = 0x40,
+ PLX9050_USER2_OUTPUT_BIT = 0x80,
+ PLX9050_USER2_DATA_BIT = 0x100,
+ PLX9050_CS3_NOT_USER3_SELECT_BIT = 0x200,
+ PLX9050_USER3_OUTPUT_BIT = 0x400,
+ PLX9050_USER3_DATA_BIT = 0x800,
+ PLX9050_PCIBAR_ENABLE_MASK = 0x3000,
+ PLX9050_PCIBAR_MEMORY_AND_IO_ENABLE_BITS = 0x0,
+ PLX9050_PCIBAR_MEMORY_NO_IO_ENABLE_BITS = 0x1000,
+ PLX9050_PCIBAR_IO_NO_MEMORY_ENABLE_BITS = 0x2000,
+ PLX9050_PCIBAR_MEMORY_AND_IO_TOO_ENABLE_BITS = 0x3000,
+ PLX9050_PCI_READ_MODE_BIT = 0x4000,
+ PLX9050_PCI_READ_WITH_WRITE_FLUSH_MODE_BIT = 0x8000,
+ PLX9050_PCI_READ_NO_FLUSH_MODE_BIT = 0x10000,
+ PLX9050_PCI_READ_NO_WRITE_MODE_BIT = 0x20000,
+ PLX9050_PCI_WRITE_MODE_BIT = 0x40000,
+ PLX9050_PCI_RETRY_DELAY_MASK = 0x780000,
+ PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT = 0x800000,
+ PLX9050_EEPROM_CLOCK_BIT = 0x1000000,
+ PLX9050_EEPROM_CHIP_SELECT_BIT = 0x2000000,
+ PLX9050_WRITE_TO_EEPROM_BIT = 0x4000000,
+ PLX9050_READ_EEPROM_DATA_BIT = 0x8000000,
+ PLX9050_EEPROM_VALID_BIT = 0x10000000,
+ PLX9050_RELOAD_CONFIG_REGISTERS_BIT = 0x20000000,
+ PLX9050_PCI_SOFTWARE_RESET_BIT = 0x40000000,
+ PLX9050_MASK_REVISION_BIT = 0x80000000
+};
+
+static inline unsigned int PLX9050_PCI_RETRY_DELAY_BITS(unsigned int clocks)
+{
+ return ((clocks / 8) << 19) & PLX9050_PCI_RETRY_DELAY_MASK;
+}
+
+#endif // _PLX9050_GPIB_H
diff --git a/drivers/gpib/include/quancom_pci.h b/drivers/gpib/include/quancom_pci.h
new file mode 100644
index 000000000000..cdaf0d056be9
--- /dev/null
+++ b/drivers/gpib/include/quancom_pci.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Quancom pci stuff
+ * copyright (C) 2005 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _QUANCOM_PCI_H
+#define _QUANCOM_PCI_H
+
+/* quancom registers */
+enum quancom_regs {
+ QUANCOM_IRQ_CONTROL_STATUS_REG = 0xfc,
+};
+
+enum quancom_irq_control_status_bits {
+ QUANCOM_IRQ_ASSERTED_BIT = 0x1, /* readable */
+ /* (any write to the register clears the interrupt)*/
+ QUANCOM_IRQ_ENABLE_BIT = 0x4, /* writeable */
+};
+
+#endif // _QUANCOM_PCI_H
diff --git a/drivers/gpib/include/tms9914.h b/drivers/gpib/include/tms9914.h
new file mode 100644
index 000000000000..e66b75e0fda8
--- /dev/null
+++ b/drivers/gpib/include/tms9914.h
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _TMS9914_H
+#define _TMS9914_H
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include "gpib_state_machines.h"
+#include "gpib_types.h"
+
+enum tms9914_holdoff_mode {
+ TMS9914_HOLDOFF_NONE,
+ TMS9914_HOLDOFF_EOI,
+ TMS9914_HOLDOFF_ALL,
+};
+
+/* struct used to provide variables local to a tms9914 chip */
+struct tms9914_priv {
+#ifdef CONFIG_HAS_IOPORT
+ u32 iobase;
+#endif
+ void __iomem *mmiobase;
+ unsigned int offset; // offset between successive tms9914 io addresses
+ unsigned int dma_channel;
+ // software copy of bits written to interrupt mask registers
+ u8 imr0_bits, imr1_bits;
+ // bits written to address mode register
+ u8 admr_bits;
+ u8 auxa_bits; // bits written to auxiliary register A
+ // used to keep track of board's state, bit definitions given below
+ unsigned long state;
+ u8 eos; // eos character
+ short eos_flags;
+ u8 spoll_status;
+ enum tms9914_holdoff_mode holdoff_mode;
+ unsigned int ppoll_line;
+ enum talker_function_state talker_state;
+ enum listener_function_state listener_state;
+ unsigned ppoll_sense : 1;
+ unsigned ppoll_enable : 1;
+ unsigned ppoll_configure_state : 1;
+ unsigned primary_listen_addressed : 1;
+ unsigned primary_talk_addressed : 1;
+ unsigned holdoff_on_end : 1;
+ unsigned holdoff_on_all : 1;
+ unsigned holdoff_active : 1;
+ // wrappers for outb, inb, readb, or writeb
+ u8 (*read_byte)(struct tms9914_priv *priv, unsigned int register_number);
+ void (*write_byte)(struct tms9914_priv *priv, u8 byte, unsigned int
+ register_number);
+};
+
+// slightly shorter way to access read_byte and write_byte
+static inline u8 read_byte(struct tms9914_priv *priv, unsigned int register_number)
+{
+ return priv->read_byte(priv, register_number);
+}
+
+static inline void write_byte(struct tms9914_priv *priv, u8 byte, unsigned int register_number)
+{
+ priv->write_byte(priv, byte, register_number);
+}
+
+// struct tms9914_priv.state bit numbers
+enum {
+ PIO_IN_PROGRESS_BN, // pio transfer in progress
+ DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress
+ DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress
+ READ_READY_BN, // board has data byte available to read
+ WRITE_READY_BN, // board is ready to send a data byte
+ COMMAND_READY_BN, // board is ready to send a command byte
+ RECEIVED_END_BN, // received END
+ BUS_ERROR_BN, // bus error
+ DEV_CLEAR_BN, // device clear received
+};
+
+// interface functions
+int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read);
+int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written);
+int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, size_t *bytes_written);
+int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int syncronous);
+/*
+ * alternate version of tms9914_take_control which works around buggy tcs
+ * implementation.
+ */
+int tms9914_take_control_workaround(struct gpib_board *board, struct tms9914_priv *priv,
+ int syncronous);
+int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv);
+int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv,
+ int request_control);
+void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert);
+void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable);
+int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_bytes,
+ int compare_8_bits);
+void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv);
+unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv,
+ unsigned int clear_mask);
+int tms9914_primary_address(struct gpib_board *board,
+ struct tms9914_priv *priv, unsigned int address);
+int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv,
+ unsigned int address, int enable);
+int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result);
+void tms9914_parallel_poll_configure(struct gpib_board *board,
+ struct tms9914_priv *priv, u8 config);
+void tms9914_parallel_poll_response(struct gpib_board *board,
+ struct tms9914_priv *priv, int ist);
+void tms9914_serial_poll_response(struct gpib_board *board,
+ struct tms9914_priv *priv, u8 status);
+u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv);
+int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv);
+unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv,
+ unsigned int nano_sec);
+void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv);
+
+// utility functions
+void tms9914_board_reset(struct tms9914_priv *priv);
+void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv);
+void tms9914_release_holdoff(struct tms9914_priv *priv);
+void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode);
+
+// wrappers for io functions
+u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num);
+void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num);
+u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num);
+void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num);
+
+// interrupt service routine
+irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv);
+irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv,
+ int status1, int status2);
+
+// tms9914 has 8 registers
+enum {
+ ms9914_num_registers = 8,
+};
+
+/*
+ * tms9914 register numbers (might need to be multiplied by
+ * a board-dependent offset to get actually io address offset)
+ */
+// write registers
+enum {
+ IMR0 = 0, /* interrupt mask 0 */
+ IMR1 = 1, /* interrupt mask 1 */
+ AUXCR = 3, /* auxiliary command */
+ ADR = 4, /* address register */
+ SPMR = 5, /* serial poll mode register */
+ PPR = 6, /* parallel poll */
+ CDOR = 7, /* data out register */
+};
+
+// read registers
+enum {
+ ISR0 = 0, /* interrupt status 0 */
+ ISR1 = 1, /* interrupt status 1 */
+ ADSR = 2, /* address status */
+ BSR = 3, /* bus status */
+ CPTR = 6, /* command pass thru */
+ DIR = 7, /* data in register */
+};
+
+// bit definitions common to tms9914 compatible registers
+
+/* ISR0 - Register bits */
+enum isr0_bits {
+ HR_MAC = (1 << 0), /* My Address Change */
+ HR_RLC = (1 << 1), /* Remote/Local change */
+ HR_SPAS = (1 << 2), /* Serial Poll active State */
+ HR_END = (1 << 3), /* END (EOI or EOS) */
+ HR_BO = (1 << 4), /* Byte Out */
+ HR_BI = (1 << 5), /* Byte In */
+};
+
+/* IMR0 - Register bits */
+enum imr0_bits {
+ HR_MACIE = (1 << 0), /* */
+ HR_RLCIE = (1 << 1), /* */
+ HR_SPASIE = (1 << 2), /* */
+ HR_ENDIE = (1 << 3), /* */
+ HR_BOIE = (1 << 4), /* */
+ HR_BIIE = (1 << 5), /* */
+};
+
+/* ISR1 - Register bits */
+enum isr1_bits {
+ HR_IFC = (1 << 0), /* IFC asserted */
+ HR_SRQ = (1 << 1), /* SRQ asserted */
+ HR_MA = (1 << 2), /* My Address */
+ HR_DCAS = (1 << 3), /* Device Clear active State */
+ HR_APT = (1 << 4), /* Address pass Through */
+ HR_UNC = (1 << 5), /* Unrecognized Command */
+ HR_ERR = (1 << 6), /* Data Transmission Error */
+ HR_GET = (1 << 7), /* Group execute Trigger */
+};
+
+/* IMR1 - Register bits */
+enum imr1_bits {
+ HR_IFCIE = (1 << 0), /* */
+ HR_SRQIE = (1 << 1), /* */
+ HR_MAIE = (1 << 2), /* */
+ HR_DCASIE = (1 << 3), /* */
+ HR_APTIE = (1 << 4), /* */
+ HR_UNCIE = (1 << 5), /* */
+ HR_ERRIE = (1 << 6), /* */
+ HR_GETIE = (1 << 7), /* */
+};
+
+/* ADSR - Register bits */
+enum adsr_bits {
+ HR_ULPA = (1 << 0), /* Store last address LSB */
+ HR_TA = (1 << 1), /* Talker Adressed */
+ HR_LA = (1 << 2), /* Listener adressed */
+ HR_TPAS = (1 << 3), /* talker primary address state */
+ HR_LPAS = (1 << 4), /* listener " */
+ HR_ATN = (1 << 5), /* ATN active */
+ HR_LLO = (1 << 6), /* LLO active */
+ HR_REM = (1 << 7), /* REM active */
+};
+
+/* ADR - Register bits */
+enum adr_bits {
+ ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits for ADR */
+ HR_DAT = (1 << 5), /* disable talker */
+ HR_DAL = (1 << 6), /* disable listener */
+ HR_EDPA = (1 << 7), /* enable dual primary addressing */
+};
+
+enum bus_status_bits {
+ BSR_REN_BIT = 0x1,
+ BSR_IFC_BIT = 0x2,
+ BSR_SRQ_BIT = 0x4,
+ BSR_EOI_BIT = 0x8,
+ BSR_NRFD_BIT = 0x10,
+ BSR_NDAC_BIT = 0x20,
+ BSR_DAV_BIT = 0x40,
+ BSR_ATN_BIT = 0x80,
+};
+
+/*---------------------------------------------------------*/
+/* TMS 9914 Auxiliary Commands */
+/*---------------------------------------------------------*/
+
+enum aux_cmd_bits {
+ AUX_CS = 0x80, /* set bit instead of clearing it, used with commands marked 'd' below */
+ AUX_CHIP_RESET = 0x0, /* d Chip reset */
+ AUX_INVAL = 0x1, /* release dac holdoff, invalid command byte */
+ AUX_VAL = (AUX_INVAL | AUX_CS), /* release dac holdoff, valid command byte */
+ AUX_RHDF = 0x2, /* X Release RFD holdoff */
+ AUX_HLDA = 0x3, /* d holdoff on all data */
+ AUX_HLDE = 0x4, /* d holdoff on EOI only */
+ AUX_NBAF = 0x5, /* X Set new byte available false */
+ AUX_FGET = 0x6, /* d force GET */
+ AUX_RTL = 0x7, /* d return to local */
+ AUX_SEOI = 0x8, /* X send EOI with next byte */
+ AUX_LON = 0x9, /* d Listen only */
+ AUX_TON = 0xa, /* d Talk only */
+ AUX_GTS = 0xb, /* X goto standby */
+ AUX_TCA = 0xc, /* X take control asynchronously */
+ AUX_TCS = 0xd, /* X take " synchronously */
+ AUX_RPP = 0xe, /* d Request parallel poll */
+ AUX_SIC = 0xf, /* d send interface clear */
+ AUX_SRE = 0x10, /* d send remote enable */
+ AUX_RQC = 0x11, /* X request control */
+ AUX_RLC = 0x12, /* X release control */
+ AUX_DAI = 0x13, /* d disable all interrupts */
+ AUX_PTS = 0x14, /* X pass through next secondary */
+ AUX_STDL = 0x15, /* d short T1 delay */
+ AUX_SHDW = 0x16, /* d shadow handshake */
+ AUX_VSTDL = 0x17, /* d very short T1 delay (smj9914 extension) */
+ AUX_RSV2 = 0x18, /* d request service bit 2 (smj9914 extension) */
+};
+
+#endif //_TMS9914_H
diff --git a/drivers/gpib/include/tnt4882_registers.h b/drivers/gpib/include/tnt4882_registers.h
new file mode 100644
index 000000000000..d54c4cc61168
--- /dev/null
+++ b/drivers/gpib/include/tnt4882_registers.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2002, 2004 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _TNT4882_REGISTERS_H
+#define _TNT4882_REGISTERS_H
+
+// tnt4882 register offsets
+enum {
+ ACCWR = 0x5,
+ // offset of auxiliary command register in 9914 mode
+ AUXCR = 0x6,
+ INTRT = 0x7,
+ // register number for auxiliary command register when swap bit is set (9914 mode)
+ SWAPPED_AUXCR = 0xa,
+ HSSEL = 0xd, // handshake select register
+ CNT2 = 0x9,
+ CNT3 = 0xb,
+ CFG = 0x10,
+ SASR = 0x1b,
+ IMR0 = 0x1d,
+ IMR3 = 0x12,
+ CNT0 = 0x14,
+ CNT1 = 0x16,
+ KEYREG = 0x17, // key control register (7210 mode only)
+ CSR = KEYREG,
+ FIFOB = 0x18,
+ FIFOA = 0x19,
+ CCR = 0x1a, // carry cycle register
+ CMDR = 0x1c, // command register
+ TIMER = 0x1e, // timer register
+
+ STS1 = 0x10, // T488 Status Register 1
+ STS2 = 0x1c, // T488 Status Register 2
+ ISR0 = IMR0,
+ ISR3 = 0x1a, // T488 Interrupt Status Register 3
+ BCR = 0x1f, // bus control/status register
+ BSR = BCR,
+};
+
+enum {
+ tnt_pagein_offset = 0x11,
+};
+
+/*============================================================*/
+
+/* TURBO-488 registers bit definitions */
+
+enum bus_control_status_bits {
+ BCSR_REN_BIT = 0x1,
+ BCSR_IFC_BIT = 0x2,
+ BCSR_SRQ_BIT = 0x4,
+ BCSR_EOI_BIT = 0x8,
+ BCSR_NRFD_BIT = 0x10,
+ BCSR_NDAC_BIT = 0x20,
+ BCSR_DAV_BIT = 0x40,
+ BCSR_ATN_BIT = 0x80,
+};
+
+/* CFG -- Configuration Register (write only) */
+enum cfg_bits {
+ TNT_COMMAND = 0x80, /* bytes are command bytes instead of data bytes
+ * (tnt4882 one-chip and newer only?)
+ */
+ TNT_TLCHE = (1 << 6), /* halt transfer on imr0, imr1, or imr2 interrupt */
+ TNT_IN = (1 << 5), /* transfer is GPIB read */
+ TNT_A_B = (1 << 4), /* order to use fifos 1=fifo A first(big endian),
+ * 0=fifo b first(little endian)
+ */
+ TNT_CCEN = (1 << 3), /* enable carry cycle */
+ TNT_TMOE = (1 << 2), /* enable CPU bus time limit */
+ TNT_TIM_BYTN = (1 << 1), /* tmot reg is: 1=125ns clocks, 0=num bytes */
+ TNT_B_16BIT = (1 << 0), /* 1=FIFO is 16-bit register, 0=8-bit */
+};
+
+/* CMDR -- Command Register */
+enum cmdr_bits {
+ CLRSC = 0x2, /* clear the system controller bit */
+ SETSC = 0x3, /* set the system controller bit */
+ GO = 0x4, /* start fifos */
+ STOP = 0x8, /* stop fifos */
+ RESET_FIFO = 0x10, /* reset the FIFOs */
+ SOFT_RESET = 0x22, /* issue a software reset */
+ HARD_RESET = 0x40 /* 500x only? */
+};
+
+/* HSSEL -- handshake select register (write only) */
+enum hssel_bits {
+ TNT_ONE_CHIP_BIT = 0x1,
+ NODMA = 0x10,
+ TNT_GO2SIDS_BIT = 0x20,
+};
+
+/* IMR0 -- Interrupt Mode Register 0 */
+enum imr0_bits {
+ TNT_SYNCIE_BIT = 0x1, /* handshake sync */
+ TNT_TOIE_BIT = 0x2, /* timeout */
+ TNT_ATNIE_BIT = 0x4, /* ATN interrupt */
+ TNT_IFCIE_BIT = 0x8, /* interface clear interrupt */
+ TNT_BTO_BIT = 0x10, /* byte timeout */
+ TNT_NLEN_BIT = 0x20, /* treat new line as EOS char */
+ TNT_STBOIE_BIT = 0x40, /* status byte out */
+ TNT_IMR0_ALWAYS_BITS = 0x80, /* always set this bit on write */
+};
+
+/* ISR0 -- Interrupt Status Register 0 */
+enum isr0_bits {
+ TNT_SYNC_BIT = 0x1, /* handshake sync */
+ TNT_TO_BIT = 0x2, /* timeout */
+ TNT_ATNI_BIT = 0x4, /* ATN interrupt */
+ TNT_IFCI_BIT = 0x8, /* interface clear interrupt */
+ TNT_EOS_BIT = 0x10, /* end of string */
+ TNT_NL_BIT = 0x20, /* new line receive */
+ TNT_STBO_BIT = 0x40, /* status byte out */
+ TNT_NBA_BIT = 0x80, /* new byte available */
+};
+
+/* ISR3 -- Interrupt Status Register 3 (read only) */
+enum isr3_bits {
+ HR_DONE = (1 << 0), /* transfer done */
+ HR_TLCI = (1 << 1), /* isr0, isr1, or isr2 interrupt asserted */
+ HR_NEF = (1 << 2), /* NOT empty fifo */
+ HR_NFF = (1 << 3), /* NOT full fifo */
+ HR_STOP = (1 << 4), /* fifo empty or STOP command issued */
+ HR_SRQI_CIC = (1 << 5), /* SRQ asserted and we are CIC (500x only?)*/
+ HR_INTR = (1 << 7), /* isr3 interrupt active */
+};
+
+enum keyreg_bits {
+ MSTD = 0x20, /* enable 350ns T1 delay */
+};
+
+/* STS1 -- Status Register 1 (read only) */
+enum sts1_bits {
+ S_DONE = 0x80, /* DMA done */
+ S_SC = 0x40, /* is system controller */
+ S_IN = 0x20, /* DMA in (to memory) */
+ S_DRQ = 0x10, /* DRQ line (for diagnostics) */
+ S_STOP = 0x08, /* DMA stopped */
+ S_NDAV = 0x04, /* inverse of DAV */
+ S_HALT = 0x02, /* status of transfer machine */
+ S_GSYNC = 0x01, /* indicates if GPIB is in sync w I/O */
+};
+
+/* STS2 -- Status Register 2 */
+enum sts2_bits {
+ AFFN = (1 << 3), /* "A full FIFO NOT" (0=FIFO full) */
+ AEFN = (1 << 2), /* "A empty FIFO NOT" (0=FIFO empty) */
+ BFFN = (1 << 1), /* "B full FIFO NOT" (0=FIFO full) */
+ BEFN = (1 << 0), /* "B empty FIFO NOT" (0=FIFO empty) */
+};
+
+// Auxiliary commands
+enum tnt4882_aux_cmds {
+ AUX_9914 = 0x15, // switch to 9914 mode
+ AUX_REQT = 0x18,
+ AUX_REQF = 0x19,
+ AUX_PAGEIN = 0x50, // page in alternate registers
+ AUX_HLDI = 0x51, // rfd holdoff immediately
+ AUX_CLEAR_END = 0x55,
+ AUX_7210 = 0x99, // switch to 7210 mode
+};
+
+enum tnt4882_aux_regs {
+ AUXRG = 0x40,
+ AUXRI = 0xe0,
+};
+
+enum auxg_bits {
+ /* no talking when no listeners bit (prevents bus errors when data written at wrong time) */
+ NTNL_BIT = 0x8,
+ RPP2_BIT = 0x4, /* set/clear local rpp message */
+ CHES_BIT = 0x1, /*clear holdoff on end select bit*/
+};
+
+enum auxi_bits {
+ SISB = 0x1, // static interrupt bits (don't clear isr1, isr2 on read)
+ PP2 = 0x4, // ignore remote parallel poll configuration
+ USTD = 0x8, // ultra short (1100 nanosec) T1 delay
+};
+
+enum sasr_bits {
+ ACRDY_BIT = 0x4, /* acceptor ready state */
+ ADHS_BIT = 0x8, /* acceptor data holdoff state */
+ ANHS2_BIT = 0x10, /* acceptor not ready holdoff immediately state */
+ ANHS1_BIT = 0x20, /* acceptor not ready holdoff state */
+ AEHS_BIT = 0x40, /* acceptor end holdoff state */
+};
+
+#endif // _TNT4882_REGISTERS_H
diff --git a/drivers/gpib/ines/Makefile b/drivers/gpib/ines/Makefile
new file mode 100644
index 000000000000..88241f15ecea
--- /dev/null
+++ b/drivers/gpib/ines/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_GPIB_INES) += ines_gpib.o
+
+
diff --git a/drivers/gpib/ines/ines.h b/drivers/gpib/ines/ines.h
new file mode 100644
index 000000000000..6ad57e9a1216
--- /dev/null
+++ b/drivers/gpib/ines/ines.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * Header for ines GPIB boards
+ * copyright : (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _INES_GPIB_H
+#define _INES_GPIB_H
+
+#include "nec7210.h"
+#include "gpibP.h"
+#include "plx9050.h"
+#include "amcc5920.h"
+#include "quancom_pci.h"
+#include <linux/interrupt.h>
+
+enum ines_pci_chip {
+ PCI_CHIP_NONE,
+ PCI_CHIP_PLX9050,
+ PCI_CHIP_AMCC5920,
+ PCI_CHIP_QUANCOM,
+ PCI_CHIP_QUICKLOGIC5030,
+};
+
+struct ines_priv {
+ struct nec7210_priv nec7210_priv;
+ struct pci_dev *pci_device;
+ // base address for plx9052 pci chip
+ unsigned long plx_iobase;
+ // base address for amcc5920 pci chip
+ unsigned long amcc_iobase;
+ unsigned int irq;
+ enum ines_pci_chip pci_chip_type;
+ u8 extend_mode_bits;
+};
+
+/* inb/outb wrappers */
+static inline unsigned int ines_inb(struct ines_priv *priv, unsigned int register_number)
+{
+ return inb(priv->nec7210_priv.iobase +
+ register_number * priv->nec7210_priv.offset);
+}
+
+static inline void ines_outb(struct ines_priv *priv, unsigned int value,
+ unsigned int register_number)
+{
+ outb(value, priv->nec7210_priv.iobase +
+ register_number * priv->nec7210_priv.offset);
+}
+
+enum ines_regs {
+ // read
+ FIFO_STATUS = 0x8,
+ ISR3 = 0x9,
+ ISR4 = 0xa,
+ IN_FIFO_COUNT = 0x10,
+ OUT_FIFO_COUNT = 0x11,
+ EXTEND_STATUS = 0xf,
+
+ // write
+ XDMA_CONTROL = 0x8,
+ IMR3 = ISR3,
+ IMR4 = ISR4,
+ IN_FIFO_WATERMARK = IN_FIFO_COUNT,
+ OUT_FIFO_WATERMARK = OUT_FIFO_COUNT,
+ EXTEND_MODE = 0xf,
+
+ // read-write
+ XFER_COUNT_LOWER = 0xb,
+ XFER_COUNT_UPPER = 0xc,
+ BUS_CONTROL_MONITOR = 0x13,
+};
+
+enum isr3_imr3_bits {
+ HW_TIMEOUT_BIT = 0x1,
+ XFER_COUNT_BIT = 0x2,
+ CMD_RECEIVED_BIT = 0x4,
+ TCT_RECEIVED_BIT = 0x8,
+ IFC_ACTIVE_BIT = 0x10,
+ ATN_ACTIVE_BIT = 0x20,
+ FIFO_ERROR_BIT = 0x40,
+};
+
+enum isr4_imr4_bits {
+ IN_FIFO_WATERMARK_BIT = 0x1,
+ OUT_FIFO_WATERMARK_BIT = 0x2,
+ IN_FIFO_FULL_BIT = 0x4,
+ OUT_FIFO_EMPTY_BIT = 0x8,
+ IN_FIFO_READY_BIT = 0x10,
+ OUT_FIFO_READY_BIT = 0x20,
+ IN_FIFO_EXIT_WATERMARK_BIT = 0x40,
+ OUT_FIFO_EXIT_WATERMARK_BIT = 0x80,
+};
+
+enum extend_mode_bits {
+ TR3_TRIG_ENABLE_BIT = 0x1, // enable generation of trigger pulse T/R3 pin
+ // clear message available status bit when chip writes byte with EOI true
+ MAV_ENABLE_BIT = 0x2,
+ EOS1_ENABLE_BIT = 0x4, // enable eos register 1
+ EOS2_ENABLE_BIT = 0x8, // enable eos register 2
+ EOIDIS_BIT = 0x10, // disable EOI interrupt when doing rfd holdoff on end?
+ XFER_COUNTER_ENABLE_BIT = 0x20,
+ XFER_COUNTER_OUTPUT_BIT = 0x40, // use counter for output, clear for input
+ // when xfer counter hits 0, assert EOI on write or RFD holdoff on read
+ LAST_BYTE_HANDLING_BIT = 0x80,
+};
+
+enum extend_status_bits {
+ OUTPUT_MESSAGE_IN_PROGRESS_BIT = 0x1,
+ SCSEL_BIT = 0x2, // statue of SCSEL pin
+ LISTEN_DISABLED = 0x4,
+ IN_FIFO_EMPTY_BIT = 0x8,
+ OUT_FIFO_FULL_BIT = 0x10,
+};
+
+// ines adds fifo enable bits to address mode register
+enum ines_admr_bits {
+ IN_FIFO_ENABLE_BIT = 0x8,
+ OUT_FIFO_ENABLE_BIT = 0x4,
+};
+
+enum xdma_control_bits {
+ DMA_OUTPUT_BIT = 0x1, // use dma for output, clear for input
+ ENABLE_SYNC_DMA_BIT = 0x2,
+ DMA_ACCESS_EVERY_CYCLE = 0x4, // dma accesses fifo every cycle, clear for every other cycle
+ DMA_16BIT = 0x8, // clear for 8 bit transfers
+};
+
+enum bus_control_monitor_bits {
+ BCM_DAV_BIT = 0x1,
+ BCM_NRFD_BIT = 0x2,
+ BCM_NDAC_BIT = 0x4,
+ BCM_IFC_BIT = 0x8,
+ BCM_ATN_BIT = 0x10,
+ BCM_SRQ_BIT = 0x20,
+ BCM_REN_BIT = 0x40,
+ BCM_EOI_BIT = 0x80,
+};
+
+enum ines_aux_reg_bits {
+ INES_AUXD = 0x40,
+};
+
+enum ines_aux_cmds {
+ INES_RFD_HLD_IMMEDIATE = 0x4,
+ INES_AUX_CLR_OUT_FIFO = 0x5,
+ INES_AUX_CLR_IN_FIFO = 0x6,
+ INES_AUX_XMODE = 0xa,
+};
+
+enum ines_auxd_bits {
+ INES_FOLLOWING_T1_MASK = 0x3,
+ INES_FOLLOWING_T1_500ns = 0x0,
+ INES_FOLLOWING_T1_350ns = 0x1,
+ INES_FOLLOWING_T1_250ns = 0x2,
+ INES_INITIAL_TI_MASK = 0xc,
+ INES_INITIAL_T1_2000ns = 0x0,
+ INES_INITIAL_T1_1100ns = 0x4,
+ INES_INITIAL_T1_700ns = 0x8,
+ INES_T6_2us = 0x0,
+ INES_T6_50us = 0x10,
+};
+
+#endif // _INES_GPIB_H
diff --git a/drivers/gpib/ines/ines_gpib.c b/drivers/gpib/ines/ines_gpib.c
new file mode 100644
index 000000000000..a3cf846fd0f9
--- /dev/null
+++ b/drivers/gpib/ines/ines_gpib.c
@@ -0,0 +1,1500 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 1999 Axel Dziemba (axel.dziemba@ines.de)
+ * (C) 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include "ines.h"
+
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/bitops.h>
+#include <asm/dma.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "gpib_pci_ids.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for Ines iGPIB 72010");
+
+static irqreturn_t ines_interrupt(struct gpib_board *board);
+
+static int ines_line_status(const struct gpib_board *board)
+{
+ int status = VALID_ALL;
+ int bcm_bits;
+ struct ines_priv *ines_priv;
+
+ ines_priv = board->private_data;
+
+ bcm_bits = ines_inb(ines_priv, BUS_CONTROL_MONITOR);
+
+ if (bcm_bits & BCM_REN_BIT)
+ status |= BUS_REN;
+ if (bcm_bits & BCM_IFC_BIT)
+ status |= BUS_IFC;
+ if (bcm_bits & BCM_SRQ_BIT)
+ status |= BUS_SRQ;
+ if (bcm_bits & BCM_EOI_BIT)
+ status |= BUS_EOI;
+ if (bcm_bits & BCM_NRFD_BIT)
+ status |= BUS_NRFD;
+ if (bcm_bits & BCM_NDAC_BIT)
+ status |= BUS_NDAC;
+ if (bcm_bits & BCM_DAV_BIT)
+ status |= BUS_DAV;
+ if (bcm_bits & BCM_ATN_BIT)
+ status |= BUS_ATN;
+
+ return status;
+}
+
+static void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count)
+{
+ if (count > 0xffff) {
+ pr_err("bug! tried to set xfer counter > 0xffff\n");
+ return;
+ }
+ ines_outb(priv, (count >> 8) & 0xff, XFER_COUNT_UPPER);
+ ines_outb(priv, count & 0xff, XFER_COUNT_LOWER);
+}
+
+static int ines_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct ines_priv *ines_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv;
+ unsigned int retval;
+
+ retval = nec7210_t1_delay(board, nec_priv, nano_sec);
+
+ if (nano_sec <= 250) {
+ write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_250ns |
+ INES_INITIAL_T1_2000ns, AUXMR);
+ retval = 250;
+ } else if (nano_sec <= 350) {
+ write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_350ns |
+ INES_INITIAL_T1_2000ns, AUXMR);
+ retval = 350;
+ } else {
+ write_byte(nec_priv, INES_AUXD | INES_FOLLOWING_T1_500ns |
+ INES_INITIAL_T1_2000ns, AUXMR);
+ retval = 500;
+ }
+
+ return retval;
+}
+
+static inline unsigned short num_in_fifo_bytes(struct ines_priv *ines_priv)
+{
+ return ines_inb(ines_priv, IN_FIFO_COUNT);
+}
+
+static ssize_t pio_read(struct gpib_board *board, struct ines_priv *ines_priv, u8 *buffer,
+ size_t length, size_t *nbytes)
+{
+ ssize_t retval = 0;
+ unsigned int num_fifo_bytes, i;
+ struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv;
+
+ *nbytes = 0;
+ while (*nbytes < length) {
+ if (wait_event_interruptible(board->wait,
+ num_in_fifo_bytes(ines_priv) ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ return -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ return -EINTR;
+
+ num_fifo_bytes = num_in_fifo_bytes(ines_priv);
+ if (num_fifo_bytes + *nbytes > length)
+ num_fifo_bytes = length - *nbytes;
+
+ for (i = 0; i < num_fifo_bytes; i++)
+ buffer[(*nbytes)++] = read_byte(nec_priv, DIR);
+ if (test_bit(RECEIVED_END_BN, &nec_priv->state) &&
+ num_in_fifo_bytes(ines_priv) == 0)
+ break;
+ if (need_resched())
+ schedule();
+ }
+ /* make sure RECEIVED_END is in sync */
+ ines_interrupt(board);
+ return retval;
+}
+
+static int ines_accel_read(struct gpib_board *board, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval = 0;
+ struct ines_priv *ines_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv;
+ int counter_setting;
+
+ *end = 0;
+ *bytes_read = 0;
+ if (length == 0)
+ return 0;
+
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state);
+
+ write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR);
+
+ // clear in fifo
+ nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, 0);
+ nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT, IN_FIFO_ENABLE_BIT);
+
+ ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT;
+ ines_priv->extend_mode_bits &= ~XFER_COUNTER_OUTPUT_BIT & ~XFER_COUNTER_ENABLE_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+
+ counter_setting = length - num_in_fifo_bytes(ines_priv);
+ if (counter_setting > 0) {
+ ines_set_xfer_counter(ines_priv, length);
+ ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+
+ // holdoff on END
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDE);
+ /* release rfd holdoff */
+ write_byte(nec_priv, AUX_FH, AUXMR);
+ }
+
+ retval = pio_read(board, ines_priv, buffer, length, bytes_read);
+ ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+ if (retval < 0) {
+ write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR);
+ return retval;
+ }
+ if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state))
+ *end = 1;
+
+ return retval;
+}
+
+static const int out_fifo_size = 0xff;
+
+static inline unsigned short num_out_fifo_bytes(struct ines_priv *ines_priv)
+{
+ return ines_inb(ines_priv, OUT_FIFO_COUNT);
+}
+
+static int ines_write_wait(struct gpib_board *board, struct ines_priv *ines_priv,
+ unsigned int fifo_threshold)
+{
+ struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv;
+
+ // wait until byte is ready to be sent
+ if (wait_event_interruptible(board->wait,
+ num_out_fifo_bytes(ines_priv) < fifo_threshold ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ return -ERESTARTSYS;
+
+ if (test_bit(BUS_ERROR_BN, &nec_priv->state))
+ return -EIO;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ return -EINTR;
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int ines_accel_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ size_t count = 0;
+ ssize_t retval = 0;
+ struct ines_priv *ines_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv;
+ unsigned int num_bytes, i;
+
+ *bytes_written = 0;
+ // clear out fifo
+ nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, 0);
+ nec7210_set_reg_bits(nec_priv, ADMR, OUT_FIFO_ENABLE_BIT, OUT_FIFO_ENABLE_BIT);
+
+ ines_priv->extend_mode_bits |= XFER_COUNTER_OUTPUT_BIT;
+ ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT;
+ ines_priv->extend_mode_bits &= ~LAST_BYTE_HANDLING_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+
+ ines_set_xfer_counter(ines_priv, length);
+ if (send_eoi)
+ ines_priv->extend_mode_bits |= LAST_BYTE_HANDLING_BIT;
+ ines_priv->extend_mode_bits |= XFER_COUNTER_ENABLE_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+
+ while (count < length) {
+ retval = ines_write_wait(board, ines_priv, out_fifo_size);
+ if (retval < 0)
+ break;
+
+ num_bytes = out_fifo_size - num_out_fifo_bytes(ines_priv);
+ if (num_bytes + count > length)
+ num_bytes = length - count;
+ for (i = 0; i < num_bytes; i++)
+ write_byte(nec_priv, buffer[count++], CDOR);
+ }
+ if (retval < 0) {
+ ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+ *bytes_written = length - num_out_fifo_bytes(ines_priv);
+ return retval;
+ }
+ // wait last byte has been sent
+ retval = ines_write_wait(board, ines_priv, 1);
+ ines_priv->extend_mode_bits &= ~XFER_COUNTER_ENABLE_BIT;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+ *bytes_written = length - num_out_fifo_bytes(ines_priv);
+
+ return retval;
+}
+
+static irqreturn_t ines_pci_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct ines_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+
+ if (priv->pci_chip_type == PCI_CHIP_QUANCOM) {
+ if ((inb(nec_priv->iobase +
+ QUANCOM_IRQ_CONTROL_STATUS_REG) &
+ QUANCOM_IRQ_ASSERTED_BIT))
+ outb(QUANCOM_IRQ_ENABLE_BIT, nec_priv->iobase +
+ QUANCOM_IRQ_CONTROL_STATUS_REG);
+ }
+
+ return ines_interrupt(board);
+}
+
+static irqreturn_t ines_interrupt(struct gpib_board *board)
+{
+ struct ines_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+ unsigned int isr3_bits, isr4_bits;
+ unsigned long flags;
+ int wake = 0;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ nec7210_interrupt(board, nec_priv);
+ isr3_bits = ines_inb(priv, ISR3);
+ isr4_bits = ines_inb(priv, ISR4);
+ if (isr3_bits & IFC_ACTIVE_BIT) {
+ push_gpib_event(board, EVENT_IFC);
+ wake++;
+ }
+ if (isr3_bits & FIFO_ERROR_BIT)
+ dev_err(board->gpib_dev, "fifo error\n");
+ if (isr3_bits & XFER_COUNT_BIT)
+ wake++;
+
+ if (isr4_bits & (IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT | OUT_FIFO_WATERMARK_BIT |
+ OUT_FIFO_EMPTY_BIT))
+ wake++;
+
+ if (wake)
+ wake_up_interruptible(&board->wait);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config);
+static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config);
+static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config);
+
+static void ines_pci_detach(struct gpib_board *board);
+static void ines_isa_detach(struct gpib_board *board);
+
+enum ines_pci_vendor_ids {
+ PCI_VENDOR_ID_INES_QUICKLOGIC = 0x16da
+};
+
+enum ines_pci_device_ids {
+ PCI_DEVICE_ID_INES_GPIB_AMCC = 0x8507,
+ PCI_DEVICE_ID_INES_GPIB_QL5030 = 0x11,
+};
+
+enum ines_pci_subdevice_ids {
+ PCI_SUBDEVICE_ID_INES_GPIB = 0x1072
+};
+
+static struct pci_device_id ines_pci_table[] = {
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX,
+ PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0},
+ {PCI_VENDOR_ID_AMCC, PCI_DEVICE_ID_INES_GPIB_AMCC, PCI_VENDOR_ID_AMCC,
+ PCI_SUBDEVICE_ID_INES_GPIB, 0, 0, 0},
+ {PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030,
+ PCI_VENDOR_ID_INES_QUICKLOGIC, PCI_DEVICE_ID_INES_GPIB_QL5030, 0, 0, 0},
+ {PCI_DEVICE(PCI_VENDOR_ID_QUANCOM, PCI_DEVICE_ID_QUANCOM_GPIB)},
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, ines_pci_table);
+
+struct ines_pci_id {
+ unsigned int vendor_id;
+ unsigned int device_id;
+ int subsystem_vendor_id;
+ int subsystem_device_id;
+ unsigned int gpib_region;
+ unsigned int io_offset;
+ enum ines_pci_chip pci_chip_type;
+};
+
+static struct ines_pci_id pci_ids[] = {
+ {.vendor_id = PCI_VENDOR_ID_PLX,
+ .device_id = PCI_DEVICE_ID_PLX_9050,
+ .subsystem_vendor_id = PCI_VENDOR_ID_PLX,
+ .subsystem_device_id = PCI_SUBDEVICE_ID_INES_GPIB,
+ .gpib_region = 2,
+ .io_offset = 1,
+ .pci_chip_type = PCI_CHIP_PLX9050,
+ },
+ {.vendor_id = PCI_VENDOR_ID_AMCC,
+ .device_id = PCI_DEVICE_ID_INES_GPIB_AMCC,
+ .subsystem_vendor_id = PCI_VENDOR_ID_AMCC,
+ .subsystem_device_id = PCI_SUBDEVICE_ID_INES_GPIB,
+ .gpib_region = 1,
+ .io_offset = 1,
+ .pci_chip_type = PCI_CHIP_AMCC5920,
+ },
+ {.vendor_id = PCI_VENDOR_ID_INES_QUICKLOGIC,
+ .device_id = PCI_DEVICE_ID_INES_GPIB_QL5030,
+ .subsystem_vendor_id = PCI_VENDOR_ID_INES_QUICKLOGIC,
+ .subsystem_device_id = PCI_DEVICE_ID_INES_GPIB_QL5030,
+ .gpib_region = 1,
+ .io_offset = 1,
+ .pci_chip_type = PCI_CHIP_QUICKLOGIC5030,
+ },
+ {.vendor_id = PCI_VENDOR_ID_QUANCOM,
+ .device_id = PCI_DEVICE_ID_QUANCOM_GPIB,
+ .subsystem_vendor_id = -1,
+ .subsystem_device_id = -1,
+ .gpib_region = 0,
+ .io_offset = 4,
+ .pci_chip_type = PCI_CHIP_QUANCOM,
+ },
+};
+
+static const int num_pci_chips = ARRAY_SIZE(pci_ids);
+
+// wrappers for interface functions
+static int ines_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ struct ines_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+ ssize_t retval;
+ int dummy;
+
+ retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+ if (retval < 0) {
+ write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR);
+
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+
+ nec7210_read_data_in(board, nec_priv, &dummy);
+ }
+ return retval;
+}
+
+static int ines_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int ines_command(struct gpib_board *board, u8 *buffer, size_t length, size_t *bytes_written)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int ines_take_control(struct gpib_board *board, int synchronous)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int ines_go_to_standby(struct gpib_board *board)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int ines_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
+}
+
+static void ines_interface_clear(struct gpib_board *board, int assert)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void ines_remote_enable(struct gpib_board *board, int enable)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int ines_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void ines_disable_eos(struct gpib_board *board)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int ines_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
+}
+
+static int ines_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int ines_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int ines_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
+}
+
+static void ines_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
+}
+
+static void ines_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+static void ines_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
+}
+
+static u8 ines_serial_poll_status(struct gpib_board *board)
+{
+ struct ines_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static void ines_return_to_local(struct gpib_board *board)
+{
+ struct ines_priv *priv = board->private_data;
+
+ nec7210_return_to_local(board, &priv->nec7210_priv);
+}
+
+static struct gpib_interface ines_pci_unaccel_interface = {
+ .name = "ines_pci_unaccel",
+ .attach = ines_pci_attach,
+ .detach = ines_pci_detach,
+ .read = ines_read,
+ .write = ines_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static struct gpib_interface ines_pci_interface = {
+ .name = "ines_pci",
+ .attach = ines_pci_accel_attach,
+ .detach = ines_pci_detach,
+ .read = ines_accel_read,
+ .write = ines_accel_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static struct gpib_interface ines_pci_accel_interface = {
+ .name = "ines_pci_accel",
+ .attach = ines_pci_accel_attach,
+ .detach = ines_pci_detach,
+ .read = ines_accel_read,
+ .write = ines_accel_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static struct gpib_interface ines_isa_interface = {
+ .name = "ines_isa",
+ .attach = ines_isa_attach,
+ .detach = ines_isa_detach,
+ .read = ines_accel_read,
+ .write = ines_accel_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static int ines_allocate_private(struct gpib_board *board)
+{
+ struct ines_priv *priv;
+
+ board->private_data = kmalloc(sizeof(struct ines_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -1;
+ priv = board->private_data;
+ memset(priv, 0, sizeof(struct ines_priv));
+ init_nec7210_private(&priv->nec7210_priv);
+ return 0;
+}
+
+static void ines_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static int ines_generic_attach(struct gpib_board *board)
+{
+ struct ines_priv *ines_priv;
+ struct nec7210_priv *nec_priv;
+
+ board->status = 0;
+
+ if (ines_allocate_private(board))
+ return -ENOMEM;
+ ines_priv = board->private_data;
+ nec_priv = &ines_priv->nec7210_priv;
+ nec_priv->read_byte = nec7210_ioport_read_byte;
+ nec_priv->write_byte = nec7210_ioport_write_byte;
+ nec_priv->offset = 1;
+ nec_priv->type = IGPIB7210;
+ ines_priv->pci_chip_type = PCI_CHIP_NONE;
+
+ return 0;
+}
+
+static void ines_online(struct ines_priv *ines_priv, const struct gpib_board *board, int use_accel)
+{
+ struct nec7210_priv *nec_priv = &ines_priv->nec7210_priv;
+
+ /* ines doesn't seem to use internal count register */
+ write_byte(nec_priv, ICR | 0, AUXMR);
+
+ write_byte(nec_priv, INES_AUX_XMODE, AUXMR);
+ write_byte(nec_priv, INES_RFD_HLD_IMMEDIATE, AUXMR);
+
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+
+ write_byte(nec_priv, INES_AUXD | 0, AUXMR);
+ ines_outb(ines_priv, 0, XDMA_CONTROL);
+ ines_priv->extend_mode_bits = 0;
+ ines_outb(ines_priv, ines_priv->extend_mode_bits, EXTEND_MODE);
+ if (use_accel) {
+ ines_outb(ines_priv, 0x80, OUT_FIFO_WATERMARK);
+ ines_outb(ines_priv, 0x80, IN_FIFO_WATERMARK);
+ ines_outb(ines_priv, IFC_ACTIVE_BIT | ATN_ACTIVE_BIT |
+ FIFO_ERROR_BIT | XFER_COUNT_BIT, IMR3);
+ ines_outb(ines_priv, IN_FIFO_WATERMARK_BIT | IN_FIFO_FULL_BIT |
+ OUT_FIFO_WATERMARK_BIT | OUT_FIFO_EMPTY_BIT, IMR4);
+ } else {
+ nec7210_set_reg_bits(nec_priv, ADMR, IN_FIFO_ENABLE_BIT | OUT_FIFO_ENABLE_BIT, 0);
+ ines_outb(ines_priv, IFC_ACTIVE_BIT | FIFO_ERROR_BIT, IMR3);
+ ines_outb(ines_priv, 0, IMR4);
+ }
+
+ nec7210_board_online(nec_priv, board);
+ if (use_accel)
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_DOIE | HR_DIIE, 0);
+}
+
+static int ines_common_pci_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct ines_priv *ines_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = 0;
+ int retval;
+ struct ines_pci_id found_id;
+ unsigned int i;
+ struct pci_dev *pdev;
+
+ memset(&found_id, 0, sizeof(found_id));
+
+ retval = ines_generic_attach(board);
+ if (retval)
+ return retval;
+
+ ines_priv = board->private_data;
+ nec_priv = &ines_priv->nec7210_priv;
+
+ // find board
+ ines_priv->pci_device = NULL;
+ for (i = 0; i < num_pci_chips && !ines_priv->pci_device; i++) {
+ pdev = NULL;
+ do {
+ if (pci_ids[i].subsystem_vendor_id >= 0 &&
+ pci_ids[i].subsystem_device_id >= 0)
+ pdev = pci_get_subsys(pci_ids[i].vendor_id, pci_ids[i].device_id,
+ pci_ids[i].subsystem_vendor_id,
+ pci_ids[i].subsystem_device_id, pdev);
+ else
+ pdev = pci_get_device(pci_ids[i].vendor_id, pci_ids[i].device_id,
+ pdev);
+ if (!pdev)
+ break;
+ if (config->pci_bus >= 0 && config->pci_bus != pdev->bus->number)
+ continue;
+ if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(pdev->devfn))
+ continue;
+ found_id = pci_ids[i];
+ ines_priv->pci_device = pdev;
+ break;
+ } while (1);
+ }
+ if (!ines_priv->pci_device) {
+ dev_err(board->gpib_dev, "could not find ines PCI board\n");
+ return -1;
+ }
+
+ if (pci_enable_device(ines_priv->pci_device)) {
+ dev_err(board->gpib_dev, "error enabling pci device\n");
+ return -1;
+ }
+
+ if (pci_request_regions(ines_priv->pci_device, DRV_NAME))
+ return -1;
+ nec_priv->iobase = pci_resource_start(ines_priv->pci_device,
+ found_id.gpib_region);
+
+ ines_priv->pci_chip_type = found_id.pci_chip_type;
+ nec_priv->offset = found_id.io_offset;
+ switch (ines_priv->pci_chip_type) {
+ case PCI_CHIP_PLX9050:
+ ines_priv->plx_iobase = pci_resource_start(ines_priv->pci_device, 1);
+ break;
+ case PCI_CHIP_AMCC5920:
+ ines_priv->amcc_iobase = pci_resource_start(ines_priv->pci_device, 0);
+ break;
+ case PCI_CHIP_QUANCOM:
+ break;
+ case PCI_CHIP_QUICKLOGIC5030:
+ break;
+ default:
+ dev_err(board->gpib_dev, "unspecified chip type? (bug)\n");
+ nec_priv->iobase = 0;
+ pci_release_regions(ines_priv->pci_device);
+ return -1;
+ }
+
+ nec7210_board_reset(nec_priv, board);
+#ifdef QUANCOM_PCI
+ if (ines_priv->pci_chip_type == PCI_CHIP_QUANCOM) {
+ /* change interrupt polarity */
+ nec_priv->auxb_bits |= HR_INV;
+ ines_outb(ines_priv, nec_priv->auxb_bits, AUXMR);
+ }
+#endif
+ isr_flags |= IRQF_SHARED;
+ if (request_irq(ines_priv->pci_device->irq, ines_pci_interrupt, isr_flags,
+ DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "can't request IRQ %d\n", ines_priv->pci_device->irq);
+ return -1;
+ }
+ ines_priv->irq = ines_priv->pci_device->irq;
+
+ // enable interrupts on pci chip
+ switch (ines_priv->pci_chip_type) {
+ case PCI_CHIP_PLX9050:
+ outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT,
+ ines_priv->plx_iobase + PLX9050_INTCSR_REG);
+ break;
+ case PCI_CHIP_AMCC5920:
+ {
+ static const int region = 1;
+ static const int num_wait_states = 7;
+ u32 bits;
+
+ bits = amcc_prefetch_bits(region, PREFETCH_DISABLED);
+ bits |= amcc_PTADR_mode_bit(region);
+ bits |= amcc_disable_write_fifo_bit(region);
+ bits |= amcc_wait_state_bits(region, num_wait_states);
+ outl(bits, ines_priv->amcc_iobase + AMCC_PASS_THRU_REG);
+ outl(AMCC_ADDON_INTR_ENABLE_BIT, ines_priv->amcc_iobase + AMCC_INTCS_REG);
+ }
+ break;
+ case PCI_CHIP_QUANCOM:
+ outb(QUANCOM_IRQ_ENABLE_BIT, nec_priv->iobase +
+ QUANCOM_IRQ_CONTROL_STATUS_REG);
+ break;
+ case PCI_CHIP_QUICKLOGIC5030:
+ break;
+ default:
+ dev_err(board->gpib_dev, "unspecified chip type? (bug)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ines_pci_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct ines_priv *ines_priv;
+ int retval;
+
+ retval = ines_common_pci_attach(board, config);
+ if (retval < 0)
+ return retval;
+
+ ines_priv = board->private_data;
+ ines_online(ines_priv, board, 0);
+
+ return 0;
+}
+
+static int ines_pci_accel_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct ines_priv *ines_priv;
+ int retval;
+
+ retval = ines_common_pci_attach(board, config);
+ if (retval < 0)
+ return retval;
+
+ ines_priv = board->private_data;
+ ines_online(ines_priv, board, 1);
+
+ return 0;
+}
+
+static const int ines_isa_iosize = 0x20;
+
+static int ines_isa_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct ines_priv *ines_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = 0;
+ int retval;
+
+ retval = ines_generic_attach(board);
+ if (retval)
+ return retval;
+
+ ines_priv = board->private_data;
+ nec_priv = &ines_priv->nec7210_priv;
+
+ if (!request_region(config->ibbase, ines_isa_iosize, DRV_NAME)) {
+ dev_err(board->gpib_dev, "ioports at 0x%x already in use\n",
+ config->ibbase);
+ return -EBUSY;
+ }
+ nec_priv->iobase = config->ibbase;
+ nec_priv->offset = 1;
+ nec7210_board_reset(nec_priv, board);
+ if (request_irq(config->ibirq, ines_pci_interrupt, isr_flags, DRV_NAME, board)) {
+ dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", config->ibirq);
+ return -1;
+ }
+ ines_priv->irq = config->ibirq;
+ ines_online(ines_priv, board, 1);
+ return 0;
+}
+
+static void ines_pci_detach(struct gpib_board *board)
+{
+ struct ines_priv *ines_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (ines_priv) {
+ nec_priv = &ines_priv->nec7210_priv;
+ if (ines_priv->irq) {
+ // disable interrupts
+ switch (ines_priv->pci_chip_type) {
+ case PCI_CHIP_AMCC5920:
+ if (ines_priv->plx_iobase)
+ outl(0, ines_priv->plx_iobase + PLX9050_INTCSR_REG);
+ break;
+ case PCI_CHIP_QUANCOM:
+ if (nec_priv->iobase)
+ outb(0, nec_priv->iobase +
+ QUANCOM_IRQ_CONTROL_STATUS_REG);
+ break;
+ default:
+ break;
+ }
+ free_irq(ines_priv->irq, board);
+ }
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ pci_release_regions(ines_priv->pci_device);
+ }
+ if (ines_priv->pci_device)
+ pci_dev_put(ines_priv->pci_device);
+ }
+ ines_free_private(board);
+}
+
+static void ines_isa_detach(struct gpib_board *board)
+{
+ struct ines_priv *ines_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (ines_priv) {
+ nec_priv = &ines_priv->nec7210_priv;
+ if (ines_priv->irq)
+ free_irq(ines_priv->irq, board);
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ release_region(nec_priv->iobase, ines_isa_iosize);
+ }
+ }
+ ines_free_private(board);
+}
+
+static int ines_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return 0;
+}
+
+static struct pci_driver ines_pci_driver = {
+ .name = "ines_gpib",
+ .id_table = ines_pci_table,
+ .probe = &ines_pci_probe
+};
+
+#ifdef CONFIG_GPIB_PCMCIA
+
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+static const int ines_pcmcia_iosize = 0x20;
+
+/*
+ * The event() function is this driver's Card Services event handler.
+ * It will be called by Card Services when an appropriate card status
+ * event is received. The config() and release() entry points are
+ * used to configure or release a socket, in response to card insertion
+ * and ejection events. They are invoked from the gpib event
+ * handler.
+ */
+
+static int ines_gpib_config(struct pcmcia_device *link);
+static void ines_gpib_release(struct pcmcia_device *link);
+static int ines_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config);
+static int ines_pcmcia_accel_attach(struct gpib_board *board,
+ const struct gpib_board_config *config);
+static void ines_pcmcia_detach(struct gpib_board *board);
+static int ines_common_pcmcia_attach(struct gpib_board *board);
+/*
+ * A linked list of "instances" of the gpib device. Each actual
+ * PCMCIA card corresponds to one device instance, and is described
+ * by one dev_link_t structure (defined in ds.h).
+ *
+ * You may not want to use a linked list for this -- for example, the
+ * memory card driver uses an array of dev_link_t pointers, where minor
+ * device numbers are used to derive the corresponding array index.
+ */
+
+static struct pcmcia_device *curr_dev;
+
+/*
+ * A dev_link_t structure has fields for most things that are needed
+ * to keep track of a socket, but there will usually be some device
+ * specific information that also needs to be kept track of. The
+ * 'priv' pointer in a dev_link_t structure can be used to point to
+ * a device-specific private data structure, like this.
+ *
+ * A driver needs to provide a dev_node_t structure for each device
+ * on a card. In some cases, there is only one device per card (for
+ * example, ethernet cards, modems). In other cases, there may be
+ * many actual or logical devices (SCSI adapters, memory cards with
+ * multiple partitions). The dev_node_t structures need to be kept
+ * in a linked list starting at the 'dev' field of a dev_link_t
+ * structure. We allocate them in the card's private data structure,
+ * because they generally can't be allocated dynamically.
+ */
+
+struct local_info {
+ struct pcmcia_device *p_dev;
+ struct gpib_board *dev;
+ u_short manfid;
+ u_short cardid;
+};
+
+/*
+ * gpib_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device. The device is registered
+ * with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+static int ines_gpib_probe(struct pcmcia_device *link)
+{
+ struct local_info *info;
+
+// int ret, i;
+
+ /* Allocate space for private device-specific data */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ /* The io structure describes IO port mapping */
+ link->resource[0]->end = 32;
+ link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->io_lines = 5;
+
+ /* General socket configuration */
+ link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+ /* Register with Card Services */
+ curr_dev = link;
+ return ines_gpib_config(link);
+}
+
+/*
+ * This deletes a driver "instance". The device is de-registered
+ * with Card Services. If it has been released, all local data
+ * structures are freed. Otherwise, the structures will be freed
+ * when the device is released.
+ */
+static void ines_gpib_remove(struct pcmcia_device *link)
+{
+ struct local_info *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ if (info->dev)
+ ines_pcmcia_detach(info->dev);
+ ines_gpib_release(link);
+
+ //free_netdev(dev);
+ kfree(info);
+}
+
+static int ines_gpib_config_iteration(struct pcmcia_device *link, void *priv_data)
+{
+ return pcmcia_request_io(link);
+}
+
+/*
+ * gpib_config() is scheduled to run after a CARD_INSERTION event
+ * is received, to configure the PCMCIA socket, and to make the
+ * device available to the system.
+ */
+static int ines_gpib_config(struct pcmcia_device *link)
+{
+ int retval;
+ void __iomem *virt;
+
+ retval = pcmcia_loop_config(link, &ines_gpib_config_iteration, NULL);
+ if (retval) {
+ dev_warn(&link->dev, "no configuration found\n");
+ ines_gpib_release(link);
+ return -ENODEV;
+ }
+
+ dev_dbg(&link->dev, "ines_cs: manufacturer: 0x%x card: 0x%x\n",
+ link->manf_id, link->card_id);
+
+ /*
+ * for the ines card we have to setup the configuration registers in
+ * attribute memory here
+ */
+ link->resource[2]->flags |= WIN_MEMORY_TYPE_AM | WIN_DATA_WIDTH_8 | WIN_ENABLE;
+ link->resource[2]->end = 0x1000;
+ retval = pcmcia_request_window(link, link->resource[2], 250);
+ if (retval) {
+ dev_warn(&link->dev, "pcmcia_request_window failed\n");
+ ines_gpib_release(link);
+ return -ENODEV;
+ }
+ retval = pcmcia_map_mem_page(link, link->resource[2], 0);
+ if (retval) {
+ dev_warn(&link->dev, "pcmcia_map_mem_page failed\n");
+ ines_gpib_release(link);
+ return -ENODEV;
+ }
+ virt = ioremap(link->resource[2]->start, resource_size(link->resource[2]));
+ writeb((link->resource[2]->start >> 2) & 0xff, virt + 0xf0); // IOWindow base
+ iounmap(virt);
+
+ /*
+ * This actually configures the PCMCIA socket -- setting up
+ * the I/O windows and the interrupt mapping.
+ */
+ retval = pcmcia_enable_device(link);
+ if (retval) {
+ ines_gpib_release(link);
+ return -ENODEV;
+ }
+ return 0;
+} /* gpib_config */
+
+/*
+ * After a card is removed, gpib_release() will unregister the net
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+
+static void ines_gpib_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+} /* gpib_release */
+
+static int ines_gpib_suspend(struct pcmcia_device *link)
+{
+ //struct local_info *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ if (link->open)
+ dev_err(&link->dev, "Device still open\n");
+ //netif_device_detach(dev);
+
+ return 0;
+}
+
+static int ines_gpib_resume(struct pcmcia_device *link)
+{
+ //struct local_info_t *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ /*if (link->open) {
+ * ni_gpib_probe(dev); / really?
+ * //netif_device_attach(dev);
+ *}
+ */
+ return ines_gpib_config(link);
+}
+
+static struct pcmcia_device_id ines_pcmcia_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x01b4, 0x4730),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, ines_pcmcia_ids);
+
+static struct pcmcia_driver ines_gpib_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "ines_gpib_cs",
+ .id_table = ines_pcmcia_ids,
+ .probe = ines_gpib_probe,
+ .remove = ines_gpib_remove,
+ .suspend = ines_gpib_suspend,
+ .resume = ines_gpib_resume,
+};
+
+static void ines_pcmcia_cleanup_module(void)
+{
+ pcmcia_unregister_driver(&ines_gpib_cs_driver);
+}
+
+static struct gpib_interface ines_pcmcia_unaccel_interface = {
+ .name = "ines_pcmcia_unaccel",
+ .attach = ines_pcmcia_attach,
+ .detach = ines_pcmcia_detach,
+ .read = ines_read,
+ .write = ines_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static struct gpib_interface ines_pcmcia_accel_interface = {
+ .name = "ines_pcmcia_accel",
+ .attach = ines_pcmcia_accel_attach,
+ .detach = ines_pcmcia_detach,
+ .read = ines_accel_read,
+ .write = ines_accel_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static struct gpib_interface ines_pcmcia_interface = {
+ .name = "ines_pcmcia",
+ .attach = ines_pcmcia_accel_attach,
+ .detach = ines_pcmcia_detach,
+ .read = ines_accel_read,
+ .write = ines_accel_write,
+ .command = ines_command,
+ .take_control = ines_take_control,
+ .go_to_standby = ines_go_to_standby,
+ .request_system_control = ines_request_system_control,
+ .interface_clear = ines_interface_clear,
+ .remote_enable = ines_remote_enable,
+ .enable_eos = ines_enable_eos,
+ .disable_eos = ines_disable_eos,
+ .parallel_poll = ines_parallel_poll,
+ .parallel_poll_configure = ines_parallel_poll_configure,
+ .parallel_poll_response = ines_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ines_line_status,
+ .update_status = ines_update_status,
+ .primary_address = ines_primary_address,
+ .secondary_address = ines_secondary_address,
+ .serial_poll_response = ines_serial_poll_response,
+ .serial_poll_status = ines_serial_poll_status,
+ .t1_delay = ines_t1_delay,
+ .return_to_local = ines_return_to_local,
+};
+
+static irqreturn_t ines_pcmcia_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+
+ return ines_interrupt(board);
+}
+
+static int ines_common_pcmcia_attach(struct gpib_board *board)
+{
+ struct ines_priv *ines_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+
+ if (!curr_dev) {
+ dev_err(board->gpib_dev, "no ines pcmcia cards found\n");
+ return -1;
+ }
+
+ retval = ines_generic_attach(board);
+ if (retval)
+ return retval;
+
+ ines_priv = board->private_data;
+ nec_priv = &ines_priv->nec7210_priv;
+
+ if (!request_region(curr_dev->resource[0]->start,
+ resource_size(curr_dev->resource[0]), DRV_NAME)) {
+ dev_err(board->gpib_dev, "ioports at 0x%lx already in use\n",
+ (unsigned long)(curr_dev->resource[0]->start));
+ return -1;
+ }
+
+ nec_priv->iobase = curr_dev->resource[0]->start;
+
+ nec7210_board_reset(nec_priv, board);
+
+ if (request_irq(curr_dev->irq, ines_pcmcia_interrupt, IRQF_SHARED,
+ "pcmcia-gpib", board)) {
+ dev_err(board->gpib_dev, "can't request IRQ %d\n", curr_dev->irq);
+ return -1;
+ }
+ ines_priv->irq = curr_dev->irq;
+
+ return 0;
+}
+
+static int ines_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct ines_priv *ines_priv;
+ int retval;
+
+ retval = ines_common_pcmcia_attach(board);
+ if (retval < 0)
+ return retval;
+
+ ines_priv = board->private_data;
+ ines_online(ines_priv, board, 0);
+
+ return 0;
+}
+
+static int ines_pcmcia_accel_attach(struct gpib_board *board,
+ const struct gpib_board_config *config)
+{
+ struct ines_priv *ines_priv;
+ int retval;
+
+ retval = ines_common_pcmcia_attach(board);
+ if (retval < 0)
+ return retval;
+
+ ines_priv = board->private_data;
+ ines_online(ines_priv, board, 1);
+
+ return 0;
+}
+
+static void ines_pcmcia_detach(struct gpib_board *board)
+{
+ struct ines_priv *ines_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (ines_priv) {
+ nec_priv = &ines_priv->nec7210_priv;
+ if (ines_priv->irq)
+ free_irq(ines_priv->irq, board);
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ release_region(nec_priv->iobase, ines_pcmcia_iosize);
+ }
+ }
+ ines_free_private(board);
+}
+
+#endif /* CONFIG_GPIB_PCMCIA */
+
+static int __init ines_init_module(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&ines_pci_driver);
+ if (ret) {
+ pr_err("pci_register_driver failed: error = %d\n", ret);
+ return ret;
+ }
+
+ ret = gpib_register_driver(&ines_pci_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pci;
+ }
+
+ ret = gpib_register_driver(&ines_pci_unaccel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pci_unaccel;
+ }
+
+ ret = gpib_register_driver(&ines_pci_accel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pci_accel;
+ }
+
+ ret = gpib_register_driver(&ines_isa_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_isa;
+ }
+
+#ifdef CONFIG_GPIB_PCMCIA
+ ret = gpib_register_driver(&ines_pcmcia_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia;
+ }
+
+ ret = gpib_register_driver(&ines_pcmcia_unaccel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia_unaccel;
+ }
+
+ ret = gpib_register_driver(&ines_pcmcia_accel_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia_accel;
+ }
+
+ ret = pcmcia_register_driver(&ines_gpib_cs_driver);
+ if (ret) {
+ pr_err("pcmcia_register_driver failed: error = %d\n", ret);
+ goto err_pcmcia_driver;
+ }
+#endif
+
+ return 0;
+
+#ifdef CONFIG_GPIB_PCMCIA
+err_pcmcia_driver:
+ gpib_unregister_driver(&ines_pcmcia_accel_interface);
+err_pcmcia_accel:
+ gpib_unregister_driver(&ines_pcmcia_unaccel_interface);
+err_pcmcia_unaccel:
+ gpib_unregister_driver(&ines_pcmcia_interface);
+err_pcmcia:
+#endif
+ gpib_unregister_driver(&ines_isa_interface);
+err_isa:
+ gpib_unregister_driver(&ines_pci_accel_interface);
+err_pci_accel:
+ gpib_unregister_driver(&ines_pci_unaccel_interface);
+err_pci_unaccel:
+ gpib_unregister_driver(&ines_pci_interface);
+err_pci:
+ pci_unregister_driver(&ines_pci_driver);
+
+ return ret;
+}
+
+static void __exit ines_exit_module(void)
+{
+ gpib_unregister_driver(&ines_pci_interface);
+ gpib_unregister_driver(&ines_pci_unaccel_interface);
+ gpib_unregister_driver(&ines_pci_accel_interface);
+ gpib_unregister_driver(&ines_isa_interface);
+#ifdef CONFIG_GPIB_PCMCIA
+ gpib_unregister_driver(&ines_pcmcia_interface);
+ gpib_unregister_driver(&ines_pcmcia_unaccel_interface);
+ gpib_unregister_driver(&ines_pcmcia_accel_interface);
+ ines_pcmcia_cleanup_module();
+#endif
+
+ pci_unregister_driver(&ines_pci_driver);
+}
+
+module_init(ines_init_module);
+module_exit(ines_exit_module);
diff --git a/drivers/gpib/lpvo_usb_gpib/Makefile b/drivers/gpib/lpvo_usb_gpib/Makefile
new file mode 100644
index 000000000000..360553488e6d
--- /dev/null
+++ b/drivers/gpib/lpvo_usb_gpib/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib.o
+
diff --git a/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
new file mode 100644
index 000000000000..dd68c4843490
--- /dev/null
+++ b/drivers/gpib/lpvo_usb_gpib/lpvo_usb_gpib.c
@@ -0,0 +1,2025 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * This code has been developed at the Department of Physics (University *
+ * of Florence, Italy) to support in linux-gpib the open usb-gpib adapter *
+ * implemented at the University of Ljubljana (lpvo.fe.uni-lj.si/gpib) *
+ * *
+ * copyright : (C) 2011 Marcello Carla' *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define NAME KBUILD_MODNAME
+
+/* base module includes */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/sched/signal.h>
+#include <linux/usb.h>
+
+#include "gpibP.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for LPVO usb devices");
+
+/*
+ * Table of devices that work with this driver.
+ *
+ * Currently, only one device is known to be used in the
+ * lpvo_usb_gpib adapter (FTDI 0403:6001).
+ * If your adapter uses a different chip, insert a line
+ * in the following table with proper <Vendor-id>, <Product-id>.
+ *
+ * To have your chip automatically handled by the driver,
+ * update files "/usr/local/etc/modprobe.d/lpvo_usb_gpib.conf"
+ * and /usr/local/etc/udev/rules.d/99-lpvo_usb_gpib.rules.
+ *
+ */
+
+static const struct usb_device_id skel_table[] = {
+ { USB_DEVICE(0x0403, 0x6001) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, skel_table);
+
+/*
+ * *** Diagnostics and Debug ***
+ * To enable the diagnostic and debug messages either compile with DEBUG set
+ * or control via the dynamic debug mechanisms.
+ * The module parameter "debug" controls the sending of debug messages to
+ * syslog. By default it is set to 0
+ * debug = 0: only attach/detach messages are sent
+ * 1: every action is logged
+ * 2: extended logging; each single exchanged byte is documented
+ * (about twice the log volume of [1])
+ * To switch debug level:
+ * At module loading: modprobe lpvo_usb_gpib debug={0,1,2}
+ * On the fly: echo {0,1,2} > /sys/modules/lpvo_usb_gpib/parameters/debug
+ */
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define DIA_LOG(level, format, ...) \
+ do { if (debug >= (level)) \
+ dev_dbg(board->gpib_dev, format, ## __VA_ARGS__); } \
+ while (0)
+
+#define WQT wait_queue_entry_t
+#define WQH head
+#define WQE entry
+
+/* standard and extended command sets of the usb-gpib adapter */
+
+#define USB_GPIB_ON "\nIB\n"
+#define USB_GPIB_OFF "\nIBO\n"
+#define USB_GPIB_IBm0 "\nIBm0\n" /* do not assert REN with IFC */
+#define USB_GPIB_IBm1 "\nIBm1\n" /* assert REN with IFC */
+#define USB_GPIB_IBCL "\nIBZ\n"
+#define USB_GPIB_STATUS "\nIBS\n"
+#define USB_GPIB_READ "\nIB?\n"
+#define USB_GPIB_READ_1 "\nIBB\n"
+#define USB_GPIB_EOI "\nIBe0\n"
+#define USB_GPIB_FTMO "\nIBf0\n" /* disable first byte timeout */
+#define USB_GPIB_TTMOZ "\nIBt0\n" /* disable byte timeout */
+
+/* incomplete commands */
+
+#define USB_GPIB_BTMO "\nIBt" /* set byte timeout */
+#define USB_GPIB_TTMO "\nIBT" /* set total timeout */
+
+#define USB_GPIB_DEBUG_ON "\nIBDE\xAA\n"
+#define USB_GPIB_SET_LISTEN "\nIBDT0\n"
+#define USB_GPIB_SET_TALK "\nIBDT1\n"
+#define USB_GPIB_SET_LINES "\nIBDC.\n"
+#define USB_GPIB_SET_DATA "\nIBDM.\n"
+#define USB_GPIB_READ_LINES "\nIBD?C\n"
+#define USB_GPIB_READ_DATA "\nIBD?M\n"
+#define USB_GPIB_READ_BUS "\nIBD??\n"
+
+/* command sequences */
+
+#define USB_GPIB_UNTALK "\nIBC_\n"
+#define USB_GPIB_UNLISTEN "\nIBC?\n"
+
+/* special characters used by the adapter */
+
+#define DLE ('\020')
+#define STX ('\02')
+#define ETX ('\03')
+#define ACK ('\06')
+#define NODATA ('\03')
+#define NODAV ('\011')
+
+#define IB_BUS_REN 0x01
+#define IB_BUS_IFC 0x02
+#define IB_BUS_NDAC 0x04
+#define IB_BUS_NRFD 0x08
+#define IB_BUS_DAV 0x10
+#define IB_BUS_EOI 0x20
+#define IB_BUS_ATN 0x40
+#define IB_BUS_SRQ 0x80
+
+#define INBUF_SIZE 128
+
+struct char_buf { /* used by one_char() routine */
+ char *inbuf;
+ int last;
+ int nchar;
+};
+
+struct usb_gpib_priv { /* private data to the device */
+ u8 eos; /* eos character */
+ short eos_flags; /* eos mode */
+ int timeout; /* current value for timeout */
+ void *dev; /* the usb device private data structure */
+};
+
+#define GPIB_DEV (((struct usb_gpib_priv *)board->private_data)->dev)
+
+static void show_status(struct gpib_board *board)
+{
+ DIA_LOG(2, "# - buffer_length %d\n", board->buffer_length);
+ DIA_LOG(2, "# - status %lx\n", board->status);
+ DIA_LOG(2, "# - use_count %d\n", board->use_count);
+ DIA_LOG(2, "# - pad %x\n", board->pad);
+ DIA_LOG(2, "# - sad %x\n", board->sad);
+ DIA_LOG(2, "# - timeout %d\n", board->usec_timeout);
+ DIA_LOG(2, "# - ppc %d\n", board->parallel_poll_configuration);
+ DIA_LOG(2, "# - t1delay %d\n", board->t1_nano_sec);
+ DIA_LOG(2, "# - online %d\n", board->online);
+ DIA_LOG(2, "# - autopoll %d\n", board->autospollers);
+ DIA_LOG(2, "# - autopoll task %p\n", board->autospoll_task);
+ DIA_LOG(2, "# - minor %d\n", board->minor);
+ DIA_LOG(2, "# - master %d\n", board->master);
+ DIA_LOG(2, "# - list %d\n", board->ist);
+}
+
+/*
+ * GLOBAL VARIABLES: required for
+ * pairing among gpib minor and usb minor.
+ * MAX_DEV is the max number of usb-gpib adapters; free
+ * to change as you like, but no more than 32
+ */
+
+#define MAX_DEV 8
+static struct usb_interface *lpvo_usb_interfaces[MAX_DEV]; /* registered interfaces */
+static int usb_minors[MAX_DEV]; /* usb minors */
+static int assigned_usb_minors; /* mask of filled slots */
+static struct mutex minors_lock; /* operations on usb_minors are to be protected */
+
+/*
+ * usb-skeleton prototypes
+ */
+
+struct usb_skel;
+static ssize_t skel_do_write(struct usb_skel *, const char *, size_t);
+static ssize_t skel_do_read(struct usb_skel *, char *, size_t);
+static int skel_do_open(struct gpib_board *, int);
+static int skel_do_release(struct gpib_board *);
+
+/*
+ * usec_diff : take difference in MICROsec between two 'timespec'
+ * (unix time in sec and NANOsec)
+ */
+
+static inline int usec_diff(struct timespec64 *a, struct timespec64 *b)
+{
+ return ((a->tv_sec - b->tv_sec) * 1000000 +
+ (a->tv_nsec - b->tv_nsec) / 1000);
+}
+
+/*
+ * *** these routines are specific to the usb-gpib adapter ***
+ */
+
+/**
+ * write_loop() - Send a byte sequence to the adapter
+ *
+ * @dev: the private device structure
+ * @msg: the byte sequence.
+ * @leng: the byte sequence length.
+ *
+ */
+
+static int write_loop(void *dev, char *msg, int leng)
+{
+ return skel_do_write(dev, msg, leng);
+}
+
+/**
+ * send_command() - Send a byte sequence and return a single byte reply.
+ *
+ * @board: the gpib_board_struct data area for this gpib interface
+ * @msg: the byte sequence.
+ * @leng: the byte sequence length; can be given as zero and is
+ * computed automatically, but if 'msg' contains a zero byte,
+ * it has to be given explicitly.
+ */
+
+static int send_command(struct gpib_board *board, char *msg, int leng)
+{
+ char buffer[64];
+ int nchar;
+ int retval;
+ struct timespec64 before, after;
+
+ ktime_get_real_ts64 (&before);
+
+ if (!leng)
+ leng = strlen(msg);
+ retval = write_loop(GPIB_DEV, msg, leng);
+ if (retval < 0)
+ return retval;
+
+ nchar = skel_do_read(GPIB_DEV, buffer, 64);
+
+ if (nchar < 0) {
+ dev_err(board->gpib_dev, " return from read: %d\n", nchar);
+ return nchar;
+ } else if (nchar != 1) {
+ dev_err(board->gpib_dev, " Irregular reply to command: %s\n", msg);
+ return -EIO;
+ }
+ ktime_get_real_ts64 (&after);
+
+ DIA_LOG(1, "Sent %d - done %d us.\n", leng, usec_diff(&after, &before));
+
+ return buffer[0] & 0xff;
+}
+
+/*
+ * set_control_line() - Set the value of a single gpib control line
+ *
+ * @board: the gpib_board_struct data area for this gpib interface
+ * @line: line mask
+ * @value: line new value (0/1)
+ */
+
+static int set_control_line(struct gpib_board *board, int line, int value)
+{
+ char msg[] = USB_GPIB_SET_LINES;
+ int retval;
+ int leng = strlen(msg);
+
+ DIA_LOG(1, "setting line %x to %x\n", line, value);
+
+ retval = send_command(board, USB_GPIB_READ_LINES, 0);
+
+ DIA_LOG(1, "old line values: %x\n", retval);
+
+ if (retval == -EIO)
+ return retval;
+
+ msg[leng - 2] = value ? (retval & ~line) : retval | line;
+
+ retval = send_command(board, msg, 0);
+
+ DIA_LOG(1, "operation result: %x\n", retval);
+
+ return retval;
+}
+
+/*
+ * one_char() - read one single byte from input buffer
+ *
+ * @board: the gpib_board_struct data area for this gpib interface
+ * @char_buf: the routine private data structure
+ */
+
+static int one_char(struct gpib_board *board, struct char_buf *b)
+{
+ struct timespec64 before, after;
+
+ if (b->nchar) {
+ DIA_LOG(2, "-> %x\n", b->inbuf[b->last - b->nchar]);
+ return b->inbuf[b->last - b->nchar--];
+ }
+ ktime_get_real_ts64 (&before);
+ b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE);
+ b->last = b->nchar;
+ ktime_get_real_ts64 (&after);
+
+ DIA_LOG(2, "read %d bytes in %d usec\n",
+ b->nchar, usec_diff(&after, &before));
+
+ if (b->nchar > 0) {
+ DIA_LOG(2, "--> %x\n", b->inbuf[b->last - b->nchar]);
+ return b->inbuf[b->last - b->nchar--];
+ }
+ return -EIO;
+}
+
+/**
+ * set_timeout() - set single byte / total timeouts on the adapter
+ *
+ * @board: the gpib_board_struct data area for this gpib interface
+ *
+ * For sake of speed, the operation is performed only if it
+ * modifies the current (saved) value. Minimum allowed timeout
+ * is 30 ms (T30ms -> 8); timeout disable (TNONE -> 0) currently
+ * not supported.
+ */
+
+static void set_timeout(struct gpib_board *board)
+{
+ int n, val;
+ char command[sizeof(USB_GPIB_TTMO) + 6];
+ struct usb_gpib_priv *data = board->private_data;
+
+ if (data->timeout == board->usec_timeout)
+ return;
+
+ n = (board->usec_timeout + 32767) / 32768;
+ if (n < 2)
+ n = 2;
+
+ DIA_LOG(1, "Set timeout to %d us -> %d\n", board->usec_timeout, n);
+
+ sprintf(command, "%s%d\n", USB_GPIB_BTMO, n > 255 ? 255 : n);
+ val = send_command(board, command, 0);
+
+ if (val == ACK) {
+ if (n > 65535)
+ n = 65535;
+ sprintf(command, "%s%d\n", USB_GPIB_TTMO, n);
+ val = send_command(board, command, 0);
+ }
+
+ if (val != ACK)
+ dev_err(board->gpib_dev, "error in timeout set: <%s>\n", command);
+ else
+ data->timeout = board->usec_timeout;
+}
+
+/*
+ * now the standard interface functions - attach and detach
+ */
+
+/**
+ * usb_gpib_attach() - activate the usb-gpib converter board
+ *
+ * @board: the gpib_board_struct data area for this gpib interface
+ * @config: firmware data, if any (from gpib_config -I <file>)
+ *
+ * The channel name is ttyUSBn, with n=0 by default. Other values for n
+ * passed with gpib_config -b <n>.
+ *
+ * In this routine I trust that when an error code is returned
+ * detach() will be called. Always.
+ */
+
+static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ int retval, j;
+ u32 base = config->ibbase;
+ char *device_path;
+ int match;
+ struct usb_device *udev;
+
+ DIA_LOG(0, "Board %p -t %s -m %d -a %p -u %d -l %d -b %d\n",
+ board, board->interface->name, board->minor, config->device_path,
+ config->pci_bus, config->pci_slot, base);
+
+ board->private_data = NULL; /* to be sure - we can detach before setting */
+
+ /* identify device to be attached */
+
+ mutex_lock(&minors_lock);
+
+ if (config->device_path) {
+ /* if config->device_path given, try that first */
+ for (j = 0 ; j < MAX_DEV ; j++) {
+ if ((assigned_usb_minors & 1 << j) == 0)
+ continue;
+ udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j]));
+ device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL);
+ match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev,
+ config->device_path);
+ DIA_LOG(1, "dev. %d: minor %d path: %s --> %d\n", j,
+ lpvo_usb_interfaces[j]->minor, device_path, match);
+ kfree(device_path);
+ if (match)
+ break;
+ }
+ } else if (config->pci_bus != -1 && config->pci_slot != -1) {
+ /* second: look for bus and slot */
+ for (j = 0 ; j < MAX_DEV ; j++) {
+ if ((assigned_usb_minors & 1 << j) == 0)
+ continue;
+ udev = usb_get_dev(interface_to_usbdev(lpvo_usb_interfaces[j]));
+ DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j,
+ udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot);
+ if (config->pci_bus == udev->bus->busnum &&
+ config->pci_slot == udev->devnum)
+ break;
+ }
+ } else { /* last chance: usb_minor, given as ibbase */
+ for (j = 0 ; j < MAX_DEV ; j++) {
+ if (usb_minors[j] == base && assigned_usb_minors & 1 << j)
+ break;
+ }
+ }
+ mutex_unlock(&minors_lock);
+
+ if (j == MAX_DEV) {
+ dev_err(board->gpib_dev, "Requested device is not registered.\n");
+ return -EIO;
+ }
+
+ board->private_data = kzalloc(sizeof(struct usb_gpib_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+
+ retval = skel_do_open(board, usb_minors[j]);
+
+ DIA_LOG(1, "Skel open: %d\n", retval);
+
+ if (retval) {
+ dev_err(board->gpib_dev, "skel open failed.\n");
+ kfree(board->private_data);
+ board->private_data = NULL;
+ return -ENODEV;
+ }
+
+ show_status(board);
+
+ retval = send_command(board, USB_GPIB_ON, 0);
+ DIA_LOG(1, "USB_GPIB_ON returns %x\n", retval);
+ if (retval != ACK)
+ return -EIO;
+
+ /*
+ * We must setup debug mode because we need the extended instruction
+ * set to cope with the Core (gpib_common) point of view
+ */
+
+ retval = send_command(board, USB_GPIB_DEBUG_ON, 0);
+ DIA_LOG(1, "USB_GPIB_DEBUG_ON returns %x\n", retval);
+ if (retval != ACK)
+ return -EIO;
+
+ /*
+ * We must keep REN off after an IFC because so it is
+ * assumed by the Core
+ */
+
+ retval = send_command(board, USB_GPIB_IBm0, 0);
+ DIA_LOG(1, "USB_GPIB_IBm0 returns %x\n", retval);
+ if (retval != ACK)
+ return -EIO;
+
+ retval = set_control_line(board, IB_BUS_REN, 0);
+ if (retval != ACK)
+ return -EIO;
+
+ retval = send_command(board, USB_GPIB_FTMO, 0);
+ DIA_LOG(1, "USB_GPIB_FTMO returns %x\n", retval);
+ if (retval != ACK)
+ return -EIO;
+
+ show_status(board);
+ DIA_LOG(0, "attached\n");
+ return 0;
+}
+
+/**
+ * usb_gpib_detach() - deactivate the usb-gpib converter board
+ *
+ * @board: the gpib_board data area for this gpib interface
+ *
+ */
+
+static void usb_gpib_detach(struct gpib_board *board)
+{
+ int retval;
+
+ show_status(board);
+
+ DIA_LOG(0, "detaching\n");
+
+ if (board->private_data) {
+ if (GPIB_DEV) {
+ write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF));
+ msleep(100);
+ DIA_LOG(1, "%s", "GPIB off\n");
+ retval = skel_do_release(board);
+ DIA_LOG(1, "skel release -> %d\n", retval);
+ }
+ kfree(board->private_data);
+ board->private_data = NULL;
+ }
+
+ DIA_LOG(0, "detached\n");
+}
+
+/*
+ * Other functions follow in alphabetical order
+ */
+/* command */
+static int usb_gpib_command(struct gpib_board *board,
+ u8 *buffer,
+ size_t length,
+ size_t *bytes_written)
+{
+ int i, retval;
+ char command[6] = "IBc.\n";
+
+ DIA_LOG(1, "enter %p\n", board);
+
+ set_timeout(board);
+
+ *bytes_written = 0;
+ for (i = 0 ; i < length ; i++) {
+ command[3] = buffer[i];
+ retval = send_command(board, command, 5);
+ DIA_LOG(2, "%d ==> %x %x\n", i, buffer[i], retval);
+ if (retval != 0x06)
+ return retval;
+ ++(*bytes_written);
+ }
+ return 0;
+}
+
+/**
+ * usb_gpib_disable_eos() - Disable END on eos byte (END on EOI only)
+ *
+ * @board: the gpib_board data area for this gpib interface
+ *
+ * With the lpvo adapter eos can only be handled via software.
+ * Cannot do nothing here, but remember for future use.
+ */
+
+static void usb_gpib_disable_eos(struct gpib_board *board)
+{
+ ((struct usb_gpib_priv *)board->private_data)->eos_flags &= ~REOS;
+ DIA_LOG(1, "done: %x\n",
+ ((struct usb_gpib_priv *)board->private_data)->eos_flags);
+}
+
+/**
+ * usb_gpib_enable_eos() - Enable END for reads when eos byte is received.
+ *
+ * @board: the gpib_board data area for this gpib interface
+ * @eos_byte: the 'eos' byte
+ * @compare_8_bits: if zero ignore eigthth bit when comparing
+ *
+ */
+
+static int usb_gpib_enable_eos(struct gpib_board *board,
+ u8 eos_byte,
+ int compare_8_bits)
+{
+ struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data;
+
+ DIA_LOG(1, "enter with %x\n", eos_byte);
+ pd->eos = eos_byte;
+ pd->eos_flags = REOS;
+ if (compare_8_bits)
+ pd->eos_flags |= BIN;
+ return 0;
+}
+
+/**
+ * usb_gpib_go_to_standby() - De-assert ATN
+ *
+ * @board: the gpib_board data area for this gpib interface
+ */
+
+static int usb_gpib_go_to_standby(struct gpib_board *board)
+{
+ int retval = set_control_line(board, IB_BUS_ATN, 0);
+
+ DIA_LOG(1, "done with %x\n", retval);
+
+ if (retval == ACK)
+ return 0;
+ return -EIO;
+}
+
+/**
+ * usb_gpib_interface_clear() - Assert or de-assert IFC
+ *
+ * @board: the gpib_board data area for this gpib interface
+ * @assert: 1: assert IFC; 0: de-assert IFC
+ *
+ * Currently on the assert request we issue the lpvo IBZ
+ * command that cycles IFC low for 100 usec, then we ignore
+ * the de-assert request.
+ */
+
+static void usb_gpib_interface_clear(struct gpib_board *board, int assert)
+{
+ int retval = 0;
+
+ DIA_LOG(1, "enter with %d\n", assert);
+
+ if (assert) {
+ retval = send_command(board, USB_GPIB_IBCL, 0);
+
+ set_bit(CIC_NUM, &board->status);
+ }
+
+ DIA_LOG(1, "done with %d %d\n", assert, retval);
+}
+
+/**
+ * usb_gpib_line_status() - Read the status of the bus lines.
+ *
+ * @board: the gpib_board data area for this gpib interface
+ *
+ * We can read all lines.
+ */
+static int usb_gpib_line_status(const struct gpib_board *board)
+{
+ int buffer;
+ int line_status = VALID_ALL; /* all lines will be read */
+ struct list_head *p, *q;
+ WQT *item;
+ unsigned long flags;
+ int sleep = 0;
+
+ DIA_LOG(1, "%s\n", "request");
+
+ /*
+ * if we are on the wait queue (board->wait), do not hurry
+ * reading status line; instead, pause a little
+ */
+
+ spin_lock_irqsave((spinlock_t *)&board->wait.lock, flags);
+ q = (struct list_head *)&board->wait.WQH;
+ list_for_each(p, q) {
+ item = container_of(p, WQT, WQE);
+ if (item->private == current) {
+ sleep = 20;
+ break;
+ }
+ /* pid is: ((struct task_struct *) item->private)->pid); */
+ }
+ spin_unlock_irqrestore((spinlock_t *)&board->wait.lock, flags);
+ if (sleep) {
+ DIA_LOG(1, "we are on the wait queue - sleep %d ms\n", sleep);
+ msleep(sleep);
+ }
+
+ buffer = send_command((struct gpib_board *)board, USB_GPIB_STATUS, 0);
+
+ if (buffer < 0) {
+ dev_err(board->gpib_dev, "line status read failed with %d\n", buffer);
+ return -1;
+ }
+
+ if ((buffer & 0x01) == 0)
+ line_status |= BUS_REN;
+ if ((buffer & 0x02) == 0)
+ line_status |= BUS_IFC;
+ if ((buffer & 0x04) == 0)
+ line_status |= BUS_NDAC;
+ if ((buffer & 0x08) == 0)
+ line_status |= BUS_NRFD;
+ if ((buffer & 0x10) == 0)
+ line_status |= BUS_DAV;
+ if ((buffer & 0x20) == 0)
+ line_status |= BUS_EOI;
+ if ((buffer & 0x40) == 0)
+ line_status |= BUS_ATN;
+ if ((buffer & 0x80) == 0)
+ line_status |= BUS_SRQ;
+
+ DIA_LOG(1, "done with %x %x\n", buffer, line_status);
+
+ return line_status;
+}
+
+/* parallel_poll */
+
+static int usb_gpib_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ /*
+ * request parallel poll asserting ATN | EOI;
+ * we suppose ATN already asserted
+ */
+
+ int retval;
+
+ DIA_LOG(1, "enter %p\n", board);
+
+ retval = set_control_line(board, IB_BUS_EOI, 1);
+ if (retval != ACK)
+ return -EIO;
+
+ *result = send_command(board, USB_GPIB_READ_DATA, 0);
+
+ DIA_LOG(1, "done with %x\n", *result);
+
+ retval = set_control_line(board, IB_BUS_EOI, 0);
+ if (retval != 0x06)
+ return -EIO;
+
+ return 0;
+}
+
+/* read */
+
+static int usb_gpib_read(struct gpib_board *board,
+ u8 *buffer,
+ size_t length,
+ int *end,
+ size_t *bytes_read)
+{
+#define MAX_READ_EXCESS 16384
+
+ struct char_buf b = {NULL, 0};
+
+ int retval;
+ char c, nc;
+ int ic;
+ struct timespec64 before, after;
+ int read_count = MAX_READ_EXCESS;
+ struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data;
+
+ DIA_LOG(1, "enter %p -> %zu\n", board, length);
+
+ *bytes_read = 0; /* by default, things go wrong */
+ *end = 0;
+
+ set_timeout(board);
+
+ /* single byte read has a special handling */
+
+ if (length == 1) {
+ char inbuf[2] = {0, 0};
+
+ /* read a single character */
+
+ ktime_get_real_ts64 (&before);
+
+ retval = write_loop(GPIB_DEV, USB_GPIB_READ_1, strlen(USB_GPIB_READ_1));
+ if (retval < 0)
+ return retval;
+
+ retval = skel_do_read(GPIB_DEV, inbuf, 1);
+ retval += skel_do_read(GPIB_DEV, inbuf + 1, 1);
+
+ ktime_get_real_ts64 (&after);
+
+ DIA_LOG(1, "single read: %x %x %x in %d\n", retval,
+ inbuf[0], inbuf[1],
+ usec_diff(&after, &before));
+
+ /* good char / last char? */
+
+ if (retval == 2 && inbuf[1] == ACK) {
+ buffer[0] = inbuf[0];
+ *bytes_read = 1;
+ return 0;
+ }
+ if (retval < 2)
+ return -EIO;
+ else
+ return -ETIME;
+ }
+
+ /* allocate buffer for multibyte read */
+
+ b.inbuf = kmalloc(INBUF_SIZE, GFP_KERNEL);
+ if (!b.inbuf)
+ return -ENOMEM;
+
+ /* send read command and check <DLE><STX> sequence */
+
+ retval = write_loop(GPIB_DEV, USB_GPIB_READ, strlen(USB_GPIB_READ));
+ if (retval < 0)
+ goto read_return;
+
+ if (one_char(board, &b) != DLE || one_char(board, &b) != STX) {
+ dev_err(board->gpib_dev, "wrong <DLE><STX> sequence\n");
+ retval = -EIO;
+ goto read_return;
+ }
+
+ /* get data flow */
+
+ while (1) {
+ ic = one_char(board, &b);
+ if (ic == -EIO) {
+ retval = -EIO;
+ goto read_return;
+ }
+ c = ic;
+
+ if (c == DLE)
+ nc = one_char(board, &b);
+ if (c != DLE || nc == DLE) {
+ /* data byte - store into buffer */
+
+ if (*bytes_read == length)
+ break; /* data overflow */
+ if (c == DLE)
+ c = nc;
+ buffer[(*bytes_read)++] = c;
+ if (c == pd->eos) {
+ *end = 1;
+ break;
+ }
+
+ } else {
+ /* we are in the closing <DLE><ETX> sequence */
+ c = nc;
+ if (c == ETX) {
+ c = one_char(board, &b);
+ if (c == ACK) {
+ *end = 1;
+ retval = 0;
+ goto read_return;
+ } else {
+ dev_err(board->gpib_dev, "wrong end of message %x", c);
+ retval = -ETIME;
+ goto read_return;
+ }
+ } else {
+ dev_err(board->gpib_dev, "lone <DLE> in stream");
+ retval = -EIO;
+ goto read_return;
+ }
+ }
+ }
+
+ /* we had a data overflow - flush excess data */
+
+ while (read_count--) {
+ if (one_char(board, &b) != DLE)
+ continue;
+ c = one_char(board, &b);
+ if (c == DLE)
+ continue;
+ if (c == ETX) {
+ c = one_char(board, &b);
+ if (c == ACK) {
+ if (MAX_READ_EXCESS - read_count > 1)
+ dev_dbg(board->gpib_dev, "small buffer - maybe some data lost");
+ retval = 0;
+ goto read_return;
+ }
+ break;
+ }
+ }
+
+ dev_err(board->gpib_dev, "no input end - board in odd state\n");
+ retval = -EIO;
+
+read_return:
+ kfree(b.inbuf);
+
+ DIA_LOG(1, "done with byte/status: %d %x %d\n", (int)*bytes_read, retval, *end);
+
+ if (retval == 0 || retval == -ETIME) {
+ if (send_command(board, USB_GPIB_UNTALK, sizeof(USB_GPIB_UNTALK)) == 0x06)
+ return retval;
+ return -EIO;
+ }
+
+ return retval;
+}
+
+/* remote_enable */
+
+static void usb_gpib_remote_enable(struct gpib_board *board, int enable)
+{
+ int retval;
+
+ retval = set_control_line(board, IB_BUS_REN, enable ? 1 : 0);
+ if (retval != ACK)
+ dev_err(board->gpib_dev, "could not set REN line: %x\n", retval);
+
+ DIA_LOG(1, "done with %x\n", retval);
+}
+
+/* request_system_control */
+
+static int usb_gpib_request_system_control(struct gpib_board *board, int request_control)
+{
+ if (!request_control)
+ return -EINVAL;
+
+ DIA_LOG(1, "done with %d -> %lx\n", request_control, board->status);
+ return 0;
+}
+
+/* take_control */
+/* beware: the sync flag is ignored; what is its real meaning? */
+
+static int usb_gpib_take_control(struct gpib_board *board, int sync)
+{
+ int retval;
+
+ retval = set_control_line(board, IB_BUS_ATN, 1);
+
+ DIA_LOG(1, "done with %d %x\n", sync, retval);
+
+ if (retval == ACK)
+ return 0;
+ return -EIO;
+}
+
+/* update_status */
+
+static unsigned int usb_gpib_update_status(struct gpib_board *board,
+ unsigned int clear_mask)
+{
+ /* There is nothing we can do here, I guess */
+
+ board->status &= ~clear_mask;
+
+ DIA_LOG(1, "done with %x %lx\n", clear_mask, board->status);
+
+ return board->status;
+}
+
+/* write */
+/* beware: DLE characters are not escaped - can only send ASCII data */
+
+static int usb_gpib_write(struct gpib_board *board,
+ u8 *buffer,
+ size_t length,
+ int send_eoi,
+ size_t *bytes_written)
+{
+ int retval;
+ char *msg;
+
+ DIA_LOG(1, "enter %p -> %zu\n", board, length);
+
+ set_timeout(board);
+
+ msg = kmalloc(length + 8, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ memcpy(msg, "\nIB\020\002", 5);
+ memcpy(msg + 5, buffer, length);
+ memcpy(msg + 5 + length, "\020\003\n", 3);
+
+ retval = send_command(board, msg, length + 8);
+ kfree(msg);
+
+ DIA_LOG(1, "<%.*s> -> %x\n", (int)length, buffer, retval);
+
+ if (retval != ACK)
+ return -EPIPE;
+
+ *bytes_written = length;
+
+ if (send_command(board, USB_GPIB_UNLISTEN, sizeof(USB_GPIB_UNLISTEN)) != 0x06)
+ return -EPIPE;
+
+ return length;
+}
+
+/*
+ * *** following functions not implemented yet ***
+ */
+
+/* parallel_poll configure */
+
+static void usb_gpib_parallel_poll_configure(struct gpib_board *board,
+ u8 configuration)
+{
+}
+
+/* parallel_poll_response */
+
+static void usb_gpib_parallel_poll_response(struct gpib_board *board, int ist)
+{
+}
+
+/* primary_address */
+
+static int usb_gpib_primary_address(struct gpib_board *board, unsigned int address)
+{
+ return 0;
+}
+
+/* return_to_local */
+
+static void usb_gpib_return_to_local(struct gpib_board *board)
+{
+}
+
+/* secondary_address */
+
+static int usb_gpib_secondary_address(struct gpib_board *board,
+ unsigned int address,
+ int enable)
+{
+ return 0;
+}
+
+/* serial_poll_response */
+
+static void usb_gpib_serial_poll_response(struct gpib_board *board, u8 status)
+{
+}
+
+/* serial_poll_status */
+
+static u8 usb_gpib_serial_poll_status(struct gpib_board *board)
+{
+ return 0;
+}
+
+/* t1_delay */
+
+static int usb_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ return 0;
+}
+
+/*
+ * *** module dispatch table and init/exit functions ***
+ */
+
+static struct gpib_interface usb_gpib_interface = {
+ .name = NAME,
+ .attach = usb_gpib_attach,
+ .detach = usb_gpib_detach,
+ .read = usb_gpib_read,
+ .write = usb_gpib_write,
+ .command = usb_gpib_command,
+ .take_control = usb_gpib_take_control,
+ .go_to_standby = usb_gpib_go_to_standby,
+ .request_system_control = usb_gpib_request_system_control,
+ .interface_clear = usb_gpib_interface_clear,
+ .remote_enable = usb_gpib_remote_enable,
+ .enable_eos = usb_gpib_enable_eos,
+ .disable_eos = usb_gpib_disable_eos,
+ .parallel_poll = usb_gpib_parallel_poll,
+ .parallel_poll_configure = usb_gpib_parallel_poll_configure,
+ .parallel_poll_response = usb_gpib_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = usb_gpib_line_status,
+ .update_status = usb_gpib_update_status,
+ .primary_address = usb_gpib_primary_address,
+ .secondary_address = usb_gpib_secondary_address,
+ .serial_poll_response = usb_gpib_serial_poll_response,
+ .serial_poll_status = usb_gpib_serial_poll_status,
+ .t1_delay = usb_gpib_t1_delay,
+ .return_to_local = usb_gpib_return_to_local,
+ .skip_check_for_command_acceptors = 1
+};
+
+/*
+ * usb_gpib_init_module(), usb_gpib_exit_module()
+ *
+ * This functions are called every time a new device is detected
+ * and registered or is removed and unregistered.
+ * We must take note of created and destroyed usb minors to be used
+ * when usb_gpib_attach() and usb_gpib_detach() will be called on
+ * request by gpib_config.
+ */
+
+static int usb_gpib_init_module(struct usb_interface *interface)
+{
+ int j, mask, rv;
+
+ rv = mutex_lock_interruptible(&minors_lock);
+ if (rv < 0)
+ return rv;
+
+ if (!assigned_usb_minors) {
+ rv = gpib_register_driver(&usb_gpib_interface, THIS_MODULE);
+ if (rv) {
+ pr_err("gpib_register_driver failed: error = %d\n", rv);
+ goto exit;
+ }
+ } else {
+ /*
+ * check if minor is already registered - maybe useless, but if
+ * it happens the code is inconsistent somewhere
+ */
+
+ for (j = 0 ; j < MAX_DEV ; j++) {
+ if (usb_minors[j] == interface->minor && assigned_usb_minors & 1 << j) {
+ pr_err("CODE BUG: USB minor %d registered at %d.\n",
+ interface->minor, j);
+ rv = -1;
+ goto exit;
+ }
+ }
+ }
+
+ /* find a free slot */
+
+ for (j = 0 ; j < MAX_DEV ; j++) {
+ mask = 1 << j;
+ if ((assigned_usb_minors & mask) == 0) {
+ usb_minors[j] = interface->minor;
+ lpvo_usb_interfaces[j] = interface;
+ assigned_usb_minors |= mask;
+ rv = 0;
+ goto exit;
+ }
+ }
+ pr_err("No slot available for interface %p minor %d\n", interface, interface->minor);
+ rv = -1;
+
+exit:
+ mutex_unlock(&minors_lock);
+ return rv;
+}
+
+static void usb_gpib_exit_module(int minor)
+{
+ int j;
+
+ mutex_lock(&minors_lock);
+ for (j = 0 ; j < MAX_DEV ; j++) {
+ if (usb_minors[j] == minor && assigned_usb_minors & 1 << j) {
+ assigned_usb_minors &= ~(1 << j);
+ usb_minors[j] = -1;
+ if (assigned_usb_minors == 0)
+ gpib_unregister_driver(&usb_gpib_interface);
+ goto exit;
+ }
+ }
+ pr_err("CODE BUG: USB minor %d not found.\n", minor);
+
+exit:
+ mutex_unlock(&minors_lock);
+}
+
+/*
+ * Default latency time (16 msec) is too long.
+ * We must use 1 msec (best); anyhow, no more than 5 msec.
+ *
+ * Defines and function taken and modified from the kernel tree
+ * (see ftdi_sio.h and ftdi_sio.c).
+ */
+
+#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
+#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
+#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
+#define WDR_TIMEOUT 5000 /* default urb timeout */
+#define WDR_SHORT_TIMEOUT 1000 /* shorter urb timeout */
+
+#define LATENCY_TIMER 1 /* use a small latency timer: 1 ... 5 msec */
+#define LATENCY_CHANNEL 0 /* channel selection in multichannel devices */
+static int write_latency_timer(struct usb_device *udev)
+{
+ int rv = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
+ FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
+ LATENCY_TIMER, LATENCY_CHANNEL,
+ NULL, 0, WDR_TIMEOUT);
+ if (rv < 0)
+ dev_err(&udev->dev, "Unable to write latency timer: %i\n", rv);
+ return rv;
+}
+
+/*****************************************************************************
+ * *
+ * The following code is a modified version of the USB Skeleton driver *
+ * written by Greg Kroah-Hartman and available in the kernel tree. *
+ * *
+ * Functions skel_open() and skel_release() have been rewritten and named *
+ * skel_do_open() and skel_do_release() to process the attach and detach *
+ * requests coming from gpib_config. *
+ * *
+ * Functions skel_read() and skel_write() have been split into a *
+ * skel_do_read() and skel_do_write(), that cover the kernel stuff of read *
+ * and write operations, and the original skel_read() and skel_write(), *
+ * that handle communication with user space and call their _do_ companion. *
+ * *
+ * Only the _do_ versions are used by the lpvo_usb_gpib driver; other ones *
+ * can be (optionally) maintained in the compilation to have direct access *
+ * to a gpib controller for debug and diagnostics. *
+ * *
+ * To avoid collisions in names, devices in user space have been renamed *
+ * lpvo_raw1, lpvo_raw2 .... and the usb driver has been renamed with the *
+ * gpib module name. *
+ * *
+ *****************************************************************************/
+
+/*
+ * USB Skeleton driver - 2.2
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
+ * but has been rewritten to be easier to read and use.
+ */
+
+#include <linux/errno.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_SKEL_MINOR_BASE 192
+
+/* private defines */
+
+#define MAX_TRANSFER (PAGE_SIZE - 512)
+/*
+ * MAX_TRANSFER is chosen so that the VM is not stressed by
+ * allocations > PAGE_SIZE and the number of packets in a page
+ * is an integer 512 is the largest possible packet on EHCI
+ */
+
+#define WRITES_IN_FLIGHT 1 /* we do not want more than one pending write */
+#define USER_DEVICE 1 /* compile for device(s) in user space */
+
+/* Structure to hold all of our device specific stuff */
+struct usb_skel {
+ struct usb_device *udev; /* the usb device for this device */
+ struct usb_interface *interface; /* the interface for this device */
+ struct semaphore limit_sem; /* limiting the number of writes in progress */
+ struct usb_anchor submitted; /* in case need to retract our submissions */
+ struct urb *bulk_in_urb; /* the urb to read data with */
+ unsigned char *bulk_in_buffer; /* the buffer to receive data */
+ size_t bulk_in_size; /* the size of the receive buffer */
+ size_t bulk_in_filled; /* number of bytes in the buffer */
+ size_t bulk_in_copied; /* already copied to user space */
+ __u8 bulk_in_endpoint_addr; /* the address of the bulk in endpoint */
+ __u8 bulk_out_endpoint_addr; /* the address of the bulk out endpoint */
+ int errors; /* the last request tanked */
+ bool ongoing_read; /* a read is going on */
+ spinlock_t err_lock; /* lock for errors */
+ struct kref kref;
+ struct mutex io_mutex; /* synchronize I/O with disconnect */
+ wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */
+};
+
+#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
+
+static struct usb_driver skel_driver;
+static void skel_draw_down(struct usb_skel *dev);
+
+static void skel_delete(struct kref *kref)
+{
+ struct usb_skel *dev = to_skel_dev(kref);
+
+ usb_free_urb(dev->bulk_in_urb);
+ usb_put_dev(dev->udev);
+ kfree(dev->bulk_in_buffer);
+ kfree(dev);
+}
+
+/*
+ * skel_do_open() - to be called by usb_gpib_attach
+ */
+
+static int skel_do_open(struct gpib_board *board, int subminor)
+{
+ struct usb_skel *dev;
+ struct usb_interface *interface;
+ int retval = 0;
+
+ interface = usb_find_interface(&skel_driver, subminor);
+ if (!interface) {
+ dev_err(board->gpib_dev, "can't find device for minor %d\n", subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ dev = usb_get_intfdata(interface);
+ if (!dev) {
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ retval = usb_autopm_get_interface(interface);
+ if (retval)
+ goto exit;
+
+ /* increment our usage count for the device */
+ kref_get(&dev->kref);
+
+ /* save our object in the file's private structure */
+ GPIB_DEV = dev;
+
+exit:
+ return retval;
+}
+
+/*
+ * skel_do_release() - to be called by usb_gpib_detach
+ */
+
+static int skel_do_release(struct gpib_board *board)
+{
+ struct usb_skel *dev;
+
+ dev = GPIB_DEV;
+ if (!dev)
+ return -ENODEV;
+
+ /* allow the device to be autosuspended */
+ mutex_lock(&dev->io_mutex);
+ if (dev->interface)
+ usb_autopm_put_interface(dev->interface);
+ mutex_unlock(&dev->io_mutex);
+
+ /* decrement the count on our device */
+ kref_put(&dev->kref, skel_delete);
+ return 0;
+}
+
+/*
+ * read functions
+ */
+
+static void skel_read_bulk_callback(struct urb *urb)
+{
+ struct usb_skel *dev;
+ unsigned long flags;
+
+ dev = urb->context;
+
+ spin_lock_irqsave(&dev->err_lock, flags);
+ /* sync/async unlink faults aren't errors */
+ if (urb->status) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN))
+ dev_err(&dev->interface->dev, "nonzero read bulk status received: %d\n",
+ urb->status);
+
+ dev->errors = urb->status;
+ } else {
+ dev->bulk_in_filled = urb->actual_length;
+ }
+ dev->ongoing_read = 0;
+ spin_unlock_irqrestore(&dev->err_lock, flags);
+
+ wake_up_interruptible(&dev->bulk_in_wait);
+}
+
+static int skel_do_read_io(struct usb_skel *dev, size_t count)
+{
+ int rv;
+
+ /* prepare a read */
+ usb_fill_bulk_urb(dev->bulk_in_urb,
+ dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpoint_addr),
+ dev->bulk_in_buffer,
+ min(dev->bulk_in_size, count),
+ skel_read_bulk_callback,
+ dev);
+ /* tell everybody to leave the URB alone */
+ spin_lock_irq(&dev->err_lock);
+ dev->ongoing_read = 1;
+ spin_unlock_irq(&dev->err_lock);
+
+ /* submit bulk in urb, which means no data to deliver */
+ dev->bulk_in_filled = 0;
+ dev->bulk_in_copied = 0;
+
+ /* do it */
+ rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
+ if (rv < 0) {
+ dev_err(&dev->interface->dev, "failed submitting read urb, error %d\n", rv);
+ rv = (rv == -ENOMEM) ? rv : -EIO;
+ spin_lock_irq(&dev->err_lock);
+ dev->ongoing_read = 0;
+ spin_unlock_irq(&dev->err_lock);
+ }
+
+ return rv;
+}
+
+/*
+ * skel_do_read() - read operations from lpvo_usb_gpib
+ */
+
+static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count)
+{
+ int rv;
+ bool ongoing_io;
+
+ /* if we cannot read at all, return EOF */
+
+ if (!dev->bulk_in_urb || !count)
+ return 0;
+
+restart: /* added to comply with ftdi timeout technique */
+
+ /* no concurrent readers */
+
+ rv = mutex_lock_interruptible(&dev->io_mutex);
+ if (rv < 0)
+ return rv;
+
+ if (!dev->interface) { /* disconnect() was called */
+ rv = -ENODEV;
+ goto exit;
+ }
+
+retry:
+ /* if IO is under way, we must not touch things */
+ spin_lock_irq(&dev->err_lock);
+ ongoing_io = dev->ongoing_read;
+ spin_unlock_irq(&dev->err_lock);
+
+ if (ongoing_io) {
+// /* nonblocking IO shall not wait */
+// /* no file, no O_NONBLOCK; maybe provide when from user space */
+// if (file->f_flags & O_NONBLOCK) {
+// rv = -EAGAIN;
+// goto exit;
+// }
+
+ /*
+ * IO may take forever
+ * hence wait in an interruptible state
+ */
+ rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
+ if (rv < 0)
+ goto exit;
+ }
+
+ /* errors must be reported */
+ rv = dev->errors;
+ if (rv < 0) {
+ /* any error is reported once */
+ dev->errors = 0;
+ /* to preserve notifications about reset */
+ rv = (rv == -EPIPE) ? rv : -EIO;
+ /* report it */
+ goto exit;
+ }
+
+ /*
+ * if the buffer is filled we may satisfy the read
+ * else we need to start IO
+ */
+
+ if (dev->bulk_in_filled) {
+ /* we had read data */
+
+ size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
+// size_t chunk = min(available, count); /* compute chunk later */
+ size_t chunk;
+
+ if (!available) {
+ /*
+ * all data has been used
+ * actual IO needs to be done
+ */
+ /*
+ * it seems that requests for less than dev->bulk_in_size
+ * are not accepted
+ */
+ rv = skel_do_read_io(dev, dev->bulk_in_size);
+ if (rv < 0)
+ goto exit;
+ else
+ goto retry;
+ }
+
+ /*
+ * data is available - chunk tells us how much shall be copied
+ */
+
+ /*
+ * Condition dev->bulk_in_copied > 0 maybe will never happen. In case,
+ * signal the event and copy using the original procedure, i.e., copy
+ * first two bytes also
+ */
+
+ if (dev->bulk_in_copied) {
+ chunk = min(available, count);
+ memcpy(buffer, dev->bulk_in_buffer + dev->bulk_in_copied, chunk);
+ rv = chunk;
+ dev->bulk_in_copied += chunk;
+
+ /* copy discarding first two bytes that contain ftdi chip status */
+
+ } else {
+ /* account for two bytes to be discarded */
+ chunk = min(available, count + 2);
+ if (chunk < 2) {
+ dev_err(&dev->udev->dev, "BAD READ - chunk: %zu\n", chunk);
+ rv = -EIO;
+ goto exit;
+ }
+
+ memcpy(buffer, dev->bulk_in_buffer + 2, chunk - 2);
+ rv = chunk;
+ dev->bulk_in_copied += chunk;
+ }
+
+ /*
+ * if we are asked for more than we have,
+ * we start IO but don't wait
+ *
+ * No, no read ahead allowed; if the case, more data will be
+ * asked for by the lpvo_usb_gpib layer.
+ */
+// if (available < count)
+// skel_do_read_io(dev, dev->bulk_in_size);
+ } else {
+ /* no data in the buffer */
+ rv = skel_do_read_io(dev, dev->bulk_in_size);
+ if (rv < 0)
+ goto exit;
+ else
+ goto retry;
+ }
+exit:
+ mutex_unlock(&dev->io_mutex);
+ if (rv == 2)
+ goto restart; /* ftdi chip returns two status bytes after a latency anyhow */
+
+ if (rv > 0)
+ return rv - 2; /* account for 2 discarded bytes in a valid buffer */
+ return rv;
+}
+
+/*
+ * write functions
+ */
+
+static void skel_write_bulk_callback(struct urb *urb)
+{
+ struct usb_skel *dev;
+ unsigned long flags;
+
+ dev = urb->context;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN))
+ dev_err(&dev->interface->dev,
+ "nonzero write bulk status received: %d\n", urb->status);
+
+ spin_lock_irqsave(&dev->err_lock, flags);
+ dev->errors = urb->status;
+ spin_unlock_irqrestore(&dev->err_lock, flags);
+ }
+
+ /* free up our allocated buffer */
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ up(&dev->limit_sem);
+}
+
+/*
+ * skel_do_write() - write operations from lpvo_usb_gpib
+ */
+
+static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count)
+{
+ int retval = 0;
+ struct urb *urb = NULL;
+ char *buf = NULL;
+ size_t writesize = min_t(size_t, count, (size_t)MAX_TRANSFER);
+
+ /* verify that we actually have some data to write */
+ if (count == 0)
+ goto exit;
+
+ /*
+ * limit the number of URBs in flight to stop a user from using up all
+ * RAM
+ */
+ /* Only one URB is used, because we can't have a pending write() and go on */
+
+// if (!(file->f_flags & O_NONBLOCK)) { /* no NONBLOCK provided */
+ if (down_interruptible(&dev->limit_sem)) {
+ retval = -ERESTARTSYS;
+ goto exit;
+ }
+// } else {
+// if (down_trylock(&dev->limit_sem)) {
+// retval = -EAGAIN;
+// goto exit;
+// }
+// }
+
+ spin_lock_irq(&dev->err_lock);
+ retval = dev->errors;
+ if (retval < 0) {
+ /* any error is reported once */
+ dev->errors = 0;
+ /* to preserve notifications about reset */
+ retval = (retval == -EPIPE) ? retval : -EIO;
+ }
+ spin_unlock_irq(&dev->err_lock);
+ if (retval < 0)
+ goto error;
+
+ /* create a urb, and a buffer for it, and copy the data to the urb */
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(buf, buffer, count);
+
+ /* this lock makes sure we don't submit URBs to gone devices */
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ mutex_unlock(&dev->io_mutex);
+ retval = -ENODEV;
+ goto error;
+ }
+
+ /* initialize the urb properly */
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr),
+ buf, writesize, skel_write_bulk_callback, dev);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &dev->submitted);
+
+ /* send the data out the bulk port */
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ mutex_unlock(&dev->io_mutex);
+ if (retval) {
+ dev_err(&dev->interface->dev, "failed submitting write urb, error %d\n", retval);
+ goto error_unanchor;
+ }
+
+ /*
+ * release our reference to this urb, the USB core will eventually free
+ * it entirely
+ */
+ usb_free_urb(urb);
+
+ return writesize;
+
+error_unanchor:
+ usb_unanchor_urb(urb);
+error:
+ if (urb) {
+ usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+ up(&dev->limit_sem);
+
+exit:
+ return retval;
+}
+
+/*
+ * services for the user space devices
+ */
+
+#if USER_DEVICE /* conditional compilation of user space device */
+
+static int skel_flush(struct file *file, fl_owner_t id)
+{
+ struct usb_skel *dev;
+ int res;
+
+ dev = file->private_data;
+ if (!dev)
+ return -ENODEV;
+
+ /* wait for io to stop */
+ mutex_lock(&dev->io_mutex);
+ skel_draw_down(dev);
+
+ /* read out errors, leave subsequent opens a clean slate */
+ spin_lock_irq(&dev->err_lock);
+ res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
+ dev->errors = 0;
+ spin_unlock_irq(&dev->err_lock);
+
+ mutex_unlock(&dev->io_mutex);
+
+ return res;
+}
+
+static int skel_open(struct inode *inode, struct file *file)
+{
+ struct usb_skel *dev;
+ struct usb_interface *interface;
+ int subminor;
+ int retval = 0;
+
+ subminor = iminor(inode);
+
+ interface = usb_find_interface(&skel_driver, subminor);
+ if (!interface) {
+ pr_err("can't find device for minor %d\n", subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ dev = usb_get_intfdata(interface);
+ if (!dev) {
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ retval = usb_autopm_get_interface(interface);
+ if (retval)
+ goto exit;
+
+ /* increment our usage count for the device */
+ kref_get(&dev->kref);
+
+ /* save our object in the file's private structure */
+ file->private_data = dev;
+
+exit:
+ return retval;
+}
+
+static int skel_release(struct inode *inode, struct file *file)
+{
+ struct usb_skel *dev;
+
+ dev = file->private_data;
+ if (!dev)
+ return -ENODEV;
+
+ /* allow the device to be autosuspended */
+ mutex_lock(&dev->io_mutex);
+ if (dev->interface)
+ usb_autopm_put_interface(dev->interface);
+ mutex_unlock(&dev->io_mutex);
+
+ /* decrement the count on our device */
+ kref_put(&dev->kref, skel_delete);
+ return 0;
+}
+
+/*
+ * user space access to read function
+ */
+
+static ssize_t skel_read(struct file *file, char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct usb_skel *dev;
+ char *buf;
+ ssize_t rv;
+
+ dev = file->private_data;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rv = skel_do_read(dev, buf, count);
+
+ if (rv > 0) {
+ if (copy_to_user(buffer, buf, rv)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ }
+ kfree(buf);
+ return rv;
+}
+
+/*
+ * user space access to write function
+ */
+
+static ssize_t skel_write(struct file *file, const char __user *user_buffer,
+ size_t count, loff_t *ppos)
+{
+ struct usb_skel *dev;
+ char *buf;
+ ssize_t rv;
+
+ dev = file->private_data;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, user_buffer, count)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ rv = skel_do_write(dev, buf, count);
+ kfree(buf);
+ return rv;
+}
+#endif
+
+static const struct file_operations skel_fops = {
+ .owner = THIS_MODULE,
+#if USER_DEVICE
+ .read = skel_read,
+ .write = skel_write,
+ .open = skel_open,
+ .release = skel_release,
+ .flush = skel_flush,
+ .llseek = noop_llseek,
+#endif
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+#if USER_DEVICE
+static struct usb_class_driver skel_class = {
+ .name = "lpvo_raw%d",
+ .fops = &skel_fops,
+ .minor_base = USB_SKEL_MINOR_BASE,
+};
+#endif
+
+static int skel_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_skel *dev;
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ int retval;
+ char *device_path;
+
+ mutex_init(&minors_lock); /* required for handling minor numbers table */
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kref_init(&dev->kref);
+ sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+ mutex_init(&dev->io_mutex);
+ spin_lock_init(&dev->err_lock);
+ init_usb_anchor(&dev->submitted);
+ init_waitqueue_head(&dev->bulk_in_wait);
+
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ dev->interface = interface;
+
+ /* set up the endpoint information */
+ /* use only the first bulk-in and bulk-out endpoints */
+ retval = usb_find_common_endpoints(interface->cur_altsetting,
+ &bulk_in, &bulk_out, NULL, NULL);
+ if (retval) {
+ dev_err(&interface->dev,
+ "Could not find both bulk-in and bulk-out endpoints\n");
+ goto error;
+ }
+
+ dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
+ dev->bulk_in_endpoint_addr = bulk_in->bEndpointAddress;
+ dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
+ if (!dev->bulk_in_buffer) {
+ retval = -ENOMEM;
+ goto error;
+ }
+ dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->bulk_in_urb) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev->bulk_out_endpoint_addr = bulk_out->bEndpointAddress;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ /* let the world know */
+
+ device_path = kobject_get_path(&dev->udev->dev.kobj, GFP_KERNEL);
+ dev_dbg(&interface->dev, "New lpvo_usb_device -> bus: %d dev: %d path: %s\n",
+ dev->udev->bus->busnum, dev->udev->devnum, device_path);
+ kfree(device_path);
+
+#if USER_DEVICE
+ /* we can register the device now, as it is ready */
+ retval = usb_register_dev(interface, &skel_class);
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(&interface->dev,
+ "Not able to get a minor for this device.\n");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+#endif
+
+ write_latency_timer(dev->udev); /* adjust the latency timer */
+
+ usb_gpib_init_module(interface); /* last, init the lpvo for this minor */
+
+ return 0;
+
+error:
+ /* this frees allocated memory */
+ kref_put(&dev->kref, skel_delete);
+
+ return retval;
+}
+
+static void skel_disconnect(struct usb_interface *interface)
+{
+ struct usb_skel *dev;
+ int minor = interface->minor;
+
+ usb_gpib_exit_module(minor); /* first, disactivate the lpvo */
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+#if USER_DEVICE
+ /* give back our minor */
+ usb_deregister_dev(interface, &skel_class);
+#endif
+
+ /* prevent more I/O from starting */
+ mutex_lock(&dev->io_mutex);
+ dev->interface = NULL;
+ mutex_unlock(&dev->io_mutex);
+
+ usb_kill_anchored_urbs(&dev->submitted);
+
+ /* decrement our usage count */
+ kref_put(&dev->kref, skel_delete);
+}
+
+static void skel_draw_down(struct usb_skel *dev)
+{
+ int time;
+
+ time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
+ if (!time)
+ usb_kill_anchored_urbs(&dev->submitted);
+ usb_kill_urb(dev->bulk_in_urb);
+}
+
+static int skel_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_skel *dev = usb_get_intfdata(intf);
+
+ if (!dev)
+ return 0;
+ skel_draw_down(dev);
+ return 0;
+}
+
+static int skel_resume(struct usb_interface *intf)
+{
+ return 0;
+}
+
+static int skel_pre_reset(struct usb_interface *intf)
+{
+ struct usb_skel *dev = usb_get_intfdata(intf);
+
+ mutex_lock(&dev->io_mutex);
+ skel_draw_down(dev);
+
+ return 0;
+}
+
+static int skel_post_reset(struct usb_interface *intf)
+{
+ struct usb_skel *dev = usb_get_intfdata(intf);
+
+ /* we are sure no URBs are active - no locking needed */
+ dev->errors = -EPIPE;
+ mutex_unlock(&dev->io_mutex);
+
+ return 0;
+}
+
+static struct usb_driver skel_driver = {
+ .name = NAME,
+ .probe = skel_probe,
+ .disconnect = skel_disconnect,
+ .suspend = skel_suspend,
+ .resume = skel_resume,
+ .pre_reset = skel_pre_reset,
+ .post_reset = skel_post_reset,
+ .id_table = skel_table,
+ .supports_autosuspend = 1,
+};
+
+module_usb_driver(skel_driver);
diff --git a/drivers/gpib/nec7210/Makefile b/drivers/gpib/nec7210/Makefile
new file mode 100644
index 000000000000..64330f2e89d1
--- /dev/null
+++ b/drivers/gpib/nec7210/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_GPIB_NEC7210) += nec7210.o
+
+
diff --git a/drivers/gpib/nec7210/board.h b/drivers/gpib/nec7210/board.h
new file mode 100644
index 000000000000..ac3fe38ade57
--- /dev/null
+++ b/drivers/gpib/nec7210/board.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _GPIB_PCIIA_BOARD_H
+#define _GPIB_PCIIA_BOARD_H
+
+#include "gpibP.h"
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include "nec7210.h"
+
+#endif //_GPIB_PCIIA_BOARD_H
+
diff --git a/drivers/gpib/nec7210/nec7210.c b/drivers/gpib/nec7210/nec7210.c
new file mode 100644
index 000000000000..bbf39367f5e4
--- /dev/null
+++ b/drivers/gpib/nec7210/nec7210.c
@@ -0,0 +1,1121 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "board.h"
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <linux/bitops.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB library code for NEC uPD7210");
+
+int nec7210_enable_eos(struct gpib_board *board, struct nec7210_priv *priv, u8 eos_byte,
+ int compare_8_bits)
+{
+ write_byte(priv, eos_byte, EOSR);
+ priv->auxa_bits |= HR_REOS;
+ if (compare_8_bits)
+ priv->auxa_bits |= HR_BIN;
+ else
+ priv->auxa_bits &= ~HR_BIN;
+ write_byte(priv, priv->auxa_bits, AUXMR);
+ return 0;
+}
+EXPORT_SYMBOL(nec7210_enable_eos);
+
+void nec7210_disable_eos(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ priv->auxa_bits &= ~HR_REOS;
+ write_byte(priv, priv->auxa_bits, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_disable_eos);
+
+int nec7210_parallel_poll(struct gpib_board *board, struct nec7210_priv *priv, u8 *result)
+{
+ int ret;
+
+ clear_bit(COMMAND_READY_BN, &priv->state);
+
+ // execute parallel poll
+ write_byte(priv, AUX_EPP, AUXMR);
+ // wait for result FIXME: support timeouts
+ ret = wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state));
+ if (ret) {
+ dev_dbg(board->gpib_dev, "gpib: parallel poll interrupted\n");
+ return -ERESTARTSYS;
+ }
+ *result = read_byte(priv, CPTR);
+
+ return 0;
+}
+EXPORT_SYMBOL(nec7210_parallel_poll);
+
+void nec7210_parallel_poll_configure(struct gpib_board *board,
+ struct nec7210_priv *priv, unsigned int configuration)
+{
+ write_byte(priv, PPR | configuration, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_parallel_poll_configure);
+
+void nec7210_parallel_poll_response(struct gpib_board *board, struct nec7210_priv *priv, int ist)
+{
+ if (ist)
+ write_byte(priv, AUX_SPPF, AUXMR);
+ else
+ write_byte(priv, AUX_CPPF, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_parallel_poll_response);
+/*
+ * This is really only adequate for chips that do a 488.2 style reqt/reqf
+ * based on bit 6 of the SPMR (see chapter 11.3.3 of 488.2). For simpler chips that simply
+ * set rsv directly based on bit 6, we either need to do more hardware setup to expose
+ * the 488.2 capability (for example with NI chips), or we need to implement the
+ * 488.2 set srv state machine in the driver (if that is even viable).
+ */
+void nec7210_serial_poll_response(struct gpib_board *board,
+ struct nec7210_priv *priv, u8 status)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (status & request_service_bit) {
+ priv->srq_pending = 1;
+ clear_bit(SPOLL_NUM, &board->status);
+
+ } else {
+ priv->srq_pending = 0;
+ }
+ write_byte(priv, status, SPMR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+EXPORT_SYMBOL(nec7210_serial_poll_response);
+
+u8 nec7210_serial_poll_status(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ return read_byte(priv, SPSR);
+}
+EXPORT_SYMBOL(nec7210_serial_poll_status);
+
+int nec7210_primary_address(const struct gpib_board *board, struct nec7210_priv *priv,
+ unsigned int address)
+{
+ // put primary address in address0
+ write_byte(priv, address & ADDRESS_MASK, ADR);
+ return 0;
+}
+EXPORT_SYMBOL(nec7210_primary_address);
+
+int nec7210_secondary_address(const struct gpib_board *board, struct nec7210_priv *priv,
+ unsigned int address, int enable)
+{
+ if (enable) {
+ // put secondary address in address1
+ write_byte(priv, HR_ARS | (address & ADDRESS_MASK), ADR);
+ // go to address mode 2
+ priv->reg_bits[ADMR] &= ~HR_ADM0;
+ priv->reg_bits[ADMR] |= HR_ADM1;
+ } else {
+ // disable address1 register
+ write_byte(priv, HR_ARS | HR_DT | HR_DL, ADR);
+ // go to address mode 1
+ priv->reg_bits[ADMR] |= HR_ADM0;
+ priv->reg_bits[ADMR] &= ~HR_ADM1;
+ }
+ write_byte(priv, priv->reg_bits[ADMR], ADMR);
+ return 0;
+}
+EXPORT_SYMBOL(nec7210_secondary_address);
+
+static void update_talker_state(struct nec7210_priv *priv, unsigned int address_status_bits)
+{
+ if ((address_status_bits & HR_TA)) {
+ if ((address_status_bits & HR_NATN)) {
+ if (address_status_bits & HR_SPMS)
+ priv->talker_state = serial_poll_active;
+ else
+ priv->talker_state = talker_active;
+ } else {
+ priv->talker_state = talker_addressed;
+ }
+ } else {
+ priv->talker_state = talker_idle;
+ }
+}
+
+static void update_listener_state(struct nec7210_priv *priv, unsigned int address_status_bits)
+{
+ if (address_status_bits & HR_LA) {
+ if ((address_status_bits & HR_NATN))
+ priv->listener_state = listener_active;
+ else
+ priv->listener_state = listener_addressed;
+ } else {
+ priv->listener_state = listener_idle;
+ }
+}
+
+unsigned int nec7210_update_status_nolock(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ int address_status_bits;
+ u8 spoll_status;
+
+ if (!priv)
+ return 0;
+
+ address_status_bits = read_byte(priv, ADSR);
+ if (address_status_bits & HR_CIC)
+ set_bit(CIC_NUM, &board->status);
+ else
+ clear_bit(CIC_NUM, &board->status);
+ // check for talker/listener addressed
+ update_talker_state(priv, address_status_bits);
+ if (priv->talker_state == talker_active || priv->talker_state == talker_addressed)
+ set_bit(TACS_NUM, &board->status);
+ else
+ clear_bit(TACS_NUM, &board->status);
+ update_listener_state(priv, address_status_bits);
+ if (priv->listener_state == listener_active ||
+ priv->listener_state == listener_addressed)
+ set_bit(LACS_NUM, &board->status);
+ else
+ clear_bit(LACS_NUM, &board->status);
+ if (address_status_bits & HR_NATN)
+ clear_bit(ATN_NUM, &board->status);
+ else
+ set_bit(ATN_NUM, &board->status);
+ spoll_status = nec7210_serial_poll_status(board, priv);
+ if (priv->srq_pending && (spoll_status & request_service_bit) == 0) {
+ priv->srq_pending = 0;
+ set_bit(SPOLL_NUM, &board->status);
+ }
+
+ /*
+ * we rely on the interrupt handler to set the
+ * rest of the status bits
+ */
+
+ return board->status;
+}
+EXPORT_SYMBOL(nec7210_update_status_nolock);
+
+unsigned int nec7210_update_status(struct gpib_board *board, struct nec7210_priv *priv,
+ unsigned int clear_mask)
+{
+ unsigned long flags;
+ unsigned int retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ board->status &= ~clear_mask;
+ retval = nec7210_update_status_nolock(board, priv);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_update_status);
+
+unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg,
+ unsigned int mask, unsigned int bits)
+{
+ priv->reg_bits[reg] &= ~mask;
+ priv->reg_bits[reg] |= mask & bits;
+ write_byte(priv, priv->reg_bits[reg], reg);
+ return priv->reg_bits[reg];
+}
+EXPORT_SYMBOL(nec7210_set_reg_bits);
+
+void nec7210_set_handshake_mode(struct gpib_board *board, struct nec7210_priv *priv, int mode)
+{
+ unsigned long flags;
+
+ mode &= HR_HANDSHAKE_MASK;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ if ((priv->auxa_bits & HR_HANDSHAKE_MASK) != mode) {
+ priv->auxa_bits &= ~HR_HANDSHAKE_MASK;
+ priv->auxa_bits |= mode;
+ write_byte(priv, priv->auxa_bits, AUXMR);
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+EXPORT_SYMBOL(nec7210_set_handshake_mode);
+
+u8 nec7210_read_data_in(struct gpib_board *board, struct nec7210_priv *priv, int *end)
+{
+ unsigned long flags;
+ u8 data;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ data = read_byte(priv, DIR);
+ clear_bit(READ_READY_BN, &priv->state);
+ if (test_and_clear_bit(RECEIVED_END_BN, &priv->state))
+ *end = 1;
+ else
+ *end = 0;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return data;
+}
+EXPORT_SYMBOL(nec7210_read_data_in);
+
+int nec7210_take_control(struct gpib_board *board, struct nec7210_priv *priv, int syncronous)
+{
+ int i;
+ const int timeout = 100;
+ int retval = 0;
+ unsigned int adsr_bits = 0;
+
+ if (syncronous)
+ write_byte(priv, AUX_TCS, AUXMR);
+ else
+ write_byte(priv, AUX_TCA, AUXMR);
+ // busy wait until ATN is asserted
+ for (i = 0; i < timeout; i++) {
+ adsr_bits = read_byte(priv, ADSR);
+ if ((adsr_bits & HR_NATN) == 0)
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+
+ clear_bit(WRITE_READY_BN, &priv->state);
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_take_control);
+
+int nec7210_go_to_standby(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ int i;
+ const int timeout = 1000;
+ unsigned int adsr_bits = 0;
+ int retval = 0;
+
+ write_byte(priv, AUX_GTS, AUXMR);
+ // busy wait until ATN is released
+ for (i = 0; i < timeout; i++) {
+ adsr_bits = read_byte(priv, ADSR);
+ if (adsr_bits & HR_NATN)
+ break;
+ udelay(1);
+ }
+ // if busy wait has failed, try sleeping
+ if (i == timeout) {
+ for (i = 0; i < HZ; i++) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout(1))
+ return -ERESTARTSYS;
+ adsr_bits = read_byte(priv, ADSR);
+ if (adsr_bits & HR_NATN)
+ break;
+ }
+ if (i == HZ)
+ return -ETIMEDOUT;
+ }
+
+ clear_bit(COMMAND_READY_BN, &priv->state);
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_go_to_standby);
+
+int nec7210_request_system_control(struct gpib_board *board, struct nec7210_priv *priv,
+ int request_control)
+{
+ if (request_control == 0) {
+ write_byte(priv, AUX_CREN, AUXMR);
+ write_byte(priv, AUX_CIFC, AUXMR);
+ write_byte(priv, AUX_DSC, AUXMR);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(nec7210_request_system_control);
+
+void nec7210_interface_clear(struct gpib_board *board, struct nec7210_priv *priv, int assert)
+{
+ if (assert)
+ write_byte(priv, AUX_SIFC, AUXMR);
+ else
+ write_byte(priv, AUX_CIFC, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_interface_clear);
+
+void nec7210_remote_enable(struct gpib_board *board, struct nec7210_priv *priv, int enable)
+{
+ if (enable)
+ write_byte(priv, AUX_SREN, AUXMR);
+ else
+ write_byte(priv, AUX_CREN, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_remote_enable);
+
+void nec7210_release_rfd_holdoff(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (test_bit(RFD_HOLDOFF_BN, &priv->state) &&
+ test_bit(READ_READY_BN, &priv->state) == 0) {
+ write_byte(priv, AUX_FH, AUXMR);
+ clear_bit(RFD_HOLDOFF_BN, &priv->state);
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+EXPORT_SYMBOL(nec7210_release_rfd_holdoff);
+
+int nec7210_t1_delay(struct gpib_board *board, struct nec7210_priv *priv,
+ unsigned int nano_sec)
+{
+ unsigned int retval;
+
+ if (nano_sec <= 500) {
+ priv->auxb_bits |= HR_TRI;
+ retval = 500;
+ } else {
+ priv->auxb_bits &= ~HR_TRI;
+ retval = 2000;
+ }
+ write_byte(priv, priv->auxb_bits, AUXMR);
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_t1_delay);
+
+void nec7210_return_to_local(const struct gpib_board *board, struct nec7210_priv *priv)
+{
+ write_byte(priv, AUX_RTL, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_return_to_local);
+
+static inline short nec7210_atn_has_changed(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ short address_status_bits = read_byte(priv, ADSR);
+
+ if (address_status_bits & HR_NATN) {
+ if (test_bit(ATN_NUM, &board->status))
+ return 1;
+ else
+ return 0;
+ } else {
+ if (test_bit(ATN_NUM, &board->status))
+ return 0;
+ else
+ return 1;
+ }
+ return -1;
+}
+
+int nec7210_command(struct gpib_board *board, struct nec7210_priv *priv, u8
+ *buffer, size_t length, size_t *bytes_written)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ *bytes_written = 0;
+
+ clear_bit(BUS_ERROR_BN, &priv->state);
+
+ while (*bytes_written < length) {
+ if (wait_event_interruptible(board->wait,
+ test_bit(COMMAND_READY_BN, &priv->state) ||
+ test_bit(BUS_ERROR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ dev_dbg(board->gpib_dev, "command wait interrupted\n");
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ break;
+ if (test_and_clear_bit(BUS_ERROR_BN, &priv->state))
+ break;
+ spin_lock_irqsave(&board->spinlock, flags);
+ clear_bit(COMMAND_READY_BN, &priv->state);
+ write_byte(priv, buffer[*bytes_written], CDOR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ ++(*bytes_written);
+
+ if (need_resched())
+ schedule();
+ }
+ // wait for last byte to get sent
+ if (wait_event_interruptible(board->wait, test_bit(COMMAND_READY_BN, &priv->state) ||
+ test_bit(BUS_ERROR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+
+ if (test_and_clear_bit(BUS_ERROR_BN, &priv->state))
+ retval = -EIO;
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_command);
+
+static int pio_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval = 0;
+
+ *bytes_read = 0;
+ *end = 0;
+
+ while (*bytes_read < length) {
+ if (wait_event_interruptible(board->wait,
+ test_bit(READ_READY_BN, &priv->state) ||
+ test_bit(DEV_CLEAR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (test_bit(READ_READY_BN, &priv->state)) {
+ if (*bytes_read == 0) {
+ /*
+ * We set the handshake mode here because we know
+ * no new bytes will arrive (it has already arrived
+ * and is awaiting being read out of the chip) while we are changing
+ * modes. This ensures we can reliably keep track
+ * of the holdoff state.
+ */
+ nec7210_set_handshake_mode(board, priv, HR_HLDA);
+ }
+ buffer[(*bytes_read)++] = nec7210_read_data_in(board, priv, end);
+ if (*end)
+ break;
+ }
+ if (test_bit(TIMO_NUM, &board->status)) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ if (test_bit(DEV_CLEAR_BN, &priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+
+ if (*bytes_read < length)
+ nec7210_release_rfd_holdoff(board, priv);
+
+ if (need_resched())
+ schedule();
+ }
+ return retval;
+}
+
+#ifdef NEC_DMA
+static ssize_t __dma_read(struct gpib_board *board, struct nec7210_priv *priv, size_t length)
+{
+ ssize_t retval = 0;
+ size_t count = 0;
+ unsigned long flags, dma_irq_flags;
+
+ if (length == 0)
+ return 0;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ dma_irq_flags = claim_dma_lock();
+ disable_dma(priv->dma_channel);
+ /* program dma controller */
+ clear_dma_ff(priv->dma_channel);
+ set_dma_count(priv->dma_channel, length);
+ set_dma_addr(priv->dma_channel, priv->dma_buffer_addr);
+ set_dma_mode(priv->dma_channel, DMA_MODE_READ);
+ release_dma_lock(dma_irq_flags);
+
+ enable_dma(priv->dma_channel);
+
+ set_bit(DMA_READ_IN_PROGRESS_BN, &priv->state);
+ clear_bit(READ_READY_BN, &priv->state);
+
+ // enable nec7210 dma
+ nec7210_set_reg_bits(priv, IMR2, HR_DMAI, HR_DMAI);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ // wait for data to transfer
+ if (wait_event_interruptible(board->wait,
+ test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state) == 0 ||
+ test_bit(DEV_CLEAR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &priv->state))
+ retval = -EINTR;
+
+ // disable nec7210 dma
+ nec7210_set_reg_bits(priv, IMR2, HR_DMAI, 0);
+
+ // record how many bytes we transferred
+ flags = claim_dma_lock();
+ clear_dma_ff(priv->dma_channel);
+ disable_dma(priv->dma_channel);
+ count += length - get_dma_residue(priv->dma_channel);
+ release_dma_lock(flags);
+
+ return retval ? retval : count;
+}
+
+static ssize_t dma_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length)
+{
+ size_t remain = length;
+ size_t transfer_size;
+ ssize_t retval = 0;
+
+ while (remain > 0) {
+ transfer_size = (priv->dma_buffer_length < remain) ?
+ priv->dma_buffer_length : remain;
+ retval = __dma_read(board, priv, transfer_size);
+ if (retval < 0)
+ break;
+ memcpy(buffer, priv->dma_buffer, transfer_size);
+ remain -= retval;
+ buffer += retval;
+ if (test_bit(RECEIVED_END_BN, &priv->state))
+ break;
+ }
+
+ if (retval < 0)
+ return retval;
+
+ return length - remain;
+}
+#endif
+
+int nec7210_read(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval = 0;
+
+ *end = 0;
+ *bytes_read = 0;
+
+ if (length == 0)
+ return 0;
+
+ clear_bit(DEV_CLEAR_BN, &priv->state); // XXX wrong
+
+ nec7210_release_rfd_holdoff(board, priv);
+
+ retval = pio_read(board, priv, buffer, length, end, bytes_read);
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_read);
+
+static int pio_write_wait(struct gpib_board *board, struct nec7210_priv *priv,
+ short wake_on_lacs, short wake_on_atn, short wake_on_bus_error)
+{
+ // wait until byte is ready to be sent
+ if (wait_event_interruptible(board->wait,
+ (test_bit(TACS_NUM, &board->status) &&
+ test_bit(WRITE_READY_BN, &priv->state)) ||
+ test_bit(DEV_CLEAR_BN, &priv->state) ||
+ (wake_on_bus_error && test_bit(BUS_ERROR_BN, &priv->state)) ||
+ (wake_on_lacs && test_bit(LACS_NUM, &board->status)) ||
+ (wake_on_atn && test_bit(ATN_NUM, &board->status)) ||
+ test_bit(TIMO_NUM, &board->status)))
+ return -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+
+ if (test_bit(DEV_CLEAR_BN, &priv->state))
+ return -EINTR;
+
+ if (wake_on_bus_error && test_and_clear_bit(BUS_ERROR_BN, &priv->state))
+ return -EIO;
+
+ return 0;
+}
+
+static int pio_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ size_t last_count = 0;
+ ssize_t retval = 0;
+ unsigned long flags;
+ const int max_bus_errors = (length > 1000) ? length : 1000;
+ int bus_error_count = 0;
+ *bytes_written = 0;
+
+ clear_bit(BUS_ERROR_BN, &priv->state);
+
+ while (*bytes_written < length) {
+ if (need_resched())
+ schedule();
+
+ retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210);
+ if (retval == -EIO) {
+ /* resend last byte on bus error */
+ *bytes_written = last_count;
+ /*
+ * we can get unrecoverable bus errors,
+ * so give up after a while
+ */
+ bus_error_count++;
+ if (bus_error_count > max_bus_errors)
+ return retval;
+ continue;
+ } else {
+ if (retval < 0)
+ return retval;
+ }
+ spin_lock_irqsave(&board->spinlock, flags);
+ clear_bit(BUS_ERROR_BN, &priv->state);
+ clear_bit(WRITE_READY_BN, &priv->state);
+ last_count = *bytes_written;
+ write_byte(priv, buffer[(*bytes_written)++], CDOR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ }
+ retval = pio_write_wait(board, priv, 1, 1, priv->type == NEC7210);
+ return retval;
+}
+
+#ifdef NEC_DMA
+static ssize_t __dma_write(struct gpib_board *board, struct nec7210_priv *priv, dma_addr_t address,
+ size_t length)
+{
+ unsigned long flags, dma_irq_flags;
+ int residue = 0;
+ int retval = 0;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ /* program dma controller */
+ dma_irq_flags = claim_dma_lock();
+ disable_dma(priv->dma_channel);
+ clear_dma_ff(priv->dma_channel);
+ set_dma_count(priv->dma_channel, length);
+ set_dma_addr(priv->dma_channel, address);
+ set_dma_mode(priv->dma_channel, DMA_MODE_WRITE);
+ enable_dma(priv->dma_channel);
+ release_dma_lock(dma_irq_flags);
+
+ // enable board's dma for output
+ nec7210_set_reg_bits(priv, IMR2, HR_DMAO, HR_DMAO);
+
+ clear_bit(WRITE_READY_BN, &priv->state);
+ set_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state);
+
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ // suspend until message is sent
+ if (wait_event_interruptible(board->wait,
+ test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0 ||
+ test_bit(BUS_ERROR_BN, &priv->state) ||
+ test_bit(DEV_CLEAR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_and_clear_bit(DEV_CLEAR_BN, &priv->state))
+ retval = -EINTR;
+ if (test_and_clear_bit(BUS_ERROR_BN, &priv->state))
+ retval = -EIO;
+
+ // disable board's dma
+ nec7210_set_reg_bits(priv, IMR2, HR_DMAO, 0);
+
+ dma_irq_flags = claim_dma_lock();
+ clear_dma_ff(priv->dma_channel);
+ disable_dma(priv->dma_channel);
+ residue = get_dma_residue(priv->dma_channel);
+ release_dma_lock(dma_irq_flags);
+
+ if (residue)
+ retval = -EPIPE;
+
+ return retval ? retval : length;
+}
+
+static ssize_t dma_write(struct gpib_board *board, struct nec7210_priv *priv, u8 *buffer,
+ size_t length)
+{
+ size_t remain = length;
+ size_t transfer_size;
+ ssize_t retval = 0;
+
+ while (remain > 0) {
+ transfer_size = (priv->dma_buffer_length < remain) ?
+ priv->dma_buffer_length : remain;
+ memcpy(priv->dma_buffer, buffer, transfer_size);
+ retval = __dma_write(board, priv, priv->dma_buffer_addr, transfer_size);
+ if (retval < 0)
+ break;
+ remain -= retval;
+ buffer += retval;
+ }
+
+ if (retval < 0)
+ return retval;
+
+ return length - remain;
+}
+#endif
+int nec7210_write(struct gpib_board *board, struct nec7210_priv *priv,
+ u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ int retval = 0;
+
+ *bytes_written = 0;
+
+ clear_bit(DEV_CLEAR_BN, &priv->state); // XXX
+
+ if (send_eoi)
+ length-- ; // save the last byte for sending EOI
+
+ if (length > 0) {
+ // isa dma transfer
+ if (0 /*priv->dma_channel*/) {
+/*
+ * dma writes are unreliable since they can't recover from bus errors
+ * (which happen when ATN is asserted in the middle of a write)
+ */
+#ifdef NEC_DMA
+ retval = dma_write(board, priv, buffer, length);
+ if (retval < 0)
+ return retval;
+ count += retval;
+#endif
+ } else { // PIO transfer
+ size_t num_bytes;
+
+ retval = pio_write(board, priv, buffer, length, &num_bytes);
+
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ }
+ if (send_eoi) {
+ size_t num_bytes;
+
+ /*
+ * We need to wait to make sure we will immediately be able to write the data byte
+ * into the chip before sending the associated AUX_SEOI command. This is really
+ * only needed for length==1 since otherwise the earlier calls to pio_write
+ * will have dont the wait already.
+ */
+ retval = pio_write_wait(board, priv, 0, 0, priv->type == NEC7210);
+ if (retval < 0)
+ return retval;
+ /*send EOI */
+ write_byte(priv, AUX_SEOI, AUXMR);
+
+ retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_write);
+
+/*
+ * interrupt service routine
+ */
+irqreturn_t nec7210_interrupt(struct gpib_board *board, struct nec7210_priv *priv)
+{
+ int status1, status2;
+
+ // read interrupt status (also clears status)
+ status1 = read_byte(priv, ISR1);
+ status2 = read_byte(priv, ISR2);
+
+ return nec7210_interrupt_have_status(board, priv, status1, status2);
+}
+EXPORT_SYMBOL(nec7210_interrupt);
+
+irqreturn_t nec7210_interrupt_have_status(struct gpib_board *board,
+ struct nec7210_priv *priv, int status1, int status2)
+{
+#ifdef NEC_DMA
+ unsigned long dma_flags;
+#endif
+ int retval = IRQ_NONE;
+
+ // record service request in status
+ if (status2 & HR_SRQI)
+ set_bit(SRQI_NUM, &board->status);
+
+ // change in lockout status
+ if (status2 & HR_LOKC) {
+ if (status2 & HR_LOK)
+ set_bit(LOK_NUM, &board->status);
+ else
+ clear_bit(LOK_NUM, &board->status);
+ }
+
+ // change in remote status
+ if (status2 & HR_REMC) {
+ if (status2 & HR_REM)
+ set_bit(REM_NUM, &board->status);
+ else
+ clear_bit(REM_NUM, &board->status);
+ }
+
+ // record reception of END
+ if (status1 & HR_END) {
+ set_bit(RECEIVED_END_BN, &priv->state);
+ if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDE)
+ set_bit(RFD_HOLDOFF_BN, &priv->state);
+ }
+
+ // get incoming data in PIO mode
+ if ((status1 & HR_DI)) {
+ set_bit(READ_READY_BN, &priv->state);
+ if ((priv->auxa_bits & HR_HANDSHAKE_MASK) == HR_HLDA)
+ set_bit(RFD_HOLDOFF_BN, &priv->state);
+ }
+#ifdef NEC_DMA
+ // check for dma read transfer complete
+ if (test_bit(DMA_READ_IN_PROGRESS_BN, &priv->state)) {
+ dma_flags = claim_dma_lock();
+ disable_dma(priv->dma_channel);
+ clear_dma_ff(priv->dma_channel);
+ if ((status1 & HR_END) || get_dma_residue(priv->dma_channel) == 0)
+ clear_bit(DMA_READ_IN_PROGRESS_BN, &priv->state);
+ else
+ enable_dma(priv->dma_channel);
+ release_dma_lock(dma_flags);
+ }
+#endif
+ if ((status1 & HR_DO)) {
+ if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state) == 0)
+ set_bit(WRITE_READY_BN, &priv->state);
+#ifdef NEC_DMA
+ if (test_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state)) { // write data, isa dma mode
+ // check if dma transfer is complete
+ dma_flags = claim_dma_lock();
+ disable_dma(priv->dma_channel);
+ clear_dma_ff(priv->dma_channel);
+ if (get_dma_residue(priv->dma_channel) == 0) {
+ clear_bit(DMA_WRITE_IN_PROGRESS_BN, &priv->state);
+ // XXX race? byte may still be in CDOR reg
+ } else {
+ clear_bit(WRITE_READY_BN, &priv->state);
+ enable_dma(priv->dma_channel);
+ }
+ release_dma_lock(dma_flags);
+ }
+#endif
+ }
+
+ // outgoing command can be sent
+ if (status2 & HR_CO)
+ set_bit(COMMAND_READY_BN, &priv->state);
+
+ // command pass through received
+ if (status1 & HR_CPT)
+ write_byte(priv, AUX_NVAL, AUXMR);
+
+ if (status1 & HR_ERR)
+ set_bit(BUS_ERROR_BN, &priv->state);
+
+ if (status1 & HR_DEC) {
+ unsigned short address_status_bits = read_byte(priv, ADSR);
+
+ // ignore device clear events if we are controller in charge
+ if ((address_status_bits & HR_CIC) == 0) {
+ push_gpib_event(board, EVENT_DEV_CLR);
+ set_bit(DEV_CLEAR_BN, &priv->state);
+ }
+ }
+
+ if (status1 & HR_DET)
+ push_gpib_event(board, EVENT_DEV_TRG);
+
+ // Addressing status has changed
+ if (status2 & HR_ADSC)
+ set_bit(ADR_CHANGE_BN, &priv->state);
+
+ if ((status1 & priv->reg_bits[IMR1]) ||
+ (status2 & (priv->reg_bits[IMR2] & IMR2_ENABLE_INTR_MASK)) ||
+ nec7210_atn_has_changed(board, priv)) {
+ nec7210_update_status_nolock(board, priv);
+ dev_dbg(board->gpib_dev, "minor %i, stat %lx, isr1 0x%x, imr1 0x%x, isr2 0x%x, imr2 0x%x\n",
+ board->minor, board->status, status1, priv->reg_bits[IMR1], status2,
+ priv->reg_bits[IMR2]);
+ wake_up_interruptible(&board->wait); /* wake up sleeping process */
+ retval = IRQ_HANDLED;
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_interrupt_have_status);
+
+void nec7210_board_reset(struct nec7210_priv *priv, const struct gpib_board *board)
+{
+ /* 7210 chip reset */
+ write_byte(priv, AUX_CR, AUXMR);
+
+ /* disable all interrupts */
+ priv->reg_bits[IMR1] = 0;
+ write_byte(priv, priv->reg_bits[IMR1], IMR1);
+ priv->reg_bits[IMR2] = 0;
+ write_byte(priv, priv->reg_bits[IMR2], IMR2);
+ write_byte(priv, 0, SPMR);
+
+ /* clear registers by reading */
+ read_byte(priv, CPTR);
+ read_byte(priv, ISR1);
+ read_byte(priv, ISR2);
+
+ /* parallel poll unconfigure */
+ write_byte(priv, PPR | HR_PPU, AUXMR);
+
+ priv->reg_bits[ADMR] = HR_TRM0 | HR_TRM1;
+
+ priv->auxa_bits = AUXRA | HR_HLDA;
+ write_byte(priv, priv->auxa_bits, AUXMR);
+
+ write_byte(priv, AUXRE | 0, AUXMR);
+
+ /* set INT pin to active high, enable command pass through of unknown commands */
+ priv->auxb_bits = AUXRB | HR_CPTE;
+ write_byte(priv, priv->auxb_bits, AUXMR);
+ write_byte(priv, AUXRE, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_board_reset);
+
+void nec7210_board_online(struct nec7210_priv *priv, const struct gpib_board *board)
+{
+ /* set GPIB address */
+ nec7210_primary_address(board, priv, board->pad);
+ nec7210_secondary_address(board, priv, board->sad, board->sad >= 0);
+
+ /* enable interrupts */
+ priv->reg_bits[IMR1] = HR_ERRIE | HR_DECIE | HR_ENDIE |
+ HR_DETIE | HR_CPTIE | HR_DOIE | HR_DIIE;
+ priv->reg_bits[IMR2] = IMR2_ENABLE_INTR_MASK;
+ write_byte(priv, priv->reg_bits[IMR1], IMR1);
+ write_byte(priv, priv->reg_bits[IMR2], IMR2);
+
+ write_byte(priv, AUX_PON, AUXMR);
+}
+EXPORT_SYMBOL(nec7210_board_online);
+
+#ifdef CONFIG_HAS_IOPORT
+/* wrappers for io */
+u8 nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num)
+{
+ return inb(priv->iobase + register_num * priv->offset);
+}
+EXPORT_SYMBOL(nec7210_ioport_read_byte);
+
+void nec7210_ioport_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num)
+{
+ if (register_num == AUXMR)
+ /*
+ * locking makes absolutely sure noone accesses the
+ * AUXMR register faster than once per microsecond
+ */
+ nec7210_locking_ioport_write_byte(priv, data, register_num);
+ else
+ outb(data, priv->iobase + register_num * priv->offset);
+}
+EXPORT_SYMBOL(nec7210_ioport_write_byte);
+
+/* locking variants of io wrappers, for chips that page-in registers */
+u8 nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num)
+{
+ u8 retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->register_page_lock, flags);
+ retval = inb(priv->iobase + register_num * priv->offset);
+ spin_unlock_irqrestore(&priv->register_page_lock, flags);
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_locking_ioport_read_byte);
+
+void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, u8 data,
+ unsigned int register_num)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->register_page_lock, flags);
+ if (register_num == AUXMR)
+ udelay(1);
+ outb(data, priv->iobase + register_num * priv->offset);
+ spin_unlock_irqrestore(&priv->register_page_lock, flags);
+}
+EXPORT_SYMBOL(nec7210_locking_ioport_write_byte);
+#endif
+
+u8 nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num)
+{
+ return readb(priv->mmiobase + register_num * priv->offset);
+}
+EXPORT_SYMBOL(nec7210_iomem_read_byte);
+
+void nec7210_iomem_write_byte(struct nec7210_priv *priv, u8 data, unsigned int register_num)
+{
+ if (register_num == AUXMR)
+ /*
+ * locking makes absolutely sure noone accesses the
+ * AUXMR register faster than once per microsecond
+ */
+ nec7210_locking_iomem_write_byte(priv, data, register_num);
+ else
+ writeb(data, priv->mmiobase + register_num * priv->offset);
+}
+EXPORT_SYMBOL(nec7210_iomem_write_byte);
+
+u8 nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num)
+{
+ u8 retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->register_page_lock, flags);
+ retval = readb(priv->mmiobase + register_num * priv->offset);
+ spin_unlock_irqrestore(&priv->register_page_lock, flags);
+ return retval;
+}
+EXPORT_SYMBOL(nec7210_locking_iomem_read_byte);
+
+void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, u8 data,
+ unsigned int register_num)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->register_page_lock, flags);
+ if (register_num == AUXMR)
+ udelay(1);
+ writeb(data, priv->mmiobase + register_num * priv->offset);
+ spin_unlock_irqrestore(&priv->register_page_lock, flags);
+}
+EXPORT_SYMBOL(nec7210_locking_iomem_write_byte);
+
+static int __init nec7210_init_module(void)
+{
+ return 0;
+}
+
+static void __exit nec7210_exit_module(void)
+{
+}
+
+module_init(nec7210_init_module);
+module_exit(nec7210_exit_module);
diff --git a/drivers/gpib/ni_usb/Makefile b/drivers/gpib/ni_usb/Makefile
new file mode 100644
index 000000000000..469c5d16add3
--- /dev/null
+++ b/drivers/gpib/ni_usb/Makefile
@@ -0,0 +1,4 @@
+
+obj-$(CONFIG_GPIB_NI_USB) += ni_usb_gpib.o
+
+
diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.c b/drivers/gpib/ni_usb/ni_usb_gpib.c
new file mode 100644
index 000000000000..1f8412de9fa3
--- /dev/null
+++ b/drivers/gpib/ni_usb/ni_usb_gpib.c
@@ -0,0 +1,2678 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * driver for National Instruments usb to gpib adapters
+ * copyright : (C) 2004 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "ni_usb_gpib.h"
+#include "gpibP.h"
+#include "nec7210.h"
+#include "tnt4882_registers.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for National Instruments USB devices");
+
+#define MAX_NUM_NI_USB_INTERFACES 128
+static struct usb_interface *ni_usb_driver_interfaces[MAX_NUM_NI_USB_INTERFACES];
+
+static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status);
+static int ni_usb_set_interrupt_monitor(struct gpib_board *board, unsigned int monitored_bits);
+static void ni_usb_stop(struct ni_usb_priv *ni_priv);
+
+static DEFINE_MUTEX(ni_usb_hotplug_lock);
+
+// calculates a reasonable timeout in that can be passed to usb functions
+static inline unsigned long ni_usb_timeout_msecs(unsigned int usec)
+{
+ if (usec == 0)
+ return 0;
+ return 2000 + usec / 500;
+};
+
+// returns timeout code byte for use in ni-usb-b instructions
+static unsigned short ni_usb_timeout_code(unsigned int usec)
+{
+ if (usec == 0)
+ return 0xf0;
+ else if (usec <= 10)
+ return 0xf1;
+ else if (usec <= 30)
+ return 0xf2;
+ else if (usec <= 100)
+ return 0xf3;
+ else if (usec <= 300)
+ return 0xf4;
+ else if (usec <= 1000)
+ return 0xf5;
+ else if (usec <= 3000)
+ return 0xf6;
+ else if (usec <= 10000)
+ return 0xf7;
+ else if (usec <= 30000)
+ return 0xf8;
+ else if (usec <= 100000)
+ return 0xf9;
+ else if (usec <= 300000)
+ return 0xfa;
+ else if (usec <= 1000000)
+ return 0xfb;
+ else if (usec <= 3000000)
+ return 0xfc;
+ else if (usec <= 10000000)
+ return 0xfd;
+ else if (usec <= 30000000)
+ return 0xfe;
+ else if (usec <= 100000000)
+ return 0xff;
+ else if (usec <= 300000000)
+ return 0x01;
+ /*
+ * NI driver actually uses 0xff for timeout T1000s, which is a bug in their code.
+ * I've verified on a usb-b that a code of 0x2 is correct for a 1000 sec timeout
+ */
+ else if (usec <= 1000000000)
+ return 0x02;
+ pr_err("bug? usec is greater than 1e9\n");
+ return 0xf0;
+}
+
+static void ni_usb_bulk_complete(struct urb *urb)
+{
+ struct ni_usb_urb_ctx *context = urb->context;
+
+ complete(&context->complete);
+}
+
+static void ni_usb_timeout_handler(struct timer_list *t)
+{
+ struct ni_usb_priv *ni_priv = timer_container_of(ni_priv, t,
+ bulk_timer);
+ struct ni_usb_urb_ctx *context = &ni_priv->context;
+
+ context->timed_out = 1;
+ complete(&context->complete);
+};
+
+// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases
+static int ni_usb_nonblocking_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data,
+ int data_length, int *actual_data_length,
+ int timeout_msecs)
+{
+ struct usb_device *usb_dev;
+ int retval;
+ unsigned int out_pipe;
+ struct ni_usb_urb_ctx *context = &ni_priv->context;
+
+ *actual_data_length = 0;
+ mutex_lock(&ni_priv->bulk_transfer_lock);
+ if (!ni_priv->bus_interface) {
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return -ENODEV;
+ }
+ if (ni_priv->bulk_urb) {
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return -EAGAIN;
+ }
+ ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ni_priv->bulk_urb) {
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return -ENOMEM;
+ }
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_pipe = usb_sndbulkpipe(usb_dev, ni_priv->bulk_out_endpoint);
+ init_completion(&context->complete);
+ context->timed_out = 0;
+ usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, out_pipe, data, data_length,
+ &ni_usb_bulk_complete, context);
+
+ if (timeout_msecs)
+ mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs));
+
+ retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL);
+ if (retval) {
+ timer_delete_sync(&ni_priv->bulk_timer);
+ usb_free_urb(ni_priv->bulk_urb);
+ ni_priv->bulk_urb = NULL;
+ dev_err(&usb_dev->dev, "failed to submit bulk out urb, retval=%i\n",
+ retval);
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return retval;
+ }
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ wait_for_completion(&context->complete); // wait for ni_usb_bulk_complete
+ if (context->timed_out) {
+ usb_kill_urb(ni_priv->bulk_urb);
+ dev_err(&usb_dev->dev, "killed urb due to timeout\n");
+ retval = -ETIMEDOUT;
+ } else {
+ retval = ni_priv->bulk_urb->status;
+ }
+
+ timer_delete_sync(&ni_priv->bulk_timer);
+ *actual_data_length = ni_priv->bulk_urb->actual_length;
+ mutex_lock(&ni_priv->bulk_transfer_lock);
+ usb_free_urb(ni_priv->bulk_urb);
+ ni_priv->bulk_urb = NULL;
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return retval;
+}
+
+static int ni_usb_send_bulk_msg(struct ni_usb_priv *ni_priv, void *data, int data_length,
+ int *actual_data_length, int timeout_msecs)
+{
+ int retval;
+ int timeout_msecs_remaining = timeout_msecs;
+
+ retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length, actual_data_length,
+ timeout_msecs_remaining);
+ while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) {
+ usleep_range(1000, 1500);
+ retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, data, data_length,
+ actual_data_length,
+ timeout_msecs_remaining);
+ if (timeout_msecs != 0)
+ --timeout_msecs_remaining;
+ }
+ if (timeout_msecs != 0 && timeout_msecs_remaining <= 0)
+ return -ETIMEDOUT;
+ return retval;
+}
+
+// I'm using nonblocking loosely here, it only means -EAGAIN can be returned in certain cases
+static int ni_usb_nonblocking_receive_bulk_msg(struct ni_usb_priv *ni_priv,
+ void *data, int data_length,
+ int *actual_data_length, int timeout_msecs,
+ int interruptible)
+{
+ struct usb_device *usb_dev;
+ int retval;
+ unsigned int in_pipe;
+ struct ni_usb_urb_ctx *context = &ni_priv->context;
+
+ *actual_data_length = 0;
+ mutex_lock(&ni_priv->bulk_transfer_lock);
+ if (!ni_priv->bus_interface) {
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return -ENODEV;
+ }
+ if (ni_priv->bulk_urb) {
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return -EAGAIN;
+ }
+ ni_priv->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ni_priv->bulk_urb) {
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return -ENOMEM;
+ }
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ in_pipe = usb_rcvbulkpipe(usb_dev, ni_priv->bulk_in_endpoint);
+ init_completion(&context->complete);
+ context->timed_out = 0;
+ usb_fill_bulk_urb(ni_priv->bulk_urb, usb_dev, in_pipe, data, data_length,
+ &ni_usb_bulk_complete, context);
+
+ if (timeout_msecs)
+ mod_timer(&ni_priv->bulk_timer, jiffies + msecs_to_jiffies(timeout_msecs));
+
+ retval = usb_submit_urb(ni_priv->bulk_urb, GFP_KERNEL);
+ if (retval) {
+ timer_delete_sync(&ni_priv->bulk_timer);
+ usb_free_urb(ni_priv->bulk_urb);
+ ni_priv->bulk_urb = NULL;
+ dev_err(&usb_dev->dev, "failed to submit bulk in urb, retval=%i\n", retval);
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return retval;
+ }
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ if (interruptible) {
+ if (wait_for_completion_interruptible(&context->complete)) {
+ /*
+ * If we got interrupted by a signal while
+ * waiting for the usb gpib to respond, we
+ * should send a stop command so it will
+ * finish up with whatever it was doing and
+ * send its response now.
+ */
+ ni_usb_stop(ni_priv);
+ retval = -ERESTARTSYS;
+ /*
+ * now do an uninterruptible wait, it shouldn't take long
+ * for the board to respond now.
+ */
+ wait_for_completion(&context->complete);
+ }
+ } else {
+ wait_for_completion(&context->complete);
+ }
+ if (context->timed_out) {
+ usb_kill_urb(ni_priv->bulk_urb);
+ dev_err(&usb_dev->dev, "killed urb due to timeout\n");
+ retval = -ETIMEDOUT;
+ } else {
+ if (ni_priv->bulk_urb->status)
+ retval = ni_priv->bulk_urb->status;
+ }
+ timer_delete_sync(&ni_priv->bulk_timer);
+ *actual_data_length = ni_priv->bulk_urb->actual_length;
+ mutex_lock(&ni_priv->bulk_transfer_lock);
+ usb_free_urb(ni_priv->bulk_urb);
+ ni_priv->bulk_urb = NULL;
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ return retval;
+}
+
+static int ni_usb_receive_bulk_msg(struct ni_usb_priv *ni_priv, void *data,
+ int data_length, int *actual_data_length, int timeout_msecs,
+ int interruptible)
+{
+ int retval;
+ int timeout_msecs_remaining = timeout_msecs;
+
+ retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length,
+ actual_data_length, timeout_msecs_remaining,
+ interruptible);
+ while (retval == -EAGAIN && (timeout_msecs == 0 || timeout_msecs_remaining > 0)) {
+ usleep_range(1000, 1500);
+ retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, data, data_length,
+ actual_data_length,
+ timeout_msecs_remaining,
+ interruptible);
+ if (timeout_msecs != 0)
+ --timeout_msecs_remaining;
+ }
+ if (timeout_msecs && timeout_msecs_remaining <= 0)
+ return -ETIMEDOUT;
+ return retval;
+}
+
+static int ni_usb_receive_control_msg(struct ni_usb_priv *ni_priv, __u8 request,
+ __u8 requesttype, __u16 value, __u16 index,
+ void *data, __u16 size, int timeout_msecs)
+{
+ struct usb_device *usb_dev;
+ int retval;
+ unsigned int in_pipe;
+
+ mutex_lock(&ni_priv->control_transfer_lock);
+ if (!ni_priv->bus_interface) {
+ mutex_unlock(&ni_priv->control_transfer_lock);
+ return -ENODEV;
+ }
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ in_pipe = usb_rcvctrlpipe(usb_dev, 0);
+ retval = usb_control_msg(usb_dev, in_pipe, request, requesttype, value, index, data,
+ size, timeout_msecs);
+ mutex_unlock(&ni_priv->control_transfer_lock);
+ return retval;
+}
+
+static void ni_usb_soft_update_status(struct gpib_board *board, unsigned int ni_usb_ibsta,
+ unsigned int clear_mask)
+{
+ static const unsigned int ni_usb_ibsta_mask = SRQI | ATN | CIC | REM | LACS | TACS | LOK;
+
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ unsigned int need_monitoring_bits = ni_usb_ibsta_monitor_mask;
+ unsigned long flags;
+
+ board->status &= ~clear_mask;
+ board->status &= ~ni_usb_ibsta_mask;
+ board->status |= ni_usb_ibsta & ni_usb_ibsta_mask;
+ if (ni_usb_ibsta & DCAS)
+ push_gpib_event(board, EVENT_DEV_CLR);
+ if (ni_usb_ibsta & DTAS)
+ push_gpib_event(board, EVENT_DEV_TRG);
+
+ spin_lock_irqsave(&board->spinlock, flags);
+/* remove set status bits from monitored set why ?***/
+ ni_priv->monitored_ibsta_bits &= ~ni_usb_ibsta;
+ need_monitoring_bits &= ~ni_priv->monitored_ibsta_bits; /* mm - monitored set */
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ dev_dbg(&usb_dev->dev, "need_monitoring_bits=0x%x\n", need_monitoring_bits);
+
+ if (need_monitoring_bits & ~ni_usb_ibsta)
+ ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask);
+ else if (need_monitoring_bits & ni_usb_ibsta)
+ wake_up_interruptible(&board->wait);
+
+ dev_dbg(&usb_dev->dev, "ibsta=0x%x\n", ni_usb_ibsta);
+}
+
+static int ni_usb_parse_status_block(const u8 *buffer, struct ni_usb_status_block *status)
+{
+ u16 count;
+
+ status->id = buffer[0];
+ status->ibsta = (buffer[1] << 8) | buffer[2];
+ status->error_code = buffer[3];
+ count = buffer[4] | (buffer[5] << 8);
+ count = ~count;
+ count++;
+ status->count = count;
+ return 8;
+};
+
+static void ni_usb_dump_raw_block(const u8 *raw_data, int length)
+{
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 8, 1, raw_data, length, true);
+}
+
+static int ni_usb_parse_register_read_block(const u8 *raw_data, unsigned int *results,
+ int num_results)
+{
+ int i = 0;
+ int j;
+ int unexpected = 0;
+ static const int results_per_chunk = 3;
+
+ for (j = 0; j < num_results;) {
+ int k;
+
+ if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_START_ID) {
+ pr_err("parse error: wrong start id\n");
+ unexpected = 1;
+ }
+ for (k = 0; k < results_per_chunk && j < num_results; ++k)
+ results[j++] = raw_data[i++];
+ }
+ while (i % 4)
+ i++;
+ if (raw_data[i++] != NIUSB_REGISTER_READ_DATA_END_ID) {
+ pr_err("parse error: wrong end id\n");
+ unexpected = 1;
+ }
+ if (raw_data[i++] % results_per_chunk != num_results % results_per_chunk) {
+ pr_err("parse error: wrong count=%i for NIUSB_REGISTER_READ_DATA_END\n",
+ (int)raw_data[i - 1]);
+ unexpected = 1;
+ }
+ while (i % 4) {
+ if (raw_data[i++] != 0) {
+ pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n",
+ i - 1, (int)raw_data[i - 1]);
+ unexpected = 1;
+ }
+ }
+ if (unexpected)
+ ni_usb_dump_raw_block(raw_data, i);
+ return i;
+}
+
+static int ni_usb_parse_termination_block(const u8 *buffer)
+{
+ int i = 0;
+
+ if (buffer[i++] != NIUSB_TERM_ID ||
+ buffer[i++] != 0x0 ||
+ buffer[i++] != 0x0 ||
+ buffer[i++] != 0x0) {
+ pr_err("received unexpected termination block\n");
+ pr_err(" expected: 0x%x 0x%x 0x%x 0x%x\n", NIUSB_TERM_ID, 0x0, 0x0, 0x0);
+ pr_err(" received: 0x%x 0x%x 0x%x 0x%x\n",
+ buffer[i - 4], buffer[i - 3], buffer[i - 2], buffer[i - 1]);
+ }
+ return i;
+};
+
+static int parse_board_ibrd_readback(const u8 *raw_data, struct ni_usb_status_block *status,
+ u8 *parsed_data, int parsed_data_length,
+ int *actual_bytes_read)
+{
+ static const int ibrd_data_block_length = 0xf;
+ static const int ibrd_extended_data_block_length = 0x1e;
+ int data_block_length = 0;
+ int i = 0;
+ int j = 0;
+ int k;
+ int num_data_blocks = 0;
+ struct ni_usb_status_block register_write_status;
+ int unexpected = 0;
+
+ while (raw_data[i] == NIUSB_IBRD_DATA_ID || raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) {
+ if (raw_data[i] == NIUSB_IBRD_DATA_ID) {
+ data_block_length = ibrd_data_block_length;
+ } else if (raw_data[i] == NIUSB_IBRD_EXTENDED_DATA_ID) {
+ data_block_length = ibrd_extended_data_block_length;
+ if (raw_data[++i] != 0) {
+ pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n",
+ i, (int)raw_data[i]);
+ unexpected = 1;
+ }
+ } else {
+ pr_err("Unexpected NIUSB_IBRD ID\n");
+ return -EINVAL;
+ }
+ ++i;
+ for (k = 0; k < data_block_length; k++) {
+ if (j < parsed_data_length)
+ parsed_data[j++] = raw_data[i++];
+ else
+ ++i;
+ }
+ ++num_data_blocks;
+ }
+ i += ni_usb_parse_status_block(&raw_data[i], status);
+ if (status->id != NIUSB_IBRD_STATUS_ID) {
+ pr_err("bug: status->id=%i, != ibrd_status_id\n", status->id);
+ return -EIO;
+ }
+ i++;
+ if (num_data_blocks) {
+ *actual_bytes_read = (num_data_blocks - 1) * data_block_length + raw_data[i++];
+ } else {
+ ++i;
+ *actual_bytes_read = 0;
+ }
+ if (*actual_bytes_read > j)
+ pr_err("bug: discarded data. actual_bytes_read=%i, j=%i\n", *actual_bytes_read, j);
+ for (k = 0; k < 2; k++)
+ if (raw_data[i++] != 0) {
+ pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n",
+ i - 1, (int)raw_data[i - 1]);
+ unexpected = 1;
+ }
+ i += ni_usb_parse_status_block(&raw_data[i], &register_write_status);
+ if (register_write_status.id != NIUSB_REG_WRITE_ID) {
+ pr_err("unexpected data: register write status id=0x%x, expected 0x%x\n",
+ register_write_status.id, NIUSB_REG_WRITE_ID);
+ unexpected = 1;
+ }
+ if (raw_data[i++] != 2) {
+ pr_err("unexpected data: register write count=%i, expected 2\n",
+ (int)raw_data[i - 1]);
+ unexpected = 1;
+ }
+ for (k = 0; k < 3; k++)
+ if (raw_data[i++] != 0) {
+ pr_err("unexpected data: raw_data[%i]=0x%x, expected 0\n",
+ i - 1, (int)raw_data[i - 1]);
+ unexpected = 1;
+ }
+ i += ni_usb_parse_termination_block(&raw_data[i]);
+ if (unexpected)
+ ni_usb_dump_raw_block(raw_data, i);
+ return i;
+}
+
+static int ni_usb_parse_reg_write_status_block(const u8 *raw_data,
+ struct ni_usb_status_block *status,
+ int *writes_completed)
+{
+ int i = 0;
+
+ i += ni_usb_parse_status_block(raw_data, status);
+ *writes_completed = raw_data[i++];
+ while (i % 4)
+ i++;
+ return i;
+}
+
+static int ni_usb_write_registers(struct ni_usb_priv *ni_priv,
+ const struct ni_usb_register *writes, int num_writes,
+ unsigned int *ibsta)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int retval;
+ u8 *out_data, *in_data;
+ int out_data_length;
+ static const int in_data_length = 0x20;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ int j;
+ struct ni_usb_status_block status;
+ static const int bytes_per_write = 3;
+ int reg_writes_completed;
+
+ out_data_length = num_writes * bytes_per_write + 0x10;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ i += ni_usb_bulk_register_write_header(&out_data[i], num_writes);
+ for (j = 0; j < num_writes; j++)
+ i += ni_usb_bulk_register_write(&out_data[i], writes[j]);
+ while (i % 4)
+ out_data[i++] = 0x00;
+ i += ni_usb_bulk_termination(&out_data[i]);
+
+ mutex_lock(&ni_priv->addressed_transfer_lock);
+
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return retval;
+ }
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0);
+ if (retval || bytes_read != 16) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ ni_usb_dump_raw_block(in_data, bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ ni_usb_parse_reg_write_status_block(in_data, &status, &reg_writes_completed);
+ // FIXME parse extra 09 status bits and termination
+ kfree(in_data);
+ if (status.id != NIUSB_REG_WRITE_ID) {
+ dev_err(&usb_dev->dev, "parse error, id=0x%x != NIUSB_REG_WRITE_ID\n", status.id);
+ return -EIO;
+ }
+ if (status.error_code) {
+ dev_err(&usb_dev->dev, "nonzero error code 0x%x\n", status.error_code);
+ return -EIO;
+ }
+ if (reg_writes_completed != num_writes) {
+ dev_err(&usb_dev->dev, "reg_writes_completed=%i, num_writes=%i\n",
+ reg_writes_completed, num_writes);
+ return -EIO;
+ }
+ if (ibsta)
+ *ibsta = status.ibsta;
+ return 0;
+}
+
+// interface functions
+static int ni_usb_read(struct gpib_board *board, u8 *buffer, size_t length,
+ int *end, size_t *bytes_read)
+{
+ int retval, parse_retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ static const int out_data_length = 0x20;
+ int in_data_length;
+ int usb_bytes_written = 0, usb_bytes_read = 0;
+ int i = 0;
+ int complement_count;
+ int actual_length;
+ struct ni_usb_status_block status;
+ static const int max_read_length = 0xffff;
+ struct ni_usb_register reg;
+
+ *bytes_read = 0;
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ if (length > max_read_length)
+ return -EINVAL;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ out_data[i++] = 0x0a;
+ out_data[i++] = ni_priv->eos_mode >> 8;
+ out_data[i++] = ni_priv->eos_char;
+ out_data[i++] = ni_usb_timeout_code(board->usec_timeout);
+ complement_count = length - 1;
+ complement_count = ~complement_count;
+ out_data[i++] = complement_count & 0xff;
+ out_data[i++] = (complement_count >> 8) & 0xff;
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_register_write_header(&out_data[i], 2);
+ reg.device = NIUSB_SUBDEV_TNT4882;
+ reg.address = nec7210_to_tnt4882_offset(AUXMR);
+ reg.value = AUX_HLDI;
+ i += ni_usb_bulk_register_write(&out_data[i], reg);
+ reg.value = AUX_CLEAR_END;
+ i += ni_usb_bulk_register_write(&out_data[i], reg);
+ while (i % 4) // pad with zeros to 4-byte boundary
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+
+ mutex_lock(&ni_priv->addressed_transfer_lock);
+
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written, 1000);
+ kfree(out_data);
+ if (retval || usb_bytes_written != i) {
+ if (retval == 0)
+ retval = -EIO;
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n",
+ retval, usb_bytes_written, i);
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return retval;
+ }
+
+ in_data_length = (length / 30 + 1) * 0x20 + 0x20;
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read,
+ ni_usb_timeout_msecs(board->usec_timeout), 1);
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ if (retval == -ERESTARTSYS) {
+ } else if (retval) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, usb_bytes_read=%i\n",
+ retval, usb_bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+ parse_retval = parse_board_ibrd_readback(in_data, &status, buffer, length, &actual_length);
+ if (parse_retval != usb_bytes_read) {
+ if (parse_retval >= 0)
+ parse_retval = -EIO;
+ dev_err(&usb_dev->dev, "retval=%i usb_bytes_read=%i\n",
+ parse_retval, usb_bytes_read);
+ kfree(in_data);
+ return parse_retval;
+ }
+ if (actual_length != length - status.count) {
+ dev_err(&usb_dev->dev, "actual_length=%i expected=%li\n",
+ actual_length, (long)(length - status.count));
+ ni_usb_dump_raw_block(in_data, usb_bytes_read);
+ }
+ kfree(in_data);
+ switch (status.error_code) {
+ case NIUSB_NO_ERROR:
+ retval = 0;
+ break;
+ case NIUSB_ABORTED_ERROR:
+ /*
+ * this is expected if ni_usb_receive_bulk_msg got
+ * interrupted by a signal and returned -ERESTARTSYS
+ */
+ break;
+ case NIUSB_ATN_STATE_ERROR:
+ if (status.ibsta & DCAS) {
+ retval = -EINTR;
+ } else {
+ retval = -EIO;
+ dev_dbg(&usb_dev->dev, "read when ATN set stat: 0x%06x\n", status.ibsta);
+ }
+ break;
+ case NIUSB_ADDRESSING_ERROR:
+ retval = -EIO;
+ break;
+ case NIUSB_TIMEOUT_ERROR:
+ retval = -ETIMEDOUT;
+ break;
+ case NIUSB_EOSMODE_ERROR:
+ dev_err(&usb_dev->dev, "driver bug, we should have been able to avoid NIUSB_EOSMODE_ERROR.\n");
+ retval = -EINVAL;
+ break;
+ default:
+ dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code);
+ retval = -EIO;
+ break;
+ }
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+ if (status.ibsta & END)
+ *end = 1;
+ else
+ *end = 0;
+ *bytes_read = actual_length;
+ return retval;
+}
+
+static int ni_usb_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, size_t *bytes_written)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ int out_data_length;
+ static const int in_data_length = 0x10;
+ int usb_bytes_written = 0, usb_bytes_read = 0;
+ int i = 0, j;
+ int complement_count;
+ struct ni_usb_status_block status;
+ static const int max_write_length = 0xffff;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ if (length > max_write_length)
+ return -EINVAL;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data_length = length + 0x10;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ out_data[i++] = 0x0d;
+ complement_count = length - 1;
+ complement_count = ~complement_count;
+ out_data[i++] = complement_count & 0xff;
+ out_data[i++] = (complement_count >> 8) & 0xff;
+ out_data[i++] = ni_usb_timeout_code(board->usec_timeout);
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ if (send_eoi)
+ out_data[i++] = 0x8;
+ else
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ for (j = 0; j < length; j++)
+ out_data[i++] = buffer[j];
+ while (i % 4) // pad with zeros to 4-byte boundary
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+
+ mutex_lock(&ni_priv->addressed_transfer_lock);
+
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &usb_bytes_written,
+ ni_usb_timeout_msecs(board->usec_timeout));
+ kfree(out_data);
+ if (retval || usb_bytes_written != i) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, usb_bytes_written=%i, i=%i\n",
+ retval, usb_bytes_written, i);
+ return retval;
+ }
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &usb_bytes_read,
+ ni_usb_timeout_msecs(board->usec_timeout), 1);
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ if ((retval && retval != -ERESTARTSYS) || usb_bytes_read != 12) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, usb_bytes_read=%i\n",
+ retval, usb_bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+ ni_usb_parse_status_block(in_data, &status);
+ kfree(in_data);
+ switch (status.error_code) {
+ case NIUSB_NO_ERROR:
+ retval = 0;
+ break;
+ case NIUSB_ABORTED_ERROR:
+ /*
+ * this is expected if ni_usb_receive_bulk_msg got
+ * interrupted by a signal and returned -ERESTARTSYS
+ */
+ break;
+ case NIUSB_ADDRESSING_ERROR:
+ dev_err(&usb_dev->dev, "Addressing error retval %d error code=%i\n",
+ retval, status.error_code);
+ retval = -ENXIO;
+ break;
+ case NIUSB_NO_LISTENER_ERROR:
+ retval = -ECOMM;
+ break;
+ case NIUSB_TIMEOUT_ERROR:
+ retval = -ETIMEDOUT;
+ break;
+ default:
+ dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code);
+ retval = -EPIPE;
+ break;
+ }
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+ *bytes_written = length - status.count;
+ return retval;
+}
+
+static int ni_usb_command_chunk(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *command_bytes_written)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ int out_data_length;
+ static const int in_data_length = 0x10;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0, j;
+ unsigned int complement_count;
+ struct ni_usb_status_block status;
+ // usb-b gives error 4 if you try to send more than 16 command bytes at once
+ static const int max_command_length = 0x10;
+
+ *command_bytes_written = 0;
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ if (length > max_command_length)
+ length = max_command_length;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data_length = length + 0x10;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ out_data[i++] = 0x0c;
+ complement_count = length - 1;
+ complement_count = ~complement_count;
+ out_data[i++] = complement_count;
+ out_data[i++] = 0x0;
+ out_data[i++] = ni_usb_timeout_code(board->usec_timeout);
+ for (j = 0; j < length; j++)
+ out_data[i++] = buffer[j];
+ while (i % 4) // pad with zeros to 4-byte boundary
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+
+ mutex_lock(&ni_priv->addressed_transfer_lock);
+
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written,
+ ni_usb_timeout_msecs(board->usec_timeout));
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return retval;
+ }
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read,
+ ni_usb_timeout_msecs(board->usec_timeout), 1);
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+ ni_usb_parse_status_block(in_data, &status);
+ kfree(in_data);
+ *command_bytes_written = length - status.count;
+ switch (status.error_code) {
+ case NIUSB_NO_ERROR:
+ break;
+ case NIUSB_ABORTED_ERROR:
+ /*
+ * this is expected if ni_usb_receive_bulk_msg got
+ * interrupted by a signal and returned -ERESTARTSYS
+ */
+ break;
+ case NIUSB_NO_BUS_ERROR:
+ return -ENOTCONN;
+ case NIUSB_EOSMODE_ERROR:
+ dev_err(&usb_dev->dev, "got eosmode error. Driver bug?\n");
+ return -EIO;
+ case NIUSB_TIMEOUT_ERROR:
+ return -ETIMEDOUT;
+ default:
+ dev_err(&usb_dev->dev, "unknown error code=%i\n", status.error_code);
+ return -EIO;
+ }
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+ return 0;
+}
+
+static int ni_usb_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ size_t count;
+ int retval;
+
+ *bytes_written = 0;
+ while (*bytes_written < length) {
+ retval = ni_usb_command_chunk(board, buffer + *bytes_written,
+ length - *bytes_written, &count);
+ *bytes_written += count;
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static int ni_usb_take_control(struct gpib_board *board, int synchronous)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ static const int out_data_length = 0x10;
+ static const int in_data_length = 0x10;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ struct ni_usb_status_block status;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+ out_data[i++] = NIUSB_IBCAC_ID;
+ if (synchronous)
+ out_data[i++] = 0x1;
+ else
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+
+ mutex_lock(&ni_priv->addressed_transfer_lock);
+
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return retval;
+ }
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 1);
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ if ((retval && retval != -ERESTARTSYS) || bytes_read != 12) {
+ if (retval == 0)
+ retval = -EIO;
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+ ni_usb_parse_status_block(in_data, &status);
+ kfree(in_data);
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+ return retval;
+}
+
+static int ni_usb_go_to_standby(struct gpib_board *board)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ static const int out_data_length = 0x10;
+ static const int in_data_length = 0x20;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ struct ni_usb_status_block status;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+
+ out_data[i++] = NIUSB_IBGTS_ID;
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+
+ mutex_lock(&ni_priv->addressed_transfer_lock);
+
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return retval;
+ }
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0);
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ if (retval || bytes_read != 12) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+ ni_usb_parse_status_block(in_data, &status);
+ kfree(in_data);
+ if (status.id != NIUSB_IBGTS_ID)
+ dev_err(&usb_dev->dev, "bug: status.id 0x%x != INUSB_IBGTS_ID\n", status.id);
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+ return 0;
+}
+
+static int ni_usb_request_system_control(struct gpib_board *board, int request_control)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[4];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ if (request_control) {
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = CMDR;
+ writes[i].value = SETSC;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_CIFC;
+ i++;
+ } else {
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_CREN;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_CIFC;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_DSC;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = CMDR;
+ writes[i].value = CLRSC;
+ i++;
+ }
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return retval;
+ }
+ if (!request_control)
+ ni_priv->ren_state = 0;
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return 0;
+}
+
+// FIXME maybe the interface should have a "pulse interface clear" function that can return an error?
+static void ni_usb_interface_clear(struct gpib_board *board, int assert)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ static const int out_data_length = 0x10;
+ static const int in_data_length = 0x10;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ struct ni_usb_status_block status;
+
+ if (!ni_priv->bus_interface)
+ return; // -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+// FIXME: we are going to pulse when assert is true, and ignore otherwise
+ if (assert == 0)
+ return;
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return;
+ out_data[i++] = NIUSB_IBSIC_ID;
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return;
+ }
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data)
+ return;
+
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0);
+ if (retval || bytes_read != 12) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ kfree(in_data);
+ return;
+ }
+ ni_usb_parse_status_block(in_data, &status);
+ kfree(in_data);
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+}
+
+static void ni_usb_remote_enable(struct gpib_board *board, int enable)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct ni_usb_register reg;
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return; // -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ reg.device = NIUSB_SUBDEV_TNT4882;
+ reg.address = nec7210_to_tnt4882_offset(AUXMR);
+ if (enable)
+ reg.value = AUX_SREN;
+ else
+ reg.value = AUX_CREN;
+ retval = ni_usb_write_registers(ni_priv, &reg, 1, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return; //retval;
+ }
+ ni_priv->ren_state = enable;
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return;// 0;
+}
+
+static int ni_usb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct ni_usb_priv *ni_priv = board->private_data;
+
+ ni_priv->eos_char = eos_byte;
+ ni_priv->eos_mode |= REOS;
+ if (compare_8_bits)
+ ni_priv->eos_mode |= BIN;
+ else
+ ni_priv->eos_mode &= ~BIN;
+ return 0;
+}
+
+static void ni_usb_disable_eos(struct gpib_board *board)
+{
+ struct ni_usb_priv *ni_priv = board->private_data;
+ /*
+ * adapter gets unhappy if you don't zero all the bits
+ * for the eos mode and eos char (returns error 4 on reads).
+ */
+ ni_priv->eos_mode = 0;
+ ni_priv->eos_char = 0;
+}
+
+static unsigned int ni_usb_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ static const int buffer_length = 8;
+ u8 *buffer;
+ struct ni_usb_status_block status;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ buffer = kmalloc(buffer_length, GFP_KERNEL);
+ if (!buffer)
+ return board->status;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x200, 0x0, buffer, buffer_length, 1000);
+ if (retval != buffer_length) {
+ dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval);
+ kfree(buffer);
+ return board->status;
+ }
+ ni_usb_parse_status_block(buffer, &status);
+ kfree(buffer);
+ ni_usb_soft_update_status(board, status.ibsta, clear_mask);
+ return board->status;
+}
+
+// tells ni-usb to immediately stop an ongoing i/o operation
+static void ni_usb_stop(struct ni_usb_priv *ni_priv)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int retval;
+ static const int buffer_length = 8;
+ u8 *buffer;
+ struct ni_usb_status_block status;
+
+ buffer = kmalloc(buffer_length, GFP_KERNEL);
+ if (!buffer)
+ return;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_STOP_REQUEST, USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, 0x0, buffer, buffer_length, 1000);
+ if (retval != buffer_length) {
+ dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval);
+ kfree(buffer);
+ return;
+ }
+ ni_usb_parse_status_block(buffer, &status);
+ kfree(buffer);
+}
+
+static int ni_usb_primary_address(struct gpib_board *board, unsigned int address)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[2];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(ADR);
+ writes[i].value = address;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_UNKNOWN2;
+ writes[i].address = 0x0;
+ writes[i].value = address;
+ i++;
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return 0;
+}
+
+static int ni_usb_write_sad(struct ni_usb_register *writes, int address, int enable)
+{
+ unsigned int adr_bits, admr_bits;
+ int i = 0;
+
+ adr_bits = HR_ARS;
+ admr_bits = HR_TRM0 | HR_TRM1;
+ if (enable) {
+ adr_bits |= address;
+ admr_bits |= HR_ADM1;
+ } else {
+ adr_bits |= HR_DT | HR_DL;
+ admr_bits |= HR_ADM0;
+ }
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(ADR);
+ writes[i].value = adr_bits;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(ADMR);
+ writes[i].value = admr_bits;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_UNKNOWN2;
+ writes[i].address = 0x1;
+ writes[i].value = enable ? MSA(address) : 0x0;
+ i++;
+ return i;
+}
+
+static int ni_usb_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[3];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ i += ni_usb_write_sad(writes, address, enable);
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return 0;
+}
+
+static int ni_usb_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ static const int out_data_length = 0x10;
+ static const int in_data_length = 0x20;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ int j = 0;
+ struct ni_usb_status_block status;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+
+ out_data[i++] = NIUSB_IBRPP_ID;
+ out_data[i++] = 0xf0; // FIXME: this should be the parallel poll timeout code
+ out_data[i++] = 0x0;
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+ /*FIXME: 1000 should use parallel poll timeout (not supported yet)*/
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000);
+
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return retval;
+ }
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data)
+ return -ENOMEM;
+
+ /*FIXME: should use parallel poll timeout (not supported yet)*/
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length,
+ &bytes_read, 1000, 1);
+
+ if (retval && retval != -ERESTARTSYS) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+ j += ni_usb_parse_status_block(in_data, &status);
+ *result = in_data[j++];
+ kfree(in_data);
+ ni_usb_soft_update_status(board, status.ibsta, 0);
+ return retval;
+}
+
+static void ni_usb_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[1];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return; // -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = PPR | config;
+ i++;
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return;// retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return;// 0;
+}
+
+static void ni_usb_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[1];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return; // -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ if (ist)
+ writes[i].value = AUX_SPPF;
+ else
+ writes[i].value = AUX_CPPF;
+ i++;
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return;// retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return;// 0;
+}
+
+static void ni_usb_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[1];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return; // -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(SPMR);
+ writes[i].value = status;
+ i++;
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return;// retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return;// 0;
+}
+
+static u8 ni_usb_serial_poll_status(struct gpib_board *board)
+{
+ return 0;
+}
+
+static void ni_usb_return_to_local(struct gpib_board *board)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int i = 0;
+ struct ni_usb_register writes[1];
+ unsigned int ibsta;
+
+ if (!ni_priv->bus_interface)
+ return; // -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_RTL;
+ i++;
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return;// retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return;// 0;
+}
+
+static int ni_usb_line_status(const struct gpib_board *board)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ u8 *out_data, *in_data;
+ static const int out_data_length = 0x20;
+ static const int in_data_length = 0x20;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ unsigned int bsr_bits;
+ int line_status = VALID_ALL;
+ // NI windows driver reads 0xd(HSSEL), 0xc (ARD0), 0x1f (BSR)
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data)
+ return -ENOMEM;
+
+ /* line status gets called during ibwait */
+ retval = mutex_trylock(&ni_priv->addressed_transfer_lock);
+
+ if (retval == 0) {
+ kfree(out_data);
+ return -EBUSY;
+ }
+ i += ni_usb_bulk_register_read_header(&out_data[i], 1);
+ i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_TNT4882, BSR);
+ while (i % 4)
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+ retval = ni_usb_nonblocking_send_bulk_msg(ni_priv, out_data, i, &bytes_written, 1000);
+ kfree(out_data);
+ if (retval || bytes_written != i) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ if (retval != -EAGAIN)
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%i\n",
+ retval, bytes_written, i);
+ return retval;
+ }
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data) {
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+ return -ENOMEM;
+ }
+ retval = ni_usb_nonblocking_receive_bulk_msg(ni_priv, in_data, in_data_length,
+ &bytes_read, 1000, 0);
+
+ mutex_unlock(&ni_priv->addressed_transfer_lock);
+
+ if (retval) {
+ if (retval != -EAGAIN)
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ kfree(in_data);
+ return retval;
+ }
+
+ ni_usb_parse_register_read_block(in_data, &bsr_bits, 1);
+ kfree(in_data);
+ if (bsr_bits & BCSR_REN_BIT)
+ line_status |= BUS_REN;
+ if (bsr_bits & BCSR_IFC_BIT)
+ line_status |= BUS_IFC;
+ if (bsr_bits & BCSR_SRQ_BIT)
+ line_status |= BUS_SRQ;
+ if (bsr_bits & BCSR_EOI_BIT)
+ line_status |= BUS_EOI;
+ if (bsr_bits & BCSR_NRFD_BIT)
+ line_status |= BUS_NRFD;
+ if (bsr_bits & BCSR_NDAC_BIT)
+ line_status |= BUS_NDAC;
+ if (bsr_bits & BCSR_DAV_BIT)
+ line_status |= BUS_DAV;
+ if (bsr_bits & BCSR_ATN_BIT)
+ line_status |= BUS_ATN;
+ return line_status;
+}
+
+static int ni_usb_setup_t1_delay(struct ni_usb_register *reg, unsigned int nano_sec,
+ unsigned int *actual_ns)
+{
+ int i = 0;
+
+ *actual_ns = 2000;
+
+ reg[i].device = NIUSB_SUBDEV_TNT4882;
+ reg[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ if (nano_sec <= 1100) {
+ reg[i].value = AUXRI | USTD | SISB;
+ *actual_ns = 1100;
+ } else {
+ reg[i].value = AUXRI | SISB;
+ }
+ i++;
+ reg[i].device = NIUSB_SUBDEV_TNT4882;
+ reg[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ if (nano_sec <= 500) {
+ reg[i].value = AUXRB | HR_TRI;
+ *actual_ns = 500;
+ } else {
+ reg[i].value = AUXRB;
+ }
+ i++;
+ reg[i].device = NIUSB_SUBDEV_TNT4882;
+ reg[i].address = KEYREG;
+ if (nano_sec <= 350) {
+ reg[i].value = MSTD;
+ *actual_ns = 350;
+ } else {
+ reg[i].value = 0x0;
+ }
+ i++;
+ return i;
+}
+
+static int ni_usb_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ struct ni_usb_register writes[3];
+ unsigned int ibsta;
+ unsigned int actual_ns;
+ int i;
+
+ if (!ni_priv->bus_interface)
+ return -ENODEV;
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ i = ni_usb_setup_t1_delay(writes, nano_sec, &actual_ns);
+ retval = ni_usb_write_registers(ni_priv, writes, i, &ibsta);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return retval;
+ }
+ board->t1_nano_sec = actual_ns;
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return actual_ns;
+}
+
+static int ni_usb_allocate_private(struct gpib_board *board)
+{
+ struct ni_usb_priv *ni_priv;
+
+ board->private_data = kmalloc(sizeof(struct ni_usb_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -ENOMEM;
+ ni_priv = board->private_data;
+ memset(ni_priv, 0, sizeof(struct ni_usb_priv));
+ mutex_init(&ni_priv->bulk_transfer_lock);
+ mutex_init(&ni_priv->control_transfer_lock);
+ mutex_init(&ni_priv->interrupt_transfer_lock);
+ mutex_init(&ni_priv->addressed_transfer_lock);
+ return 0;
+}
+
+static void ni_usb_free_private(struct ni_usb_priv *ni_priv)
+{
+ usb_free_urb(ni_priv->interrupt_urb);
+ kfree(ni_priv);
+}
+
+#define NUM_INIT_WRITES 26
+static int ni_usb_setup_init(struct gpib_board *board, struct ni_usb_register *writes)
+{
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ unsigned int mask, actual_ns;
+ int i = 0;
+
+ writes[i].device = NIUSB_SUBDEV_UNKNOWN3;
+ writes[i].address = 0x10;
+ writes[i].value = 0x0;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = CMDR;
+ writes[i].value = SOFT_RESET;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ mask = AUXRA | HR_HLDA;
+ if (ni_priv->eos_mode & BIN)
+ mask |= HR_BIN;
+ writes[i].value = mask;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = AUXCR;
+ writes[i].value = mask;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = HSSEL;
+ writes[i].value = TNT_ONE_CHIP_BIT;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_CR;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = IMR0;
+ writes[i].value = TNT_IMR0_ALWAYS_BITS;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(IMR1);
+ writes[i].value = 0x0;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(IMR2);
+ writes[i].value = 0x0;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = IMR3;
+ writes[i].value = 0x0;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_HLDI;
+ i++;
+
+ i += ni_usb_setup_t1_delay(&writes[i], board->t1_nano_sec, &actual_ns);
+
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUXRG | NTNL_BIT;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = CMDR;
+ if (board->master)
+ mask = SETSC; // set system controller
+ else
+ mask = CLRSC; // clear system controller
+ writes[i].value = mask;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_CIFC;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(ADR);
+ writes[i].value = board->pad;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_UNKNOWN2;
+ writes[i].address = 0x0;
+ writes[i].value = board->pad;
+ i++;
+
+ i += ni_usb_write_sad(&writes[i], board->sad, board->sad >= 0);
+
+ writes[i].device = NIUSB_SUBDEV_UNKNOWN2;
+ writes[i].address = 0x2; // could this be a timeout ?
+ writes[i].value = 0xfd;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = 0xf; // undocumented address
+ writes[i].value = 0x11;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_PON;
+ i++;
+ writes[i].device = NIUSB_SUBDEV_TNT4882;
+ writes[i].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[i].value = AUX_CPPF;
+ i++;
+ if (i > NUM_INIT_WRITES) {
+ dev_err(&usb_dev->dev, "bug!, buffer overrun, i=%i\n", i);
+ return 0;
+ }
+ return i;
+}
+
+static int ni_usb_init(struct gpib_board *board)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ struct ni_usb_register *writes;
+ unsigned int ibsta;
+ int writes_len;
+
+ writes = kmalloc_array(NUM_INIT_WRITES, sizeof(*writes), GFP_KERNEL);
+ if (!writes)
+ return -ENOMEM;
+
+ writes_len = ni_usb_setup_init(board, writes);
+ if (writes_len)
+ retval = ni_usb_write_registers(ni_priv, writes, writes_len, &ibsta);
+ else
+ return -EFAULT;
+ kfree(writes);
+ if (retval) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return retval;
+ }
+ ni_usb_soft_update_status(board, ibsta, 0);
+ return 0;
+}
+
+static void ni_usb_interrupt_complete(struct urb *urb)
+{
+ struct gpib_board *board = urb->context;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int retval;
+ struct ni_usb_status_block status;
+ unsigned long flags;
+
+ switch (urb->status) {
+ /* success */
+ case 0:
+ break;
+ /* unlinked, don't resubmit */
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* other error, resubmit */
+ retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n");
+ return;
+ }
+
+ ni_usb_parse_status_block(urb->transfer_buffer, &status);
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ ni_priv->monitored_ibsta_bits &= ~status.ibsta;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ wake_up_interruptible(&board->wait);
+
+ retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&usb_dev->dev, "failed to resubmit interrupt urb\n");
+}
+
+static int ni_usb_set_interrupt_monitor(struct gpib_board *board, unsigned int monitored_bits)
+{
+ int retval;
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ static const int buffer_length = 8;
+ u8 *buffer;
+ struct ni_usb_status_block status;
+ unsigned long flags;
+
+ buffer = kmalloc(buffer_length, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ ni_priv->monitored_ibsta_bits = ni_usb_ibsta_monitor_mask & monitored_bits;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_WAIT_REQUEST, USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x300, ni_usb_ibsta_monitor_mask & monitored_bits,
+ buffer, buffer_length, 1000);
+ if (retval != buffer_length) {
+ dev_err(&usb_dev->dev, "usb_control_msg returned %i\n", retval);
+ kfree(buffer);
+ return -1;
+ }
+ ni_usb_parse_status_block(buffer, &status);
+ kfree(buffer);
+ return 0;
+}
+
+static int ni_usb_setup_urbs(struct gpib_board *board)
+{
+ struct ni_usb_priv *ni_priv = board->private_data;
+ struct usb_device *usb_dev;
+ int int_pipe;
+ int retval;
+
+ if (ni_priv->interrupt_in_endpoint < 0)
+ return 0;
+
+ mutex_lock(&ni_priv->interrupt_transfer_lock);
+ if (!ni_priv->bus_interface) {
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ return -ENODEV;
+ }
+ ni_priv->interrupt_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ni_priv->interrupt_urb) {
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ return -ENOMEM;
+ }
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int_pipe = usb_rcvintpipe(usb_dev, ni_priv->interrupt_in_endpoint);
+ usb_fill_int_urb(ni_priv->interrupt_urb, usb_dev, int_pipe, ni_priv->interrupt_buffer,
+ sizeof(ni_priv->interrupt_buffer), &ni_usb_interrupt_complete, board, 1);
+ retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL);
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ if (retval) {
+ dev_err(&usb_dev->dev, "failed to submit first interrupt urb, retval=%i\n", retval);
+ return retval;
+ }
+ return 0;
+}
+
+static void ni_usb_cleanup_urbs(struct ni_usb_priv *ni_priv)
+{
+ if (ni_priv && ni_priv->bus_interface) {
+ if (ni_priv->interrupt_urb)
+ usb_kill_urb(ni_priv->interrupt_urb);
+ if (ni_priv->bulk_urb)
+ usb_kill_urb(ni_priv->bulk_urb);
+ }
+}
+
+static int ni_usb_b_read_serial_number(struct ni_usb_priv *ni_priv)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int retval;
+ u8 *out_data;
+ u8 *in_data;
+ static const int out_data_length = 0x20;
+ static const int in_data_length = 0x20;
+ int bytes_written = 0, bytes_read = 0;
+ int i = 0;
+ static const int num_reads = 4;
+ unsigned int results[4];
+ int j;
+ unsigned int serial_number;
+
+ in_data = kmalloc(in_data_length, GFP_KERNEL);
+ if (!in_data)
+ return -ENOMEM;
+
+ out_data = kmalloc(out_data_length, GFP_KERNEL);
+ if (!out_data) {
+ kfree(in_data);
+ return -ENOMEM;
+ }
+ i += ni_usb_bulk_register_read_header(&out_data[i], num_reads);
+ i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_1_REG);
+ i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_2_REG);
+ i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_3_REG);
+ i += ni_usb_bulk_register_read(&out_data[i], NIUSB_SUBDEV_UNKNOWN3, SERIAL_NUMBER_4_REG);
+ while (i % 4)
+ out_data[i++] = 0x0;
+ i += ni_usb_bulk_termination(&out_data[i]);
+ retval = ni_usb_send_bulk_msg(ni_priv, out_data, out_data_length, &bytes_written, 1000);
+ if (retval) {
+ dev_err(&usb_dev->dev, "send_bulk_msg returned %i, bytes_written=%i, i=%li\n",
+ retval, bytes_written, (long)out_data_length);
+ goto serial_out;
+ }
+ retval = ni_usb_receive_bulk_msg(ni_priv, in_data, in_data_length, &bytes_read, 1000, 0);
+ if (retval) {
+ dev_err(&usb_dev->dev, "receive_bulk_msg returned %i, bytes_read=%i\n",
+ retval, bytes_read);
+ ni_usb_dump_raw_block(in_data, bytes_read);
+ goto serial_out;
+ }
+ if (ARRAY_SIZE(results) < num_reads) {
+ dev_err(&usb_dev->dev, "serial number eetup bug\n");
+ retval = -EINVAL;
+ goto serial_out;
+ }
+ ni_usb_parse_register_read_block(in_data, results, num_reads);
+ serial_number = 0;
+ for (j = 0; j < num_reads; ++j)
+ serial_number |= (results[j] & 0xff) << (8 * j);
+ dev_dbg(&usb_dev->dev, "board serial number is 0x%x\n", serial_number);
+ retval = 0;
+serial_out:
+ kfree(in_data);
+ kfree(out_data);
+ return retval;
+}
+
+static int ni_usb_hs_wait_for_ready(struct ni_usb_priv *ni_priv)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ static const int buffer_size = 0x10;
+ static const int timeout = 50;
+ static const int msec_sleep_duration = 100;
+ int i; int retval;
+ int j;
+ int unexpected = 0;
+ unsigned int serial_number;
+ u8 *buffer;
+
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_SERIAL_NUMBER_REQUEST,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, 0x0, buffer, buffer_size, 1000);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n",
+ NI_USB_SERIAL_NUMBER_REQUEST, retval);
+ goto ready_out;
+ }
+ j = 0;
+ if (buffer[j] != NI_USB_SERIAL_NUMBER_REQUEST) {
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n",
+ j, (int)buffer[j], NI_USB_SERIAL_NUMBER_REQUEST);
+ unexpected = 1;
+ }
+ if (unexpected)
+ ni_usb_dump_raw_block(buffer, retval);
+ // NI-USB-HS+ pads the serial with 0x0 to make 16 bytes
+ if (retval != 5 && retval != 16) {
+ dev_err(&usb_dev->dev, "received unexpected number of bytes = %i, expected 5 or 16\n",
+ retval);
+ ni_usb_dump_raw_block(buffer, retval);
+ }
+ serial_number = 0;
+ serial_number |= buffer[++j];
+ serial_number |= (buffer[++j] << 8);
+ serial_number |= (buffer[++j] << 16);
+ serial_number |= (buffer[++j] << 24);
+ dev_dbg(&usb_dev->dev, "board serial number is 0x%x\n", serial_number);
+ for (i = 0; i < timeout; ++i) {
+ int ready = 0;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_POLL_READY_REQUEST,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, 0x0, buffer, buffer_size, 100);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n",
+ NI_USB_POLL_READY_REQUEST, retval);
+ goto ready_out;
+ }
+ j = 0;
+ unexpected = 0;
+ if (buffer[j] != NI_USB_POLL_READY_REQUEST) { // [0]
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n",
+ j, (int)buffer[j], NI_USB_POLL_READY_REQUEST);
+ unexpected = 1;
+ }
+ ++j;
+ if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [1] HS+ sends 0x0
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ if (buffer[++j] != 0x0) { // [2]
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x%x\n",
+ j, (int)buffer[j], 0x0);
+ unexpected = 1;
+ }
+ ++j;
+ /*
+ * MC usb-488 (and sometimes NI-USB-HS?) sends 0x8 here; MC usb-488A sends 0x7 here
+ * NI-USB-HS+ sends 0x0
+ */
+ if (buffer[j] != 0x1 && buffer[j] != 0x8 && buffer[j] != 0x7 && buffer[j] != 0x0) {
+ // [3]
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0, 0x1, 0x7 or 0x8\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ ++j;
+ // NI-USB-HS+ sends 0 here
+ if (buffer[j] != 0x30 && buffer[j] != 0x0) { // [4]
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x30\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ ++j;
+ // MC usb-488 (and sometimes NI-USB-HS?) and NI-USB-HS+ sends 0x0 here
+ if (buffer[j] != 0x1 && buffer[j] != 0x0) { // [5]
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x1 or 0x0\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ if (buffer[++j] != 0x0) { // [6]
+ ready = 1;
+ // NI-USB-HS+ sends 0xf or 0x19 here
+ if (buffer[j] != 0x2 && buffer[j] != 0xe && buffer[j] != 0xf &&
+ buffer[j] != 0x16 && buffer[j] != 0x19) {
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x2, 0xe, 0xf, 0x16 or 0x19\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ }
+ if (buffer[++j] != 0x0) { // [7]
+ ready = 1;
+ // MC usb-488 sends 0x5 here; MC usb-488A sends 0x6 here
+ if (buffer[j] != 0x3 && buffer[j] != 0x5 && buffer[j] != 0x6 &&
+ buffer[j] != 0x8) {
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x3 or 0x5, 0x6 or 0x08\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ }
+ ++j;
+ if (buffer[j] != 0x0 && buffer[j] != 0x2) { // [8] MC usb-488 sends 0x2 here
+ dev_err(&usb_dev->dev, " unexpected data: buffer[%i]=0x%x, expected 0x0 or 0x2\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ ++j;
+ // MC usb-488A and NI-USB-HS sends 0x3 here; NI-USB-HS+ sends 0x30 here
+ if (buffer[j] != 0x0 && buffer[j] != 0x3 && buffer[j] != 0x30) { // [9]
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x0, 0x3 or 0x30\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ if (buffer[++j] != 0x0) { // [10] MC usb-488 sends 0x7 here, new HS+ sends 0x59
+ ready = 1;
+ if (buffer[j] != 0x96 && buffer[j] != 0x7 && buffer[j] != 0x6e &&
+ buffer[j] != 0x59) {
+ dev_err(&usb_dev->dev, "unexpected data: buffer[%i]=0x%x, expected 0x96, 0x07, 0x6e or 0x59\n",
+ j, (int)buffer[j]);
+ unexpected = 1;
+ }
+ }
+ if (unexpected)
+ ni_usb_dump_raw_block(buffer, retval);
+ if (ready)
+ break;
+ retval = msleep_interruptible(msec_sleep_duration);
+ if (retval) {
+ retval = -ERESTARTSYS;
+ goto ready_out;
+ }
+ }
+ retval = 0;
+
+ready_out:
+ kfree(buffer);
+ dev_dbg(&usb_dev->dev, "exit retval=%d\n", retval);
+ return retval;
+}
+
+/*
+ * This does some extra init for HS+ models, as observed on Windows. One of the
+ * control requests causes the LED to stop blinking.
+ * I'm not sure what the other 2 requests do. None of these requests are actually required
+ * for the adapter to work, maybe they do some init for the analyzer interface
+ * (which we don't use).
+ */
+static int ni_usb_hs_plus_extra_init(struct ni_usb_priv *ni_priv)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int retval;
+ u8 *buffer;
+ static const int buffer_size = 16;
+ int transfer_size;
+
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ do {
+ transfer_size = 16;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0x48_REQUEST,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0, 0x0, buffer, transfer_size, 1000);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n",
+ NI_USB_HS_PLUS_0x48_REQUEST, retval);
+ break;
+ }
+ // expected response data: 48 f3 30 00 00 00 00 00 00 00 00 00 00 00 00 00
+ if (buffer[0] != NI_USB_HS_PLUS_0x48_REQUEST)
+ dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n",
+ (int)buffer[0], NI_USB_HS_PLUS_0x48_REQUEST);
+
+ transfer_size = 2;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_LED_REQUEST,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x1, 0x0, buffer, transfer_size, 1000);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n",
+ NI_USB_HS_PLUS_LED_REQUEST, retval);
+ break;
+ }
+ // expected response data: 4b 00
+ if (buffer[0] != NI_USB_HS_PLUS_LED_REQUEST)
+ dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n",
+ (int)buffer[0], NI_USB_HS_PLUS_LED_REQUEST);
+
+ transfer_size = 9;
+
+ retval = ni_usb_receive_control_msg(ni_priv, NI_USB_HS_PLUS_0xf8_REQUEST,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE,
+ 0x0, 0x1, buffer, transfer_size, 1000);
+ if (retval < 0) {
+ dev_err(&usb_dev->dev, "usb_control_msg request 0x%x returned %i\n",
+ NI_USB_HS_PLUS_0xf8_REQUEST, retval);
+ break;
+ }
+ // expected response data: f8 01 00 00 00 01 00 00 00
+ if (buffer[0] != NI_USB_HS_PLUS_0xf8_REQUEST)
+ dev_err(&usb_dev->dev, "unexpected data: buffer[0]=0x%x, expected 0x%x\n",
+ (int)buffer[0], NI_USB_HS_PLUS_0xf8_REQUEST);
+ } while (0);
+
+ // cleanup
+ kfree(buffer);
+ return retval;
+}
+
+static inline int ni_usb_device_match(struct usb_interface *interface,
+ const struct gpib_board_config *config)
+{
+ if (gpib_match_device_path(&interface->dev, config->device_path) == 0)
+ return 0;
+ return 1;
+}
+
+static int ni_usb_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ int retval;
+ int i, index;
+ struct ni_usb_priv *ni_priv;
+ int product_id;
+ struct usb_device *usb_dev;
+
+ mutex_lock(&ni_usb_hotplug_lock);
+ retval = ni_usb_allocate_private(board);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ ni_priv = board->private_data;
+ for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) {
+ if (ni_usb_driver_interfaces[i] &&
+ !usb_get_intfdata(ni_usb_driver_interfaces[i]) &&
+ ni_usb_device_match(ni_usb_driver_interfaces[i], config)) {
+ ni_priv->bus_interface = ni_usb_driver_interfaces[i];
+ usb_set_intfdata(ni_usb_driver_interfaces[i], board);
+ usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ index = i;
+ break;
+ }
+ }
+ if (i == MAX_NUM_NI_USB_INTERFACES) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ dev_err(board->gpib_dev, "No supported adapters found, have you loaded its firmware?\n");
+ return -ENODEV;
+ }
+ if (usb_reset_configuration(interface_to_usbdev(ni_priv->bus_interface)))
+ dev_err(&usb_dev->dev, "usb_reset_configuration() failed.\n");
+
+ product_id = le16_to_cpu(usb_dev->descriptor.idProduct);
+ ni_priv->product_id = product_id;
+
+ timer_setup(&ni_priv->bulk_timer, ni_usb_timeout_handler, 0);
+
+ switch (product_id) {
+ case USB_DEVICE_ID_NI_USB_B:
+ ni_priv->bulk_out_endpoint = NIUSB_B_BULK_OUT_ENDPOINT;
+ ni_priv->bulk_in_endpoint = NIUSB_B_BULK_IN_ENDPOINT;
+ ni_priv->interrupt_in_endpoint = NIUSB_B_INTERRUPT_IN_ENDPOINT;
+ ni_usb_b_read_serial_number(ni_priv);
+ break;
+ case USB_DEVICE_ID_NI_USB_HS:
+ case USB_DEVICE_ID_MC_USB_488:
+ case USB_DEVICE_ID_KUSB_488A:
+ ni_priv->bulk_out_endpoint = NIUSB_HS_BULK_OUT_ENDPOINT;
+ ni_priv->bulk_in_endpoint = NIUSB_HS_BULK_IN_ENDPOINT;
+ ni_priv->interrupt_in_endpoint = NIUSB_HS_INTERRUPT_IN_ENDPOINT;
+ retval = ni_usb_hs_wait_for_ready(ni_priv);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ break;
+ case USB_DEVICE_ID_NI_USB_HS_PLUS:
+ ni_priv->bulk_out_endpoint = NIUSB_HS_PLUS_BULK_OUT_ENDPOINT;
+ ni_priv->bulk_in_endpoint = NIUSB_HS_PLUS_BULK_IN_ENDPOINT;
+ ni_priv->interrupt_in_endpoint = NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT;
+ retval = ni_usb_hs_wait_for_ready(ni_priv);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ retval = ni_usb_hs_plus_extra_init(ni_priv);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ break;
+ default:
+ mutex_unlock(&ni_usb_hotplug_lock);
+ dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id %x\n",
+ product_id);
+ return -EINVAL;
+ }
+
+ retval = ni_usb_setup_urbs(board);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ retval = ni_usb_set_interrupt_monitor(board, 0);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+
+ board->t1_nano_sec = 500;
+
+ retval = ni_usb_init(board);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+
+ mutex_unlock(&ni_usb_hotplug_lock);
+ dev_info(&usb_dev->dev,
+ "bus %d dev num %d attached to gpib%d, intf %i\n",
+ usb_dev->bus->busnum, usb_dev->devnum, board->minor, index);
+ return retval;
+}
+
+static int ni_usb_shutdown_hardware(struct ni_usb_priv *ni_priv)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(ni_priv->bus_interface);
+ int retval;
+ struct ni_usb_register writes[2];
+ static const int writes_length = ARRAY_SIZE(writes);
+ unsigned int ibsta;
+
+ writes[0].device = NIUSB_SUBDEV_TNT4882;
+ writes[0].address = nec7210_to_tnt4882_offset(AUXMR);
+ writes[0].value = AUX_CR;
+ writes[1].device = NIUSB_SUBDEV_UNKNOWN3;
+ writes[1].address = 0x10;
+ writes[1].value = 0x0;
+ retval = ni_usb_write_registers(ni_priv, writes, writes_length, &ibsta);
+ if (retval) {
+ dev_err(&usb_dev->dev, "register write failed, retval=%i\n", retval);
+ return retval;
+ }
+ return 0;
+}
+
+static void ni_usb_detach(struct gpib_board *board)
+{
+ struct ni_usb_priv *ni_priv;
+
+ mutex_lock(&ni_usb_hotplug_lock);
+ /*
+ * under windows, software unplug does chip_reset nec7210 aux command,
+ * then writes 0x0 to address 0x10 of device 3
+ */
+ ni_priv = board->private_data;
+ if (ni_priv) {
+ if (ni_priv->bus_interface) {
+ ni_usb_set_interrupt_monitor(board, 0);
+ ni_usb_shutdown_hardware(ni_priv);
+ usb_set_intfdata(ni_priv->bus_interface, NULL);
+ }
+ mutex_lock(&ni_priv->bulk_transfer_lock);
+ mutex_lock(&ni_priv->control_transfer_lock);
+ mutex_lock(&ni_priv->interrupt_transfer_lock);
+ ni_usb_cleanup_urbs(ni_priv);
+ ni_usb_free_private(ni_priv);
+ }
+ mutex_unlock(&ni_usb_hotplug_lock);
+}
+
+static struct gpib_interface ni_usb_gpib_interface = {
+ .name = "ni_usb_b",
+ .attach = ni_usb_attach,
+ .detach = ni_usb_detach,
+ .read = ni_usb_read,
+ .write = ni_usb_write,
+ .command = ni_usb_command,
+ .take_control = ni_usb_take_control,
+ .go_to_standby = ni_usb_go_to_standby,
+ .request_system_control = ni_usb_request_system_control,
+ .interface_clear = ni_usb_interface_clear,
+ .remote_enable = ni_usb_remote_enable,
+ .enable_eos = ni_usb_enable_eos,
+ .disable_eos = ni_usb_disable_eos,
+ .parallel_poll = ni_usb_parallel_poll,
+ .parallel_poll_configure = ni_usb_parallel_poll_configure,
+ .parallel_poll_response = ni_usb_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = ni_usb_line_status,
+ .update_status = ni_usb_update_status,
+ .primary_address = ni_usb_primary_address,
+ .secondary_address = ni_usb_secondary_address,
+ .serial_poll_response = ni_usb_serial_poll_response,
+ .serial_poll_status = ni_usb_serial_poll_status,
+ .t1_delay = ni_usb_t1_delay,
+ .return_to_local = ni_usb_return_to_local,
+ .skip_check_for_command_acceptors = 1
+};
+
+// Table with the USB-devices: just now only testing IDs
+static struct usb_device_id ni_usb_driver_device_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_B)},
+ {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS)},
+ // gpib-usb-hs+ has a second interface for the analyzer, which we ignore
+ {USB_DEVICE_INTERFACE_NUMBER(USB_VENDOR_ID_NI, USB_DEVICE_ID_NI_USB_HS_PLUS, 0)},
+ {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_KUSB_488A)},
+ {USB_DEVICE(USB_VENDOR_ID_NI, USB_DEVICE_ID_MC_USB_488)},
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, ni_usb_driver_device_table);
+
+static int ni_usb_driver_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+ int i;
+ char *path;
+ static const int path_length = 1024;
+
+ mutex_lock(&ni_usb_hotplug_lock);
+ usb_get_dev(usb_dev);
+ for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) {
+ if (!ni_usb_driver_interfaces[i]) {
+ ni_usb_driver_interfaces[i] = interface;
+ usb_set_intfdata(interface, NULL);
+ break;
+ }
+ }
+ if (i == MAX_NUM_NI_USB_INTERFACES) {
+ usb_put_dev(usb_dev);
+ mutex_unlock(&ni_usb_hotplug_lock);
+ dev_err(&usb_dev->dev, "ni_usb_driver_interfaces[] full\n");
+ return -1;
+ }
+ path = kmalloc(path_length, GFP_KERNEL);
+ if (!path) {
+ usb_put_dev(usb_dev);
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return -ENOMEM;
+ }
+ usb_make_path(usb_dev, path, path_length);
+ dev_info(&usb_dev->dev, "probe succeeded for path: %s\n", path);
+ kfree(path);
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return 0;
+}
+
+static void ni_usb_driver_disconnect(struct usb_interface *interface)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+ int i;
+
+ mutex_lock(&ni_usb_hotplug_lock);
+ for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) {
+ if (ni_usb_driver_interfaces[i] == interface) {
+ struct gpib_board *board = usb_get_intfdata(interface);
+
+ if (board) {
+ struct ni_usb_priv *ni_priv = board->private_data;
+
+ if (ni_priv) {
+ mutex_lock(&ni_priv->bulk_transfer_lock);
+ mutex_lock(&ni_priv->control_transfer_lock);
+ mutex_lock(&ni_priv->interrupt_transfer_lock);
+ ni_usb_cleanup_urbs(ni_priv);
+ ni_priv->bus_interface = NULL;
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ mutex_unlock(&ni_priv->control_transfer_lock);
+ mutex_unlock(&ni_priv->bulk_transfer_lock);
+ }
+ }
+ ni_usb_driver_interfaces[i] = NULL;
+ break;
+ }
+ }
+ if (i == MAX_NUM_NI_USB_INTERFACES)
+ dev_err(&usb_dev->dev, "unable to find interface bug?\n");
+ usb_put_dev(usb_dev);
+ mutex_unlock(&ni_usb_hotplug_lock);
+}
+
+static int ni_usb_driver_suspend(struct usb_interface *interface, pm_message_t message)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+ struct gpib_board *board;
+ int i, retval;
+
+ mutex_lock(&ni_usb_hotplug_lock);
+
+ for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) {
+ if (ni_usb_driver_interfaces[i] == interface) {
+ board = usb_get_intfdata(interface);
+ if (board)
+ break;
+ }
+ }
+ if (i == MAX_NUM_NI_USB_INTERFACES) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return 0;
+ }
+
+ struct ni_usb_priv *ni_priv = board->private_data;
+
+ if (ni_priv) {
+ ni_usb_set_interrupt_monitor(board, 0);
+ retval = ni_usb_shutdown_hardware(ni_priv);
+ if (retval) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ if (ni_priv->interrupt_urb) {
+ mutex_lock(&ni_priv->interrupt_transfer_lock);
+ ni_usb_cleanup_urbs(ni_priv);
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ }
+ dev_dbg(&usb_dev->dev,
+ "bus %d dev num %d gpib%d, interface %i suspended\n",
+ usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+ }
+
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return 0;
+}
+
+static int ni_usb_driver_resume(struct usb_interface *interface)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+
+ struct gpib_board *board;
+ int i, retval;
+
+ mutex_lock(&ni_usb_hotplug_lock);
+
+ for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++) {
+ if (ni_usb_driver_interfaces[i] == interface) {
+ board = usb_get_intfdata(interface);
+ if (board)
+ break;
+ }
+ }
+ if (i == MAX_NUM_NI_USB_INTERFACES) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return 0;
+ }
+
+ struct ni_usb_priv *ni_priv = board->private_data;
+
+ if (ni_priv) {
+ if (ni_priv->interrupt_urb) {
+ mutex_lock(&ni_priv->interrupt_transfer_lock);
+ retval = usb_submit_urb(ni_priv->interrupt_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&usb_dev->dev, "resume failed to resubmit interrupt urb, retval=%i\n",
+ retval);
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ mutex_unlock(&ni_priv->interrupt_transfer_lock);
+ } else {
+ dev_err(&usb_dev->dev, "bug! resume int urb not set up\n");
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return -EINVAL;
+ }
+
+ switch (ni_priv->product_id) {
+ case USB_DEVICE_ID_NI_USB_B:
+ ni_usb_b_read_serial_number(ni_priv);
+ break;
+ case USB_DEVICE_ID_NI_USB_HS:
+ case USB_DEVICE_ID_MC_USB_488:
+ case USB_DEVICE_ID_KUSB_488A:
+ retval = ni_usb_hs_wait_for_ready(ni_priv);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ break;
+ case USB_DEVICE_ID_NI_USB_HS_PLUS:
+ retval = ni_usb_hs_wait_for_ready(ni_priv);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ retval = ni_usb_hs_plus_extra_init(ni_priv);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ break;
+ default:
+ mutex_unlock(&ni_usb_hotplug_lock);
+ dev_err(&usb_dev->dev, "\tDriver bug: unknown endpoints for usb device id\n");
+ return -EINVAL;
+ }
+
+ retval = ni_usb_set_interrupt_monitor(board, 0);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+
+ retval = ni_usb_init(board);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ retval = ni_usb_set_interrupt_monitor(board, ni_usb_ibsta_monitor_mask);
+ if (retval < 0) {
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return retval;
+ }
+ if (board->master)
+ ni_usb_interface_clear(board, 1); // this is a pulsed action
+ if (ni_priv->ren_state)
+ ni_usb_remote_enable(board, 1);
+
+ dev_dbg(&usb_dev->dev,
+ "bus %d dev num %d gpib%d, interface %i resumed\n",
+ usb_dev->bus->busnum, usb_dev->devnum, board->minor, i);
+ }
+
+ mutex_unlock(&ni_usb_hotplug_lock);
+ return 0;
+}
+
+static struct usb_driver ni_usb_bus_driver = {
+ .name = DRV_NAME,
+ .probe = ni_usb_driver_probe,
+ .disconnect = ni_usb_driver_disconnect,
+ .suspend = ni_usb_driver_suspend,
+ .resume = ni_usb_driver_resume,
+ .id_table = ni_usb_driver_device_table,
+};
+
+static int __init ni_usb_init_module(void)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < MAX_NUM_NI_USB_INTERFACES; i++)
+ ni_usb_driver_interfaces[i] = NULL;
+
+ ret = usb_register(&ni_usb_bus_driver);
+ if (ret) {
+ pr_err("usb_register failed: error = %d\n", ret);
+ return ret;
+ }
+
+ ret = gpib_register_driver(&ni_usb_gpib_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit ni_usb_exit_module(void)
+{
+ gpib_unregister_driver(&ni_usb_gpib_interface);
+ usb_deregister(&ni_usb_bus_driver);
+}
+
+module_init(ni_usb_init_module);
+module_exit(ni_usb_exit_module);
diff --git a/drivers/gpib/ni_usb/ni_usb_gpib.h b/drivers/gpib/ni_usb/ni_usb_gpib.h
new file mode 100644
index 000000000000..688f5e08792f
--- /dev/null
+++ b/drivers/gpib/ni_usb/ni_usb_gpib.h
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/***************************************************************************
+ * copyright : (C) 2004 by Frank Mori Hess
+ ***************************************************************************/
+
+#ifndef _NI_USB_GPIB_H
+#define _NI_USB_GPIB_H
+
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+#include "gpibP.h"
+
+enum {
+ USB_VENDOR_ID_NI = 0x3923
+};
+
+enum {
+ USB_DEVICE_ID_NI_USB_B = 0x702a,
+ USB_DEVICE_ID_NI_USB_B_PREINIT = 0x702b, // device id before firmware is loaded
+ USB_DEVICE_ID_NI_USB_HS = 0x709b,
+ USB_DEVICE_ID_NI_USB_HS_PLUS = 0x7618,
+ USB_DEVICE_ID_KUSB_488A = 0x725c,
+ USB_DEVICE_ID_MC_USB_488 = 0x725d
+};
+
+enum ni_usb_device {
+ NIUSB_SUBDEV_TNT4882 = 1,
+ NIUSB_SUBDEV_UNKNOWN2 = 2,
+ NIUSB_SUBDEV_UNKNOWN3 = 3,
+};
+
+enum endpoint_addresses {
+ NIUSB_B_BULK_OUT_ENDPOINT = 0x2,
+ NIUSB_B_BULK_IN_ENDPOINT = 0x2,
+ NIUSB_B_BULK_IN_ALT_ENDPOINT = 0x6,
+ NIUSB_B_INTERRUPT_IN_ENDPOINT = 0x4,
+};
+
+enum hs_enpoint_addresses {
+ NIUSB_HS_BULK_OUT_ENDPOINT = 0x2,
+ NIUSB_HS_BULK_OUT_ALT_ENDPOINT = 0x6,
+ NIUSB_HS_BULK_IN_ENDPOINT = 0x4,
+ NIUSB_HS_BULK_IN_ALT_ENDPOINT = 0x8,
+ NIUSB_HS_INTERRUPT_IN_ENDPOINT = 0x1,
+};
+
+enum hs_plus_endpoint_addresses {
+ NIUSB_HS_PLUS_BULK_OUT_ENDPOINT = 0x1,
+ NIUSB_HS_PLUS_BULK_OUT_ALT_ENDPOINT = 0x4,
+ NIUSB_HS_PLUS_BULK_IN_ENDPOINT = 0x2,
+ NIUSB_HS_PLUS_BULK_IN_ALT_ENDPOINT = 0x5,
+ NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT = 0x3,
+};
+
+struct ni_usb_urb_ctx {
+ struct completion complete;
+ unsigned timed_out : 1;
+};
+
+// struct which defines private_data for ni_usb devices
+struct ni_usb_priv {
+ struct usb_interface *bus_interface;
+ int bulk_out_endpoint;
+ int bulk_in_endpoint;
+ int interrupt_in_endpoint;
+ u8 eos_char;
+ unsigned short eos_mode;
+ unsigned int monitored_ibsta_bits;
+ struct urb *bulk_urb;
+ struct urb *interrupt_urb;
+ u8 interrupt_buffer[0x11];
+ struct mutex addressed_transfer_lock; // protect transfer lock
+ struct mutex bulk_transfer_lock; // protect bulk message sends
+ struct mutex control_transfer_lock; // protect control messages
+ struct mutex interrupt_transfer_lock; // protect interrupt messages
+ struct timer_list bulk_timer;
+ struct ni_usb_urb_ctx context;
+ int product_id;
+ unsigned short ren_state;
+};
+
+struct ni_usb_status_block {
+ short id;
+ unsigned short ibsta;
+ short error_code;
+ unsigned short count;
+};
+
+struct ni_usb_register {
+ enum ni_usb_device device;
+ short address;
+ unsigned short value;
+};
+
+enum ni_usb_bulk_ids {
+ NIUSB_IBCAC_ID = 0x1,
+ NIUSB_UNKNOWN3_ID = 0x3, // device level function id?
+ NIUSB_TERM_ID = 0x4,
+ NIUSB_IBGTS_ID = 0x6,
+ NIUSB_IBRPP_ID = 0x7,
+ NIUSB_REG_READ_ID = 0x8,
+ NIUSB_REG_WRITE_ID = 0x9,
+ NIUSB_IBSIC_ID = 0xf,
+ NIUSB_REGISTER_READ_DATA_START_ID = 0x34,
+ NIUSB_REGISTER_READ_DATA_END_ID = 0x35,
+ NIUSB_IBRD_DATA_ID = 0x36,
+ NIUSB_IBRD_EXTENDED_DATA_ID = 0x37,
+ NIUSB_IBRD_STATUS_ID = 0x38
+};
+
+enum ni_usb_error_codes {
+ NIUSB_NO_ERROR = 0,
+ /*
+ * NIUSB_ABORTED_ERROR occurs when I/O is interrupted early by
+ * doing a NI_USB_STOP_REQUEST on the control endpoint.
+ */
+ NIUSB_ABORTED_ERROR = 1,
+ /*
+ * NIUSB_READ_ATN_ERROR occurs when you do a board read while
+ * ATN is set
+ */
+ NIUSB_ATN_STATE_ERROR = 2,
+ /*
+ * NIUSB_ADDRESSING_ERROR occurs when you do a board
+ * read/write as CIC but are not in LACS/TACS
+ */
+ NIUSB_ADDRESSING_ERROR = 3,
+ /*
+ * NIUSB_EOSMODE_ERROR occurs on reads if any eos mode or char
+ * bits are set when REOS is not set.
+ * Have also seen error 4 if you try to send more than 16
+ * command bytes at once on a usb-b.
+ */
+ NIUSB_EOSMODE_ERROR = 4,
+ /*
+ * NIUSB_NO_BUS_ERROR occurs when you try to write a command
+ * byte but there are no devices connected to the gpib bus
+ */
+ NIUSB_NO_BUS_ERROR = 5,
+ /*
+ * NIUSB_NO_LISTENER_ERROR occurs when you do a board write as
+ * CIC with no listener
+ */
+ NIUSB_NO_LISTENER_ERROR = 8,
+ /* get NIUSB_TIMEOUT_ERROR on board read/write timeout */
+ NIUSB_TIMEOUT_ERROR = 10,
+};
+
+enum ni_usb_control_requests {
+ NI_USB_STOP_REQUEST = 0x20,
+ NI_USB_WAIT_REQUEST = 0x21,
+ NI_USB_POLL_READY_REQUEST = 0x40,
+ NI_USB_SERIAL_NUMBER_REQUEST = 0x41,
+ NI_USB_HS_PLUS_0x48_REQUEST = 0x48,
+ NI_USB_HS_PLUS_LED_REQUEST = 0x4b,
+ NI_USB_HS_PLUS_0xf8_REQUEST = 0xf8
+};
+
+static const unsigned int ni_usb_ibsta_monitor_mask =
+ SRQI | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS;
+
+static inline int nec7210_to_tnt4882_offset(int offset)
+{
+ return 2 * offset;
+};
+
+static inline int ni_usb_bulk_termination(u8 *buffer)
+{
+ int i = 0;
+
+ buffer[i++] = NIUSB_TERM_ID;
+ buffer[i++] = 0x0;
+ buffer[i++] = 0x0;
+ buffer[i++] = 0x0;
+ return i;
+}
+
+enum ni_usb_unknown3_register {
+ SERIAL_NUMBER_4_REG = 0x8,
+ SERIAL_NUMBER_3_REG = 0x9,
+ SERIAL_NUMBER_2_REG = 0xa,
+ SERIAL_NUMBER_1_REG = 0xb,
+};
+
+static inline int ni_usb_bulk_register_write_header(u8 *buffer, int num_writes)
+{
+ int i = 0;
+
+ buffer[i++] = NIUSB_REG_WRITE_ID;
+ buffer[i++] = num_writes;
+ buffer[i++] = 0x0;
+ return i;
+}
+
+static inline int ni_usb_bulk_register_write(u8 *buffer, struct ni_usb_register reg)
+{
+ int i = 0;
+
+ buffer[i++] = reg.device;
+ buffer[i++] = reg.address;
+ buffer[i++] = reg.value;
+ return i;
+}
+
+static inline int ni_usb_bulk_register_read_header(u8 *buffer, int num_reads)
+{
+ int i = 0;
+
+ buffer[i++] = NIUSB_REG_READ_ID;
+ buffer[i++] = num_reads;
+ return i;
+}
+
+static inline int ni_usb_bulk_register_read(u8 *buffer, int device, int address)
+{
+ int i = 0;
+
+ buffer[i++] = device;
+ buffer[i++] = address;
+ return i;
+}
+
+#endif // _NI_USB_GPIB_H
diff --git a/drivers/gpib/pc2/Makefile b/drivers/gpib/pc2/Makefile
new file mode 100644
index 000000000000..481ee4296e1b
--- /dev/null
+++ b/drivers/gpib/pc2/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_GPIB_PC2) += pc2_gpib.o
+
+
+
diff --git a/drivers/gpib/pc2/pc2_gpib.c b/drivers/gpib/pc2/pc2_gpib.c
new file mode 100644
index 000000000000..9f3943d1df66
--- /dev/null
+++ b/drivers/gpib/pc2/pc2_gpib.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include "nec7210.h"
+#include "gpibP.h"
+
+// struct which defines private_data for pc2 driver
+struct pc2_priv {
+ struct nec7210_priv nec7210_priv;
+ unsigned int irq;
+ // io address that clears interrupt for pc2a (0x2f0 + irq)
+ unsigned int clear_intr_addr;
+};
+
+// pc2 uses 8 consecutive io addresses
+static const int pc2_iosize = 8;
+static const int pc2a_iosize = 8;
+static const int pc2_2a_iosize = 16;
+
+// offset between io addresses of successive nec7210 registers
+static const int pc2a_reg_offset = 0x400;
+static const int pc2_reg_offset = 1;
+
+// interrupt service routine
+static irqreturn_t pc2_interrupt(int irq, void *arg);
+static irqreturn_t pc2a_interrupt(int irq, void *arg);
+
+// pc2 specific registers and bits
+
+// interrupt clear register address
+static const int pc2a_clear_intr_iobase = 0x2f0;
+static inline unsigned int CLEAR_INTR_REG(unsigned int irq)
+{
+ return pc2a_clear_intr_iobase + irq;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for PC2/PC2a and compatible devices");
+
+/*
+ * GPIB interrupt service routines
+ */
+
+irqreturn_t pc2_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct pc2_priv *priv = board->private_data;
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = nec7210_interrupt(board, &priv->nec7210_priv);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+irqreturn_t pc2a_interrupt(int irq, void *arg)
+{
+ struct gpib_board *board = arg;
+ struct pc2_priv *priv = board->private_data;
+ int status1, status2;
+ unsigned long flags;
+ irqreturn_t retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ // read interrupt status (also clears status)
+ status1 = read_byte(&priv->nec7210_priv, ISR1);
+ status2 = read_byte(&priv->nec7210_priv, ISR2);
+ /* clear interrupt circuit */
+ if (priv->irq)
+ outb(0xff, CLEAR_INTR_REG(priv->irq));
+ retval = nec7210_interrupt_have_status(board, &priv->nec7210_priv, status1, status2);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return retval;
+}
+
+// wrappers for interface functions
+static int pc2_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+}
+
+static int pc2_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int pc2_command(struct gpib_board *board, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int pc2_take_control(struct gpib_board *board, int synchronous)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int pc2_go_to_standby(struct gpib_board *board)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int pc2_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
+}
+
+static void pc2_interface_clear(struct gpib_board *board, int assert)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void pc2_remote_enable(struct gpib_board *board, int enable)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int pc2_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void pc2_disable_eos(struct gpib_board *board)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int pc2_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
+}
+
+static int pc2_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int pc2_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int pc2_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
+}
+
+static void pc2_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
+}
+
+static void pc2_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+static void pc2_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
+}
+
+static u8 pc2_serial_poll_status(struct gpib_board *board)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static int pc2_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec);
+}
+
+static void pc2_return_to_local(struct gpib_board *board)
+{
+ struct pc2_priv *priv = board->private_data;
+
+ nec7210_return_to_local(board, &priv->nec7210_priv);
+}
+
+static int allocate_private(struct gpib_board *board)
+{
+ struct pc2_priv *priv;
+
+ board->private_data = kmalloc(sizeof(struct pc2_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -1;
+ priv = board->private_data;
+ memset(priv, 0, sizeof(struct pc2_priv));
+ init_nec7210_private(&priv->nec7210_priv);
+ return 0;
+}
+
+static void free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static int pc2_generic_attach(struct gpib_board *board, const struct gpib_board_config *config,
+ enum nec7210_chipset chipset)
+{
+ struct pc2_priv *pc2_priv;
+ struct nec7210_priv *nec_priv;
+
+ board->status = 0;
+ if (allocate_private(board))
+ return -ENOMEM;
+ pc2_priv = board->private_data;
+ nec_priv = &pc2_priv->nec7210_priv;
+ nec_priv->read_byte = nec7210_ioport_read_byte;
+ nec_priv->write_byte = nec7210_ioport_write_byte;
+ nec_priv->type = chipset;
+
+#ifndef PC2_DMA
+ /*
+ * board->dev hasn't been initialized, so forget about DMA until this driver
+ * is adapted to use isa_register_driver.
+ */
+ if (config->ibdma)
+ // driver needs to be adapted to use isa_register_driver to get a struct device*
+ dev_err(board->gpib_dev, "DMA disabled for pc2 gpib");
+#else
+ if (config->ibdma) {
+ nec_priv->dma_buffer_length = 0x1000;
+ nec_priv->dma_buffer = dma_alloc_coherent(board->dev,
+ nec_priv->dma_buffer_length, &
+ nec_priv->dma_buffer_addr, GFP_ATOMIC);
+ if (!nec_priv->dma_buffer)
+ return -ENOMEM;
+
+ // request isa dma channel
+ if (request_dma(config->ibdma, "pc2")) {
+ dev_err(board->gpib_dev, "can't request DMA %d\n", config->ibdma);
+ return -1;
+ }
+ nec_priv->dma_channel = config->ibdma;
+ }
+#endif
+
+ return 0;
+}
+
+static int pc2_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ int isr_flags = 0;
+ struct pc2_priv *pc2_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+
+ retval = pc2_generic_attach(board, config, NEC7210);
+ if (retval)
+ return retval;
+
+ pc2_priv = board->private_data;
+ nec_priv = &pc2_priv->nec7210_priv;
+ nec_priv->offset = pc2_reg_offset;
+
+ if (!request_region(config->ibbase, pc2_iosize, "pc2")) {
+ dev_err(board->gpib_dev, "ioports are already in use\n");
+ return -EBUSY;
+ }
+ nec_priv->iobase = config->ibbase;
+
+ nec7210_board_reset(nec_priv, board);
+
+ // install interrupt handler
+ if (config->ibirq) {
+ if (request_irq(config->ibirq, pc2_interrupt, isr_flags, "pc2", board)) {
+ dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq);
+ return -EBUSY;
+ }
+ }
+ pc2_priv->irq = config->ibirq;
+ /* poll so we can detect assertion of ATN */
+ if (gpib_request_pseudo_irq(board, pc2_interrupt)) {
+ dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n");
+ return -1;
+ }
+ /* set internal counter register for 8 MHz input clock */
+ write_byte(nec_priv, ICR | 8, AUXMR);
+
+ nec7210_board_online(nec_priv, board);
+
+ return 0;
+}
+
+static void pc2_detach(struct gpib_board *board)
+{
+ struct pc2_priv *pc2_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (pc2_priv) {
+ nec_priv = &pc2_priv->nec7210_priv;
+#ifdef PC2_DMA
+ if (nec_priv->dma_channel)
+ free_dma(nec_priv->dma_channel);
+#endif
+ gpib_free_pseudo_irq(board);
+ if (pc2_priv->irq)
+ free_irq(pc2_priv->irq, board);
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ release_region(nec_priv->iobase, pc2_iosize);
+ }
+ if (nec_priv->dma_buffer) {
+ dma_free_coherent(board->dev, nec_priv->dma_buffer_length,
+ nec_priv->dma_buffer, nec_priv->dma_buffer_addr);
+ nec_priv->dma_buffer = NULL;
+ }
+ }
+ free_private(board);
+}
+
+static int pc2a_common_attach(struct gpib_board *board, const struct gpib_board_config *config,
+ unsigned int num_registers, enum nec7210_chipset chipset)
+{
+ unsigned int i, j;
+ struct pc2_priv *pc2_priv;
+ struct nec7210_priv *nec_priv;
+ int retval;
+
+ retval = pc2_generic_attach(board, config, chipset);
+ if (retval)
+ return retval;
+
+ pc2_priv = board->private_data;
+ nec_priv = &pc2_priv->nec7210_priv;
+ nec_priv->offset = pc2a_reg_offset;
+
+ switch (config->ibbase) {
+ case 0x02e1:
+ case 0x22e1:
+ case 0x42e1:
+ case 0x62e1:
+ break;
+ default:
+ dev_err(board->gpib_dev, "PCIIa base range invalid, must be one of 0x[0246]2e1, but is 0x%x\n",
+ config->ibbase);
+ return -1;
+ }
+
+ if (config->ibirq) {
+ if (config->ibirq < 2 || config->ibirq > 7) {
+ dev_err(board->gpib_dev, "illegal interrupt level %i\n",
+ config->ibirq);
+ return -1;
+ }
+ } else {
+ dev_err(board->gpib_dev, "interrupt disabled, using polling mode (slow)\n");
+ }
+#ifdef CHECK_IOPORTS
+ unsigned int err = 0;
+
+ for (i = 0; i < num_registers; i++) {
+ if (check_region(config->ibbase + i * pc2a_reg_offset, 1))
+ err++;
+ }
+ if (config->ibirq && check_region(pc2a_clear_intr_iobase + config->ibirq, 1))
+ err++;
+ if (err) {
+ dev_err(board->gpib_dev, "ioports are already in use");
+ return -EBUSY;
+ }
+#endif
+ for (i = 0; i < num_registers; i++) {
+ if (!request_region(config->ibbase +
+ i * pc2a_reg_offset, 1, "pc2a")) {
+ dev_err(board->gpib_dev, "ioports are already in use");
+ for (j = 0; j < i; j++)
+ release_region(config->ibbase +
+ j * pc2a_reg_offset, 1);
+ return -EBUSY;
+ }
+ }
+ nec_priv->iobase = config->ibbase;
+ if (config->ibirq) {
+ if (!request_region(pc2a_clear_intr_iobase + config->ibirq, 1, "pc2a")) {
+ dev_err(board->gpib_dev, "ioports are already in use");
+ return -1;
+ }
+ pc2_priv->clear_intr_addr = pc2a_clear_intr_iobase + config->ibirq;
+ if (request_irq(config->ibirq, pc2a_interrupt, 0, "pc2a", board)) {
+ dev_err(board->gpib_dev, "can't request IRQ %d\n", config->ibirq);
+ return -EBUSY;
+ }
+ }
+ pc2_priv->irq = config->ibirq;
+ /* poll so we can detect assertion of ATN */
+ if (gpib_request_pseudo_irq(board, pc2_interrupt)) {
+ dev_err(board->gpib_dev, "failed to allocate pseudo_irq\n");
+ return -1;
+ }
+
+ // make sure interrupt is clear
+ if (pc2_priv->irq)
+ outb(0xff, CLEAR_INTR_REG(pc2_priv->irq));
+
+ nec7210_board_reset(nec_priv, board);
+
+ /* set internal counter register for 8 MHz input clock */
+ write_byte(nec_priv, ICR | 8, AUXMR);
+
+ nec7210_board_online(nec_priv, board);
+
+ return 0;
+}
+
+static int pc2a_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return pc2a_common_attach(board, config, pc2a_iosize, NEC7210);
+}
+
+static int pc2a_cb7210_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return pc2a_common_attach(board, config, pc2a_iosize, CB7210);
+}
+
+static int pc2_2a_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return pc2a_common_attach(board, config, pc2_2a_iosize, NAT4882);
+}
+
+static void pc2a_common_detach(struct gpib_board *board, unsigned int num_registers)
+{
+ int i;
+ struct pc2_priv *pc2_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (pc2_priv) {
+ nec_priv = &pc2_priv->nec7210_priv;
+#ifdef PC2_DMA
+ if (nec_priv->dma_channel)
+ free_dma(nec_priv->dma_channel);
+#endif
+ gpib_free_pseudo_irq(board);
+ if (pc2_priv->irq)
+ free_irq(pc2_priv->irq, board);
+ if (nec_priv->iobase) {
+ nec7210_board_reset(nec_priv, board);
+ for (i = 0; i < num_registers; i++)
+ release_region(nec_priv->iobase +
+ i * pc2a_reg_offset, 1);
+ }
+ if (pc2_priv->clear_intr_addr)
+ release_region(pc2_priv->clear_intr_addr, 1);
+ if (nec_priv->dma_buffer) {
+ dma_free_coherent(board->dev, nec_priv->dma_buffer_length,
+ nec_priv->dma_buffer,
+ nec_priv->dma_buffer_addr);
+ nec_priv->dma_buffer = NULL;
+ }
+ }
+ free_private(board);
+}
+
+static void pc2a_detach(struct gpib_board *board)
+{
+ pc2a_common_detach(board, pc2a_iosize);
+}
+
+static void pc2_2a_detach(struct gpib_board *board)
+{
+ pc2a_common_detach(board, pc2_2a_iosize);
+}
+
+static struct gpib_interface pc2_interface = {
+ .name = "pcII",
+ .attach = pc2_attach,
+ .detach = pc2_detach,
+ .read = pc2_read,
+ .write = pc2_write,
+ .command = pc2_command,
+ .take_control = pc2_take_control,
+ .go_to_standby = pc2_go_to_standby,
+ .request_system_control = pc2_request_system_control,
+ .interface_clear = pc2_interface_clear,
+ .remote_enable = pc2_remote_enable,
+ .enable_eos = pc2_enable_eos,
+ .disable_eos = pc2_disable_eos,
+ .parallel_poll = pc2_parallel_poll,
+ .parallel_poll_configure = pc2_parallel_poll_configure,
+ .parallel_poll_response = pc2_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL,
+ .update_status = pc2_update_status,
+ .primary_address = pc2_primary_address,
+ .secondary_address = pc2_secondary_address,
+ .serial_poll_response = pc2_serial_poll_response,
+ .serial_poll_status = pc2_serial_poll_status,
+ .t1_delay = pc2_t1_delay,
+ .return_to_local = pc2_return_to_local,
+};
+
+static struct gpib_interface pc2a_interface = {
+ .name = "pcIIa",
+ .attach = pc2a_attach,
+ .detach = pc2a_detach,
+ .read = pc2_read,
+ .write = pc2_write,
+ .command = pc2_command,
+ .take_control = pc2_take_control,
+ .go_to_standby = pc2_go_to_standby,
+ .request_system_control = pc2_request_system_control,
+ .interface_clear = pc2_interface_clear,
+ .remote_enable = pc2_remote_enable,
+ .enable_eos = pc2_enable_eos,
+ .disable_eos = pc2_disable_eos,
+ .parallel_poll = pc2_parallel_poll,
+ .parallel_poll_configure = pc2_parallel_poll_configure,
+ .parallel_poll_response = pc2_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL,
+ .update_status = pc2_update_status,
+ .primary_address = pc2_primary_address,
+ .secondary_address = pc2_secondary_address,
+ .serial_poll_response = pc2_serial_poll_response,
+ .serial_poll_status = pc2_serial_poll_status,
+ .t1_delay = pc2_t1_delay,
+ .return_to_local = pc2_return_to_local,
+};
+
+static struct gpib_interface pc2a_cb7210_interface = {
+ .name = "pcIIa_cb7210",
+ .attach = pc2a_cb7210_attach,
+ .detach = pc2a_detach,
+ .read = pc2_read,
+ .write = pc2_write,
+ .command = pc2_command,
+ .take_control = pc2_take_control,
+ .go_to_standby = pc2_go_to_standby,
+ .request_system_control = pc2_request_system_control,
+ .interface_clear = pc2_interface_clear,
+ .remote_enable = pc2_remote_enable,
+ .enable_eos = pc2_enable_eos,
+ .disable_eos = pc2_disable_eos,
+ .parallel_poll = pc2_parallel_poll,
+ .parallel_poll_configure = pc2_parallel_poll_configure,
+ .parallel_poll_response = pc2_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL, // XXX
+ .update_status = pc2_update_status,
+ .primary_address = pc2_primary_address,
+ .secondary_address = pc2_secondary_address,
+ .serial_poll_response = pc2_serial_poll_response,
+ .serial_poll_status = pc2_serial_poll_status,
+ .t1_delay = pc2_t1_delay,
+ .return_to_local = pc2_return_to_local,
+};
+
+static struct gpib_interface pc2_2a_interface = {
+ .name = "pcII_IIa",
+ .attach = pc2_2a_attach,
+ .detach = pc2_2a_detach,
+ .read = pc2_read,
+ .write = pc2_write,
+ .command = pc2_command,
+ .take_control = pc2_take_control,
+ .go_to_standby = pc2_go_to_standby,
+ .request_system_control = pc2_request_system_control,
+ .interface_clear = pc2_interface_clear,
+ .remote_enable = pc2_remote_enable,
+ .enable_eos = pc2_enable_eos,
+ .disable_eos = pc2_disable_eos,
+ .parallel_poll = pc2_parallel_poll,
+ .parallel_poll_configure = pc2_parallel_poll_configure,
+ .parallel_poll_response = pc2_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL,
+ .update_status = pc2_update_status,
+ .primary_address = pc2_primary_address,
+ .secondary_address = pc2_secondary_address,
+ .serial_poll_response = pc2_serial_poll_response,
+ .serial_poll_status = pc2_serial_poll_status,
+ .t1_delay = pc2_t1_delay,
+ .return_to_local = pc2_return_to_local,
+};
+
+static int __init pc2_init_module(void)
+{
+ int ret;
+
+ ret = gpib_register_driver(&pc2_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ return ret;
+ }
+
+ ret = gpib_register_driver(&pc2a_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pc2a;
+ }
+
+ ret = gpib_register_driver(&pc2a_cb7210_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_cb7210;
+ }
+
+ ret = gpib_register_driver(&pc2_2a_interface, THIS_MODULE);
+ if (ret) {
+ pr_err("gpib_register_driver failed: error = %d\n", ret);
+ goto err_pc2_2a;
+ }
+
+ return 0;
+
+err_pc2_2a:
+ gpib_unregister_driver(&pc2a_cb7210_interface);
+err_cb7210:
+ gpib_unregister_driver(&pc2a_interface);
+err_pc2a:
+ gpib_unregister_driver(&pc2_interface);
+
+ return ret;
+}
+
+static void __exit pc2_exit_module(void)
+{
+ gpib_unregister_driver(&pc2_interface);
+ gpib_unregister_driver(&pc2a_interface);
+ gpib_unregister_driver(&pc2a_cb7210_interface);
+ gpib_unregister_driver(&pc2_2a_interface);
+}
+
+module_init(pc2_init_module);
+module_exit(pc2_exit_module);
+
diff --git a/drivers/gpib/tms9914/Makefile b/drivers/gpib/tms9914/Makefile
new file mode 100644
index 000000000000..4705ab07f413
--- /dev/null
+++ b/drivers/gpib/tms9914/Makefile
@@ -0,0 +1,6 @@
+
+obj-$(CONFIG_GPIB_TMS9914) += tms9914.o
+
+
+
+
diff --git a/drivers/gpib/tms9914/tms9914.c b/drivers/gpib/tms9914/tms9914.c
new file mode 100644
index 000000000000..72a11596a35e
--- /dev/null
+++ b/drivers/gpib/tms9914/tms9914.c
@@ -0,0 +1,914 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/dma.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+
+#include "gpibP.h"
+#include "tms9914.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB library for tms9914");
+
+static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv);
+
+int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int synchronous)
+{
+ int i;
+ const int timeout = 100;
+
+ if (synchronous)
+ write_byte(priv, AUX_TCS, AUXCR);
+ else
+ write_byte(priv, AUX_TCA, AUXCR);
+ // busy wait until ATN is asserted
+ for (i = 0; i < timeout; i++) {
+ if ((read_byte(priv, ADSR) & HR_ATN))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+
+ clear_bit(WRITE_READY_BN, &priv->state);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tms9914_take_control);
+
+/*
+ * The agilent 82350B has a buggy implementation of tcs which interferes with the
+ * operation of tca. It appears to be based on the controller state machine
+ * described in the TI 9900 TMS9914A data manual published in 1982. This
+ * manual describes tcs as putting the controller into a CWAS
+ * state where it waits indefinitely for ANRS and ignores tca. Since a
+ * functioning tca is far more important than tcs, we work around the
+ * problem by never issuing tcs.
+ *
+ * I don't know if this problem exists in the real tms9914a or just in the fpga
+ * of the 82350B. For now, only the agilent_82350b uses this workaround.
+ * The rest of the tms9914 based drivers still use tms9914_take_control
+ * directly (which does issue tcs).
+ */
+int tms9914_take_control_workaround(struct gpib_board *board,
+ struct tms9914_priv *priv, int synchronous)
+{
+ if (synchronous)
+ return -ETIMEDOUT;
+ return tms9914_take_control(board, priv, synchronous);
+}
+EXPORT_SYMBOL_GPL(tms9914_take_control_workaround);
+
+int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ int i;
+ const int timeout = 1000;
+
+ write_byte(priv, AUX_GTS, AUXCR);
+ // busy wait until ATN is released
+ for (i = 0; i < timeout; i++) {
+ if ((read_byte(priv, ADSR) & HR_ATN) == 0)
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+
+ clear_bit(COMMAND_READY_BN, &priv->state);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tms9914_go_to_standby);
+
+void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert)
+{
+ if (assert) {
+ write_byte(priv, AUX_SIC | AUX_CS, AUXCR);
+
+ set_bit(CIC_NUM, &board->status);
+ } else {
+ write_byte(priv, AUX_SIC, AUXCR);
+ }
+}
+EXPORT_SYMBOL_GPL(tms9914_interface_clear);
+
+void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable)
+{
+ if (enable)
+ write_byte(priv, AUX_SRE | AUX_CS, AUXCR);
+ else
+ write_byte(priv, AUX_SRE, AUXCR);
+}
+EXPORT_SYMBOL_GPL(tms9914_remote_enable);
+
+int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv,
+ int request_control)
+{
+ if (request_control) {
+ write_byte(priv, AUX_RQC, AUXCR);
+ } else {
+ clear_bit(CIC_NUM, &board->status);
+ write_byte(priv, AUX_RLC, AUXCR);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tms9914_request_system_control);
+
+unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv,
+ unsigned int nano_sec)
+{
+ static const int clock_period = 200; // assuming 5Mhz input clock
+ int num_cycles;
+
+ num_cycles = 12;
+
+ if (nano_sec <= 8 * clock_period) {
+ write_byte(priv, AUX_STDL | AUX_CS, AUXCR);
+ num_cycles = 8;
+ } else {
+ write_byte(priv, AUX_STDL, AUXCR);
+ }
+
+ if (nano_sec <= 4 * clock_period) {
+ write_byte(priv, AUX_VSTDL | AUX_CS, AUXCR);
+ num_cycles = 4;
+ } else {
+ write_byte(priv, AUX_VSTDL, AUXCR);
+ }
+
+ return num_cycles * clock_period;
+}
+EXPORT_SYMBOL_GPL(tms9914_t1_delay);
+
+void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv)
+{
+ write_byte(priv, AUX_RTL, AUXCR);
+}
+EXPORT_SYMBOL_GPL(tms9914_return_to_local);
+
+void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode)
+{
+ switch (mode) {
+ case TMS9914_HOLDOFF_NONE:
+ write_byte(priv, AUX_HLDE, AUXCR);
+ write_byte(priv, AUX_HLDA, AUXCR);
+ break;
+ case TMS9914_HOLDOFF_EOI:
+ write_byte(priv, AUX_HLDE | AUX_CS, AUXCR);
+ write_byte(priv, AUX_HLDA, AUXCR);
+ break;
+ case TMS9914_HOLDOFF_ALL:
+ write_byte(priv, AUX_HLDE, AUXCR);
+ write_byte(priv, AUX_HLDA | AUX_CS, AUXCR);
+ break;
+ default:
+ pr_err("bug! bad holdoff mode %i\n", mode);
+ break;
+ }
+ priv->holdoff_mode = mode;
+}
+EXPORT_SYMBOL_GPL(tms9914_set_holdoff_mode);
+
+void tms9914_release_holdoff(struct tms9914_priv *priv)
+{
+ if (priv->holdoff_active) {
+ write_byte(priv, AUX_RHDF, AUXCR);
+ priv->holdoff_active = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(tms9914_release_holdoff);
+
+int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_byte,
+ int compare_8_bits)
+{
+ priv->eos = eos_byte;
+ priv->eos_flags = REOS;
+ if (compare_8_bits)
+ priv->eos_flags |= BIN;
+ return 0;
+}
+EXPORT_SYMBOL(tms9914_enable_eos);
+
+void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ priv->eos_flags &= ~REOS;
+}
+EXPORT_SYMBOL(tms9914_disable_eos);
+
+int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result)
+{
+ // execute parallel poll
+ write_byte(priv, AUX_CS | AUX_RPP, AUXCR);
+ udelay(2);
+ *result = read_byte(priv, CPTR);
+ // clear parallel poll state
+ write_byte(priv, AUX_RPP, AUXCR);
+ return 0;
+}
+EXPORT_SYMBOL(tms9914_parallel_poll);
+
+static void set_ppoll_reg(struct tms9914_priv *priv, int enable,
+ unsigned int dio_line, int sense, int ist)
+{
+ u8 dio_byte;
+
+ if (enable && ((sense && ist) || (!sense && !ist))) {
+ dio_byte = 1 << (dio_line - 1);
+ write_byte(priv, dio_byte, PPR);
+ } else {
+ write_byte(priv, 0, PPR);
+ }
+}
+
+void tms9914_parallel_poll_configure(struct gpib_board *board,
+ struct tms9914_priv *priv, u8 config)
+{
+ priv->ppoll_enable = (config & PPC_DISABLE) == 0;
+ priv->ppoll_line = (config & PPC_DIO_MASK) + 1;
+ priv->ppoll_sense = (config & PPC_SENSE) != 0;
+ set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, board->ist);
+}
+EXPORT_SYMBOL(tms9914_parallel_poll_configure);
+
+void tms9914_parallel_poll_response(struct gpib_board *board,
+ struct tms9914_priv *priv, int ist)
+{
+ set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, ist);
+}
+EXPORT_SYMBOL(tms9914_parallel_poll_response);
+
+void tms9914_serial_poll_response(struct gpib_board *board,
+ struct tms9914_priv *priv, u8 status)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ write_byte(priv, status, SPMR);
+ priv->spoll_status = status;
+ if (status & request_service_bit)
+ write_byte(priv, AUX_RSV2 | AUX_CS, AUXCR);
+ else
+ write_byte(priv, AUX_RSV2, AUXCR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+EXPORT_SYMBOL(tms9914_serial_poll_response);
+
+u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ u8 status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ status = priv->spoll_status;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return status;
+}
+EXPORT_SYMBOL(tms9914_serial_poll_status);
+
+int tms9914_primary_address(struct gpib_board *board,
+ struct tms9914_priv *priv, unsigned int address)
+{
+ // put primary address in address0
+ write_byte(priv, address & ADDRESS_MASK, ADR);
+ return 0;
+}
+EXPORT_SYMBOL(tms9914_primary_address);
+
+int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv,
+ unsigned int address, int enable)
+{
+ if (enable)
+ priv->imr1_bits |= HR_APTIE;
+ else
+ priv->imr1_bits &= ~HR_APTIE;
+
+ write_byte(priv, priv->imr1_bits, IMR1);
+ return 0;
+}
+EXPORT_SYMBOL(tms9914_secondary_address);
+
+unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv,
+ unsigned int clear_mask)
+{
+ unsigned long flags;
+ unsigned int retval;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ retval = update_status_nolock(board, priv);
+ board->status &= ~clear_mask;
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return retval;
+}
+EXPORT_SYMBOL(tms9914_update_status);
+
+static void update_talker_state(struct tms9914_priv *priv, unsigned int address_status_bits)
+{
+ if (address_status_bits & HR_TA) {
+ if (address_status_bits & HR_ATN)
+ priv->talker_state = talker_addressed;
+ else
+ /*
+ * this could also be serial_poll_active, but the tms9914 provides no
+ * way to distinguish, so we'll assume talker_active
+ */
+ priv->talker_state = talker_active;
+ } else {
+ priv->talker_state = talker_idle;
+ }
+}
+
+static void update_listener_state(struct tms9914_priv *priv, unsigned int address_status_bits)
+{
+ if (address_status_bits & HR_LA) {
+ if (address_status_bits & HR_ATN)
+ priv->listener_state = listener_addressed;
+ else
+ priv->listener_state = listener_active;
+ } else {
+ priv->listener_state = listener_idle;
+ }
+}
+
+static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ int address_status;
+ int bsr_bits;
+
+ address_status = read_byte(priv, ADSR);
+
+ // check for remote/local
+ if (address_status & HR_REM)
+ set_bit(REM_NUM, &board->status);
+ else
+ clear_bit(REM_NUM, &board->status);
+ // check for lockout
+ if (address_status & HR_LLO)
+ set_bit(LOK_NUM, &board->status);
+ else
+ clear_bit(LOK_NUM, &board->status);
+ // check for ATN
+ if (address_status & HR_ATN)
+ set_bit(ATN_NUM, &board->status);
+ else
+ clear_bit(ATN_NUM, &board->status);
+ // check for talker/listener addressed
+ update_talker_state(priv, address_status);
+ if (priv->talker_state == talker_active || priv->talker_state == talker_addressed)
+ set_bit(TACS_NUM, &board->status);
+ else
+ clear_bit(TACS_NUM, &board->status);
+
+ update_listener_state(priv, address_status);
+ if (priv->listener_state == listener_active || priv->listener_state == listener_addressed)
+ set_bit(LACS_NUM, &board->status);
+ else
+ clear_bit(LACS_NUM, &board->status);
+ // Check for SRQI - not reset elsewhere except in autospoll
+ if (board->status & SRQI) {
+ bsr_bits = read_byte(priv, BSR);
+ if (!(bsr_bits & BSR_SRQ_BIT))
+ clear_bit(SRQI_NUM, &board->status);
+ }
+
+ dev_dbg(board->gpib_dev, "status 0x%lx, state 0x%lx\n", board->status, priv->state);
+
+ return board->status;
+}
+
+int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv)
+{
+ int bsr_bits;
+ int status = VALID_ALL;
+
+ bsr_bits = read_byte(priv, BSR);
+
+ if (bsr_bits & BSR_REN_BIT)
+ status |= BUS_REN;
+ if (bsr_bits & BSR_IFC_BIT)
+ status |= BUS_IFC;
+ if (bsr_bits & BSR_SRQ_BIT)
+ status |= BUS_SRQ;
+ if (bsr_bits & BSR_EOI_BIT)
+ status |= BUS_EOI;
+ if (bsr_bits & BSR_NRFD_BIT)
+ status |= BUS_NRFD;
+ if (bsr_bits & BSR_NDAC_BIT)
+ status |= BUS_NDAC;
+ if (bsr_bits & BSR_DAV_BIT)
+ status |= BUS_DAV;
+ if (bsr_bits & BSR_ATN_BIT)
+ status |= BUS_ATN;
+
+ return status;
+}
+EXPORT_SYMBOL(tms9914_line_status);
+
+static int check_for_eos(struct tms9914_priv *priv, u8 byte)
+{
+ static const u8 seven_bit_compare_mask = 0x7f;
+
+ if ((priv->eos_flags & REOS) == 0)
+ return 0;
+
+ if (priv->eos_flags & BIN) {
+ if (priv->eos == byte)
+ return 1;
+ } else {
+ if ((priv->eos & seven_bit_compare_mask) == (byte & seven_bit_compare_mask))
+ return 1;
+ }
+ return 0;
+}
+
+static int wait_for_read_byte(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ if (wait_event_interruptible(board->wait,
+ test_bit(READ_READY_BN, &priv->state) ||
+ test_bit(DEV_CLEAR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ return -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+
+ if (test_bit(DEV_CLEAR_BN, &priv->state))
+ return -EINTR;
+ return 0;
+}
+
+static inline u8 tms9914_read_data_in(struct gpib_board *board,
+ struct tms9914_priv *priv, int *end)
+{
+ unsigned long flags;
+ u8 data;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ clear_bit(READ_READY_BN, &priv->state);
+ data = read_byte(priv, DIR);
+ if (test_and_clear_bit(RECEIVED_END_BN, &priv->state))
+ *end = 1;
+ else
+ *end = 0;
+ switch (priv->holdoff_mode) {
+ case TMS9914_HOLDOFF_EOI:
+ if (*end)
+ priv->holdoff_active = 1;
+ break;
+ case TMS9914_HOLDOFF_ALL:
+ priv->holdoff_active = 1;
+ break;
+ case TMS9914_HOLDOFF_NONE:
+ break;
+ default:
+ dev_err(board->gpib_dev, "bug! bad holdoff mode %i\n", priv->holdoff_mode);
+ break;
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ return data;
+}
+
+static int pio_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval = 0;
+
+ *bytes_read = 0;
+ *end = 0;
+ while (*bytes_read < length && *end == 0) {
+ tms9914_release_holdoff(priv);
+ retval = wait_for_read_byte(board, priv);
+ if (retval < 0)
+ return retval;
+ buffer[(*bytes_read)++] = tms9914_read_data_in(board, priv, end);
+
+ if (check_for_eos(priv, buffer[*bytes_read - 1]))
+ *end = 1;
+ }
+
+ return retval;
+}
+
+int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, int *end, size_t *bytes_read)
+{
+ ssize_t retval = 0;
+ size_t num_bytes;
+
+ *end = 0;
+ *bytes_read = 0;
+ if (length == 0)
+ return 0;
+
+ clear_bit(DEV_CLEAR_BN, &priv->state);
+
+ // transfer data (except for last byte)
+ if (length > 1) {
+ if (priv->eos_flags & REOS)
+ tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL);
+ else
+ tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_EOI);
+ // PIO transfer
+ retval = pio_read(board, priv, buffer, length - 1, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ return retval;
+ buffer += num_bytes;
+ length -= num_bytes;
+ }
+ // read last bytes if we haven't received an END yet
+ if (*end == 0) {
+ // make sure we holdoff after last byte read
+ tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL);
+ retval = pio_read(board, priv, buffer, length, end, &num_bytes);
+ *bytes_read += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tms9914_read);
+
+static int pio_write_wait(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ // wait until next byte is ready to be sent
+ if (wait_event_interruptible(board->wait,
+ test_bit(WRITE_READY_BN, &priv->state) ||
+ test_bit(BUS_ERROR_BN, &priv->state) ||
+ test_bit(DEV_CLEAR_BN, &priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ return -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+ if (test_bit(BUS_ERROR_BN, &priv->state))
+ return -EIO;
+ if (test_bit(DEV_CLEAR_BN, &priv->state))
+ return -EINTR;
+
+ return 0;
+}
+
+static int pio_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ ssize_t retval = 0;
+ unsigned long flags;
+
+ *bytes_written = 0;
+ while (*bytes_written < length) {
+ retval = pio_write_wait(board, priv);
+ if (retval < 0)
+ break;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ clear_bit(WRITE_READY_BN, &priv->state);
+ write_byte(priv, buffer[(*bytes_written)++], CDOR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ }
+ retval = pio_write_wait(board, priv);
+ if (retval < 0)
+ return retval;
+
+ return length;
+}
+
+int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv,
+ u8 *buffer, size_t length, int send_eoi, size_t *bytes_written)
+{
+ ssize_t retval = 0;
+
+ *bytes_written = 0;
+ if (length == 0)
+ return 0;
+
+ clear_bit(BUS_ERROR_BN, &priv->state);
+ clear_bit(DEV_CLEAR_BN, &priv->state);
+
+ if (send_eoi)
+ length-- ; /* save the last byte for sending EOI */
+
+ if (length > 0) {
+ size_t num_bytes;
+ // PIO transfer
+ retval = pio_write(board, priv, buffer, length, &num_bytes);
+ *bytes_written += num_bytes;
+ if (retval < 0)
+ return retval;
+ }
+ if (send_eoi) {
+ size_t num_bytes;
+ /*send EOI */
+ write_byte(priv, AUX_SEOI, AUXCR);
+
+ retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes);
+ *bytes_written += num_bytes;
+ }
+ return retval;
+}
+EXPORT_SYMBOL(tms9914_write);
+
+static void check_my_address_state(struct gpib_board *board,
+ struct tms9914_priv *priv, int cmd_byte)
+{
+ if (cmd_byte == MLA(board->pad)) {
+ priv->primary_listen_addressed = 1;
+ // become active listener
+ if (board->sad < 0)
+ write_byte(priv, AUX_LON | AUX_CS, AUXCR);
+ } else if (board->sad >= 0 && priv->primary_listen_addressed &&
+ cmd_byte == MSA(board->sad)) {
+ // become active listener
+ write_byte(priv, AUX_LON | AUX_CS, AUXCR);
+ } else if (cmd_byte != MLA(board->pad) && (cmd_byte & 0xe0) == LAD) {
+ priv->primary_listen_addressed = 0;
+ } else if (cmd_byte == UNL) {
+ priv->primary_listen_addressed = 0;
+ write_byte(priv, AUX_LON, AUXCR);
+ } else if (cmd_byte == MTA(board->pad)) {
+ priv->primary_talk_addressed = 1;
+ if (board->sad < 0)
+ // make active talker
+ write_byte(priv, AUX_TON | AUX_CS, AUXCR);
+ } else if (board->sad >= 0 && priv->primary_talk_addressed &&
+ cmd_byte == MSA(board->sad)) {
+ // become active talker
+ write_byte(priv, AUX_TON | AUX_CS, AUXCR);
+ } else if (cmd_byte != MTA(board->pad) && (cmd_byte & 0xe0) == TAD) {
+ // Other Talk Address
+ priv->primary_talk_addressed = 0;
+ write_byte(priv, AUX_TON, AUXCR);
+ } else if (cmd_byte == UNT) {
+ priv->primary_talk_addressed = 0;
+ write_byte(priv, AUX_TON, AUXCR);
+ }
+}
+
+int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ *bytes_written = 0;
+ while (*bytes_written < length) {
+ if (wait_event_interruptible(board->wait,
+ test_bit(COMMAND_READY_BN,
+ &priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ break;
+ if (test_bit(TIMO_NUM, &board->status))
+ break;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ clear_bit(COMMAND_READY_BN, &priv->state);
+ write_byte(priv, buffer[*bytes_written], CDOR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ check_my_address_state(board, priv, buffer[*bytes_written]);
+
+ ++(*bytes_written);
+ }
+ // wait until last command byte is written
+ if (wait_event_interruptible(board->wait,
+ test_bit(COMMAND_READY_BN,
+ &priv->state) || test_bit(TIMO_NUM, &board->status)))
+ retval = -ERESTARTSYS;
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+
+ return retval;
+}
+EXPORT_SYMBOL(tms9914_command);
+
+irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ int status0, status1;
+
+ // read interrupt status (also clears status)
+ status0 = read_byte(priv, ISR0);
+ status1 = read_byte(priv, ISR1);
+ return tms9914_interrupt_have_status(board, priv, status0, status1);
+}
+EXPORT_SYMBOL(tms9914_interrupt);
+
+irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv,
+ int status0, int status1)
+{
+ // record reception of END
+ if (status0 & HR_END)
+ set_bit(RECEIVED_END_BN, &priv->state);
+ // get incoming data in PIO mode
+ if ((status0 & HR_BI))
+ set_bit(READ_READY_BN, &priv->state);
+ if ((status0 & HR_BO)) {
+ if (read_byte(priv, ADSR) & HR_ATN)
+ set_bit(COMMAND_READY_BN, &priv->state);
+ else
+ set_bit(WRITE_READY_BN, &priv->state);
+ }
+
+ if (status0 & HR_SPAS) {
+ priv->spoll_status &= ~request_service_bit;
+ write_byte(priv, priv->spoll_status, SPMR);
+ // FIXME: set SPOLL status bit
+ }
+ // record service request in status
+ if (status1 & HR_SRQ)
+ set_bit(SRQI_NUM, &board->status);
+ // have been addressed (with secondary addressing disabled)
+ if (status1 & HR_MA)
+ // clear dac holdoff
+ write_byte(priv, AUX_VAL, AUXCR);
+ // unrecognized command received
+ if (status1 & HR_UNC) {
+ unsigned short command_byte = read_byte(priv, CPTR) & gpib_command_mask;
+
+ switch (command_byte) {
+ case PP_CONFIG:
+ priv->ppoll_configure_state = 1;
+ /*
+ * AUX_PTS generates another UNC interrupt on the next command byte
+ * if it is in the secondary address group (such as PPE and PPD).
+ */
+ write_byte(priv, AUX_PTS, AUXCR);
+ write_byte(priv, AUX_VAL, AUXCR);
+ break;
+ case PPU:
+ tms9914_parallel_poll_configure(board, priv, command_byte);
+ write_byte(priv, AUX_VAL, AUXCR);
+ break;
+ default:
+ if (is_PPE(command_byte) || is_PPD(command_byte)) {
+ if (priv->ppoll_configure_state) {
+ tms9914_parallel_poll_configure(board, priv, command_byte);
+ write_byte(priv, AUX_VAL, AUXCR);
+ } else {// bad parallel poll configure byte
+ // clear dac holdoff
+ write_byte(priv, AUX_INVAL, AUXCR);
+ }
+ } else {
+ // clear dac holdoff
+ write_byte(priv, AUX_INVAL, AUXCR);
+ }
+ break;
+ }
+
+ if (in_primary_command_group(command_byte) && command_byte != PP_CONFIG)
+ priv->ppoll_configure_state = 0;
+ }
+
+ if (status1 & HR_ERR) {
+ dev_dbg(board->gpib_dev, "gpib bus error\n");
+ set_bit(BUS_ERROR_BN, &priv->state);
+ }
+
+ if (status1 & HR_IFC) {
+ push_gpib_event(board, EVENT_IFC);
+ clear_bit(CIC_NUM, &board->status);
+ }
+
+ if (status1 & HR_GET) {
+ push_gpib_event(board, EVENT_DEV_TRG);
+ // clear dac holdoff
+ write_byte(priv, AUX_VAL, AUXCR);
+ }
+
+ if (status1 & HR_DCAS) {
+ push_gpib_event(board, EVENT_DEV_CLR);
+ // clear dac holdoff
+ write_byte(priv, AUX_VAL, AUXCR);
+ set_bit(DEV_CLEAR_BN, &priv->state);
+ }
+
+ // check for being addressed with secondary addressing
+ if (status1 & HR_APT) {
+ if (board->sad < 0)
+ dev_err(board->gpib_dev, "bug, APT interrupt without secondary addressing?\n");
+ if ((read_byte(priv, CPTR) & gpib_command_mask) == MSA(board->sad))
+ write_byte(priv, AUX_VAL, AUXCR);
+ else
+ write_byte(priv, AUX_INVAL, AUXCR);
+ }
+
+ if ((status0 & priv->imr0_bits) || (status1 & priv->imr1_bits)) {
+ dev_dbg(board->gpib_dev, "isr0 0x%x, imr0 0x%x, isr1 0x%x, imr1 0x%x\n",
+ status0, priv->imr0_bits, status1, priv->imr1_bits);
+ update_status_nolock(board, priv);
+ wake_up_interruptible(&board->wait);
+ }
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(tms9914_interrupt_have_status);
+
+void tms9914_board_reset(struct tms9914_priv *priv)
+{
+ /* chip reset */
+ write_byte(priv, AUX_CHIP_RESET | AUX_CS, AUXCR);
+
+ /* disable all interrupts */
+ priv->imr0_bits = 0;
+ write_byte(priv, priv->imr0_bits, IMR0);
+ priv->imr1_bits = 0;
+ write_byte(priv, priv->imr1_bits, IMR1);
+ write_byte(priv, AUX_DAI | AUX_CS, AUXCR);
+
+ /* clear registers by reading */
+ read_byte(priv, CPTR);
+ read_byte(priv, ISR0);
+ read_byte(priv, ISR1);
+
+ write_byte(priv, 0, SPMR);
+
+ /* parallel poll unconfigure */
+ write_byte(priv, 0, PPR);
+ /* request for data holdoff */
+ tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL);
+}
+EXPORT_SYMBOL_GPL(tms9914_board_reset);
+
+void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv)
+{
+ /* set GPIB address */
+ tms9914_primary_address(board, priv, board->pad);
+ tms9914_secondary_address(board, priv, board->sad, board->sad >= 0);
+
+ /* enable tms9914 interrupts */
+ priv->imr0_bits |= HR_MACIE | HR_RLCIE | HR_ENDIE | HR_BOIE | HR_BIIE |
+ HR_SPASIE;
+ priv->imr1_bits |= HR_MAIE | HR_SRQIE | HR_UNCIE | HR_ERRIE | HR_IFCIE |
+ HR_GETIE | HR_DCASIE;
+ write_byte(priv, priv->imr0_bits, IMR0);
+ write_byte(priv, priv->imr1_bits, IMR1);
+ write_byte(priv, AUX_DAI, AUXCR);
+
+ /* turn off reset state */
+ write_byte(priv, AUX_CHIP_RESET, AUXCR);
+}
+EXPORT_SYMBOL_GPL(tms9914_online);
+
+#ifdef CONFIG_HAS_IOPORT
+// wrapper for inb
+u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num)
+{
+ return inb(priv->iobase + register_num * priv->offset);
+}
+EXPORT_SYMBOL_GPL(tms9914_ioport_read_byte);
+
+// wrapper for outb
+void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num)
+{
+ outb(data, priv->iobase + register_num * priv->offset);
+ if (register_num == AUXCR)
+ udelay(1);
+}
+EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte);
+#endif
+
+// wrapper for readb
+u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num)
+{
+ return readb(priv->mmiobase + register_num * priv->offset);
+}
+EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte);
+
+// wrapper for writeb
+void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num)
+{
+ writeb(data, priv->mmiobase + register_num * priv->offset);
+ if (register_num == AUXCR)
+ udelay(1);
+}
+EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte);
+
+static int __init tms9914_init_module(void)
+{
+ return 0;
+}
+
+static void __exit tms9914_exit_module(void)
+{
+}
+
+module_init(tms9914_init_module);
+module_exit(tms9914_exit_module);
+
diff --git a/drivers/gpib/tnt4882/Makefile b/drivers/gpib/tnt4882/Makefile
new file mode 100644
index 000000000000..fa1687ad0d1b
--- /dev/null
+++ b/drivers/gpib/tnt4882/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882.o
+
+tnt4882-objs := tnt4882_gpib.o mite.o
+
+
+
diff --git a/drivers/gpib/tnt4882/mite.c b/drivers/gpib/tnt4882/mite.c
new file mode 100644
index 000000000000..847b96f411bd
--- /dev/null
+++ b/drivers/gpib/tnt4882/mite.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Hardware driver for NI Mite PCI interface chip,
+ * adapted from COMEDI
+ *
+ * Copyright (C) 1997-8 David A. Schleef
+ * Copyright (C) 2002 Frank Mori Hess
+ *
+ * The PCI-MIO E series driver was originally written by
+ * Tomasz Motylewski <...>, and ported to comedi by ds.
+ *
+ * References for specifications:
+ *
+ * 321747b.pdf Register Level Programmer Manual (obsolete)
+ * 321747c.pdf Register Level Programmer Manual (new)
+ * DAQ-STC reference manual
+ *
+ * Other possibly relevant info:
+ *
+ * 320517c.pdf User manual (obsolete)
+ * 320517f.pdf User manual (new)
+ * 320889a.pdf delete
+ * 320906c.pdf maximum signal ratings
+ * 321066a.pdf about 16x
+ * 321791a.pdf discontinuation of at-mio-16e-10 rev. c
+ * 321808a.pdf about at-mio-16e-10 rev P
+ * 321837a.pdf discontinuation of at-mio-16de-10 rev d
+ * 321838a.pdf about at-mio-16de-10 rev N
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include "mite.h"
+
+#define PCI_MITE_SIZE 4096
+#define PCI_DAQ_SIZE 4096
+
+struct mite_struct *mite_devices;
+
+#define TOP_OF_PAGE(x) ((x) | (~(PAGE_MASK)))
+
+void mite_init(void)
+{
+ struct pci_dev *pcidev;
+ struct mite_struct *mite;
+
+ for (pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, NULL);
+ pcidev;
+ pcidev = pci_get_device(PCI_VENDOR_ID_NATINST, PCI_ANY_ID, pcidev)) {
+ mite = kzalloc(sizeof(*mite), GFP_KERNEL);
+ if (!mite)
+ return;
+
+ mite->pcidev = pcidev;
+ pci_dev_get(mite->pcidev);
+ mite->next = mite_devices;
+ mite_devices = mite;
+ }
+}
+
+int mite_setup(struct mite_struct *mite)
+{
+ u32 addr;
+
+ if (pci_enable_device(mite->pcidev)) {
+ pr_err("mite: error enabling mite.\n");
+ return -EIO;
+ }
+ pci_set_master(mite->pcidev);
+ if (pci_request_regions(mite->pcidev, "mite")) {
+ pr_err("mite: failed to request mite io regions.\n");
+ return -EIO;
+ }
+ addr = pci_resource_start(mite->pcidev, 0);
+ mite->mite_phys_addr = addr;
+ mite->mite_io_addr = ioremap(addr, pci_resource_len(mite->pcidev, 0));
+ if (!mite->mite_io_addr) {
+ pr_err("mite: failed to remap mite io memory address.\n");
+ return -ENOMEM;
+ }
+ addr = pci_resource_start(mite->pcidev, 1);
+ mite->daq_phys_addr = addr;
+ mite->daq_io_addr = ioremap(mite->daq_phys_addr, pci_resource_len(mite->pcidev, 1));
+ if (!mite->daq_io_addr) {
+ pr_err("mite: failed to remap daq io memory address.\n");
+ return -ENOMEM;
+ }
+ writel(mite->daq_phys_addr | WENAB, mite->mite_io_addr + MITE_IODWBSR);
+ mite->used = 1;
+ return 0;
+}
+
+void mite_cleanup(void)
+{
+ struct mite_struct *mite, *next;
+
+ for (mite = mite_devices; mite; mite = next) {
+ next = mite->next;
+ if (mite->pcidev)
+ pci_dev_put(mite->pcidev);
+ kfree(mite);
+ }
+}
+
+void mite_unsetup(struct mite_struct *mite)
+{
+ if (!mite)
+ return;
+ if (mite->mite_io_addr) {
+ iounmap(mite->mite_io_addr);
+ mite->mite_io_addr = NULL;
+ }
+ if (mite->daq_io_addr) {
+ iounmap(mite->daq_io_addr);
+ mite->daq_io_addr = NULL;
+ }
+ if (mite->mite_phys_addr) {
+ pci_release_regions(mite->pcidev);
+ pci_disable_device(mite->pcidev);
+ mite->mite_phys_addr = 0;
+ }
+ mite->used = 0;
+}
diff --git a/drivers/gpib/tnt4882/mite.h b/drivers/gpib/tnt4882/mite.h
new file mode 100644
index 000000000000..a1fdba9672a0
--- /dev/null
+++ b/drivers/gpib/tnt4882/mite.h
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Hardware driver for NI Mite PCI interface chip
+ *
+ * Copyright (C) 1999 David A. Schleef <ds@stm.lbl.gov>
+ */
+
+#ifndef _MITE_H_
+#define _MITE_H_
+
+#include <linux/pci.h>
+
+#define PCI_VENDOR_ID_NATINST 0x1093
+
+//#define DEBUG_MITE
+
+#ifdef DEBUG_MITE
+#define MDPRINTK(format, args...) pr_debug(format, ## args)
+#else
+#define MDPRINTK(args...)
+#endif
+
+#define MITE_RING_SIZE 3000
+struct mite_dma_chain {
+ u32 count;
+ u32 addr;
+ u32 next;
+};
+
+struct mite_struct {
+ struct mite_struct *next;
+ int used;
+
+ struct pci_dev *pcidev;
+ unsigned long mite_phys_addr;
+ void __iomem *mite_io_addr;
+ unsigned long daq_phys_addr;
+ void __iomem *daq_io_addr;
+
+ int DMA_CheckNearEnd;
+
+ struct mite_dma_chain ring[MITE_RING_SIZE];
+};
+
+extern struct mite_struct *mite_devices;
+
+extern inline unsigned int mite_irq(struct mite_struct *mite)
+{
+ return mite->pcidev->irq;
+};
+
+extern inline unsigned int mite_device_id(struct mite_struct *mite)
+{
+ return mite->pcidev->device;
+};
+
+void mite_init(void);
+void mite_cleanup(void);
+int mite_setup(struct mite_struct *mite);
+void mite_unsetup(struct mite_struct *mite);
+void mite_list_devices(void);
+
+#define CHAN_OFFSET(x) (0x100 * (x))
+
+/* DMA base for chan 0 is 0x500, chan 1 is 0x600 */
+
+#define MITE_CHOR 0x500
+#define CHOR_DMARESET BIT(31)
+#define CHOR_SET_SEND_TC BIT(11)
+#define CHOR_CLR_SEND_TC BIT(10)
+#define CHOR_SET_LPAUSE BIT(9)
+#define CHOR_CLR_LPAUSE BIT(8)
+#define CHOR_CLRDONE BIT(7)
+#define CHOR_CLRRB BIT(6)
+#define CHOR_CLRLC BIT(5)
+#define CHOR_FRESET BIT(4)
+#define CHOR_ABORT BIT(3)
+#define CHOR_STOP BIT(2)
+#define CHOR_CONT BIT(1)
+#define CHOR_START BIT(0)
+#define CHOR_PON (CHOR_CLR_SEND_TC | CHOR_CLR_LPAUSE)
+
+#define MITE_CHCR 0x504
+#define CHCR_SET_DMA_IE BIT(31)
+#define CHCR_CLR_DMA_IE BIT(30)
+#define CHCR_SET_LINKP_IE BIT(29)
+#define CHCR_CLR_LINKP_IE BIT(28)
+#define CHCR_SET_SAR_IE BIT(27)
+#define CHCR_CLR_SAR_IE BIT(26)
+#define CHCR_SET_DONE_IE BIT(25)
+#define CHCR_CLR_DONE_IE BIT(24)
+#define CHCR_SET_MRDY_IE BIT(23)
+#define CHCR_CLR_MRDY_IE BIT(22)
+#define CHCR_SET_DRDY_IE BIT(21)
+#define CHCR_CLR_DRDY_IE BIT(20)
+#define CHCR_SET_LC_IE BIT(19)
+#define CHCR_CLR_LC_IE BIT(18)
+#define CHCR_SET_CONT_RB_IE BIT(17)
+#define CHCR_CLR_CONT_RB_IE BIT(16)
+#define CHCR_FIFODIS BIT(15)
+#define CHCR_FIFO_ON 0
+#define CHCR_BURSTEN BIT(14)
+#define CHCR_NO_BURSTEN 0
+#define CHCR_NFTP(x) ((x) << 11)
+#define CHCR_NFTP0 CHCR_NFTP(0)
+#define CHCR_NFTP1 CHCR_NFTP(1)
+#define CHCR_NFTP2 CHCR_NFTP(2)
+#define CHCR_NFTP4 CHCR_NFTP(3)
+#define CHCR_NFTP8 CHCR_NFTP(4)
+#define CHCR_NFTP16 CHCR_NFTP(5)
+#define CHCR_NETP(x) ((x) << 11)
+#define CHCR_NETP0 CHCR_NETP(0)
+#define CHCR_NETP1 CHCR_NETP(1)
+#define CHCR_NETP2 CHCR_NETP(2)
+#define CHCR_NETP4 CHCR_NETP(3)
+#define CHCR_NETP8 CHCR_NETP(4)
+#define CHCR_CHEND1 BIT(5)
+#define CHCR_CHEND0 BIT(4)
+#define CHCR_DIR BIT(3)
+#define CHCR_DEV_TO_MEM CHCR_DIR
+#define CHCR_MEM_TO_DEV 0
+#define CHCR_NORMAL ((0) << 0)
+#define CHCR_CONTINUE ((1) << 0)
+#define CHCR_RINGBUFF ((2) << 0)
+#define CHCR_LINKSHORT ((4) << 0)
+#define CHCR_LINKLONG ((5) << 0)
+#define CHCRPON (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE | \
+ CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE | \
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_IE)
+
+#define MITE_TCR 0x508
+
+/* CR bits */
+#define CR_RL(x) ((x) << 21)
+#define CR_RL0 CR_RL(0)
+#define CR_RL1 CR_RL(1)
+#define CR_RL2 CR_RL(2)
+#define CR_RL4 CR_RL(3)
+#define CR_RL8 CR_RL(4)
+#define CR_RL16 CR_RL(5)
+#define CR_RL32 CR_RL(6)
+#define CR_RL64 CR_RL(7)
+#define CR_RD(x) ((x) << 19)
+#define CR_RD0 CR_RD(0)
+#define CR_RD32 CR_RD(1)
+#define CR_RD512 CR_RD(2)
+#define CR_RD8192 CR_RD(3)
+#define CR_REQS(x) ((x) << 16)
+#define CR_REQSDRQ0 CR_REQS(4)
+#define CR_REQSDRQ1 CR_REQS(5)
+#define CR_REQSDRQ2 CR_REQS(6)
+#define CR_REQSDRQ3 CR_REQS(7)
+#define CR_ASEQX(x) ((x) << 10)
+#define CR_ASEQX0 CR_ASEQX(0)
+#define CR_ASEQDONT CR_ASEQX0
+#define CR_ASEQXP1 CR_ASEQX(1)
+#define CR_ASEQUP CR_ASEQXP1
+#define CR_ASEQXP2 CR_ASEQX(2)
+#define CR_ASEQDOWN CR_ASEQXP2
+#define CR_ASEQXP4 CR_ASEQX(3)
+#define CR_ASEQXP8 CR_ASEQX(4)
+#define CR_ASEQXP16 CR_ASEQX(5)
+#define CR_ASEQXP32 CR_ASEQX(6)
+#define CR_ASEQXP64 CR_ASEQX(7)
+#define CR_ASEQXM1 CR_ASEQX(9)
+#define CR_ASEQXM2 CR_ASEQX(10)
+#define CR_ASEQXM4 CR_ASEQX(11)
+#define CR_ASEQXM8 CR_ASEQX(12)
+#define CR_ASEQXM16 CR_ASEQX(13)
+#define CR_ASEQXM32 CR_ASEQX(14)
+#define CR_ASEQXM64 CR_ASEQX(15)
+#define CR_PSIZEBYTE BIT(8)
+#define CR_PSIZEHALF (2 << 8)
+#define CR_PSIZEWORD (3 << 8)
+#define CR_PORTCPU (0 << 6)
+#define CR_PORTIO BIT(6)
+#define CR_PORTVXI (2 << 6)
+#define CR_PORTMXI (3 << 6)
+#define CR_AMDEVICE BIT(0)
+
+#define CHSR_INT 0x80000000
+#define CHSR_DONE 0x02000000
+#define CHSR_LINKC 0x00080000
+
+#define MITE_MCR 0x50c
+#define MCRPON 0
+
+#define MITE_MAR 0x510
+
+#define MITE_DCR 0x514
+#define DCR_NORMAL BIT(29)
+#define DCRPON 0
+
+#define MITE_DAR 0x518
+
+#define MITE_LKCR 0x51c
+
+#define MITE_LKAR 0x520
+#define MITE_LLKAR 0x524
+#define MITE_BAR 0x528
+#define MITE_BCR 0x52c
+#define MITE_SAR 0x530
+#define MITE_WSCR 0x534
+#define MITE_WSER 0x538
+#define MITE_CHSR 0x53c
+#define MITE_FCR 0x540
+
+#define MITE_FIFO 0x80
+#define MITE_FIFOEND 0xff
+
+#define MITE_AMRAM 0x00
+#define MITE_AMDEVICE 0x01
+#define MITE_AMHOST_A32_SINGLE 0x09
+#define MITE_AMHOST_A24_SINGLE 0x39
+#define MITE_AMHOST_A16_SINGLE 0x29
+#define MITE_AMHOST_A32_BLOCK 0x0b
+#define MITE_AMHOST_A32D64_BLOCK 0x08
+#define MITE_AMHOST_A24_BLOCK 0x3b
+
+enum mite_registers {
+ MITE_IODWBSR = 0xc0, // IO Device Window Base Size Register
+ MITE_CSIGR = 0x460, // chip signature
+ MITE_IODWBSR_1 = 0xc4, // IO Device Window Base Size Register 1 (used by 6602 boards)
+ MITE_IODWCR_1 = 0xf4
+};
+
+enum MITE_IODWBSR_bits {
+ WENAB = 0x80, // window enable
+ WENAB_6602 = 0x8c // window enable for 6602 boards
+};
+
+#endif
+
diff --git a/drivers/gpib/tnt4882/tnt4882_gpib.c b/drivers/gpib/tnt4882/tnt4882_gpib.c
new file mode 100644
index 000000000000..c03a976b7380
--- /dev/null
+++ b/drivers/gpib/tnt4882/tnt4882_gpib.c
@@ -0,0 +1,1838 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/***************************************************************************
+ * National Instruments boards using tnt4882 or compatible chips (at-gpib, etc).
+ * copyright : (C) 2001, 2002 by Frank Mori Hess
+ ***************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define dev_fmt pr_fmt
+#define DRV_NAME KBUILD_MODNAME
+
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/isapnp.h>
+
+#include "nec7210.h"
+#include "gpibP.h"
+#include "mite.h"
+#include "tnt4882_registers.h"
+
+static const int ISAPNP_VENDOR_ID_NI = ISAPNP_VENDOR('N', 'I', 'C');
+static const int ISAPNP_ID_NI_ATGPIB_TNT = 0xc601;
+enum {
+ PCI_DEVICE_ID_NI_GPIB = 0xc801,
+ PCI_DEVICE_ID_NI_GPIB_PLUS = 0xc811,
+ PCI_DEVICE_ID_NI_GPIB_PLUS2 = 0x71ad,
+ PCI_DEVICE_ID_NI_PXIGPIB = 0xc821,
+ PCI_DEVICE_ID_NI_PMCGPIB = 0xc831,
+ PCI_DEVICE_ID_NI_PCIEGPIB = 0x70cf,
+ PCI_DEVICE_ID_NI_PCIE2GPIB = 0x710e,
+// Measurement Computing PCI-488 same design as PCI-GPIB with TNT5004
+ PCI_DEVICE_ID_MC_PCI488 = 0x7259,
+ PCI_DEVICE_ID_CEC_NI_GPIB = 0x7258
+};
+
+// struct which defines private_data for tnt4882 devices
+struct tnt4882_priv {
+ struct nec7210_priv nec7210_priv;
+ struct mite_struct *mite;
+ struct pnp_dev *pnp_dev;
+ unsigned int irq;
+ unsigned short imr0_bits;
+ unsigned short imr3_bits;
+ unsigned short auxg_bits; // bits written to auxiliary register G
+};
+
+static irqreturn_t tnt4882_internal_interrupt(struct gpib_board *board);
+
+// register offset for nec7210 compatible registers
+static const int atgpib_reg_offset = 2;
+
+// number of ioports used
+static const int atgpib_iosize = 32;
+
+/* paged io */
+static inline unsigned int tnt_paged_readb(struct tnt4882_priv *priv, unsigned long offset)
+{
+ iowrite8(AUX_PAGEIN, priv->nec7210_priv.mmiobase + AUXMR * priv->nec7210_priv.offset);
+ udelay(1);
+ return ioread8(priv->nec7210_priv.mmiobase + offset);
+}
+
+static inline void tnt_paged_writeb(struct tnt4882_priv *priv, unsigned int value,
+ unsigned long offset)
+{
+ iowrite8(AUX_PAGEIN, priv->nec7210_priv.mmiobase + AUXMR * priv->nec7210_priv.offset);
+ udelay(1);
+ iowrite8(value, priv->nec7210_priv.mmiobase + offset);
+}
+
+/* readb/writeb wrappers */
+static inline unsigned short tnt_readb(struct tnt4882_priv *priv, unsigned long offset)
+{
+ void __iomem *address = priv->nec7210_priv.mmiobase + offset;
+ unsigned long flags;
+ unsigned short retval;
+ spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock;
+
+ spin_lock_irqsave(register_lock, flags);
+ switch (offset) {
+ case CSR:
+ case SASR:
+ case ISR0:
+ case BSR:
+ switch (priv->nec7210_priv.type) {
+ case TNT4882:
+ case TNT5004:
+ retval = ioread8(address);
+ break;
+ case NAT4882:
+ retval = tnt_paged_readb(priv, offset - tnt_pagein_offset);
+ break;
+ case NEC7210:
+ retval = 0;
+ break;
+ default:
+ retval = 0;
+ break;
+ }
+ break;
+ default:
+ retval = ioread8(address);
+ break;
+ }
+ spin_unlock_irqrestore(register_lock, flags);
+ return retval;
+}
+
+static inline void tnt_writeb(struct tnt4882_priv *priv, unsigned short value, unsigned long offset)
+{
+ void __iomem *address = priv->nec7210_priv.mmiobase + offset;
+ unsigned long flags;
+ spinlock_t *register_lock = &priv->nec7210_priv.register_page_lock;
+
+ spin_lock_irqsave(register_lock, flags);
+ switch (offset) {
+ case KEYREG:
+ case IMR0:
+ case BCR:
+ switch (priv->nec7210_priv.type) {
+ case TNT4882:
+ case TNT5004:
+ iowrite8(value, address);
+ break;
+ case NAT4882:
+ tnt_paged_writeb(priv, value, offset - tnt_pagein_offset);
+ break;
+ case NEC7210:
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ iowrite8(value, address);
+ break;
+ }
+ spin_unlock_irqrestore(register_lock, flags);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("GPIB driver for National Instruments boards using tnt4882 or compatible chips");
+
+static int tnt4882_line_status(const struct gpib_board *board)
+{
+ int status = VALID_ALL;
+ int bcsr_bits;
+ struct tnt4882_priv *tnt_priv;
+
+ tnt_priv = board->private_data;
+
+ bcsr_bits = tnt_readb(tnt_priv, BSR);
+
+ if (bcsr_bits & BCSR_REN_BIT)
+ status |= BUS_REN;
+ if (bcsr_bits & BCSR_IFC_BIT)
+ status |= BUS_IFC;
+ if (bcsr_bits & BCSR_SRQ_BIT)
+ status |= BUS_SRQ;
+ if (bcsr_bits & BCSR_EOI_BIT)
+ status |= BUS_EOI;
+ if (bcsr_bits & BCSR_NRFD_BIT)
+ status |= BUS_NRFD;
+ if (bcsr_bits & BCSR_NDAC_BIT)
+ status |= BUS_NDAC;
+ if (bcsr_bits & BCSR_DAV_BIT)
+ status |= BUS_DAV;
+ if (bcsr_bits & BCSR_ATN_BIT)
+ status |= BUS_ATN;
+
+ return status;
+}
+
+static int tnt4882_t1_delay(struct gpib_board *board, unsigned int nano_sec)
+{
+ struct tnt4882_priv *tnt_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+ unsigned int retval;
+
+ retval = nec7210_t1_delay(board, nec_priv, nano_sec);
+ if (nec_priv->type == NEC7210)
+ return retval;
+
+ if (nano_sec <= 350) {
+ tnt_writeb(tnt_priv, MSTD, KEYREG);
+ retval = 350;
+ } else {
+ tnt_writeb(tnt_priv, 0, KEYREG);
+ }
+ if (nano_sec > 500 && nano_sec <= 1100) {
+ write_byte(nec_priv, AUXRI | USTD, AUXMR);
+ retval = 1100;
+ } else {
+ write_byte(nec_priv, AUXRI, AUXMR);
+ }
+ return retval;
+}
+
+static int fifo_word_available(struct tnt4882_priv *tnt_priv)
+{
+ int status2;
+ int retval;
+
+ status2 = tnt_readb(tnt_priv, STS2);
+ retval = (status2 & AEFN) && (status2 & BEFN);
+
+ return retval;
+}
+
+static int fifo_byte_available(struct tnt4882_priv *tnt_priv)
+{
+ int status2;
+ int retval;
+
+ status2 = tnt_readb(tnt_priv, STS2);
+ retval = (status2 & AEFN) || (status2 & BEFN);
+
+ return retval;
+}
+
+static int fifo_xfer_done(struct tnt4882_priv *tnt_priv)
+{
+ int status1;
+ int retval;
+
+ status1 = tnt_readb(tnt_priv, STS1);
+ retval = status1 & (S_DONE | S_HALT);
+
+ return retval;
+}
+
+static int drain_fifo_words(struct tnt4882_priv *tnt_priv, u8 *buffer, int num_bytes)
+{
+ int count = 0;
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+
+ while (fifo_word_available(tnt_priv) && count + 2 <= num_bytes) {
+ short word;
+
+ word = ioread16(nec_priv->mmiobase + FIFOB);
+ buffer[count++] = word & 0xff;
+ buffer[count++] = (word >> 8) & 0xff;
+ }
+ return count;
+}
+
+static void tnt4882_release_holdoff(struct gpib_board *board, struct tnt4882_priv *tnt_priv)
+{
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+ unsigned short sasr_bits;
+
+ sasr_bits = tnt_readb(tnt_priv, SASR);
+
+ /*
+ * tnt4882 not in one-chip mode won't always release holdoff unless we
+ * are in the right mode when release handshake command is given
+ */
+ if (sasr_bits & AEHS_BIT) /* holding off due to holdoff on end mode*/ {
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDE);
+ write_byte(nec_priv, AUX_FH, AUXMR);
+ } else if (sasr_bits & ANHS1_BIT) { /* held off due to holdoff on all data mode*/
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDA);
+ write_byte(nec_priv, AUX_FH, AUXMR);
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDE);
+ } else { /* held off due to holdoff immediately command */
+ nec7210_set_handshake_mode(board, nec_priv, HR_HLDE);
+ write_byte(nec_priv, AUX_FH, AUXMR);
+ }
+}
+
+static int tnt4882_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ size_t count = 0;
+ ssize_t retval = 0;
+ struct tnt4882_priv *tnt_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+ unsigned int bits;
+ s32 hw_count;
+ unsigned long flags;
+
+ *bytes_read = 0;
+ // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state);
+ clear_bit(ADR_CHANGE_BN, &nec_priv->state);
+
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, HR_ENDIE);
+ if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004)
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, HR_DMAI);
+ else
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+ tnt_writeb(tnt_priv, nec_priv->auxa_bits | HR_HLDA, CCR);
+ bits = TNT_B_16BIT | TNT_IN | TNT_CCEN;
+ tnt_writeb(tnt_priv, bits, CFG);
+ tnt_writeb(tnt_priv, RESET_FIFO, CMDR);
+ udelay(1);
+ // load 2's complement of count into hardware counters
+ hw_count = -length;
+ tnt_writeb(tnt_priv, hw_count & 0xff, CNT0);
+ tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1);
+ tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2);
+ tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3);
+
+ tnt4882_release_holdoff(board, tnt_priv);
+
+ tnt_writeb(tnt_priv, GO, CMDR);
+ udelay(1);
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ tnt_priv->imr3_bits |= HR_DONE | HR_NEF;
+ tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ while (count + 2 <= length &&
+ test_bit(RECEIVED_END_BN, &nec_priv->state) == 0 &&
+ fifo_xfer_done(tnt_priv) == 0) {
+ // wait until a word is ready
+ if (wait_event_interruptible(board->wait,
+ fifo_word_available(tnt_priv) ||
+ fifo_xfer_done(tnt_priv) ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(ADR_CHANGE_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (test_bit(TIMO_NUM, &board->status)) {
+ retval = -ETIMEDOUT;
+ break;
+ }
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+ if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) {
+ retval = -EINTR;
+ break;
+ }
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ count += drain_fifo_words(tnt_priv, &buffer[count], length - count);
+ tnt_priv->imr3_bits |= HR_NEF;
+ tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ if (need_resched())
+ schedule();
+ }
+ // wait for last byte
+ if (count < length) {
+ spin_lock_irqsave(&board->spinlock, flags);
+ tnt_priv->imr3_bits |= HR_DONE | HR_NEF;
+ tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ if (wait_event_interruptible(board->wait,
+ fifo_xfer_done(tnt_priv) ||
+ test_bit(RECEIVED_END_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(ADR_CHANGE_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status))) {
+ retval = -ERESTARTSYS;
+ }
+ if (test_bit(TIMO_NUM, &board->status))
+ retval = -ETIMEDOUT;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ retval = -EINTR;
+ if (test_bit(ADR_CHANGE_BN, &nec_priv->state))
+ retval = -EINTR;
+ count += drain_fifo_words(tnt_priv, &buffer[count], length - count);
+ if (fifo_byte_available(tnt_priv) && count < length)
+ buffer[count++] = tnt_readb(tnt_priv, FIFOB);
+ }
+ if (count < length)
+ tnt_writeb(tnt_priv, STOP, CMDR);
+ udelay(1);
+
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_ENDIE, 0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAI, 0);
+ /*
+ * force handling of any pending interrupts (seems to be needed
+ * to keep interrupts from getting hosed, plus for syncing
+ * with RECEIVED_END below)
+ */
+ tnt4882_internal_interrupt(board);
+ /* RECEIVED_END should be in sync now */
+ if (test_and_clear_bit(RECEIVED_END_BN, &nec_priv->state))
+ *end = 1;
+ if (retval < 0) {
+ // force immediate holdoff
+ write_byte(nec_priv, AUX_HLDI, AUXMR);
+
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+ }
+ *bytes_read = count;
+
+ return retval;
+}
+
+static int fifo_space_available(struct tnt4882_priv *tnt_priv)
+{
+ int status2;
+ int retval;
+
+ status2 = tnt_readb(tnt_priv, STS2);
+ retval = (status2 & AFFN) && (status2 & BFFN);
+
+ return retval;
+}
+
+static unsigned int tnt_transfer_count(struct tnt4882_priv *tnt_priv)
+{
+ unsigned int count = 0;
+
+ count |= tnt_readb(tnt_priv, CNT0) & 0xff;
+ count |= (tnt_readb(tnt_priv, CNT1) << 8) & 0xff00;
+ count |= (tnt_readb(tnt_priv, CNT2) << 16) & 0xff0000;
+ count |= (tnt_readb(tnt_priv, CNT3) << 24) & 0xff000000;
+ // return two's complement
+ return -count;
+};
+
+static int write_wait(struct gpib_board *board, struct tnt4882_priv *tnt_priv,
+ int wait_for_done, int send_commands)
+{
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+
+ if (wait_event_interruptible(board->wait,
+ (!wait_for_done && fifo_space_available(tnt_priv)) ||
+ fifo_xfer_done(tnt_priv) ||
+ test_bit(BUS_ERROR_BN, &nec_priv->state) ||
+ test_bit(DEV_CLEAR_BN, &nec_priv->state) ||
+ test_bit(TIMO_NUM, &board->status)))
+ return -ERESTARTSYS;
+
+ if (test_bit(TIMO_NUM, &board->status))
+ return -ETIMEDOUT;
+ if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state))
+ return (send_commands) ? -ENOTCONN : -ECOMM;
+ if (test_bit(DEV_CLEAR_BN, &nec_priv->state))
+ return -EINTR;
+ return 0;
+}
+
+static int generic_write(struct gpib_board *board, u8 *buffer, size_t length,
+ int send_eoi, int send_commands, size_t *bytes_written)
+{
+ size_t count = 0;
+ ssize_t retval = 0;
+ struct tnt4882_priv *tnt_priv = board->private_data;
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+ unsigned int bits;
+ s32 hw_count;
+ unsigned long flags;
+
+ *bytes_written = 0;
+ // FIXME: really, DEV_CLEAR_BN should happen elsewhere to prevent race
+ clear_bit(DEV_CLEAR_BN, &nec_priv->state);
+
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_ERRIE, HR_ERRIE);
+
+ if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004)
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, HR_DMAO);
+ else
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0);
+
+ tnt_writeb(tnt_priv, RESET_FIFO, CMDR);
+ udelay(1);
+
+ bits = TNT_B_16BIT;
+ if (send_eoi) {
+ bits |= TNT_CCEN;
+ if (nec_priv->type != TNT4882 && nec_priv->type != TNT5004)
+ tnt_writeb(tnt_priv, AUX_SEOI, CCR);
+ }
+ if (send_commands)
+ bits |= TNT_COMMAND;
+ tnt_writeb(tnt_priv, bits, CFG);
+
+ // load 2's complement of count into hardware counters
+ hw_count = -length;
+ tnt_writeb(tnt_priv, hw_count & 0xff, CNT0);
+ tnt_writeb(tnt_priv, (hw_count >> 8) & 0xff, CNT1);
+ tnt_writeb(tnt_priv, (hw_count >> 16) & 0xff, CNT2);
+ tnt_writeb(tnt_priv, (hw_count >> 24) & 0xff, CNT3);
+
+ tnt_writeb(tnt_priv, GO, CMDR);
+ udelay(1);
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ tnt_priv->imr3_bits |= HR_DONE;
+ tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ while (count < length) {
+ // wait until byte is ready to be sent
+ retval = write_wait(board, tnt_priv, 0, send_commands);
+ if (retval < 0)
+ break;
+ if (fifo_xfer_done(tnt_priv))
+ break;
+ spin_lock_irqsave(&board->spinlock, flags);
+ while (fifo_space_available(tnt_priv) && count < length) {
+ u16 word;
+
+ word = buffer[count++] & 0xff;
+ if (count < length)
+ word |= (buffer[count++] << 8) & 0xff00;
+ iowrite16(word, nec_priv->mmiobase + FIFOB);
+ }
+// avoid unnecessary HR_NFF interrupts
+// tnt_priv->imr3_bits |= HR_NFF;
+// tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+
+ if (need_resched())
+ schedule();
+ }
+ // wait last byte has been sent
+ if (retval == 0)
+ retval = write_wait(board, tnt_priv, 1, send_commands);
+
+ tnt_writeb(tnt_priv, STOP, CMDR);
+ udelay(1);
+
+ nec7210_set_reg_bits(nec_priv, IMR1, HR_ERR, 0x0);
+ nec7210_set_reg_bits(nec_priv, IMR2, HR_DMAO, 0x0);
+ /*
+ * force handling of any interrupts that happened
+ * while they were masked (this appears to be needed)
+ */
+ tnt4882_internal_interrupt(board);
+ *bytes_written = length - tnt_transfer_count(tnt_priv);
+ return retval;
+}
+
+static int tnt4882_accel_write(struct gpib_board *board, u8 *buffer,
+ size_t length, int send_eoi, size_t *bytes_written)
+{
+ return generic_write(board, buffer, length, send_eoi, 0, bytes_written);
+}
+
+static int tnt4882_command(struct gpib_board *board, u8 *buffer, size_t length,
+ size_t *bytes_written)
+{
+ return generic_write(board, buffer, length, 0, 1, bytes_written);
+}
+
+static irqreturn_t tnt4882_internal_interrupt(struct gpib_board *board)
+{
+ struct tnt4882_priv *priv = board->private_data;
+ int isr0_bits, isr3_bits, imr3_bits;
+ unsigned long flags;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+
+ nec7210_interrupt(board, &priv->nec7210_priv);
+
+ isr0_bits = tnt_readb(priv, ISR0);
+ isr3_bits = tnt_readb(priv, ISR3);
+ imr3_bits = priv->imr3_bits;
+
+ if (isr0_bits & TNT_IFCI_BIT)
+ push_gpib_event(board, EVENT_IFC);
+ // XXX don't need this wakeup, one below should do?
+// wake_up_interruptible(&board->wait);
+
+ if (isr3_bits & HR_NFF)
+ priv->imr3_bits &= ~HR_NFF;
+ if (isr3_bits & HR_NEF)
+ priv->imr3_bits &= ~HR_NEF;
+ if (isr3_bits & HR_DONE)
+ priv->imr3_bits &= ~HR_DONE;
+ if (isr3_bits & (HR_INTR | HR_TLCI)) {
+ dev_dbg(board->gpib_dev, "minor %i isr0 0x%x imr0 0x%x isr3 0x%x imr3 0x%x\n",
+ board->minor, isr0_bits, priv->imr0_bits, isr3_bits, imr3_bits);
+ tnt_writeb(priv, priv->imr3_bits, IMR3);
+ wake_up_interruptible(&board->wait);
+ }
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t tnt4882_interrupt(int irq, void *arg)
+{
+ return tnt4882_internal_interrupt(arg);
+}
+
+// wrappers for interface functions
+static int tnt4882_read(struct gpib_board *board, u8 *buffer, size_t length, int *end,
+ size_t *bytes_read)
+{
+ struct tnt4882_priv *priv = board->private_data;
+ struct nec7210_priv *nec_priv = &priv->nec7210_priv;
+ int retval;
+ int dummy;
+
+ retval = nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
+
+ if (retval < 0) { // force immediate holdoff
+ write_byte(nec_priv, AUX_HLDI, AUXMR);
+
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+
+ nec7210_read_data_in(board, nec_priv, &dummy);
+ }
+ return retval;
+}
+
+static int tnt4882_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi,
+ size_t *bytes_written)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
+}
+
+static int tnt4882_command_unaccel(struct gpib_board *board, u8 *buffer,
+ size_t length, size_t *bytes_written)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
+}
+
+static int tnt4882_take_control(struct gpib_board *board, int synchronous)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
+}
+
+static int tnt4882_go_to_standby(struct gpib_board *board)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_go_to_standby(board, &priv->nec7210_priv);
+}
+
+static int tnt4882_request_system_control(struct gpib_board *board, int request_control)
+{
+ struct tnt4882_priv *priv = board->private_data;
+ int retval;
+
+ if (request_control) {
+ tnt_writeb(priv, SETSC, CMDR);
+ udelay(1);
+ }
+ retval = nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
+ if (!request_control) {
+ tnt_writeb(priv, CLRSC, CMDR);
+ udelay(1);
+ }
+ return retval;
+}
+
+static void tnt4882_interface_clear(struct gpib_board *board, int assert)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ nec7210_interface_clear(board, &priv->nec7210_priv, assert);
+}
+
+static void tnt4882_remote_enable(struct gpib_board *board, int enable)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ nec7210_remote_enable(board, &priv->nec7210_priv, enable);
+}
+
+static int tnt4882_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
+}
+
+static void tnt4882_disable_eos(struct gpib_board *board)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ nec7210_disable_eos(board, &priv->nec7210_priv);
+}
+
+static unsigned int tnt4882_update_status(struct gpib_board *board, unsigned int clear_mask)
+{
+ unsigned long flags;
+ u8 line_status;
+ struct tnt4882_priv *priv = board->private_data;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ board->status &= ~clear_mask;
+ nec7210_update_status_nolock(board, &priv->nec7210_priv);
+ /* set / clear SRQ state since it is not cleared by interrupt */
+ line_status = tnt_readb(priv, BSR);
+ if (line_status & BCSR_SRQ_BIT)
+ set_bit(SRQI_NUM, &board->status);
+ else
+ clear_bit(SRQI_NUM, &board->status);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+ return board->status;
+}
+
+static int tnt4882_primary_address(struct gpib_board *board, unsigned int address)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_primary_address(board, &priv->nec7210_priv, address);
+}
+
+static int tnt4882_secondary_address(struct gpib_board *board, unsigned int address, int enable)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
+}
+
+static int tnt4882_parallel_poll(struct gpib_board *board, u8 *result)
+{
+ struct tnt4882_priv *tnt_priv = board->private_data;
+
+ if (tnt_priv->nec7210_priv.type != NEC7210) {
+ tnt_priv->auxg_bits |= RPP2_BIT;
+ write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR);
+ udelay(2); // FIXME use parallel poll timeout
+ *result = read_byte(&tnt_priv->nec7210_priv, CPTR);
+ tnt_priv->auxg_bits &= ~RPP2_BIT;
+ write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR);
+ return 0;
+ } else {
+ return nec7210_parallel_poll(board, &tnt_priv->nec7210_priv, result);
+ }
+}
+
+static void tnt4882_parallel_poll_configure(struct gpib_board *board, u8 config)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ if (priv->nec7210_priv.type == TNT5004) {
+ /* configure locally */
+ write_byte(&priv->nec7210_priv, AUXRI | 0x4, AUXMR);
+ if (config)
+ /* set response + clear sense */
+ write_byte(&priv->nec7210_priv, PPR | config, AUXMR);
+ else
+ /* disable ppoll */
+ write_byte(&priv->nec7210_priv, PPR | 0x10, AUXMR);
+ } else {
+ nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
+ }
+}
+
+static void tnt4882_parallel_poll_response(struct gpib_board *board, int ist)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
+}
+
+/*
+ * this is just used by the old nec7210 isa interfaces, the newer
+ * boards use tnt4882_serial_poll_response2
+ */
+static void tnt4882_serial_poll_response(struct gpib_board *board, u8 status)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
+}
+
+static void tnt4882_serial_poll_response2(struct gpib_board *board, u8 status,
+ int new_reason_for_service)
+{
+ struct tnt4882_priv *priv = board->private_data;
+ unsigned long flags;
+ const int MSS = status & request_service_bit;
+ const int reqt = MSS && new_reason_for_service;
+ const int reqf = MSS == 0;
+
+ spin_lock_irqsave(&board->spinlock, flags);
+ if (reqt) {
+ priv->nec7210_priv.srq_pending = 1;
+ clear_bit(SPOLL_NUM, &board->status);
+ } else {
+ if (reqf)
+ priv->nec7210_priv.srq_pending = 0;
+ }
+ if (reqt)
+ /*
+ * It may seem like a race to issue reqt before updating
+ * the status byte, but it is not. The chip does not
+ * issue the reqt until the SPMR is written to at
+ * a later time.
+ */
+ write_byte(&priv->nec7210_priv, AUX_REQT, AUXMR);
+ else if (reqf)
+ write_byte(&priv->nec7210_priv, AUX_REQF, AUXMR);
+ /*
+ * We need to always zero bit 6 of the status byte before writing it to
+ * the SPMR to insure we are using
+ * serial poll mode SP1, and not accidentally triggering mode SP3.
+ */
+ write_byte(&priv->nec7210_priv, status & ~request_service_bit, SPMR);
+ spin_unlock_irqrestore(&board->spinlock, flags);
+}
+
+static u8 tnt4882_serial_poll_status(struct gpib_board *board)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ return nec7210_serial_poll_status(board, &priv->nec7210_priv);
+}
+
+static void tnt4882_return_to_local(struct gpib_board *board)
+{
+ struct tnt4882_priv *priv = board->private_data;
+
+ nec7210_return_to_local(board, &priv->nec7210_priv);
+}
+
+static void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, struct gpib_board *board)
+{
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+
+ tnt_priv->imr0_bits = 0;
+ tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0);
+ tnt_priv->imr3_bits = 0;
+ tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+ tnt_readb(tnt_priv, IMR0);
+ tnt_readb(tnt_priv, IMR3);
+ nec7210_board_reset(nec_priv, board);
+}
+
+static int tnt4882_allocate_private(struct gpib_board *board)
+{
+ struct tnt4882_priv *tnt_priv;
+
+ board->private_data = kmalloc(sizeof(struct tnt4882_priv), GFP_KERNEL);
+ if (!board->private_data)
+ return -1;
+ tnt_priv = board->private_data;
+ memset(tnt_priv, 0, sizeof(struct tnt4882_priv));
+ init_nec7210_private(&tnt_priv->nec7210_priv);
+ return 0;
+}
+
+static void tnt4882_free_private(struct gpib_board *board)
+{
+ kfree(board->private_data);
+ board->private_data = NULL;
+}
+
+static void tnt4882_init(struct tnt4882_priv *tnt_priv, const struct gpib_board *board)
+{
+ struct nec7210_priv *nec_priv = &tnt_priv->nec7210_priv;
+
+ /* Turbo488 software reset */
+ tnt_writeb(tnt_priv, SOFT_RESET, CMDR);
+ udelay(1);
+
+ // turn off one-chip mode
+ tnt_writeb(tnt_priv, NODMA, HSSEL);
+ tnt_writeb(tnt_priv, 0, ACCWR);
+ // make sure we are in 7210 mode
+ tnt_writeb(tnt_priv, AUX_7210, AUXCR);
+ udelay(1);
+ // registers might be swapped, so write it to the swapped address too
+ tnt_writeb(tnt_priv, AUX_7210, SWAPPED_AUXCR);
+ udelay(1);
+ // turn on one-chip mode
+ if (nec_priv->type == TNT4882 || nec_priv->type == TNT5004)
+ tnt_writeb(tnt_priv, NODMA | TNT_ONE_CHIP_BIT, HSSEL);
+ else
+ tnt_writeb(tnt_priv, NODMA, HSSEL);
+
+ nec7210_board_reset(nec_priv, board);
+ // read-clear isr0
+ tnt_readb(tnt_priv, ISR0);
+
+ // enable passing of nat4882 interrupts
+ tnt_priv->imr3_bits = HR_TLCI;
+ tnt_writeb(tnt_priv, tnt_priv->imr3_bits, IMR3);
+
+ // enable interrupt
+ tnt_writeb(tnt_priv, 0x1, INTRT);
+
+ // force immediate holdoff
+ write_byte(&tnt_priv->nec7210_priv, AUX_HLDI, AUXMR);
+
+ set_bit(RFD_HOLDOFF_BN, &nec_priv->state);
+
+ tnt_priv->auxg_bits = AUXRG | NTNL_BIT;
+ write_byte(&tnt_priv->nec7210_priv, tnt_priv->auxg_bits, AUXMR);
+
+ nec7210_board_online(nec_priv, board);
+ // enable interface clear interrupt for event queue
+ tnt_priv->imr0_bits = TNT_IMR0_ALWAYS_BITS | TNT_ATNI_BIT | TNT_IFCIE_BIT;
+ tnt_writeb(tnt_priv, tnt_priv->imr0_bits, IMR0);
+}
+
+static int ni_pci_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct tnt4882_priv *tnt_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = IRQF_SHARED;
+ int retval;
+ struct mite_struct *mite;
+
+ board->status = 0;
+
+ if (tnt4882_allocate_private(board))
+ return -ENOMEM;
+ tnt_priv = board->private_data;
+ nec_priv = &tnt_priv->nec7210_priv;
+ nec_priv->type = TNT4882;
+ nec_priv->read_byte = nec7210_locking_iomem_read_byte;
+ nec_priv->write_byte = nec7210_locking_iomem_write_byte;
+ nec_priv->offset = atgpib_reg_offset;
+
+ if (!mite_devices)
+ return -ENODEV;
+
+ for (mite = mite_devices; mite; mite = mite->next) {
+ short found_board;
+
+ if (mite->used)
+ continue;
+ if (config->pci_bus >= 0 && config->pci_bus != mite->pcidev->bus->number)
+ continue;
+ if (config->pci_slot >= 0 && config->pci_slot != PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ switch (mite_device_id(mite)) {
+ case PCI_DEVICE_ID_NI_GPIB:
+ case PCI_DEVICE_ID_NI_GPIB_PLUS:
+ case PCI_DEVICE_ID_NI_GPIB_PLUS2:
+ case PCI_DEVICE_ID_NI_PXIGPIB:
+ case PCI_DEVICE_ID_NI_PMCGPIB:
+ case PCI_DEVICE_ID_NI_PCIEGPIB:
+ case PCI_DEVICE_ID_NI_PCIE2GPIB:
+// support for Measurement Computing PCI-488
+ case PCI_DEVICE_ID_MC_PCI488:
+ case PCI_DEVICE_ID_CEC_NI_GPIB:
+ found_board = 1;
+ break;
+ default:
+ found_board = 0;
+ break;
+ }
+ if (found_board)
+ break;
+ }
+ if (!mite)
+ return -ENODEV;
+
+ tnt_priv->mite = mite;
+ retval = mite_setup(tnt_priv->mite);
+ if (retval < 0)
+ return retval;
+
+ nec_priv->mmiobase = tnt_priv->mite->daq_io_addr;
+
+ // get irq
+ retval = request_irq(mite_irq(tnt_priv->mite), tnt4882_interrupt, isr_flags, "ni-pci-gpib",
+ board);
+ if (retval) {
+ dev_err(board->gpib_dev, "failed to obtain pci irq %d\n", mite_irq(tnt_priv->mite));
+ return retval;
+ }
+ tnt_priv->irq = mite_irq(tnt_priv->mite);
+
+ // TNT5004 detection
+ switch (tnt_readb(tnt_priv, CSR) & 0xf0) {
+ case 0x30:
+ nec_priv->type = TNT4882;
+ break;
+ case 0x40:
+ nec_priv->type = TNT5004;
+ break;
+ }
+ tnt4882_init(tnt_priv, board);
+
+ return 0;
+}
+
+static void ni_pci_detach(struct gpib_board *board)
+{
+ struct tnt4882_priv *tnt_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (tnt_priv) {
+ nec_priv = &tnt_priv->nec7210_priv;
+
+ if (nec_priv->mmiobase)
+ tnt4882_board_reset(tnt_priv, board);
+ if (tnt_priv->irq)
+ free_irq(tnt_priv->irq, board);
+ if (tnt_priv->mite)
+ mite_unsetup(tnt_priv->mite);
+ }
+ tnt4882_free_private(board);
+}
+
+static int ni_isapnp_find(struct pnp_dev **dev)
+{
+ *dev = pnp_find_dev(NULL, ISAPNP_VENDOR_ID_NI,
+ ISAPNP_FUNCTION(ISAPNP_ID_NI_ATGPIB_TNT), NULL);
+ if (!*dev || !(*dev)->card)
+ return -ENODEV;
+ if (pnp_device_attach(*dev) < 0)
+ return -EBUSY;
+ if (pnp_activate_dev(*dev) < 0) {
+ pnp_device_detach(*dev);
+ return -EAGAIN;
+ }
+ if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) {
+ pnp_device_detach(*dev);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ni_isa_attach_common(struct gpib_board *board, const struct gpib_board_config *config,
+ enum nec7210_chipset chipset)
+{
+ struct tnt4882_priv *tnt_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = 0;
+ u32 iobase;
+ int irq;
+ int retval;
+
+ board->status = 0;
+
+ if (tnt4882_allocate_private(board))
+ return -ENOMEM;
+ tnt_priv = board->private_data;
+ nec_priv = &tnt_priv->nec7210_priv;
+ nec_priv->type = chipset;
+ nec_priv->read_byte = nec7210_locking_ioport_read_byte;
+ nec_priv->write_byte = nec7210_locking_ioport_write_byte;
+ nec_priv->offset = atgpib_reg_offset;
+
+ // look for plug-n-play board
+ if (config->ibbase == 0) {
+ struct pnp_dev *dev;
+
+ retval = ni_isapnp_find(&dev);
+ if (retval < 0)
+ return retval;
+ tnt_priv->pnp_dev = dev;
+ iobase = pnp_port_start(dev, 0);
+ irq = pnp_irq(dev, 0);
+ } else {
+ iobase = config->ibbase;
+ irq = config->ibirq;
+ }
+ // allocate ioports
+ if (!request_region(iobase, atgpib_iosize, "atgpib"))
+ return -EBUSY;
+
+ nec_priv->mmiobase = ioport_map(iobase, atgpib_iosize);
+ if (!nec_priv->mmiobase)
+ return -EBUSY;
+
+ // get irq
+ retval = request_irq(irq, tnt4882_interrupt, isr_flags, "atgpib", board);
+ if (retval) {
+ dev_err(board->gpib_dev, "failed to request ISA irq %d\n", irq);
+ return retval;
+ }
+ tnt_priv->irq = irq;
+
+ tnt4882_init(tnt_priv, board);
+
+ return 0;
+}
+
+static int ni_tnt_isa_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return ni_isa_attach_common(board, config, TNT4882);
+}
+
+static int ni_nat4882_isa_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return ni_isa_attach_common(board, config, NAT4882);
+}
+
+static int ni_nec_isa_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ return ni_isa_attach_common(board, config, NEC7210);
+}
+
+static void ni_isa_detach(struct gpib_board *board)
+{
+ struct tnt4882_priv *tnt_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (tnt_priv) {
+ nec_priv = &tnt_priv->nec7210_priv;
+ if (nec_priv->iobase)
+ tnt4882_board_reset(tnt_priv, board);
+ if (tnt_priv->irq)
+ free_irq(tnt_priv->irq, board);
+ if (nec_priv->mmiobase)
+ ioport_unmap(nec_priv->mmiobase);
+ if (nec_priv->iobase)
+ release_region(nec_priv->iobase, atgpib_iosize);
+ if (tnt_priv->pnp_dev)
+ pnp_device_detach(tnt_priv->pnp_dev);
+ }
+ tnt4882_free_private(board);
+}
+
+static int tnt4882_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return 0;
+}
+
+static struct gpib_interface ni_pci_interface = {
+ .name = "ni_pci",
+ .attach = ni_pci_attach,
+ .detach = ni_pci_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response2 = tnt4882_serial_poll_response2,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_pci_accel_interface = {
+ .name = "ni_pci_accel",
+ .attach = ni_pci_attach,
+ .detach = ni_pci_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response2 = tnt4882_serial_poll_response2,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_isa_interface = {
+ .name = "ni_isa",
+ .attach = ni_tnt_isa_attach,
+ .detach = ni_isa_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response2 = tnt4882_serial_poll_response2,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_nat4882_isa_interface = {
+ .name = "ni_nat4882_isa",
+ .attach = ni_nat4882_isa_attach,
+ .detach = ni_isa_detach,
+ .read = tnt4882_read,
+ .write = tnt4882_write,
+ .command = tnt4882_command_unaccel,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response2 = tnt4882_serial_poll_response2,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_nec_isa_interface = {
+ .name = "ni_nec_isa",
+ .attach = ni_nec_isa_attach,
+ .detach = ni_isa_detach,
+ .read = tnt4882_read,
+ .write = tnt4882_write,
+ .command = tnt4882_command_unaccel,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response = tnt4882_serial_poll_response,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_isa_accel_interface = {
+ .name = "ni_isa_accel",
+ .attach = ni_tnt_isa_attach,
+ .detach = ni_isa_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response2 = tnt4882_serial_poll_response2,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_nat4882_isa_accel_interface = {
+ .name = "ni_nat4882_isa_accel",
+ .attach = ni_nat4882_isa_attach,
+ .detach = ni_isa_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command_unaccel,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response2 = tnt4882_serial_poll_response2,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_nec_isa_accel_interface = {
+ .name = "ni_nec_isa_accel",
+ .attach = ni_nec_isa_attach,
+ .detach = ni_isa_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command_unaccel,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = NULL,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response = tnt4882_serial_poll_response,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static const struct pci_device_id tnt4882_pci_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_GPIB_PLUS2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PXIGPIB)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PMCGPIB)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIEGPIB)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_NI_PCIE2GPIB)},
+ // support for Measurement Computing PCI-488
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_MC_PCI488)},
+ {PCI_DEVICE(PCI_VENDOR_ID_NATINST, PCI_DEVICE_ID_CEC_NI_GPIB)},
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, tnt4882_pci_table);
+
+static struct pci_driver tnt4882_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = tnt4882_pci_table,
+ .probe = &tnt4882_pci_probe
+};
+
+#if 0
+/* unused, will be needed when the driver is turned into a pnp_driver */
+static const struct pnp_device_id tnt4882_pnp_table[] = {
+ {.id = "NICC601"},
+ {.id = ""}
+};
+MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table);
+#endif
+
+#ifdef CONFIG_GPIB_PCMCIA
+static struct gpib_interface ni_pcmcia_interface;
+static struct gpib_interface ni_pcmcia_accel_interface;
+static int __init init_ni_gpib_cs(void);
+static void __exit exit_ni_gpib_cs(void);
+#endif
+
+static int __init tnt4882_init_module(void)
+{
+ int result;
+
+ result = pci_register_driver(&tnt4882_pci_driver);
+ if (result) {
+ pr_err("pci_register_driver failed: error = %d\n", result);
+ return result;
+ }
+
+ result = gpib_register_driver(&ni_isa_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_isa;
+ }
+
+ result = gpib_register_driver(&ni_isa_accel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_isa_accel;
+ }
+
+ result = gpib_register_driver(&ni_nat4882_isa_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_nat4882_isa;
+ }
+
+ result = gpib_register_driver(&ni_nat4882_isa_accel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_nat4882_isa_accel;
+ }
+
+ result = gpib_register_driver(&ni_nec_isa_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_nec_isa;
+ }
+
+ result = gpib_register_driver(&ni_nec_isa_accel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_nec_isa_accel;
+ }
+
+ result = gpib_register_driver(&ni_pci_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_pci;
+ }
+
+ result = gpib_register_driver(&ni_pci_accel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_pci_accel;
+ }
+
+#ifdef CONFIG_GPIB_PCMCIA
+ result = gpib_register_driver(&ni_pcmcia_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_pcmcia;
+ }
+
+ result = gpib_register_driver(&ni_pcmcia_accel_interface, THIS_MODULE);
+ if (result) {
+ pr_err("gpib_register_driver failed: error = %d\n", result);
+ goto err_pcmcia_accel;
+ }
+
+ result = init_ni_gpib_cs();
+ if (result) {
+ pr_err("pcmcia_register_driver failed: error = %d\n", result);
+ goto err_pcmcia_driver;
+ }
+#endif
+
+ mite_init();
+
+ return 0;
+
+#ifdef CONFIG_GPIB_PCMCIA
+err_pcmcia_driver:
+ gpib_unregister_driver(&ni_pcmcia_accel_interface);
+err_pcmcia_accel:
+ gpib_unregister_driver(&ni_pcmcia_interface);
+err_pcmcia:
+#endif
+ gpib_unregister_driver(&ni_pci_accel_interface);
+err_pci_accel:
+ gpib_unregister_driver(&ni_pci_interface);
+err_pci:
+ gpib_unregister_driver(&ni_nec_isa_accel_interface);
+err_nec_isa_accel:
+ gpib_unregister_driver(&ni_nec_isa_interface);
+err_nec_isa:
+ gpib_unregister_driver(&ni_nat4882_isa_accel_interface);
+err_nat4882_isa_accel:
+ gpib_unregister_driver(&ni_nat4882_isa_interface);
+err_nat4882_isa:
+ gpib_unregister_driver(&ni_isa_accel_interface);
+err_isa_accel:
+ gpib_unregister_driver(&ni_isa_interface);
+err_isa:
+ pci_unregister_driver(&tnt4882_pci_driver);
+
+ return result;
+}
+
+static void __exit tnt4882_exit_module(void)
+{
+ gpib_unregister_driver(&ni_isa_interface);
+ gpib_unregister_driver(&ni_isa_accel_interface);
+ gpib_unregister_driver(&ni_nat4882_isa_interface);
+ gpib_unregister_driver(&ni_nat4882_isa_accel_interface);
+ gpib_unregister_driver(&ni_nec_isa_interface);
+ gpib_unregister_driver(&ni_nec_isa_accel_interface);
+ gpib_unregister_driver(&ni_pci_interface);
+ gpib_unregister_driver(&ni_pci_accel_interface);
+#ifdef CONFIG_GPIB_PCMCIA
+ gpib_unregister_driver(&ni_pcmcia_interface);
+ gpib_unregister_driver(&ni_pcmcia_accel_interface);
+ exit_ni_gpib_cs();
+#endif
+
+ mite_cleanup();
+
+ pci_unregister_driver(&tnt4882_pci_driver);
+}
+
+#ifdef CONFIG_GPIB_PCMCIA
+
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+static int ni_gpib_config(struct pcmcia_device *link);
+static void ni_gpib_release(struct pcmcia_device *link);
+static void ni_pcmcia_detach(struct gpib_board *board);
+
+/*
+ * A linked list of "instances" of the dummy device. Each actual
+ * PCMCIA card corresponds to one device instance, and is described
+ * by one dev_link_t structure (defined in ds.h).
+ *
+ * You may not want to use a linked list for this -- for example, the
+ * memory card driver uses an array of dev_link_t pointers, where minor
+ * device numbers are used to derive the corresponding array index.
+ *
+ * I think this dev_list is obsolete but the pointer is needed to keep
+ * the module instance for the ni_pcmcia_attach function.
+ */
+
+static struct pcmcia_device *curr_dev;
+
+struct local_info_t {
+ struct pcmcia_device *p_dev;
+ struct gpib_board *dev;
+ int stop;
+ struct bus_operations *bus;
+};
+
+/*
+ * ni_gpib_probe() creates an "instance" of the driver, allocating
+ * local data structures for one device. The device is registered
+ * with Card Services.
+ */
+
+static int ni_gpib_probe(struct pcmcia_device *link)
+{
+ struct local_info_t *info;
+ //struct struct gpib_board *dev;
+
+ /* Allocate space for private device-specific data */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ /*
+ * General socket configuration defaults can go here. In this
+ * client, we assume very little, and rely on the CIS for almost
+ * everything. In most clients, many details (i.e., number, sizes,
+ * and attributes of IO windows) are fixed by the nature of the
+ * device, and can be hard-wired here.
+ */
+ link->config_flags = CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+ /* Register with Card Services */
+ curr_dev = link;
+ return ni_gpib_config(link);
+}
+
+/*
+ * This deletes a driver "instance". The device is de-registered
+ * with Card Services. If it has been released, all local data
+ * structures are freed. Otherwise, the structures will be freed
+ * when the device is released.
+ */
+static void ni_gpib_remove(struct pcmcia_device *link)
+{
+ struct local_info_t *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ if (info->dev)
+ ni_pcmcia_detach(info->dev);
+ ni_gpib_release(link);
+
+ //free_netdev(dev);
+ kfree(info);
+}
+
+static int ni_gpib_config_iteration(struct pcmcia_device *link, void *priv_data)
+{
+ int retval;
+
+ retval = pcmcia_request_io(link);
+ if (retval != 0)
+ return retval;
+
+ return 0;
+}
+
+/*
+ * ni_gpib_config() is scheduled to run after a CARD_INSERTION event
+ * is received, to configure the PCMCIA socket, and to make the
+ * device available to the system.
+ */
+static int ni_gpib_config(struct pcmcia_device *link)
+{
+ //struct local_info_t *info = link->priv;
+ //struct gpib_board *dev = info->dev;
+ int last_ret;
+
+ last_ret = pcmcia_loop_config(link, &ni_gpib_config_iteration, NULL);
+ if (last_ret) {
+ dev_warn(&link->dev, "no configuration found\n");
+ ni_gpib_release(link);
+ return last_ret;
+ }
+
+ last_ret = pcmcia_enable_device(link);
+ if (last_ret) {
+ ni_gpib_release(link);
+ return last_ret;
+ }
+ return 0;
+} /* ni_gpib_config */
+
+/*
+ * After a card is removed, ni_gpib_release() will unregister the
+ * device, and release the PCMCIA configuration. If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void ni_gpib_release(struct pcmcia_device *link)
+{
+ pcmcia_disable_device(link);
+} /* ni_gpib_release */
+
+static int ni_gpib_suspend(struct pcmcia_device *link)
+{
+ //struct local_info_t *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ if (link->open)
+ dev_warn(&link->dev, "Device still open\n");
+ //netif_device_detach(dev);
+
+ return 0;
+}
+
+static int ni_gpib_resume(struct pcmcia_device *link)
+{
+ //struct local_info_t *info = link->priv;
+ //struct struct gpib_board *dev = info->dev;
+
+ /*if (link->open) {
+ * ni_gpib_probe(dev); / really?
+ * //netif_device_attach(dev);
+ *}
+ */
+ return ni_gpib_config(link);
+}
+
+static struct pcmcia_device_id ni_pcmcia_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4882),
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0c71), // NI PCMCIA-GPIB+
+ PCMCIA_DEVICE_NULL
+};
+
+MODULE_DEVICE_TABLE(pcmcia, ni_pcmcia_ids);
+
+static struct pcmcia_driver ni_gpib_cs_driver = {
+ .name = "ni_gpib_cs",
+ .owner = THIS_MODULE,
+ .drv = { .name = "ni_gpib_cs", },
+ .id_table = ni_pcmcia_ids,
+ .probe = ni_gpib_probe,
+ .remove = ni_gpib_remove,
+ .suspend = ni_gpib_suspend,
+ .resume = ni_gpib_resume,
+};
+
+static int __init init_ni_gpib_cs(void)
+{
+ return pcmcia_register_driver(&ni_gpib_cs_driver);
+}
+
+static void __exit exit_ni_gpib_cs(void)
+{
+ pcmcia_unregister_driver(&ni_gpib_cs_driver);
+}
+
+static const int pcmcia_gpib_iosize = 32;
+
+static int ni_pcmcia_attach(struct gpib_board *board, const struct gpib_board_config *config)
+{
+ struct local_info_t *info;
+ struct tnt4882_priv *tnt_priv;
+ struct nec7210_priv *nec_priv;
+ int isr_flags = IRQF_SHARED;
+ int retval;
+
+ if (!curr_dev)
+ return -ENODEV;
+
+ info = curr_dev->priv;
+ info->dev = board;
+
+ board->status = 0;
+
+ if (tnt4882_allocate_private(board))
+ return -ENOMEM;
+
+ tnt_priv = board->private_data;
+ nec_priv = &tnt_priv->nec7210_priv;
+ nec_priv->type = TNT4882;
+ nec_priv->read_byte = nec7210_locking_ioport_read_byte;
+ nec_priv->write_byte = nec7210_locking_ioport_write_byte;
+ nec_priv->offset = atgpib_reg_offset;
+
+ if (!request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]),
+ DRV_NAME))
+ return -ENOMEM;
+
+ nec_priv->mmiobase = ioport_map(curr_dev->resource[0]->start,
+ resource_size(curr_dev->resource[0]));
+ if (!nec_priv->mmiobase)
+ return -ENOMEM;
+
+ // get irq
+ retval = request_irq(curr_dev->irq, tnt4882_interrupt, isr_flags, DRV_NAME, board);
+ if (retval) {
+ dev_err(board->gpib_dev, "failed to obtain PCMCIA irq %d\n", curr_dev->irq);
+ return retval;
+ }
+ tnt_priv->irq = curr_dev->irq;
+
+ tnt4882_init(tnt_priv, board);
+
+ return 0;
+}
+
+static void ni_pcmcia_detach(struct gpib_board *board)
+{
+ struct tnt4882_priv *tnt_priv = board->private_data;
+ struct nec7210_priv *nec_priv;
+
+ if (tnt_priv) {
+ nec_priv = &tnt_priv->nec7210_priv;
+ if (tnt_priv->irq)
+ free_irq(tnt_priv->irq, board);
+ if (nec_priv->mmiobase)
+ ioport_unmap(nec_priv->mmiobase);
+ if (nec_priv->iobase) {
+ tnt4882_board_reset(tnt_priv, board);
+ release_region(nec_priv->iobase, pcmcia_gpib_iosize);
+ }
+ }
+ tnt4882_free_private(board);
+}
+
+static struct gpib_interface ni_pcmcia_interface = {
+ .name = "ni_pcmcia",
+ .attach = ni_pcmcia_attach,
+ .detach = ni_pcmcia_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response = tnt4882_serial_poll_response,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+static struct gpib_interface ni_pcmcia_accel_interface = {
+ .name = "ni_pcmcia_accel",
+ .attach = ni_pcmcia_attach,
+ .detach = ni_pcmcia_detach,
+ .read = tnt4882_accel_read,
+ .write = tnt4882_accel_write,
+ .command = tnt4882_command,
+ .take_control = tnt4882_take_control,
+ .go_to_standby = tnt4882_go_to_standby,
+ .request_system_control = tnt4882_request_system_control,
+ .interface_clear = tnt4882_interface_clear,
+ .remote_enable = tnt4882_remote_enable,
+ .enable_eos = tnt4882_enable_eos,
+ .disable_eos = tnt4882_disable_eos,
+ .parallel_poll = tnt4882_parallel_poll,
+ .parallel_poll_configure = tnt4882_parallel_poll_configure,
+ .parallel_poll_response = tnt4882_parallel_poll_response,
+ .local_parallel_poll_mode = NULL, // XXX
+ .line_status = tnt4882_line_status,
+ .update_status = tnt4882_update_status,
+ .primary_address = tnt4882_primary_address,
+ .secondary_address = tnt4882_secondary_address,
+ .serial_poll_response = tnt4882_serial_poll_response,
+ .serial_poll_status = tnt4882_serial_poll_status,
+ .t1_delay = tnt4882_t1_delay,
+ .return_to_local = tnt4882_return_to_local,
+};
+
+#endif // CONFIG_GPIB_PCMCIA
+
+module_init(tnt4882_init_module);
+module_exit(tnt4882_exit_module);