| // SPDX-License-Identifier: GPL-2.0 |
| |
| /*************************************************************************** |
| * Driver for hp 82341a/b/c/d boards. * |
| * Might be worth merging with Agilent 82350b driver. * |
| * copyright : (C) 2002, 2005 by Frank Mori Hess * |
| ***************************************************************************/ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| #define dev_fmt pr_fmt |
| #define DRV_NAME KBUILD_MODNAME |
| |
| #include "hp_82341.h" |
| #include <linux/delay.h> |
| #include <linux/ioport.h> |
| #include <linux/sched.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/init.h> |
| #include <linux/isapnp.h> |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("GPIB driver for hp 82341a/b/c/d boards"); |
| |
| static unsigned short read_and_clear_event_status(struct gpib_board *board); |
| static void set_transfer_counter(struct hp_82341_priv *hp_priv, int count); |
| static int read_transfer_counter(struct hp_82341_priv *hp_priv); |
| static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, |
| size_t *bytes_written); |
| static irqreturn_t hp_82341_interrupt(int irq, void *arg); |
| |
| static int hp_82341_accel_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, |
| size_t *bytes_read) |
| { |
| struct hp_82341_priv *hp_priv = board->private_data; |
| struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; |
| int retval = 0; |
| unsigned short event_status; |
| int i; |
| int num_fifo_bytes; |
| // hardware doesn't support checking for end-of-string character when using fifo |
| if (tms_priv->eos_flags & REOS) |
| return tms9914_read(board, tms_priv, buffer, length, end, bytes_read); |
| |
| clear_bit(DEV_CLEAR_BN, &tms_priv->state); |
| |
| read_and_clear_event_status(board); |
| *end = 0; |
| *bytes_read = 0; |
| if (length == 0) |
| return 0; |
| // disable fifo for the moment |
| outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); |
| /* |
| * Handle corner case of board not in holdoff and one byte has slipped in already. |
| * Also, board sometimes has problems (spurious 1 byte reads) when read fifo is |
| * started up with board in TACS under certain data holdoff conditions. |
| * Doing a 1 byte tms9914-style read avoids these problems. |
| */ |
| if (/*tms_priv->holdoff_active == 0 && */length > 1) { |
| size_t num_bytes; |
| |
| retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes); |
| *bytes_read += num_bytes; |
| if (retval < 0) |
| dev_err(board->gpib_dev, "tms9914_read failed retval=%i\n", retval); |
| if (retval < 0 || *end) |
| return retval; |
| ++buffer; |
| --length; |
| } |
| tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI); |
| tms9914_release_holdoff(tms_priv); |
| outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); |
| i = 0; |
| num_fifo_bytes = length - 1; |
| while (i < num_fifo_bytes && *end == 0) { |
| int block_size; |
| int j; |
| int count; |
| |
| block_size = min(num_fifo_bytes - i, hp_82341_fifo_size); |
| set_transfer_counter(hp_priv, block_size); |
| outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + |
| BUFFER_CONTROL_REG); |
| if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) |
| outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); |
| |
| clear_bit(READ_READY_BN, &tms_priv->state); |
| |
| retval = wait_event_interruptible(board->wait, |
| ((event_status = |
| read_and_clear_event_status(board)) & |
| (TERMINAL_COUNT_EVENT_BIT | |
| BUFFER_END_EVENT_BIT)) || |
| test_bit(DEV_CLEAR_BN, &tms_priv->state) || |
| test_bit(TIMO_NUM, &board->status)); |
| if (retval) { |
| retval = -ERESTARTSYS; |
| break; |
| } |
| // have to disable buffer before we can read from buffer port |
| outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); |
| count = block_size - read_transfer_counter(hp_priv); |
| j = 0; |
| while (j < count && i < num_fifo_bytes) { |
| unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); |
| |
| buffer[i++] = data_word & 0xff; |
| ++j; |
| if (j < count && i < num_fifo_bytes) { |
| buffer[i++] = (data_word >> 8) & 0xff; |
| ++j; |
| } |
| } |
| if (event_status & BUFFER_END_EVENT_BIT) { |
| clear_bit(RECEIVED_END_BN, &tms_priv->state); |
| |
| *end = 1; |
| tms_priv->holdoff_active = 1; |
| } |
| if (test_bit(TIMO_NUM, &board->status)) { |
| retval = -ETIMEDOUT; |
| break; |
| } |
| if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { |
| retval = -EINTR; |
| break; |
| } |
| } |
| *bytes_read += i; |
| buffer += i; |
| length -= i; |
| if (retval < 0) |
| return retval; |
| // read last byte if we havn't received an END yet |
| if (*end == 0) { |
| size_t num_bytes; |
| // try to make sure we holdoff after last byte read |
| retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes); |
| *bytes_read += num_bytes; |
| if (retval < 0) |
| return retval; |
| } |
| return 0; |
| } |
| |
| static int restart_write_fifo(struct gpib_board *board, struct hp_82341_priv *hp_priv) |
| { |
| struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; |
| |
| if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0) |
| return 0; |
| while (1) { |
| int status; |
| |
| // restart doesn't work if data holdoff is in effect |
| status = tms9914_line_status(board, tms_priv); |
| if ((status & BUS_NRFD) == 0) { |
| outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG); |
| return 0; |
| } |
| if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) |
| return -EINTR; |
| if (test_bit(TIMO_NUM, &board->status)) |
| return -ETIMEDOUT; |
| if (msleep_interruptible(1)) |
| return -EINTR; |
| } |
| return 0; |
| } |
| |
| static int hp_82341_accel_write(struct gpib_board *board, u8 *buffer, size_t length, |
| int send_eoi, size_t *bytes_written) |
| { |
| struct hp_82341_priv *hp_priv = board->private_data; |
| struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv; |
| int i, j; |
| unsigned short event_status; |
| int retval = 0; |
| int fifo_xfer_len = length; |
| |
| *bytes_written = 0; |
| if (send_eoi) |
| --fifo_xfer_len; |
| |
| clear_bit(DEV_CLEAR_BN, &tms_priv->state); |
| |
| read_and_clear_event_status(board); |
| outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); |
| outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG); |
| for (i = 0; i < fifo_xfer_len;) { |
| int block_size; |
| |
| block_size = min(fifo_xfer_len - i, hp_82341_fifo_size); |
| set_transfer_counter(hp_priv, block_size); |
| // load data into board's fifo |
| for (j = 0; j < block_size;) { |
| unsigned short data_word = buffer[i++]; |
| ++j; |
| if (j < block_size) { |
| data_word |= buffer[i++] << 8; |
| ++j; |
| } |
| outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG); |
| } |
| clear_bit(WRITE_READY_BN, &tms_priv->state); |
| outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG); |
| retval = restart_write_fifo(board, hp_priv); |
| if (retval < 0) { |
| dev_err(board->gpib_dev, "failed to restart write stream\n"); |
| break; |
| } |
| retval = wait_event_interruptible(board->wait, |
| ((event_status = |
| read_and_clear_event_status(board)) & |
| TERMINAL_COUNT_EVENT_BIT) || |
| test_bit(DEV_CLEAR_BN, &tms_priv->state) || |
| test_bit(TIMO_NUM, &board->status)); |
| outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG); |
| *bytes_written += block_size - read_transfer_counter(hp_priv); |
| if (retval) { |
| retval = -ERESTARTSYS; |
| break; |
| } |
| if (test_bit(TIMO_NUM, &board->status)) { |
| retval = -ETIMEDOUT; |
| break; |
| } |
| if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) { |
| retval = -EINTR; |
| break; |
| } |
| } |
| if (retval) |
| return retval; |
| if (send_eoi) { |
| size_t num_bytes; |
| |
| retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes); |
| *bytes_written += num_bytes; |
| if (retval < 0) |
| return retval; |
| } |
| return 0; |
| } |
| |
| static int hp_82341_attach(struct gpib_board *board, const struct gpib_board_config *config); |
| |
| static void hp_82341_detach(struct gpib_board *board); |
| |
| // wrappers for interface functions |
| static int hp_82341_read(struct gpib_board *board, u8 *buffer, size_t length, int *end, |
| size_t *bytes_read) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read); |
| } |
| |
| static int hp_82341_write(struct gpib_board *board, u8 *buffer, size_t length, int send_eoi, |
| size_t *bytes_written) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written); |
| } |
| |
| static int hp_82341_command(struct gpib_board *board, u8 *buffer, size_t length, |
| size_t *bytes_written) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written); |
| } |
| |
| static int hp_82341_take_control(struct gpib_board *board, int synchronous) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_take_control(board, &priv->tms9914_priv, synchronous); |
| } |
| |
| static int hp_82341_go_to_standby(struct gpib_board *board) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_go_to_standby(board, &priv->tms9914_priv); |
| } |
| |
| static int hp_82341_request_system_control(struct gpib_board *board, int request_control) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| if (request_control) |
| priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT; |
| else |
| priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT; |
| outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG); |
| return tms9914_request_system_control(board, &priv->tms9914_priv, request_control); |
| } |
| |
| static void hp_82341_interface_clear(struct gpib_board *board, int assert) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_interface_clear(board, &priv->tms9914_priv, assert); |
| } |
| |
| static void hp_82341_remote_enable(struct gpib_board *board, int enable) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_remote_enable(board, &priv->tms9914_priv, enable); |
| } |
| |
| static int hp_82341_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits); |
| } |
| |
| static void hp_82341_disable_eos(struct gpib_board *board) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_disable_eos(board, &priv->tms9914_priv); |
| } |
| |
| static unsigned int hp_82341_update_status(struct gpib_board *board, unsigned int clear_mask) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_update_status(board, &priv->tms9914_priv, clear_mask); |
| } |
| |
| static int hp_82341_primary_address(struct gpib_board *board, unsigned int address) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_primary_address(board, &priv->tms9914_priv, address); |
| } |
| |
| static int hp_82341_secondary_address(struct gpib_board *board, unsigned int address, int enable) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable); |
| } |
| |
| static int hp_82341_parallel_poll(struct gpib_board *board, u8 *result) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_parallel_poll(board, &priv->tms9914_priv, result); |
| } |
| |
| static void hp_82341_parallel_poll_configure(struct gpib_board *board, u8 config) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config); |
| } |
| |
| static void hp_82341_parallel_poll_response(struct gpib_board *board, int ist) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist); |
| } |
| |
| static void hp_82341_serial_poll_response(struct gpib_board *board, u8 status) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_serial_poll_response(board, &priv->tms9914_priv, status); |
| } |
| |
| static u8 hp_82341_serial_poll_status(struct gpib_board *board) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_serial_poll_status(board, &priv->tms9914_priv); |
| } |
| |
| static int hp_82341_line_status(const struct gpib_board *board) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_line_status(board, &priv->tms9914_priv); |
| } |
| |
| static int hp_82341_t1_delay(struct gpib_board *board, unsigned int nano_sec) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec); |
| } |
| |
| static void hp_82341_return_to_local(struct gpib_board *board) |
| { |
| struct hp_82341_priv *priv = board->private_data; |
| |
| tms9914_return_to_local(board, &priv->tms9914_priv); |
| } |
| |
| static struct gpib_interface hp_82341_unaccel_interface = { |
| .name = "hp_82341_unaccel", |
| .attach = hp_82341_attach, |
| .detach = hp_82341_detach, |
| .read = hp_82341_read, |
| .write = hp_82341_write, |
| .command = hp_82341_command, |
| .request_system_control = hp_82341_request_system_control, |
| .take_control = hp_82341_take_control, |
| .go_to_standby = hp_82341_go_to_standby, |
| .interface_clear = hp_82341_interface_clear, |
| .remote_enable = hp_82341_remote_enable, |
| .enable_eos = hp_82341_enable_eos, |
| .disable_eos = hp_82341_disable_eos, |
| .parallel_poll = hp_82341_parallel_poll, |
| .parallel_poll_configure = hp_82341_parallel_poll_configure, |
| .parallel_poll_response = hp_82341_parallel_poll_response, |
| .local_parallel_poll_mode = NULL, // XXX |
| .line_status = hp_82341_line_status, |
| .update_status = hp_82341_update_status, |
| .primary_address = hp_82341_primary_address, |
| .secondary_address = hp_82341_secondary_address, |
| .serial_poll_response = hp_82341_serial_poll_response, |
| .serial_poll_status = hp_82341_serial_poll_status, |
| .t1_delay = hp_82341_t1_delay, |
| .return_to_local = hp_82341_return_to_local, |
| }; |
| |
| static struct gpib_interface hp_82341_interface = { |
| .name = "hp_82341", |
| .attach = hp_82341_attach, |
| .detach = hp_82341_detach, |
| .read = hp_82341_accel_read, |
| .write = hp_82341_accel_write, |
| .command = hp_82341_command, |
| .request_system_control = hp_82341_request_system_control, |
| .take_control = hp_82341_take_control, |
| .go_to_standby = hp_82341_go_to_standby, |
| .interface_clear = hp_82341_interface_clear, |
| .remote_enable = hp_82341_remote_enable, |
| .enable_eos = hp_82341_enable_eos, |
| .disable_eos = hp_82341_disable_eos, |
| .parallel_poll = hp_82341_parallel_poll, |
| .parallel_poll_configure = hp_82341_parallel_poll_configure, |
| .parallel_poll_response = hp_82341_parallel_poll_response, |
| .local_parallel_poll_mode = NULL, // XXX |
| .line_status = hp_82341_line_status, |
| .update_status = hp_82341_update_status, |
| .primary_address = hp_82341_primary_address, |
| .secondary_address = hp_82341_secondary_address, |
| .serial_poll_response = hp_82341_serial_poll_response, |
| .t1_delay = hp_82341_t1_delay, |
| .return_to_local = hp_82341_return_to_local, |
| }; |
| |
| static int hp_82341_allocate_private(struct gpib_board *board) |
| { |
| board->private_data = kzalloc_obj(struct hp_82341_priv); |
| 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; |
| retval = hp_82341_allocate_private(board); |
| if (retval) |
| return retval; |
| 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); |
| } |
| |