mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Move the gpib drivers out of staging and into the "real" part of the kernel. This entails: - Remove the gpib Kconfig menu and Makefile build rule from staging. - Remove gpib/uapi from the header file search path in subdir-ccflags of the gpib Makefile - move the gpib/uapi files to include/uapi/linux - Move the gpib tree out of staging to drivers. - Remove the word "Linux" from the gpib Kconfig file. - Add the gpib Kconfig menu and Makefile build rule to drivers Signed-off-by: Dave Penkler <dpenkler@gmail.com> Link: https://patch.msgid.link/20251117144021.23569-5-dpenkler@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
915 lines
24 KiB
C
915 lines
24 KiB
C
// 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);
|
|
|