mirror of
https://github.com/torvalds/linux.git
synced 2026-04-22 08:44:02 -04:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
909 lines
26 KiB
C
909 lines
26 KiB
C
// 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);
|
|
}
|
|
|