root/drivers/gpib/ines/ines_gpib.c
// 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 int ines72130_line_status(const struct gpib_board *board)
{
        int status = VALID_ALL;
        int bsr_bits;
        struct ines_priv *ines_priv = board->private_data;

        bsr_bits = ines_inb(ines_priv, BUS_STATUS_REG);

        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;
}

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 (ines_priv->pci_chip_type == PCI_CHIP_INES_72130)
                return retval;

        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);
        if (priv->pci_chip_type == PCI_CHIP_INES_72130)
                goto out;
        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);
out:
        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_xl_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_xl_interface = {
        .name = "ines_pci_xl",
        .attach = ines_pci_xl_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 = ines72130_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 = kzalloc_obj(struct ines_priv);
        if (!board->private_data)
                return -ENOMEM;
        priv = board->private_data;
        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;
        int retval;

        board->status = 0;

        retval = ines_allocate_private(board);
        if (retval)
                return retval;
        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_xl_attach(struct gpib_board *board, const struct gpib_board_config *config)
{
        struct ines_priv *ines_priv;
        struct nec7210_priv *nec_priv;
        int retval;

        retval = ines_common_pci_attach(board, config);
        if (retval < 0)
                return retval;

        ines_priv = board->private_data;
        ines_priv->pci_chip_type = PCI_CHIP_INES_72130;
        nec_priv = &ines_priv->nec7210_priv;
        nec7210_board_online(nec_priv, board);

        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_obj(*info);
        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_xl_interface, THIS_MODULE);
        if (ret) {
                pr_err("gpib_register_driver failed: error = %d\n", ret);
                goto err_pci_xl;
        }

        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_xl:
        gpib_unregister_driver(&ines_pci_xl_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_xl_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);