diff options
author | Dave Penkler <dpenkler@gmail.com> | 2024-09-18 14:19:08 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2024-10-10 15:28:50 +0200 |
commit | 0cd5b05551e02242f9fa15f1e87cefff9efa3080 (patch) | |
tree | d44fdf79ab87566dd31a9c08acd7c021e4b9c253 | |
parent | 0dc1ad1c0051c29eaca6202e08d0d5787d296649 (diff) |
staging: gpib: Add TNT4882 chip based GPIB driver
Driver for National Instruments TNT4882 based boards
Signed-off-by: Dave Penkler <dpenkler@gmail.com>
Link: https://lore.kernel.org/r/20240918121908.19366-22-dpenkler@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/staging/gpib/tnt4882/Makefile | 7 | ||||
-rw-r--r-- | drivers/staging/gpib/tnt4882/mite.c | 221 | ||||
-rw-r--r-- | drivers/staging/gpib/tnt4882/mite.h | 243 | ||||
-rw-r--r-- | drivers/staging/gpib/tnt4882/tnt4882_gpib.c | 1874 |
4 files changed, 2345 insertions, 0 deletions
diff --git a/drivers/staging/gpib/tnt4882/Makefile b/drivers/staging/gpib/tnt4882/Makefile new file mode 100644 index 000000000000..f767c990db7a --- /dev/null +++ b/drivers/staging/gpib/tnt4882/Makefile @@ -0,0 +1,7 @@ +ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA +obj-m += tnt4882.o + +tnt4882-objs := tnt4882_gpib.o mite.o + + + diff --git a/drivers/staging/gpib/tnt4882/mite.c b/drivers/staging/gpib/tnt4882/mite.c new file mode 100644 index 000000000000..adb656a5eb2c --- /dev/null +++ b/drivers/staging/gpib/tnt4882/mite.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2 + +/* + * 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 = kmalloc(sizeof(*mite), GFP_KERNEL); + if (!mite) + return; + + memset(mite, 0, sizeof(*mite)); + + 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; + } + pr_info("mite: 0x%08lx mapped to %p\n", mite->mite_phys_addr, mite->mite_io_addr); + 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; + } + pr_info("mite: daq: 0x%08lx mapped to %p\n", mite->daq_phys_addr, mite->daq_io_addr); + 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; +} + +void mite_list_devices(void) +{ + struct mite_struct *mite, *next; + + pr_info("Available NI PCI device IDs:"); + if (mite_devices) + for (mite = mite_devices; mite; mite = next) { + next = mite->next; + pr_info(" 0x%04x", mite_device_id(mite)); + if (mite->used) + pr_info("(used)"); + } + pr_info("\n"); +} + +int mite_bytes_transferred(struct mite_struct *mite, int chan) +{ + int dar, fcr; + + dar = readl(mite->mite_io_addr + MITE_DAR + CHAN_OFFSET(chan)); + fcr = readl(mite->mite_io_addr + MITE_FCR + CHAN_OFFSET(chan)) & 0x000000FF; + return dar - fcr; +} + +int mite_dma_tcr(struct mite_struct *mite) +{ + int tcr; + int lkar; + + lkar = readl(mite->mite_io_addr + CHAN_OFFSET(0) + MITE_LKAR); + tcr = readl(mite->mite_io_addr + CHAN_OFFSET(0) + MITE_TCR); + MDPRINTK("lkar=0x%08x tcr=%d\n", lkar, tcr); + + return tcr; +} + +void mite_dma_disarm(struct mite_struct *mite) +{ + int chor; + + /* disarm */ + chor = CHOR_ABORT; + writel(chor, mite->mite_io_addr + CHAN_OFFSET(0) + MITE_CHOR); +} + +void mite_dump_regs(struct mite_struct *mite) +{ + void *addr = 0; + unsigned long temp = 0; + + pr_info("mite address is =0x%p\n", mite->mite_io_addr); + + addr = mite->mite_io_addr + MITE_CHOR + CHAN_OFFSET(0); + pr_info("mite status[CHOR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CHOR_strings,temp); + addr = mite->mite_io_addr + MITE_CHCR + CHAN_OFFSET(0); + pr_info("mite status[CHCR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CHCR_strings,temp); + addr = mite->mite_io_addr + MITE_TCR + CHAN_OFFSET(0); + pr_info("mite status[TCR] at 0x%p =0x%08x\n", addr, readl(addr)); + addr = mite->mite_io_addr + MITE_MCR + CHAN_OFFSET(0); + pr_info("mite status[MCR] at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_MCR_strings,temp); + addr = mite->mite_io_addr + MITE_MAR + CHAN_OFFSET(0); + pr_info("mite status[MAR] at 0x%p =0x%08x\n", addr, readl(addr)); + addr = mite->mite_io_addr + MITE_DCR + CHAN_OFFSET(0); + pr_info("mite status[DCR] at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CR_strings,temp); + addr = mite->mite_io_addr + MITE_DAR + CHAN_OFFSET(0); + pr_info("mite status[DAR] at 0x%p =0x%08x\n", addr, readl(addr)); + addr = mite->mite_io_addr + MITE_LKCR + CHAN_OFFSET(0); + pr_info("mite status[LKCR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CR_strings,temp); + addr = mite->mite_io_addr + MITE_LKAR + CHAN_OFFSET(0); + pr_info("mite status[LKAR]at 0x%p =0x%08x\n", addr, readl(addr)); + + addr = mite->mite_io_addr + MITE_CHSR + CHAN_OFFSET(0); + pr_info("mite status[CHSR]at 0x%p =0x%08lx\n", addr, temp = readl(addr)); + //mite_decode(mite_CHSR_strings,temp); + addr = mite->mite_io_addr + MITE_FCR + CHAN_OFFSET(0); + pr_info("mite status[FCR] at 0x%p =0x%08x\n\n", addr, readl(addr)); +} + diff --git a/drivers/staging/gpib/tnt4882/mite.h b/drivers/staging/gpib/tnt4882/mite.h new file mode 100644 index 000000000000..6454d069b8cc --- /dev/null +++ b/drivers/staging/gpib/tnt4882/mite.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2 */ + +/* + * 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 *mite_io_addr; + unsigned long daq_phys_addr; + void *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); + +int mite_dma_tcr(struct mite_struct *mite); + +void mite_dma_arm(struct mite_struct *mite); +void mite_dma_disarm(struct mite_struct *mite); + +void mite_dump_regs(struct mite_struct *mite); +void mite_setregs(struct mite_struct *mite, unsigned long ll_start, int chan, int dir); +int mite_bytes_transferred(struct mite_struct *mite, int chan); + +#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/staging/gpib/tnt4882/tnt4882_gpib.c b/drivers/staging/gpib/tnt4882/tnt4882_gpib.c new file mode 100644 index 000000000000..ef4b9ce36741 --- /dev/null +++ b/drivers/staging/gpib/tnt4882/tnt4882_gpib.c @@ -0,0 +1,1874 @@ +// 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 + ***************************************************************************/ + +#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 + void (*io_writeb)(unsigned int value, void *address); + void (*io_writew)(unsigned int value, void *address); + unsigned int (*io_readb)(void *address); + unsigned int (*io_readw)(void *address); +}; + +// interface functions +static int tnt4882_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +static int tnt4882_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, + int *end, size_t *bytes_read); +static int tnt4882_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +static int tnt4882_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, + int send_eoi, size_t *bytes_written); +static int tnt4882_command(gpib_board_t *board, uint8_t *buffer, size_t length, + size_t *bytes_written); +static int tnt4882_command_unaccel(gpib_board_t *board, uint8_t *buffer, + size_t length, size_t *bytes_written); +static int tnt4882_take_control(gpib_board_t *board, int synchronous); +static int tnt4882_go_to_standby(gpib_board_t *board); +static void tnt4882_request_system_control(gpib_board_t *board, int request_control); +static void tnt4882_interface_clear(gpib_board_t *board, int assert); +static void tnt4882_remote_enable(gpib_board_t *board, int enable); +static int tnt4882_enable_eos(gpib_board_t *board, uint8_t eos_byte, int + compare_8_bits); +static void tnt4882_disable_eos(gpib_board_t *board); +static unsigned int tnt4882_update_status(gpib_board_t *board, unsigned int clear_mask); +static int tnt4882_primary_address(gpib_board_t *board, unsigned int address); +static int tnt4882_secondary_address(gpib_board_t *board, unsigned int address, + int enable); +static int tnt4882_parallel_poll(gpib_board_t *board, uint8_t *result); +static void tnt4882_parallel_poll_configure(gpib_board_t *board, uint8_t config); +static void tnt4882_parallel_poll_response(gpib_board_t *board, int ist); +static void tnt4882_serial_poll_response(gpib_board_t *board, uint8_t status); +static uint8_t tnt4882_serial_poll_status(gpib_board_t *board); +static int tnt4882_line_status(const gpib_board_t *board); +static unsigned int tnt4882_t1_delay(gpib_board_t *board, unsigned int nano_sec); +static void tnt4882_return_to_local(gpib_board_t *board); + +// interrupt service routines +static irqreturn_t tnt4882_internal_interrupt(gpib_board_t *board); +static irqreturn_t tnt4882_interrupt(int irq, void *arg); + +// utility functions +static int tnt4882_allocate_private(gpib_board_t *board); +static void tnt4882_free_private(gpib_board_t *board); +static void tnt4882_init(struct tnt4882_priv *tnt_priv, const gpib_board_t *board); +static void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, gpib_board_t *board); + +// register offset for nec7210 compatible registers +static const int atgpib_reg_offset = 2; + +// number of ioports used +static const int atgpib_iosize = 32; +static const int pcmcia_gpib_iosize = 32; + +/* paged io */ +static inline unsigned int tnt_paged_readb(struct tnt4882_priv *priv, unsigned long offset) +{ + priv->io_writeb(AUX_PAGEIN, priv->nec7210_priv.iobase + AUXMR * priv->nec7210_priv.offset); + udelay(1); + return priv->io_readb(priv->nec7210_priv.iobase + offset); +} + +static inline void tnt_paged_writeb(struct tnt4882_priv *priv, unsigned int value, + unsigned long offset) +{ + priv->io_writeb(AUX_PAGEIN, priv->nec7210_priv.iobase + AUXMR * priv->nec7210_priv.offset); + udelay(1); + priv->io_writeb(value, priv->nec7210_priv.iobase + offset); +} + +/* readb/writeb wrappers */ +static inline unsigned short tnt_readb(struct tnt4882_priv *priv, unsigned long offset) +{ + void *address = priv->nec7210_priv.iobase + 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 = priv->io_readb(address); + break; + case NAT4882: + retval = tnt_paged_readb(priv, offset - tnt_pagein_offset); + break; + case NEC7210: + retval = 0; + break; + default: + pr_err("tnt4882: bug! unsupported ni_chipset\n"); + retval = 0; + break; + } + break; + default: + retval = priv->io_readb(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 *address = priv->nec7210_priv.iobase + 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: + priv->io_writeb(value, address); + break; + case NAT4882: + tnt_paged_writeb(priv, value, offset - tnt_pagein_offset); + break; + case NEC7210: + break; + default: + pr_err("tnt4882: bug! unsupported ni_chipset\n"); + break; + } + break; + default: + priv->io_writeb(value, address); + break; + } + spin_unlock_irqrestore(register_lock, flags); +} + +MODULE_LICENSE("GPL"); + +int tnt4882_line_status(const gpib_board_t *board) +{ + int status = ValidALL; + 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 |= BusREN; + if (bcsr_bits & BCSR_IFC_BIT) + status |= BusIFC; + if (bcsr_bits & BCSR_SRQ_BIT) + status |= BusSRQ; + if (bcsr_bits & BCSR_EOI_BIT) + status |= BusEOI; + if (bcsr_bits & BCSR_NRFD_BIT) + status |= BusNRFD; + if (bcsr_bits & BCSR_NDAC_BIT) + status |= BusNDAC; + if (bcsr_bits & BCSR_DAV_BIT) + status |= BusDAV; + if (bcsr_bits & BCSR_ATN_BIT) + status |= BusATN; + + return status; +} + +unsigned int tnt4882_t1_delay(gpib_board_t *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, uint8_t *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 = tnt_priv->io_readw(nec_priv->iobase + FIFOB); + buffer[count++] = word & 0xff; + buffer[count++] = (word >> 8) & 0xff; + } + return count; +} + +static void tnt4882_release_holdoff(gpib_board_t *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); + } +} + +int tnt4882_accel_read(gpib_board_t *board, uint8_t *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))) { + pr_err("tnt4882: read interrupted\n"); + retval = -ERESTARTSYS; + break; + } + if (test_bit(TIMO_NUM, &board->status)) { + //pr_info("tnt4882: minor %i read timed out\n", board->minor); + retval = -ETIMEDOUT; + break; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + pr_err("tnt4882: device clear interrupted read\n"); + retval = -EINTR; + break; + } + if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) { + pr_err("tnt4882: address change interrupted read\n"); + 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))) { + pr_err("tnt4882: read interrupted\n"); + retval = -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) + //pr_info("tnt4882: read timed out\n"); + retval = -ETIMEDOUT; + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + pr_err("tnt4882: device clear interrupted read\n"); + retval = -EINTR; + } + if (test_bit(ADR_CHANGE_BN, &nec_priv->state)) { + pr_err("tnt4882: address change interrupted read\n"); + 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(gpib_board_t *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))) { + GPIB_DPRINTK("gpib write interrupted\n"); + return -ERESTARTSYS; + } + if (test_bit(TIMO_NUM, &board->status)) { + pr_info("tnt4882: write timed out\n"); + return -ETIMEDOUT; + } + if (test_and_clear_bit(BUS_ERROR_BN, &nec_priv->state)) { + pr_err("tnt4882: write bus error\n"); + return (send_commands) ? -ENOTCONN : -ECOMM; + } + if (test_bit(DEV_CLEAR_BN, &nec_priv->state)) { + pr_err("tnt4882: device clear interrupted write\n"); + return -EINTR; + } + return 0; +} + +static int generic_write(gpib_board_t *board, uint8_t *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; + tnt_priv->io_writew(word, nec_priv->iobase + 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; +} + +int tnt4882_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi, + size_t *bytes_written) +{ + return generic_write(board, buffer, length, send_eoi, 0, bytes_written); +} + +int tnt4882_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written) +{ + return generic_write(board, buffer, length, 0, 1, bytes_written); +} + +irqreturn_t tnt4882_internal_interrupt(gpib_board_t *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, EventIFC); + //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)) { + GPIB_DPRINTK("tnt4882: 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; +} + +irqreturn_t tnt4882_interrupt(int irq, void *arg) +{ + return tnt4882_internal_interrupt(arg); +} + +static int ni_tnt_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ni_nat4882_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ni_nec_isa_attach(gpib_board_t *board, const gpib_board_config_t *config); +static int ni_pci_attach(gpib_board_t *board, const gpib_board_config_t *config); + +static void ni_isa_detach(gpib_board_t *board); +static void ni_pci_detach(gpib_board_t *board); + +#ifdef GPIB_PCMCIA +static int ni_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config); +static void ni_pcmcia_detach(gpib_board_t *board); +static int init_ni_gpib_cs(void); +static void __exit exit_ni_gpib_cs(void); +#endif + +// wrappers for interface functions +int tnt4882_read(gpib_board_t *board, uint8_t *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; +} + +int tnt4882_write(gpib_board_t *board, uint8_t *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); +} + +int tnt4882_command_unaccel(gpib_board_t *board, uint8_t *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); +} + +int tnt4882_take_control(gpib_board_t *board, int synchronous) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_take_control(board, &priv->nec7210_priv, synchronous); +} + +int tnt4882_go_to_standby(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_go_to_standby(board, &priv->nec7210_priv); +} + +void tnt4882_request_system_control(gpib_board_t *board, int request_control) +{ + struct tnt4882_priv *priv = board->private_data; + + if (request_control) { + tnt_writeb(priv, SETSC, CMDR); + udelay(1); + } + nec7210_request_system_control(board, &priv->nec7210_priv, request_control); + if (!request_control) { + tnt_writeb(priv, CLRSC, CMDR); + udelay(1); + } +} + +void tnt4882_interface_clear(gpib_board_t *board, int assert) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_interface_clear(board, &priv->nec7210_priv, assert); +} + +void tnt4882_remote_enable(gpib_board_t *board, int enable) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_remote_enable(board, &priv->nec7210_priv, enable); +} + +int tnt4882_enable_eos(gpib_board_t *board, uint8_t 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); +} + +void tnt4882_disable_eos(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_disable_eos(board, &priv->nec7210_priv); +} + +unsigned int tnt4882_update_status(gpib_board_t *board, unsigned int clear_mask) +{ + unsigned long flags; + u8 line_status; + unsigned int retval; + struct tnt4882_priv *priv = board->private_data; + + spin_lock_irqsave(&board->spinlock, flags); + board->status &= ~clear_mask; + retval = 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; +} + +int tnt4882_primary_address(gpib_board_t *board, unsigned int address) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_primary_address(board, &priv->nec7210_priv, address); +} + +int tnt4882_secondary_address(gpib_board_t *board, unsigned int address, int enable) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable); +} + +int tnt4882_parallel_poll(gpib_board_t *board, uint8_t *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); + } +} + +void tnt4882_parallel_poll_configure(gpib_board_t *board, uint8_t 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); + } +} + +void tnt4882_parallel_poll_response(gpib_board_t *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 + */ +void tnt4882_serial_poll_response(gpib_board_t *board, uint8_t status) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_serial_poll_response(board, &priv->nec7210_priv, status); +} + +static void tnt4882_serial_poll_response2(gpib_board_t *board, uint8_t 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); +} + +uint8_t tnt4882_serial_poll_status(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + return nec7210_serial_poll_status(board, &priv->nec7210_priv); +} + +void tnt4882_return_to_local(gpib_board_t *board) +{ + struct tnt4882_priv *priv = board->private_data; + + nec7210_return_to_local(board, &priv->nec7210_priv); +} + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +gpib_interface_t 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, +}; + +#ifdef GPIB_PCMCIA +gpib_interface_t 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, +}; + +gpib_interface_t 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 + +void tnt4882_board_reset(struct tnt4882_priv *tnt_priv, gpib_board_t *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); +} + +int tnt4882_allocate_private(gpib_board_t *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; +} + +void tnt4882_free_private(gpib_board_t *board) +{ + kfree(board->private_data); + board->private_data = NULL; +} + +void tnt4882_init(struct tnt4882_priv *tnt_priv, const gpib_board_t *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); +} + +int ni_pci_attach(gpib_board_t *board, const gpib_board_config_t *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; + tnt_priv->io_writeb = writeb_wrapper; + tnt_priv->io_readb = readb_wrapper; + tnt_priv->io_writew = writew_wrapper; + tnt_priv->io_readw = readw_wrapper; + 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) { + pr_err("no National Instruments PCI boards found\n"); + return -1; + } + + 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) { + pr_err("no NI PCI-GPIB boards found\n"); + return -1; + } + tnt_priv->mite = mite; + retval = mite_setup(tnt_priv->mite); + if (retval < 0) { + pr_err("tnt4882: error setting up mite.\n"); + return retval; + } + + nec_priv->iobase = tnt_priv->mite->daq_io_addr; + + // get irq + if (request_irq(mite_irq(tnt_priv->mite), tnt4882_interrupt, isr_flags, + "ni-pci-gpib", board)) { + pr_err("gpib: can't request IRQ %d\n", mite_irq(tnt_priv->mite)); + return -1; + } + tnt_priv->irq = mite_irq(tnt_priv->mite); + pr_info("tnt4882: irq %i\n", tnt_priv->irq); + + // TNT5004 detection + switch (tnt_readb(tnt_priv, CSR) & 0xf0) { + case 0x30: + nec_priv->type = TNT4882; + pr_info("tnt4882: TNT4882 chipset detected\n"); + break; + case 0x40: + nec_priv->type = TNT5004; + pr_info("tnt4882: TNT5004 chipset detected\n"); + break; + } + tnt4882_init(tnt_priv, board); + + return 0; +} + +void ni_pci_detach(gpib_board_t *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 (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) { + pr_err("tnt4882: failed to find isapnp board\n"); + return -ENODEV; + } + if (pnp_device_attach(*dev) < 0) { + pr_err("tnt4882: atgpib/tnt board already active, skipping\n"); + return -EBUSY; + } + if (pnp_activate_dev(*dev) < 0) { + pnp_device_detach(*dev); + pr_err("tnt4882: failed to activate() atgpib/tnt, aborting\n"); + return -EAGAIN; + } + if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { + pnp_device_detach(*dev); + pr_err("tnt4882: invalid port or irq for atgpib/tnt, aborting\n"); + return -ENOMEM; + } + return 0; +} + +static int ni_isa_attach_common(gpib_board_t *board, const gpib_board_config_t *config, + enum nec7210_chipset chipset) +{ + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = 0; + void *iobase; + int irq; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + tnt_priv->io_writeb = outb_wrapper; + tnt_priv->io_readb = inb_wrapper; + tnt_priv->io_writew = outw_wrapper; + tnt_priv->io_readw = inw_wrapper; + 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; + int retval; + + retval = ni_isapnp_find(&dev); + if (retval < 0) + return retval; + tnt_priv->pnp_dev = dev; + iobase = (void *)(pnp_port_start(dev, 0)); + irq = pnp_irq(dev, 0); + } else { + iobase = config->ibbase; + irq = config->ibirq; + } + // allocate ioports + if (!request_region((unsigned long)(iobase), atgpib_iosize, "atgpib")) { + pr_err("tnt4882: failed to allocate ioports\n"); + return -1; + } + nec_priv->iobase = iobase; + + // get irq + if (request_irq(irq, tnt4882_interrupt, isr_flags, "atgpib", board)) { + pr_err("gpib: can't request IRQ %d\n", irq); + return -1; + } + tnt_priv->irq = irq; + + tnt4882_init(tnt_priv, board); + + return 0; +} + +int ni_tnt_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return ni_isa_attach_common(board, config, TNT4882); +} + +int ni_nat4882_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return ni_isa_attach_common(board, config, NAT4882); +} + +int ni_nec_isa_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + return ni_isa_attach_common(board, config, NEC7210); +} + +void ni_isa_detach(gpib_board_t *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->iobase) + release_region((unsigned long)(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 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 = "tnt4882", + .id_table = tnt4882_pci_table, + .probe = &tnt4882_pci_probe +}; + +static const struct pnp_device_id tnt4882_pnp_table[] = { + {.id = "NICC601"}, + {.id = ""} +}; +MODULE_DEVICE_TABLE(pnp, tnt4882_pnp_table); + +static int __init tnt4882_init_module(void) +{ + int result; + + result = pci_register_driver(&tnt4882_pci_driver); + if (result) { + pr_err("tnt4882: pci_driver_register failed!\n"); + return result; + } + + gpib_register_driver(&ni_isa_interface, THIS_MODULE); + gpib_register_driver(&ni_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&ni_nat4882_isa_interface, THIS_MODULE); + gpib_register_driver(&ni_nat4882_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&ni_nec_isa_interface, THIS_MODULE); + gpib_register_driver(&ni_nec_isa_accel_interface, THIS_MODULE); + gpib_register_driver(&ni_pci_interface, THIS_MODULE); + gpib_register_driver(&ni_pci_accel_interface, THIS_MODULE); +#ifdef GPIB_PCMCIA + gpib_register_driver(&ni_pcmcia_interface, THIS_MODULE); + gpib_register_driver(&ni_pcmcia_accel_interface, THIS_MODULE); + if (init_ni_gpib_cs() < 0) + return -1; +#endif + + mite_init(); + mite_list_devices(); + + return 0; +} + +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 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 GPIB_PCMCIA + +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/io.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> + +/* + * All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If + * you do not define PCMCIA_DEBUG at all, all the debug code will be + * left out. If you compile with PCMCIA_DEBUG=0, the debug code will + * be present but disabled -- but it can then be enabled for specific + * modules at load time with a 'pc_debug=#' option to insmod. + */ +#define PCMCIA_DEBUG 1 +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +module_param(pc_debug, int, 0); +#define DEBUG(n, args...) \ + do {if (pc_debug > (n)) \ + pr_debug(args); } \ + while (0) +#else +#define DEBUG(args...) +#endif + +static int ni_gpib_config(struct pcmcia_device *link); +static void ni_gpib_release(struct pcmcia_device *link); +static int ni_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config); +static void ni_pcmcia_detach(gpib_board_t *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; + gpib_board_t *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 gpib_board_t *dev; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /* Allocate space for private device-specific data */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + memset(info, 0, sizeof(*info)); + + 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 gpib_board_t *dev = info->dev; + + DEBUG(0, "%s(%p)\n", __func__, link); + + 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; + //gpib_board_t *dev = info->dev; + int last_ret; + + DEBUG(0, "%s(0x%p)\n", __func__, link); + + 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) +{ + DEBUG(0, "%s(0x%p)\n", __func__, 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 gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + if (link->open) + pr_err("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 gpib_board_t *dev = info->dev; + DEBUG(0, "%s(0x%p)\n", __func__, link); + + /*if (link->open) { + * ni_gpib_probe(dev); / really? + * printk("Gpib resumed ???\n"); + * //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, +}; + +int __init init_ni_gpib_cs(void) +{ + return pcmcia_register_driver(&ni_gpib_cs_driver); +} + +void __exit exit_ni_gpib_cs(void) +{ + DEBUG(0, "ni_gpib_cs: unloading\n"); + pcmcia_unregister_driver(&ni_gpib_cs_driver); +} + +int ni_pcmcia_attach(gpib_board_t *board, const gpib_board_config_t *config) +{ + struct local_info_t *info; + struct tnt4882_priv *tnt_priv; + struct nec7210_priv *nec_priv; + int isr_flags = IRQF_SHARED; + + DEBUG(0, "%s(0x%p)\n", __func__, board); + + if (!curr_dev) { + pr_err("gpib: no NI PCMCIA board found\n"); + return -1; + } + + info = curr_dev->priv; + info->dev = board; + + board->status = 0; + + if (tnt4882_allocate_private(board)) + return -ENOMEM; + tnt_priv = board->private_data; + tnt_priv->io_writeb = outb_wrapper; + tnt_priv->io_readb = inb_wrapper; + tnt_priv->io_writew = outw_wrapper; + tnt_priv->io_readw = inw_wrapper; + 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; + + DEBUG(0, "ioport1 window attributes: 0x%lx\n", curr_dev->resource[0]->flags); + if (request_region(curr_dev->resource[0]->start, resource_size(curr_dev->resource[0]), + "tnt4882") == 0) { + pr_err("gpib: ioports starting at 0x%lx are already in use\n", + (unsigned long)curr_dev->resource[0]->start); + return -EIO; + } + + nec_priv->iobase = (void *)(unsigned long)curr_dev->resource[0]->start; + + // get irq + if (request_irq(curr_dev->irq, tnt4882_interrupt, isr_flags, "tnt4882", board)) { + pr_err("gpib: can't request IRQ %d\n", curr_dev->irq); + return -1; + } + tnt_priv->irq = curr_dev->irq; + + tnt4882_init(tnt_priv, board); + + return 0; +} + +void ni_pcmcia_detach(gpib_board_t *board) +{ + struct tnt4882_priv *tnt_priv = board->private_data; + struct nec7210_priv *nec_priv; + + DEBUG(0, "%s(0x%p)\n", __func__, board); + + if (tnt_priv) { + nec_priv = &tnt_priv->nec7210_priv; + if (tnt_priv->irq) + free_irq(tnt_priv->irq, board); + if (nec_priv->iobase) { + tnt4882_board_reset(tnt_priv, board); + release_region((unsigned long)nec_priv->iobase, pcmcia_gpib_iosize); + } + } + tnt4882_free_private(board); +} + +#endif // GPIB_PCMCIA + +module_init(tnt4882_init_module); +module_exit(tnt4882_exit_module); |