// SPDX-License-Identifier: GPL-2.0 /*************************************************************************** * Driver for hp 82341a/b/c/d boards. * * Might be worth merging with Agilent 82350b driver. * * copyright : (C) 2002, 2005 by Frank Mori Hess * ***************************************************************************/ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define dev_fmt pr_fmt #define DRV_NAME KBUILD_MODNAME #include "hp_82341.h" #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("GPIB driver for hp 82341a/b/c/d boards"); static unsigned short read_and_clear_event_status(struct gpib_board *board); static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count); static int read_transfer_counter(struct hp_82341_priv *hp_priv); static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, size_t *bytes_written); static irqreturn_t hp_82341_interrupt(int irq, void *arg); static int hp_82341_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, size_t *bytes_read) { struct hp_82341_priv *hp_priv = board->private_data; struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; int retval = 0; unsigned short event_status; int i; int num_fifo_bytes; // hardware doesn't support checking for end-of-string character when using fifo if (tms_priv->eos_flags & REOS) return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); clear_bit(DEV_CLEAR_BN, &tms_priv->state); read_and_clear_event_status(board); *end = 0; *bytes_read = 0; if (length == 0) return 0; // disable fifo for the moment outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); /* * Handle corner case of board not in holdoff and one byte has slipped in already. * Also, board sometimes has problems (spurious 1 byte reads) when read fifo is * started up with board in TACS under certain data holdoff conditions. * Doing a 1 byte tms9914-style read avoids these problems. */ if (/*tms_priv->holdoff_active == 0 && */length > 1) { size_t num_bytes; retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); *bytes_read += num_bytes; if (retval < 0) dev_err(board->gpib_dev, "tms9914_read failed retval=%i\n", retval); if (retval < 0 || *end) return retval; ++buffer; --length; } tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); tms9914_release_holdoff(tms_priv); outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); i = 0; num_fifo_bytes = length - 1; while (i < num_fifo_bytes && *end == 0) { int block_size; int j; int count; block_size = min(num_fifo_bytes - i, hp_82341_fifo_size); set_transfer_counter(hp_priv, block_size); outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); clear_bit(READ_READY_BN, &tms_priv->state); retval = wait_event_interruptible(board->wait, ((event_status = read_and_clear_event_status(board)) & (TERMINAL_COUNT_EVENT_BIT | BUFFER_END_EVENT_BIT)) || test_bit(DEV_CLEAR_BN, &tms_priv->state) || test_bit(TIMO_NUM, &board->status)); if (retval) { retval = -ERESTARTSYS; break; } // have to disable buffer before we can read from buffer port outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); count = block_size - read_transfer_counter(hp_priv); j = 0; while (j < count && i < num_fifo_bytes) { unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); buffer[i++] = data_word & 0xff; ++j; if (j < count && i < num_fifo_bytes) { buffer[i++] = (data_word >> 8) & 0xff; ++j; } } if (event_status & BUFFER_END_EVENT_BIT) { clear_bit(RECEIVED_END_BN, &tms_priv->state); *end = 1; tms_priv->holdoff_active = 1; } if (test_bit(TIMO_NUM, &board->status)) { retval = -ETIMEDOUT; break; } if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { retval = -EINTR; break; } } *bytes_read += i; buffer += i; length -= i; if (retval < 0) return retval; // read last byte if we havn't received an END yet if (*end == 0) { size_t num_bytes; // try to make sure we holdoff after last byte read retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); *bytes_read += num_bytes; if (retval < 0) return retval; } return 0; } static int restart_write_fifo(struct gpib_board *board, struct hp_82341_priv *hp_priv) { struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0) return 0; while (1) { int status; // restart doesn't work if data holdoff is in effect status = tms9914_line_status(board, tms_priv); if ((status & BUS_NRFD) == 0) { outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); return 0; } if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) return -EINTR; if (test_bit(TIMO_NUM, &board->status)) return -ETIMEDOUT; if (msleep_interruptible(1)) return -EINTR; } return 0; } static int hp_82341_accel_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, size_t *bytes_written) { struct hp_82341_priv *hp_priv = board->private_data; struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; int i, j; unsigned short event_status; int retval = 0; int fifo_xfer_len = length; *bytes_written = 0; if (send_eoi) --fifo_xfer_len; clear_bit(DEV_CLEAR_BN, &tms_priv->state); read_and_clear_event_status(board); outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); for (i = 0; i < fifo_xfer_len;) { int block_size; block_size = min(fifo_xfer_len - i, hp_82341_fifo_size); set_transfer_counter(hp_priv, block_size); // load data into board's fifo for (j = 0; j < block_size;) { unsigned short data_word = buffer[i++]; ++j; if (j < block_size) { data_word |= buffer[i++] << 8; ++j; } outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); } clear_bit(WRITE_READY_BN, &tms_priv->state); outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); retval = restart_write_fifo(board, hp_priv); if (retval < 0) { dev_err(board->gpib_dev, "failed to restart write stream\n"); break; } retval = wait_event_interruptible(board->wait, ((event_status = read_and_clear_event_status(board)) & TERMINAL_COUNT_EVENT_BIT) || test_bit(DEV_CLEAR_BN, &tms_priv->state) || test_bit(TIMO_NUM, &board->status)); outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); *bytes_written += block_size - read_transfer_counter(hp_priv); if (retval) { retval = -ERESTARTSYS; break; } if (test_bit(TIMO_NUM, &board->status)) { retval = -ETIMEDOUT; break; } if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { retval = -EINTR; break; } } if (retval) return retval; if (send_eoi) { size_t num_bytes; retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes); *bytes_written += num_bytes; if (retval < 0) return retval; } return 0; } static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config); static void hp_82341_detach(struct gpib_board *board); // wrappers for interface functions static int hp_82341_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, size_t *bytes_read) { struct hp_82341_priv *priv = board->private_data; return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); } static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, size_t *bytes_written) { struct hp_82341_priv *priv = board->private_data; return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); } static int hp_82341_command(struct gpib_board *board, u8 *buffer, size_t length, size_t *bytes_written) { struct hp_82341_priv *priv = board->private_data; return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); } static int hp_82341_take_control(struct gpib_board *board, int synchronous) { struct hp_82341_priv *priv = board->private_data; return tms9914_take_control(board, &priv->tms9914_priv, synchronous); } static int hp_82341_go_to_standby(struct gpib_board *board) { struct hp_82341_priv *priv = board->private_data; return tms9914_go_to_standby(board, &priv->tms9914_priv); } static int hp_82341_request_system_control(struct gpib_board *board, int request_control) { struct hp_82341_priv *priv = board->private_data; if (request_control) priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT; else priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT; outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG); return tms9914_request_system_control(board, &priv->tms9914_priv, request_control); } static void hp_82341_interface_clear(struct gpib_board *board, int assert) { struct hp_82341_priv *priv = board->private_data; tms9914_interface_clear(board, &priv->tms9914_priv, assert); } static void hp_82341_remote_enable(struct gpib_board *board, int enable) { struct hp_82341_priv *priv = board->private_data; tms9914_remote_enable(board, &priv->tms9914_priv, enable); } static int hp_82341_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) { struct hp_82341_priv *priv = board->private_data; return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); } static void hp_82341_disable_eos(struct gpib_board *board) { struct hp_82341_priv *priv = board->private_data; tms9914_disable_eos(board, &priv->tms9914_priv); } static unsigned int hp_82341_update_status(struct gpib_board *board, unsigned int clear_mask) { struct hp_82341_priv *priv = board->private_data; return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); } static int hp_82341_primary_address(struct gpib_board *board, unsigned int address) { struct hp_82341_priv *priv = board->private_data; return tms9914_primary_address(board, &priv->tms9914_priv, address); } static int hp_82341_secondary_address(struct gpib_board *board, unsigned int address, int enable) { struct hp_82341_priv *priv = board->private_data; return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); } static int hp_82341_parallel_poll(struct gpib_board *board, u8 *result) { struct hp_82341_priv *priv = board->private_data; return tms9914_parallel_poll(board, &priv->tms9914_priv, result); } static void hp_82341_parallel_poll_configure(struct gpib_board *board, u8 config) { struct hp_82341_priv *priv = board->private_data; tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); } static void hp_82341_parallel_poll_response(struct gpib_board *board, int ist) { struct hp_82341_priv *priv = board->private_data; tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); } static void hp_82341_serial_poll_response(struct gpib_board *board, u8 status) { struct hp_82341_priv *priv = board->private_data; tms9914_serial_poll_response(board, &priv->tms9914_priv, status); } static u8 hp_82341_serial_poll_status(struct gpib_board *board) { struct hp_82341_priv *priv = board->private_data; return tms9914_serial_poll_status(board, &priv->tms9914_priv); } static int hp_82341_line_status(const struct gpib_board *board) { struct hp_82341_priv *priv = board->private_data; return tms9914_line_status(board, &priv->tms9914_priv); } static int hp_82341_t1_delay(struct gpib_board *board, unsigned int nano_sec) { struct hp_82341_priv *priv = board->private_data; return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); } static void hp_82341_return_to_local(struct gpib_board *board) { struct hp_82341_priv *priv = board->private_data; tms9914_return_to_local(board, &priv->tms9914_priv); } static struct gpib_interface hp_82341_unaccel_interface = { .name = "hp_82341_unaccel", .attach = hp_82341_attach, .detach = hp_82341_detach, .read = hp_82341_read, .write = hp_82341_write, .command = hp_82341_command, .request_system_control = hp_82341_request_system_control, .take_control = hp_82341_take_control, .go_to_standby = hp_82341_go_to_standby, .interface_clear = hp_82341_interface_clear, .remote_enable = hp_82341_remote_enable, .enable_eos = hp_82341_enable_eos, .disable_eos = hp_82341_disable_eos, .parallel_poll = hp_82341_parallel_poll, .parallel_poll_configure = hp_82341_parallel_poll_configure, .parallel_poll_response = hp_82341_parallel_poll_response, .local_parallel_poll_mode = NULL, // XXX .line_status = hp_82341_line_status, .update_status = hp_82341_update_status, .primary_address = hp_82341_primary_address, .secondary_address = hp_82341_secondary_address, .serial_poll_response = hp_82341_serial_poll_response, .serial_poll_status = hp_82341_serial_poll_status, .t1_delay = hp_82341_t1_delay, .return_to_local = hp_82341_return_to_local, }; static struct gpib_interface hp_82341_interface = { .name = "hp_82341", .attach = hp_82341_attach, .detach = hp_82341_detach, .read = hp_82341_accel_read, .write = hp_82341_accel_write, .command = hp_82341_command, .request_system_control = hp_82341_request_system_control, .take_control = hp_82341_take_control, .go_to_standby = hp_82341_go_to_standby, .interface_clear = hp_82341_interface_clear, .remote_enable = hp_82341_remote_enable, .enable_eos = hp_82341_enable_eos, .disable_eos = hp_82341_disable_eos, .parallel_poll = hp_82341_parallel_poll, .parallel_poll_configure = hp_82341_parallel_poll_configure, .parallel_poll_response = hp_82341_parallel_poll_response, .local_parallel_poll_mode = NULL, // XXX .line_status = hp_82341_line_status, .update_status = hp_82341_update_status, .primary_address = hp_82341_primary_address, .secondary_address = hp_82341_secondary_address, .serial_poll_response = hp_82341_serial_poll_response, .t1_delay = hp_82341_t1_delay, .return_to_local = hp_82341_return_to_local, }; static int hp_82341_allocate_private(struct gpib_board *board) { board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL); if (!board->private_data) return -ENOMEM; return 0; } static void hp_82341_free_private(struct gpib_board *board) { kfree(board->private_data); board->private_data = NULL; } static u8 hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num) { return inb(priv->iobase + register_num); } static void hp_82341_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) { outb(data, priv->iobase + register_num); } static int hp_82341_find_isapnp_board(struct pnp_dev **dev) { *dev = pnp_find_dev(NULL, ISAPNP_VENDOR('H', 'W', 'P'), ISAPNP_FUNCTION(0x1411), NULL); if (!*dev || !(*dev)->card) { pr_err("failed to find isapnp board\n"); return -ENODEV; } if (pnp_device_attach(*dev) < 0) { pr_err("board already active, skipping\n"); return -EBUSY; } if (pnp_activate_dev(*dev) < 0) { pnp_device_detach(*dev); pr_err("failed to activate(), aborting\n"); return -EAGAIN; } if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) { pnp_device_detach(*dev); pr_err("invalid port or irq, aborting\n"); return -ENOMEM; } return 0; } static int xilinx_ready(struct hp_82341_priv *hp_priv) { switch (hp_priv->hw_version) { case HW_VERSION_82341C: if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & XILINX_READY_BIT) return 1; else return 0; break; case HW_VERSION_82341D: if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_READY_BIT) return 1; else return 0; default: pr_err("bug! unknown hw_version\n"); break; } return 0; } static int xilinx_done(struct hp_82341_priv *hp_priv) { switch (hp_priv->hw_version) { case HW_VERSION_82341C: if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & DONE_PGL_BIT) return 1; else return 0; case HW_VERSION_82341D: if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_DONE_BIT) return 1; else return 0; default: pr_err("bug! unknown hw_version\n"); break; } return 0; } static int irq_valid(struct hp_82341_priv *hp_priv, int irq) { switch (hp_priv->hw_version) { case HW_VERSION_82341C: switch (irq) { case 3: case 5: case 7: case 9: case 10: case 11: case 12: case 15: return 1; default: pr_err("invalid irq=%i for 82341C, irq must be 3, 5, 7, 9, 10, 11, 12, or 15.\n", irq); return 0; } break; case HW_VERSION_82341D: return 1; default: pr_err("bug! unknown hw_version\n"); break; } return 0; } static int hp_82341_load_firmware_array(struct hp_82341_priv *hp_priv, const unsigned char *firmware_data, unsigned int firmware_length) { int i, j; static const int timeout = 100; for (i = 0; i < firmware_length; ++i) { for (j = 0; j < timeout; ++j) { if (need_resched()) schedule(); if (xilinx_ready(hp_priv)) break; usleep_range(10, 15); } if (j == timeout) { pr_err("timed out waiting for Xilinx ready.\n"); return -ETIMEDOUT; } outb(firmware_data[i], hp_priv->iobase[0] + XILINX_DATA_REG); } for (j = 0; j < timeout; ++j) { if (xilinx_done(hp_priv)) break; if (need_resched()) schedule(); usleep_range(10, 15); } if (j == timeout) { pr_err("timed out waiting for Xilinx done.\n"); return -ETIMEDOUT; } return 0; } static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv, const struct gpib_board_config *config) { if (config->init_data_length == 0) { if (xilinx_done(hp_priv)) return 0; pr_err("board needs be initialized with firmware upload.\n" "\tUse the --init-data option of gpib_config.\n"); return -EINVAL; } switch (hp_priv->hw_version) { case HW_VERSION_82341C: if (config->init_data_length != hp_82341c_firmware_length) { pr_err("bad firmware length=%i for 82341c (expected %i).\n", config->init_data_length, hp_82341c_firmware_length); return -EINVAL; } break; case HW_VERSION_82341D: if (config->init_data_length != hp_82341d_firmware_length) { pr_err("bad firmware length=%i for 82341d (expected %i).\n", config->init_data_length, hp_82341d_firmware_length); return -EINVAL; } break; default: pr_err("bug! unknown hw_version\n"); break; } return hp_82341_load_firmware_array(hp_priv, config->init_data, config->init_data_length); } static void set_xilinx_not_prog(struct hp_82341_priv *hp_priv, int assert) { switch (hp_priv->hw_version) { case HW_VERSION_82341C: if (assert) hp_priv->config_control_bits |= DONE_PGL_BIT; else hp_priv->config_control_bits &= ~DONE_PGL_BIT; outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); break; case HW_VERSION_82341D: if (assert) isapnp_write_byte(PIO_DATA_REG, HP_82341D_NOT_PROG_BIT); else isapnp_write_byte(PIO_DATA_REG, 0x0); break; default: break; } } // clear xilinx firmware static int clear_xilinx(struct hp_82341_priv *hp_priv) { set_xilinx_not_prog(hp_priv, 1); if (msleep_interruptible(1)) return -EINTR; set_xilinx_not_prog(hp_priv, 0); if (msleep_interruptible(1)) return -EINTR; set_xilinx_not_prog(hp_priv, 1); if (msleep_interruptible(1)) return -EINTR; return 0; } static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config) { struct hp_82341_priv *hp_priv; struct tms9914_priv *tms_priv; u32 start_addr; u32 iobase; int irq; int i; int retval; board->status = 0; if (hp_82341_allocate_private(board)) return -ENOMEM; hp_priv = board->private_data; tms_priv = &hp_priv->tms9914_priv; tms_priv->read_byte = hp_82341_read_byte; tms_priv->write_byte = hp_82341_write_byte; tms_priv->offset = 1; if (config->ibbase == 0) { struct pnp_dev *dev; int retval = hp_82341_find_isapnp_board(&dev); if (retval < 0) return retval; hp_priv->pnp_dev = dev; iobase = pnp_port_start(dev, 0); irq = pnp_irq(dev, 0); hp_priv->hw_version = HW_VERSION_82341D; hp_priv->io_region_offset = 0x8; } else { iobase = config->ibbase; irq = config->ibirq; hp_priv->hw_version = HW_VERSION_82341C; hp_priv->io_region_offset = 0x400; } for (i = 0; i < hp_82341_num_io_regions; ++i) { start_addr = iobase + i * hp_priv->io_region_offset; if (!request_region(start_addr, hp_82341_region_iosize, DRV_NAME)) { dev_err(board->gpib_dev, "failed to allocate io ports 0x%x-0x%x\n", start_addr, start_addr + hp_82341_region_iosize - 1); return -EIO; } hp_priv->iobase[i] = start_addr; } tms_priv->iobase = hp_priv->iobase[2]; if (hp_priv->hw_version == HW_VERSION_82341D) { retval = isapnp_cfg_begin(hp_priv->pnp_dev->card->number, hp_priv->pnp_dev->number); if (retval < 0) { dev_err(board->gpib_dev, "isapnp_cfg_begin returned error\n"); return retval; } isapnp_write_byte(PIO_DIRECTION_REG, HP_82341D_XILINX_READY_BIT | HP_82341D_XILINX_DONE_BIT); } retval = clear_xilinx(hp_priv); if (retval < 0) return retval; retval = hp_82341_load_firmware(hp_priv, config); if (hp_priv->hw_version == HW_VERSION_82341D) isapnp_cfg_end(); if (retval < 0) return retval; if (irq_valid(hp_priv, irq) == 0) return -EINVAL; if (request_irq(irq, hp_82341_interrupt, 0, DRV_NAME, board)) { dev_err(board->gpib_dev, "failed to allocate IRQ %d\n", irq); return -EIO; } hp_priv->irq = irq; hp_priv->config_control_bits &= ~IRQ_SELECT_MASK; hp_priv->config_control_bits |= IRQ_SELECT_BITS(irq); outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG); hp_priv->mode_control_bits |= ENABLE_IRQ_CONFIG_BIT; outb(hp_priv->mode_control_bits, hp_priv->iobase[0] + MODE_CONTROL_STATUS_REG); tms9914_board_reset(tms_priv); outb(ENABLE_BUFFER_END_EVENT_BIT | ENABLE_TERMINAL_COUNT_EVENT_BIT | ENABLE_TI_INTERRUPT_EVENT_BIT, hp_priv->iobase[0] + EVENT_ENABLE_REG); outb(ENABLE_BUFFER_END_INTERRUPT_BIT | ENABLE_TERMINAL_COUNT_INTERRUPT_BIT | ENABLE_TI_INTERRUPT_BIT, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); // write clear event register outb((TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), hp_priv->iobase[0] + EVENT_STATUS_REG); tms9914_online(board, tms_priv); return 0; } static void hp_82341_detach(struct gpib_board *board) { struct hp_82341_priv *hp_priv = board->private_data; struct tms9914_priv *tms_priv; int i; if (hp_priv) { tms_priv = &hp_priv->tms9914_priv; if (hp_priv->iobase[0]) { outb(0, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG); if (tms_priv->iobase) tms9914_board_reset(tms_priv); if (hp_priv->irq) free_irq(hp_priv->irq, board); } for (i = 0; i < hp_82341_num_io_regions; ++i) { if (hp_priv->iobase[i]) release_region(hp_priv->iobase[i], hp_82341_region_iosize); } if (hp_priv->pnp_dev) pnp_device_detach(hp_priv->pnp_dev); } hp_82341_free_private(board); } #if 0 /* unused, will be needed when the driver is turned into a pnp_driver */ static const struct pnp_device_id hp_82341_pnp_table[] = { {.id = "HWP1411"}, {.id = ""} }; MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table); #endif static int __init hp_82341_init_module(void) { int ret; ret = gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE); if (ret) { pr_err("gpib_register_driver failed: error = %d\n", ret); return ret; } ret = gpib_register_driver(&hp_82341_interface, THIS_MODULE); if (ret) { pr_err("gpib_register_driver failed: error = %d\n", ret); gpib_unregister_driver(&hp_82341_unaccel_interface); return ret; } return 0; } static void __exit hp_82341_exit_module(void) { gpib_unregister_driver(&hp_82341_interface); gpib_unregister_driver(&hp_82341_unaccel_interface); } module_init(hp_82341_init_module); module_exit(hp_82341_exit_module); /* * GPIB interrupt service routines */ static unsigned short read_and_clear_event_status(struct gpib_board *board) { struct hp_82341_priv *hp_priv = board->private_data; unsigned long flags; unsigned short status; spin_lock_irqsave(&board->spinlock, flags); status = hp_priv->event_status_bits; hp_priv->event_status_bits = 0; spin_unlock_irqrestore(&board->spinlock, flags); return status; } static irqreturn_t hp_82341_interrupt(int irq, void *arg) { int status1, status2; struct gpib_board *board = arg; struct hp_82341_priv *hp_priv = board->private_data; struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; unsigned long flags; irqreturn_t retval = IRQ_NONE; int event_status; spin_lock_irqsave(&board->spinlock, flags); event_status = inb(hp_priv->iobase[0] + EVENT_STATUS_REG); if (event_status & INTERRUPT_PENDING_EVENT_BIT) retval = IRQ_HANDLED; // write-clear status bits if (event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT)) { outb(event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT | BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT), hp_priv->iobase[0] + EVENT_STATUS_REG); hp_priv->event_status_bits |= event_status; } if (event_status & TI_INTERRUPT_EVENT_BIT) { status1 = read_byte(tms_priv, ISR0); status2 = read_byte(tms_priv, ISR1); tms9914_interrupt_have_status(board, tms_priv, status1, status2); } spin_unlock_irqrestore(&board->spinlock, flags); return retval; } static int read_transfer_counter(struct hp_82341_priv *hp_priv) { int lo, mid, value; lo = inb(hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); mid = inb(hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); value = (lo & 0xff) | ((mid << 8) & 0x7f00); value = ~(value - 1) & 0x7fff; return value; } static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count) { int complement = -count; outb(complement & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG); outb((complement >> 8) & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG); // I don't think the hi count reg is even used, but oh well outb((complement >> 16) & 0xf, hp_priv->iobase[1] + TRANSFER_COUNT_HIGH_REG); }