| // SPDX-License-Identifier: GPL-2.0 |
| |
| /*************************************************************************** |
| * copyright : (C) 2001, 2002 by Frank Mori Hess |
| ***************************************************************************/ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| #define dev_fmt pr_fmt |
| |
| #include <linux/ioport.h> |
| #include <linux/sched.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <asm/dma.h> |
| #include <linux/io.h> |
| #include <linux/bitops.h> |
| #include <linux/pci.h> |
| #include <linux/pci_ids.h> |
| #include <linux/string.h> |
| #include <linux/init.h> |
| #include <linux/spinlock.h> |
| #include <linux/delay.h> |
| |
| #include "gpibP.h" |
| #include "tms9914.h" |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("GPIB library for tms9914"); |
| |
| static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv); |
| |
| int tms9914_take_control(struct gpib_board *board, struct tms9914_priv *priv, int synchronous) |
| { |
| int i; |
| const int timeout = 100; |
| |
| if (synchronous) |
| write_byte(priv, AUX_TCS, AUXCR); |
| else |
| write_byte(priv, AUX_TCA, AUXCR); |
| // busy wait until ATN is asserted |
| for (i = 0; i < timeout; i++) { |
| if ((read_byte(priv, ADSR) & HR_ATN)) |
| break; |
| udelay(1); |
| } |
| if (i == timeout) |
| return -ETIMEDOUT; |
| |
| clear_bit(WRITE_READY_BN, &priv->state); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(tms9914_take_control); |
| |
| /* |
| * The agilent 82350B has a buggy implementation of tcs which interferes with the |
| * operation of tca. It appears to be based on the controller state machine |
| * described in the TI 9900 TMS9914A data manual published in 1982. This |
| * manual describes tcs as putting the controller into a CWAS |
| * state where it waits indefinitely for ANRS and ignores tca. Since a |
| * functioning tca is far more important than tcs, we work around the |
| * problem by never issuing tcs. |
| * |
| * I don't know if this problem exists in the real tms9914a or just in the fpga |
| * of the 82350B. For now, only the agilent_82350b uses this workaround. |
| * The rest of the tms9914 based drivers still use tms9914_take_control |
| * directly (which does issue tcs). |
| */ |
| int tms9914_take_control_workaround(struct gpib_board *board, |
| struct tms9914_priv *priv, int synchronous) |
| { |
| if (synchronous) |
| return -ETIMEDOUT; |
| return tms9914_take_control(board, priv, synchronous); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_take_control_workaround); |
| |
| int tms9914_go_to_standby(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| int i; |
| const int timeout = 1000; |
| |
| write_byte(priv, AUX_GTS, AUXCR); |
| // busy wait until ATN is released |
| for (i = 0; i < timeout; i++) { |
| if ((read_byte(priv, ADSR) & HR_ATN) == 0) |
| break; |
| udelay(1); |
| } |
| if (i == timeout) |
| return -ETIMEDOUT; |
| |
| clear_bit(COMMAND_READY_BN, &priv->state); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(tms9914_go_to_standby); |
| |
| void tms9914_interface_clear(struct gpib_board *board, struct tms9914_priv *priv, int assert) |
| { |
| if (assert) { |
| write_byte(priv, AUX_SIC | AUX_CS, AUXCR); |
| |
| set_bit(CIC_NUM, &board->status); |
| } else { |
| write_byte(priv, AUX_SIC, AUXCR); |
| } |
| } |
| EXPORT_SYMBOL_GPL(tms9914_interface_clear); |
| |
| void tms9914_remote_enable(struct gpib_board *board, struct tms9914_priv *priv, int enable) |
| { |
| if (enable) |
| write_byte(priv, AUX_SRE | AUX_CS, AUXCR); |
| else |
| write_byte(priv, AUX_SRE, AUXCR); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_remote_enable); |
| |
| int tms9914_request_system_control(struct gpib_board *board, struct tms9914_priv *priv, |
| int request_control) |
| { |
| if (request_control) { |
| write_byte(priv, AUX_RQC, AUXCR); |
| } else { |
| clear_bit(CIC_NUM, &board->status); |
| write_byte(priv, AUX_RLC, AUXCR); |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(tms9914_request_system_control); |
| |
| unsigned int tms9914_t1_delay(struct gpib_board *board, struct tms9914_priv *priv, |
| unsigned int nano_sec) |
| { |
| static const int clock_period = 200; // assuming 5Mhz input clock |
| int num_cycles; |
| |
| num_cycles = 12; |
| |
| if (nano_sec <= 8 * clock_period) { |
| write_byte(priv, AUX_STDL | AUX_CS, AUXCR); |
| num_cycles = 8; |
| } else { |
| write_byte(priv, AUX_STDL, AUXCR); |
| } |
| |
| if (nano_sec <= 4 * clock_period) { |
| write_byte(priv, AUX_VSTDL | AUX_CS, AUXCR); |
| num_cycles = 4; |
| } else { |
| write_byte(priv, AUX_VSTDL, AUXCR); |
| } |
| |
| return num_cycles * clock_period; |
| } |
| EXPORT_SYMBOL_GPL(tms9914_t1_delay); |
| |
| void tms9914_return_to_local(const struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| write_byte(priv, AUX_RTL, AUXCR); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_return_to_local); |
| |
| void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode) |
| { |
| switch (mode) { |
| case TMS9914_HOLDOFF_NONE: |
| write_byte(priv, AUX_HLDE, AUXCR); |
| write_byte(priv, AUX_HLDA, AUXCR); |
| break; |
| case TMS9914_HOLDOFF_EOI: |
| write_byte(priv, AUX_HLDE | AUX_CS, AUXCR); |
| write_byte(priv, AUX_HLDA, AUXCR); |
| break; |
| case TMS9914_HOLDOFF_ALL: |
| write_byte(priv, AUX_HLDE, AUXCR); |
| write_byte(priv, AUX_HLDA | AUX_CS, AUXCR); |
| break; |
| default: |
| pr_err("bug! bad holdoff mode %i\n", mode); |
| break; |
| } |
| priv->holdoff_mode = mode; |
| } |
| EXPORT_SYMBOL_GPL(tms9914_set_holdoff_mode); |
| |
| void tms9914_release_holdoff(struct tms9914_priv *priv) |
| { |
| if (priv->holdoff_active) { |
| write_byte(priv, AUX_RHDF, AUXCR); |
| priv->holdoff_active = 0; |
| } |
| } |
| EXPORT_SYMBOL_GPL(tms9914_release_holdoff); |
| |
| int tms9914_enable_eos(struct gpib_board *board, struct tms9914_priv *priv, u8 eos_byte, |
| int compare_8_bits) |
| { |
| priv->eos = eos_byte; |
| priv->eos_flags = REOS; |
| if (compare_8_bits) |
| priv->eos_flags |= BIN; |
| return 0; |
| } |
| EXPORT_SYMBOL(tms9914_enable_eos); |
| |
| void tms9914_disable_eos(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| priv->eos_flags &= ~REOS; |
| } |
| EXPORT_SYMBOL(tms9914_disable_eos); |
| |
| int tms9914_parallel_poll(struct gpib_board *board, struct tms9914_priv *priv, u8 *result) |
| { |
| // execute parallel poll |
| write_byte(priv, AUX_CS | AUX_RPP, AUXCR); |
| udelay(2); |
| *result = read_byte(priv, CPTR); |
| // clear parallel poll state |
| write_byte(priv, AUX_RPP, AUXCR); |
| return 0; |
| } |
| EXPORT_SYMBOL(tms9914_parallel_poll); |
| |
| static void set_ppoll_reg(struct tms9914_priv *priv, int enable, |
| unsigned int dio_line, int sense, int ist) |
| { |
| u8 dio_byte; |
| |
| if (enable && ((sense && ist) || (!sense && !ist))) { |
| dio_byte = 1 << (dio_line - 1); |
| write_byte(priv, dio_byte, PPR); |
| } else { |
| write_byte(priv, 0, PPR); |
| } |
| } |
| |
| void tms9914_parallel_poll_configure(struct gpib_board *board, |
| struct tms9914_priv *priv, u8 config) |
| { |
| priv->ppoll_enable = (config & PPC_DISABLE) == 0; |
| priv->ppoll_line = (config & PPC_DIO_MASK) + 1; |
| priv->ppoll_sense = (config & PPC_SENSE) != 0; |
| set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, board->ist); |
| } |
| EXPORT_SYMBOL(tms9914_parallel_poll_configure); |
| |
| void tms9914_parallel_poll_response(struct gpib_board *board, |
| struct tms9914_priv *priv, int ist) |
| { |
| set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, ist); |
| } |
| EXPORT_SYMBOL(tms9914_parallel_poll_response); |
| |
| void tms9914_serial_poll_response(struct gpib_board *board, |
| struct tms9914_priv *priv, u8 status) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&board->spinlock, flags); |
| write_byte(priv, status, SPMR); |
| priv->spoll_status = status; |
| if (status & request_service_bit) |
| write_byte(priv, AUX_RSV2 | AUX_CS, AUXCR); |
| else |
| write_byte(priv, AUX_RSV2, AUXCR); |
| spin_unlock_irqrestore(&board->spinlock, flags); |
| } |
| EXPORT_SYMBOL(tms9914_serial_poll_response); |
| |
| u8 tms9914_serial_poll_status(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| u8 status; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&board->spinlock, flags); |
| status = priv->spoll_status; |
| spin_unlock_irqrestore(&board->spinlock, flags); |
| |
| return status; |
| } |
| EXPORT_SYMBOL(tms9914_serial_poll_status); |
| |
| int tms9914_primary_address(struct gpib_board *board, |
| struct tms9914_priv *priv, unsigned int address) |
| { |
| // put primary address in address0 |
| write_byte(priv, address & ADDRESS_MASK, ADR); |
| return 0; |
| } |
| EXPORT_SYMBOL(tms9914_primary_address); |
| |
| int tms9914_secondary_address(struct gpib_board *board, struct tms9914_priv *priv, |
| unsigned int address, int enable) |
| { |
| if (enable) |
| priv->imr1_bits |= HR_APTIE; |
| else |
| priv->imr1_bits &= ~HR_APTIE; |
| |
| write_byte(priv, priv->imr1_bits, IMR1); |
| return 0; |
| } |
| EXPORT_SYMBOL(tms9914_secondary_address); |
| |
| unsigned int tms9914_update_status(struct gpib_board *board, struct tms9914_priv *priv, |
| unsigned int clear_mask) |
| { |
| unsigned long flags; |
| unsigned int retval; |
| |
| spin_lock_irqsave(&board->spinlock, flags); |
| retval = update_status_nolock(board, priv); |
| board->status &= ~clear_mask; |
| spin_unlock_irqrestore(&board->spinlock, flags); |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(tms9914_update_status); |
| |
| static void update_talker_state(struct tms9914_priv *priv, unsigned int address_status_bits) |
| { |
| if (address_status_bits & HR_TA) { |
| if (address_status_bits & HR_ATN) |
| priv->talker_state = talker_addressed; |
| else |
| /* |
| * this could also be serial_poll_active, but the tms9914 provides no |
| * way to distinguish, so we'll assume talker_active |
| */ |
| priv->talker_state = talker_active; |
| } else { |
| priv->talker_state = talker_idle; |
| } |
| } |
| |
| static void update_listener_state(struct tms9914_priv *priv, unsigned int address_status_bits) |
| { |
| if (address_status_bits & HR_LA) { |
| if (address_status_bits & HR_ATN) |
| priv->listener_state = listener_addressed; |
| else |
| priv->listener_state = listener_active; |
| } else { |
| priv->listener_state = listener_idle; |
| } |
| } |
| |
| static unsigned int update_status_nolock(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| int address_status; |
| int bsr_bits; |
| |
| address_status = read_byte(priv, ADSR); |
| |
| // check for remote/local |
| if (address_status & HR_REM) |
| set_bit(REM_NUM, &board->status); |
| else |
| clear_bit(REM_NUM, &board->status); |
| // check for lockout |
| if (address_status & HR_LLO) |
| set_bit(LOK_NUM, &board->status); |
| else |
| clear_bit(LOK_NUM, &board->status); |
| // check for ATN |
| if (address_status & HR_ATN) |
| set_bit(ATN_NUM, &board->status); |
| else |
| clear_bit(ATN_NUM, &board->status); |
| // check for talker/listener addressed |
| update_talker_state(priv, address_status); |
| if (priv->talker_state == talker_active || priv->talker_state == talker_addressed) |
| set_bit(TACS_NUM, &board->status); |
| else |
| clear_bit(TACS_NUM, &board->status); |
| |
| update_listener_state(priv, address_status); |
| if (priv->listener_state == listener_active || priv->listener_state == listener_addressed) |
| set_bit(LACS_NUM, &board->status); |
| else |
| clear_bit(LACS_NUM, &board->status); |
| // Check for SRQI - not reset elsewhere except in autospoll |
| if (board->status & SRQI) { |
| bsr_bits = read_byte(priv, BSR); |
| if (!(bsr_bits & BSR_SRQ_BIT)) |
| clear_bit(SRQI_NUM, &board->status); |
| } |
| |
| dev_dbg(board->gpib_dev, "status 0x%lx, state 0x%lx\n", board->status, priv->state); |
| |
| return board->status; |
| } |
| |
| int tms9914_line_status(const struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| int bsr_bits; |
| int status = VALID_ALL; |
| |
| bsr_bits = read_byte(priv, BSR); |
| |
| if (bsr_bits & BSR_REN_BIT) |
| status |= BUS_REN; |
| if (bsr_bits & BSR_IFC_BIT) |
| status |= BUS_IFC; |
| if (bsr_bits & BSR_SRQ_BIT) |
| status |= BUS_SRQ; |
| if (bsr_bits & BSR_EOI_BIT) |
| status |= BUS_EOI; |
| if (bsr_bits & BSR_NRFD_BIT) |
| status |= BUS_NRFD; |
| if (bsr_bits & BSR_NDAC_BIT) |
| status |= BUS_NDAC; |
| if (bsr_bits & BSR_DAV_BIT) |
| status |= BUS_DAV; |
| if (bsr_bits & BSR_ATN_BIT) |
| status |= BUS_ATN; |
| |
| return status; |
| } |
| EXPORT_SYMBOL(tms9914_line_status); |
| |
| static int check_for_eos(struct tms9914_priv *priv, u8 byte) |
| { |
| static const u8 seven_bit_compare_mask = 0x7f; |
| |
| if ((priv->eos_flags & REOS) == 0) |
| return 0; |
| |
| if (priv->eos_flags & BIN) { |
| if (priv->eos == byte) |
| return 1; |
| } else { |
| if ((priv->eos & seven_bit_compare_mask) == (byte & seven_bit_compare_mask)) |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int wait_for_read_byte(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| if (wait_event_interruptible(board->wait, |
| test_bit(READ_READY_BN, &priv->state) || |
| test_bit(DEV_CLEAR_BN, &priv->state) || |
| test_bit(TIMO_NUM, &board->status))) |
| return -ERESTARTSYS; |
| |
| if (test_bit(TIMO_NUM, &board->status)) |
| return -ETIMEDOUT; |
| |
| if (test_bit(DEV_CLEAR_BN, &priv->state)) |
| return -EINTR; |
| return 0; |
| } |
| |
| static inline u8 tms9914_read_data_in(struct gpib_board *board, |
| struct tms9914_priv *priv, int *end) |
| { |
| unsigned long flags; |
| u8 data; |
| |
| spin_lock_irqsave(&board->spinlock, flags); |
| clear_bit(READ_READY_BN, &priv->state); |
| data = read_byte(priv, DIR); |
| if (test_and_clear_bit(RECEIVED_END_BN, &priv->state)) |
| *end = 1; |
| else |
| *end = 0; |
| switch (priv->holdoff_mode) { |
| case TMS9914_HOLDOFF_EOI: |
| if (*end) |
| priv->holdoff_active = 1; |
| break; |
| case TMS9914_HOLDOFF_ALL: |
| priv->holdoff_active = 1; |
| break; |
| case TMS9914_HOLDOFF_NONE: |
| break; |
| default: |
| dev_err(board->gpib_dev, "bug! bad holdoff mode %i\n", priv->holdoff_mode); |
| break; |
| } |
| spin_unlock_irqrestore(&board->spinlock, flags); |
| |
| return data; |
| } |
| |
| static int pio_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, |
| size_t length, int *end, size_t *bytes_read) |
| { |
| ssize_t retval = 0; |
| |
| *bytes_read = 0; |
| *end = 0; |
| while (*bytes_read < length && *end == 0) { |
| tms9914_release_holdoff(priv); |
| retval = wait_for_read_byte(board, priv); |
| if (retval < 0) |
| return retval; |
| buffer[(*bytes_read)++] = tms9914_read_data_in(board, priv, end); |
| |
| if (check_for_eos(priv, buffer[*bytes_read - 1])) |
| *end = 1; |
| } |
| |
| return retval; |
| } |
| |
| int tms9914_read(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, |
| size_t length, int *end, size_t *bytes_read) |
| { |
| ssize_t retval = 0; |
| size_t num_bytes; |
| |
| *end = 0; |
| *bytes_read = 0; |
| if (length == 0) |
| return 0; |
| |
| clear_bit(DEV_CLEAR_BN, &priv->state); |
| |
| // transfer data (except for last byte) |
| if (length > 1) { |
| if (priv->eos_flags & REOS) |
| tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); |
| else |
| tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_EOI); |
| // PIO transfer |
| retval = pio_read(board, priv, buffer, length - 1, end, &num_bytes); |
| *bytes_read += num_bytes; |
| if (retval < 0) |
| return retval; |
| buffer += num_bytes; |
| length -= num_bytes; |
| } |
| // read last bytes if we haven't received an END yet |
| if (*end == 0) { |
| // make sure we holdoff after last byte read |
| tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); |
| retval = pio_read(board, priv, buffer, length, end, &num_bytes); |
| *bytes_read += num_bytes; |
| if (retval < 0) |
| return retval; |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(tms9914_read); |
| |
| static int pio_write_wait(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| // wait until next byte is ready to be sent |
| if (wait_event_interruptible(board->wait, |
| test_bit(WRITE_READY_BN, &priv->state) || |
| test_bit(BUS_ERROR_BN, &priv->state) || |
| test_bit(DEV_CLEAR_BN, &priv->state) || |
| test_bit(TIMO_NUM, &board->status))) |
| return -ERESTARTSYS; |
| |
| if (test_bit(TIMO_NUM, &board->status)) |
| return -ETIMEDOUT; |
| if (test_bit(BUS_ERROR_BN, &priv->state)) |
| return -EIO; |
| if (test_bit(DEV_CLEAR_BN, &priv->state)) |
| return -EINTR; |
| |
| return 0; |
| } |
| |
| static int pio_write(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, |
| size_t length, size_t *bytes_written) |
| { |
| ssize_t retval = 0; |
| unsigned long flags; |
| |
| *bytes_written = 0; |
| while (*bytes_written < length) { |
| retval = pio_write_wait(board, priv); |
| if (retval < 0) |
| break; |
| |
| spin_lock_irqsave(&board->spinlock, flags); |
| clear_bit(WRITE_READY_BN, &priv->state); |
| write_byte(priv, buffer[(*bytes_written)++], CDOR); |
| spin_unlock_irqrestore(&board->spinlock, flags); |
| } |
| retval = pio_write_wait(board, priv); |
| if (retval < 0) |
| return retval; |
| |
| return length; |
| } |
| |
| int tms9914_write(struct gpib_board *board, struct tms9914_priv *priv, |
| u8 *buffer, size_t length, int send_eoi, size_t *bytes_written) |
| { |
| ssize_t retval = 0; |
| |
| *bytes_written = 0; |
| if (length == 0) |
| return 0; |
| |
| clear_bit(BUS_ERROR_BN, &priv->state); |
| clear_bit(DEV_CLEAR_BN, &priv->state); |
| |
| if (send_eoi) |
| length-- ; /* save the last byte for sending EOI */ |
| |
| if (length > 0) { |
| size_t num_bytes; |
| // PIO transfer |
| retval = pio_write(board, priv, buffer, length, &num_bytes); |
| *bytes_written += num_bytes; |
| if (retval < 0) |
| return retval; |
| } |
| if (send_eoi) { |
| size_t num_bytes; |
| /*send EOI */ |
| write_byte(priv, AUX_SEOI, AUXCR); |
| |
| retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes); |
| *bytes_written += num_bytes; |
| } |
| return retval; |
| } |
| EXPORT_SYMBOL(tms9914_write); |
| |
| static void check_my_address_state(struct gpib_board *board, |
| struct tms9914_priv *priv, int cmd_byte) |
| { |
| if (cmd_byte == MLA(board->pad)) { |
| priv->primary_listen_addressed = 1; |
| // become active listener |
| if (board->sad < 0) |
| write_byte(priv, AUX_LON | AUX_CS, AUXCR); |
| } else if (board->sad >= 0 && priv->primary_listen_addressed && |
| cmd_byte == MSA(board->sad)) { |
| // become active listener |
| write_byte(priv, AUX_LON | AUX_CS, AUXCR); |
| } else if (cmd_byte != MLA(board->pad) && (cmd_byte & 0xe0) == LAD) { |
| priv->primary_listen_addressed = 0; |
| } else if (cmd_byte == UNL) { |
| priv->primary_listen_addressed = 0; |
| write_byte(priv, AUX_LON, AUXCR); |
| } else if (cmd_byte == MTA(board->pad)) { |
| priv->primary_talk_addressed = 1; |
| if (board->sad < 0) |
| // make active talker |
| write_byte(priv, AUX_TON | AUX_CS, AUXCR); |
| } else if (board->sad >= 0 && priv->primary_talk_addressed && |
| cmd_byte == MSA(board->sad)) { |
| // become active talker |
| write_byte(priv, AUX_TON | AUX_CS, AUXCR); |
| } else if (cmd_byte != MTA(board->pad) && (cmd_byte & 0xe0) == TAD) { |
| // Other Talk Address |
| priv->primary_talk_addressed = 0; |
| write_byte(priv, AUX_TON, AUXCR); |
| } else if (cmd_byte == UNT) { |
| priv->primary_talk_addressed = 0; |
| write_byte(priv, AUX_TON, AUXCR); |
| } |
| } |
| |
| int tms9914_command(struct gpib_board *board, struct tms9914_priv *priv, u8 *buffer, |
| size_t length, size_t *bytes_written) |
| { |
| int retval = 0; |
| unsigned long flags; |
| |
| *bytes_written = 0; |
| while (*bytes_written < length) { |
| if (wait_event_interruptible(board->wait, |
| test_bit(COMMAND_READY_BN, |
| &priv->state) || |
| test_bit(TIMO_NUM, &board->status))) |
| break; |
| if (test_bit(TIMO_NUM, &board->status)) |
| break; |
| |
| spin_lock_irqsave(&board->spinlock, flags); |
| clear_bit(COMMAND_READY_BN, &priv->state); |
| write_byte(priv, buffer[*bytes_written], CDOR); |
| spin_unlock_irqrestore(&board->spinlock, flags); |
| |
| check_my_address_state(board, priv, buffer[*bytes_written]); |
| |
| ++(*bytes_written); |
| } |
| // wait until last command byte is written |
| if (wait_event_interruptible(board->wait, |
| test_bit(COMMAND_READY_BN, |
| &priv->state) || test_bit(TIMO_NUM, &board->status))) |
| retval = -ERESTARTSYS; |
| if (test_bit(TIMO_NUM, &board->status)) |
| retval = -ETIMEDOUT; |
| |
| return retval; |
| } |
| EXPORT_SYMBOL(tms9914_command); |
| |
| irqreturn_t tms9914_interrupt(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| int status0, status1; |
| |
| // read interrupt status (also clears status) |
| status0 = read_byte(priv, ISR0); |
| status1 = read_byte(priv, ISR1); |
| return tms9914_interrupt_have_status(board, priv, status0, status1); |
| } |
| EXPORT_SYMBOL(tms9914_interrupt); |
| |
| irqreturn_t tms9914_interrupt_have_status(struct gpib_board *board, struct tms9914_priv *priv, |
| int status0, int status1) |
| { |
| // record reception of END |
| if (status0 & HR_END) |
| set_bit(RECEIVED_END_BN, &priv->state); |
| // get incoming data in PIO mode |
| if ((status0 & HR_BI)) |
| set_bit(READ_READY_BN, &priv->state); |
| if ((status0 & HR_BO)) { |
| if (read_byte(priv, ADSR) & HR_ATN) |
| set_bit(COMMAND_READY_BN, &priv->state); |
| else |
| set_bit(WRITE_READY_BN, &priv->state); |
| } |
| |
| if (status0 & HR_SPAS) { |
| priv->spoll_status &= ~request_service_bit; |
| write_byte(priv, priv->spoll_status, SPMR); |
| // FIXME: set SPOLL status bit |
| } |
| // record service request in status |
| if (status1 & HR_SRQ) |
| set_bit(SRQI_NUM, &board->status); |
| // have been addressed (with secondary addressing disabled) |
| if (status1 & HR_MA) |
| // clear dac holdoff |
| write_byte(priv, AUX_VAL, AUXCR); |
| // unrecognized command received |
| if (status1 & HR_UNC) { |
| unsigned short command_byte = read_byte(priv, CPTR) & gpib_command_mask; |
| |
| switch (command_byte) { |
| case PP_CONFIG: |
| priv->ppoll_configure_state = 1; |
| /* |
| * AUX_PTS generates another UNC interrupt on the next command byte |
| * if it is in the secondary address group (such as PPE and PPD). |
| */ |
| write_byte(priv, AUX_PTS, AUXCR); |
| write_byte(priv, AUX_VAL, AUXCR); |
| break; |
| case PPU: |
| tms9914_parallel_poll_configure(board, priv, command_byte); |
| write_byte(priv, AUX_VAL, AUXCR); |
| break; |
| default: |
| if (is_PPE(command_byte) || is_PPD(command_byte)) { |
| if (priv->ppoll_configure_state) { |
| tms9914_parallel_poll_configure(board, priv, command_byte); |
| write_byte(priv, AUX_VAL, AUXCR); |
| } else {// bad parallel poll configure byte |
| // clear dac holdoff |
| write_byte(priv, AUX_INVAL, AUXCR); |
| } |
| } else { |
| // clear dac holdoff |
| write_byte(priv, AUX_INVAL, AUXCR); |
| } |
| break; |
| } |
| |
| if (in_primary_command_group(command_byte) && command_byte != PP_CONFIG) |
| priv->ppoll_configure_state = 0; |
| } |
| |
| if (status1 & HR_ERR) { |
| dev_dbg(board->gpib_dev, "gpib bus error\n"); |
| set_bit(BUS_ERROR_BN, &priv->state); |
| } |
| |
| if (status1 & HR_IFC) { |
| push_gpib_event(board, EVENT_IFC); |
| clear_bit(CIC_NUM, &board->status); |
| } |
| |
| if (status1 & HR_GET) { |
| push_gpib_event(board, EVENT_DEV_TRG); |
| // clear dac holdoff |
| write_byte(priv, AUX_VAL, AUXCR); |
| } |
| |
| if (status1 & HR_DCAS) { |
| push_gpib_event(board, EVENT_DEV_CLR); |
| // clear dac holdoff |
| write_byte(priv, AUX_VAL, AUXCR); |
| set_bit(DEV_CLEAR_BN, &priv->state); |
| } |
| |
| // check for being addressed with secondary addressing |
| if (status1 & HR_APT) { |
| if (board->sad < 0) |
| dev_err(board->gpib_dev, "bug, APT interrupt without secondary addressing?\n"); |
| if ((read_byte(priv, CPTR) & gpib_command_mask) == MSA(board->sad)) |
| write_byte(priv, AUX_VAL, AUXCR); |
| else |
| write_byte(priv, AUX_INVAL, AUXCR); |
| } |
| |
| if ((status0 & priv->imr0_bits) || (status1 & priv->imr1_bits)) { |
| dev_dbg(board->gpib_dev, "isr0 0x%x, imr0 0x%x, isr1 0x%x, imr1 0x%x\n", |
| status0, priv->imr0_bits, status1, priv->imr1_bits); |
| update_status_nolock(board, priv); |
| wake_up_interruptible(&board->wait); |
| } |
| return IRQ_HANDLED; |
| } |
| EXPORT_SYMBOL(tms9914_interrupt_have_status); |
| |
| void tms9914_board_reset(struct tms9914_priv *priv) |
| { |
| /* chip reset */ |
| write_byte(priv, AUX_CHIP_RESET | AUX_CS, AUXCR); |
| |
| /* disable all interrupts */ |
| priv->imr0_bits = 0; |
| write_byte(priv, priv->imr0_bits, IMR0); |
| priv->imr1_bits = 0; |
| write_byte(priv, priv->imr1_bits, IMR1); |
| write_byte(priv, AUX_DAI | AUX_CS, AUXCR); |
| |
| /* clear registers by reading */ |
| read_byte(priv, CPTR); |
| read_byte(priv, ISR0); |
| read_byte(priv, ISR1); |
| |
| write_byte(priv, 0, SPMR); |
| |
| /* parallel poll unconfigure */ |
| write_byte(priv, 0, PPR); |
| /* request for data holdoff */ |
| tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_board_reset); |
| |
| void tms9914_online(struct gpib_board *board, struct tms9914_priv *priv) |
| { |
| /* set GPIB address */ |
| tms9914_primary_address(board, priv, board->pad); |
| tms9914_secondary_address(board, priv, board->sad, board->sad >= 0); |
| |
| /* enable tms9914 interrupts */ |
| priv->imr0_bits |= HR_MACIE | HR_RLCIE | HR_ENDIE | HR_BOIE | HR_BIIE | |
| HR_SPASIE; |
| priv->imr1_bits |= HR_MAIE | HR_SRQIE | HR_UNCIE | HR_ERRIE | HR_IFCIE | |
| HR_GETIE | HR_DCASIE; |
| write_byte(priv, priv->imr0_bits, IMR0); |
| write_byte(priv, priv->imr1_bits, IMR1); |
| write_byte(priv, AUX_DAI, AUXCR); |
| |
| /* turn off reset state */ |
| write_byte(priv, AUX_CHIP_RESET, AUXCR); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_online); |
| |
| #ifdef CONFIG_HAS_IOPORT |
| // wrapper for inb |
| u8 tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num) |
| { |
| return inb(priv->iobase + register_num * priv->offset); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_ioport_read_byte); |
| |
| // wrapper for outb |
| void tms9914_ioport_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) |
| { |
| outb(data, priv->iobase + register_num * priv->offset); |
| if (register_num == AUXCR) |
| udelay(1); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte); |
| #endif |
| |
| // wrapper for readb |
| u8 tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num) |
| { |
| return readb(priv->mmiobase + register_num * priv->offset); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte); |
| |
| // wrapper for writeb |
| void tms9914_iomem_write_byte(struct tms9914_priv *priv, u8 data, unsigned int register_num) |
| { |
| writeb(data, priv->mmiobase + register_num * priv->offset); |
| if (register_num == AUXCR) |
| udelay(1); |
| } |
| EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte); |
| |
| static int __init tms9914_init_module(void) |
| { |
| return 0; |
| } |
| |
| static void __exit tms9914_exit_module(void) |
| { |
| } |
| |
| module_init(tms9914_init_module); |
| module_exit(tms9914_exit_module); |
| |