Files
linux/drivers/gpib/gpio/gpib_bitbang.c
Linus Torvalds bf4afc53b7 Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
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>
2026-02-21 17:09:51 -08:00

1471 lines
40 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*************************************************************************
* This code has been developed at the Institute of Sensor and Actuator *
* Systems (Technical University of Vienna, Austria) to enable the GPIO *
* lines (e.g. of a raspberry pi) to function as a GPIO master device *
* *
* authors : Thomas Klima *
* Marcello Carla' *
* Dave Penkler *
* *
* copyright : (C) 2016 Thomas Klima *
* *
*************************************************************************/
/*
* limitations:
* works only on RPi
* cannot function as non-CIC system controller with SN7516x because
* SN75161B cannot simultaneously make ATN input with IFC and REN as
* outputs.
* not implemented:
* parallel poll
* return2local
* device support (non master operation)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define dev_fmt pr_fmt
#define NAME KBUILD_MODNAME
#define ENABLE_IRQ(IRQ, TYPE) irq_set_irq_type(IRQ, TYPE)
#define DISABLE_IRQ(IRQ) irq_set_irq_type(IRQ, IRQ_TYPE_NONE)
/*
* Debug print levels:
* 0 = load/unload info and errors that make the driver fail;
* 1 = + warnings for unforeseen events that may break the current
* operation and lead to a timeout, but do not affect the
* driver integrity (mainly unexpected interrupts);
* 2 = + trace of function calls;
* 3 = + trace of protocol codes;
* 4 = + trace of interrupt operation.
*/
#define dbg_printk(level, frm, ...) \
do { if (debug >= (level)) \
dev_dbg(board->gpib_dev, frm, ## __VA_ARGS__); } \
while (0)
#define LINVAL gpiod_get_value(DAV), \
gpiod_get_value(NRFD), \
gpiod_get_value(NDAC), \
gpiod_get_value(SRQ)
#define LINFMT "DAV: %d NRFD:%d NDAC: %d SRQ: %d"
#include "gpibP.h"
#include "gpib_state_machines.h"
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/gpio.h>
#include <linux/irq.h>
static int sn7516x_used = 1, sn7516x;
module_param(sn7516x_used, int, 0660);
#define PINMAP_0 "elektronomikon"
#define PINMAP_1 "gpib4pi-1.1"
#define PINMAP_2 "yoga"
static char *pin_map = PINMAP_0;
module_param(pin_map, charp, 0660);
MODULE_PARM_DESC(pin_map, " valid values: " PINMAP_0 " " PINMAP_1 " " PINMAP_2);
/**********************************************
* Signal pairing and pin wiring between the *
* Raspberry-Pi connector and the GPIB bus *
* *
* signal pin wiring *
* GPIB Pi-gpio GPIB -> RPi *
**********************************************
*/
enum lines_t {
D01_pin_nr = 20, /* 1 -> 38 */
D02_pin_nr = 26, /* 2 -> 37 */
D03_pin_nr = 16, /* 3 -> 36 */
D04_pin_nr = 19, /* 4 -> 35 */
D05_pin_nr = 13, /* 13 -> 33 */
D06_pin_nr = 12, /* 14 -> 32 */
D07_pin_nr = 6, /* 15 -> 31 */
D08_pin_nr = 5, /* 16 -> 29 */
EOI_pin_nr = 9, /* 5 -> 21 */
DAV_pin_nr = 10, /* 6 -> 19 */
NRFD_pin_nr = 24, /* 7 -> 18 */
NDAC_pin_nr = 23, /* 8 -> 16 */
IFC_pin_nr = 22, /* 9 -> 15 */
SRQ_pin_nr = 11, /* 10 -> 23 */
_ATN_pin_nr = 25, /* 11 -> 22 */
REN_pin_nr = 27, /* 17 -> 13 */
/*
* GROUND PINS
* 12,18,19,20,21,22,23,24 => 14,20,25,30,34,39
*/
/*
* These lines are used to control the external
* SN75160/161 driver chips when used.
* When not used there is reduced fan out;
* currently tested with up to 4 devices.
*/
/* Pi GPIO RPI 75161B 75160B Description */
PE_pin_nr = 7, /* 26 -> nc 11 Pullup Enable */
DC_pin_nr = 8, /* 24 -> 12 nc Direction control */
TE_pin_nr = 18, /* 12 -> 2 1 Talk Enable */
ACT_LED_pin_nr = 4, /* 7 -> LED */
/* YOGA adapter uses different pinout to ease layout */
YOGA_D03_pin_nr = 13,
YOGA_D04_pin_nr = 12,
YOGA_D05_pin_nr = 21,
YOGA_D06_pin_nr = 19,
};
/*
* GPIO descriptors and pins - WARNING: STRICTLY KEEP ITEMS ORDER
*/
#define GPIB_PINS 16
#define SN7516X_PINS 4
#define NUM_PINS (GPIB_PINS + SN7516X_PINS)
#define ACT_LED_ON do { \
if (ACT_LED) \
gpiod_direction_output(ACT_LED, 1); \
} while (0)
#define ACT_LED_OFF do { \
if (ACT_LED) \
gpiod_direction_output(ACT_LED, 0); \
} while (0)
static struct gpio_desc *all_descriptors[GPIB_PINS + SN7516X_PINS];
#define D01 all_descriptors[0]
#define D02 all_descriptors[1]
#define D03 all_descriptors[2]
#define D04 all_descriptors[3]
#define D05 all_descriptors[4]
#define D06 all_descriptors[5]
#define D07 all_descriptors[6]
#define D08 all_descriptors[7]
#define EOI all_descriptors[8]
#define NRFD all_descriptors[9]
#define IFC all_descriptors[10]
#define _ATN all_descriptors[11]
#define REN all_descriptors[12]
#define DAV all_descriptors[13]
#define NDAC all_descriptors[14]
#define SRQ all_descriptors[15]
#define PE all_descriptors[16]
#define DC all_descriptors[17]
#define TE all_descriptors[18]
#define ACT_LED all_descriptors[19]
/* YOGA adapter uses a global enable for the buffer chips, re-using the TE pin */
#define YOGA_ENABLE TE
static int gpios_vector[] = {
D01_pin_nr,
D02_pin_nr,
D03_pin_nr,
D04_pin_nr,
D05_pin_nr,
D06_pin_nr,
D07_pin_nr,
D08_pin_nr,
EOI_pin_nr,
NRFD_pin_nr,
IFC_pin_nr,
_ATN_pin_nr,
REN_pin_nr,
DAV_pin_nr,
NDAC_pin_nr,
SRQ_pin_nr,
PE_pin_nr,
DC_pin_nr,
TE_pin_nr,
ACT_LED_pin_nr
};
/* Lookup table for general GPIOs */
static struct gpiod_lookup_table gpib_gpio_table_1 = {
// for bcm2835/6
.dev_id = "", // device id of board device
.table = {
GPIO_LOOKUP_IDX("GPIO_GCLK", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("SPI_CE1_N", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("SPI_CE0_N", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("SPI_MISO", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("SPI_MOSI", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("SPI_SCLK", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table gpib_gpio_table_0 = {
.dev_id = "", // device id of board device
.table = {
// for bcm27xx based pis (b b+ 2b 3b 3b+ 4 5)
GPIO_LOOKUP_IDX("GPIO4", U16_MAX, NULL, 4, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO5", U16_MAX, NULL, 5, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO6", U16_MAX, NULL, 6, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO7", U16_MAX, NULL, 7, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO8", U16_MAX, NULL, 8, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO9", U16_MAX, NULL, 9, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO10", U16_MAX, NULL, 10, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO11", U16_MAX, NULL, 11, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO12", U16_MAX, NULL, 12, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO13", U16_MAX, NULL, 13, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO16", U16_MAX, NULL, 16, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO17", U16_MAX, NULL, 17, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO18", U16_MAX, NULL, 18, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO19", U16_MAX, NULL, 19, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO20", U16_MAX, NULL, 20, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO21", U16_MAX, NULL, 21, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO22", U16_MAX, NULL, 22, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO23", U16_MAX, NULL, 23, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO24", U16_MAX, NULL, 24, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO25", U16_MAX, NULL, 25, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO26", U16_MAX, NULL, 26, GPIO_ACTIVE_HIGH),
GPIO_LOOKUP_IDX("GPIO27", U16_MAX, NULL, 27, GPIO_ACTIVE_HIGH),
{ }
},
};
static struct gpiod_lookup_table *lookup_tables[] = {
&gpib_gpio_table_0,
&gpib_gpio_table_1,
NULL
};
/* struct which defines private_data for gpio driver */
struct bb_priv {
int irq_NRFD;
int irq_NDAC;
int irq_DAV;
int irq_SRQ;
int dav_mode; /* dav interrupt mode 0/1 -> edge/levels */
int nrfd_mode; /* nrfd interrupt mode 0/1 -> edge/levels */
int ndac_mode; /* nrfd interrupt mode 0/1 -> edge/levels */
int dav_tx; /* keep trace of DAV status while sending */
int dav_rx; /* keep trace of DAV status while receiving */
u8 eos; /* eos character */
short eos_flags; /* eos mode */
short eos_check; /* eos check required in current operation ... */
short eos_check_8; /* ... with byte comparison */
short eos_mask_7; /* ... with 7 bit masked character */
short int end;
int request;
int count;
int direction;
int t1_delay;
u8 *rbuf;
u8 *wbuf;
int end_flag;
int r_busy; /* 0==idle 1==busy */
int w_busy;
int write_done;
int cmd; /* 1 = cmd write in progress */
size_t w_cnt;
size_t length;
u8 *w_buf;
spinlock_t rw_lock; /* protect mods to rw_lock */
int phase;
int ndac_idle;
int ndac_seq;
int nrfd_idle;
int nrfd_seq;
int dav_seq;
long all_irqs;
int dav_idle;
enum talker_function_state talker_state;
enum listener_function_state listener_state;
};
static inline long usec_diff(struct timespec64 *a, struct timespec64 *b);
static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length,
int cmd, int eoi);
static void set_data_lines(u8 byte);
static u8 get_data_lines(void);
static void set_data_lines_input(void);
static void set_data_lines_output(void);
static inline int check_for_eos(struct bb_priv *priv, u8 byte);
static void set_atn(struct gpib_board *board, int atn_asserted);
static inline void SET_DIR_WRITE(struct bb_priv *priv);
static inline void SET_DIR_READ(struct bb_priv *priv);
#define DIR_READ 0
#define DIR_WRITE 1
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB helper functions for bitbanging I/O");
/**** global variables ****/
static int debug;
module_param(debug, int, 0644);
static char printable(char x)
{
if (x < 32 || x > 126)
return ' ';
return x;
}
/***************************************************************************
* *
* READ *
* *
***************************************************************************/
static int bb_read(struct gpib_board *board, u8 *buffer, size_t length,
int *end, size_t *bytes_read)
{
struct bb_priv *priv = board->private_data;
unsigned long flags;
int retval = 0;
ACT_LED_ON;
SET_DIR_READ(priv);
dbg_printk(2, "board: %p lock %d length: %zu\n",
board, mutex_is_locked(&board->user_mutex), length);
priv->end = 0;
priv->count = 0;
priv->rbuf = buffer;
if (length == 0)
goto read_end;
priv->request = length;
priv->eos_check = (priv->eos_flags & REOS) == 0; /* do eos check */
priv->eos_check_8 = priv->eos_flags & BIN; /* over 8 bits */
priv->eos_mask_7 = priv->eos & 0x7f; /* with this 7 bit eos */
dbg_printk(3, ".........." LINFMT "\n", LINVAL);
spin_lock_irqsave(&priv->rw_lock, flags);
priv->dav_mode = 1;
priv->dav_rx = 1;
ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_LEVEL_LOW);
priv->end_flag = 0;
gpiod_set_value(NRFD, 1); // ready for data
priv->r_busy = 1;
priv->phase = 100;
spin_unlock_irqrestore(&priv->rw_lock, flags);
/* wait for the interrupt routines finish their work */
retval = wait_event_interruptible(board->wait,
(priv->end_flag || board->status & TIMO));
dbg_printk(3, "awake from wait queue: %d\n", retval);
if (retval == 0 && board->status & TIMO) {
retval = -ETIMEDOUT;
dbg_printk(1, "timeout\n");
} else if (retval) {
retval = -ERESTARTSYS;
}
DISABLE_IRQ(priv->irq_DAV);
spin_lock_irqsave(&priv->rw_lock, flags);
gpiod_set_value(NRFD, 0); // DIR_READ line state
priv->r_busy = 0;
spin_unlock_irqrestore(&priv->rw_lock, flags);
read_end:
ACT_LED_OFF;
*bytes_read = priv->count;
*end = priv->end;
priv->r_busy = 0;
dbg_printk(2, "return: %d eoi|eos: %d count: %d\n\n", retval, priv->end, priv->count);
return retval;
}
/***************************************************************************
* *
* READ interrupt routine (DAV line) *
* *
***************************************************************************/
static irqreturn_t bb_DAV_interrupt(int irq, void *arg)
{
struct gpib_board *board = arg;
struct bb_priv *priv = board->private_data;
int val;
unsigned long flags;
spin_lock_irqsave(&priv->rw_lock, flags);
priv->all_irqs++;
if (priv->dav_mode) {
ENABLE_IRQ(priv->irq_DAV, IRQ_TYPE_EDGE_BOTH);
priv->dav_mode = 0;
}
if (priv->r_busy == 0) {
dbg_printk(1, "interrupt while idle after %d at %d\n",
priv->count, priv->phase);
priv->dav_idle++;
priv->phase = 200;
goto dav_exit; /* idle */
}
val = gpiod_get_value(DAV);
if (val == priv->dav_rx) {
dbg_printk(1, "out of order DAV interrupt %d/%d after %zu/%zu at %d cmd %d "
LINFMT ".\n", val, priv->dav_rx, priv->w_cnt, priv->length,
priv->phase, priv->cmd, LINVAL);
priv->dav_seq++;
}
priv->dav_rx = val;
dbg_printk(3, "> irq: %d DAV: %d st: %4lx dir: %d busy: %d:%d\n",
irq, val, board->status, priv->direction, priv->r_busy, priv->w_busy);
if (val == 0) {
gpiod_set_value(NRFD, 0); // not ready for data
priv->rbuf[priv->count++] = get_data_lines();
priv->end = !gpiod_get_value(EOI);
gpiod_set_value(NDAC, 1); // data accepted
priv->end |= check_for_eos(priv, priv->rbuf[priv->count - 1]);
priv->end_flag = ((priv->count >= priv->request) || priv->end);
priv->phase = 210;
} else {
gpiod_set_value(NDAC, 0); // data not accepted
if (priv->end_flag) {
priv->r_busy = 0;
wake_up_interruptible(&board->wait);
priv->phase = 220;
} else {
gpiod_set_value(NRFD, 1); // ready for data
priv->phase = 230;
}
}
dav_exit:
spin_unlock_irqrestore(&priv->rw_lock, flags);
dbg_printk(3, "< irq: %d count %d\n", irq, priv->count);
return IRQ_HANDLED;
}
/***************************************************************************
* *
* WRITE *
* *
***************************************************************************/
static int bb_write(struct gpib_board *board, u8 *buffer, size_t length,
int send_eoi, size_t *bytes_written)
{
unsigned long flags;
int retval = 0;
struct bb_priv *priv = board->private_data;
ACT_LED_ON;
priv->w_cnt = 0;
priv->w_buf = buffer;
dbg_printk(2, "board %p lock %d length: %zu\n",
board, mutex_is_locked(&board->user_mutex), length);
if (debug > 1)
bb_buffer_print(board, buffer, length, priv->cmd, send_eoi);
priv->count = 0;
priv->phase = 300;
if (length == 0)
goto write_end;
priv->end = send_eoi;
priv->length = length;
SET_DIR_WRITE(priv);
dbg_printk(2, "Enabling interrupts - NRFD: %d NDAC: %d\n",
gpiod_get_value(NRFD), gpiod_get_value(NDAC));
if (gpiod_get_value(NRFD) && gpiod_get_value(NDAC)) { /* check for listener */
retval = -ENOTCONN;
goto write_end;
}
spin_lock_irqsave(&priv->rw_lock, flags);
priv->w_busy = 1; /* make the interrupt routines active */
priv->write_done = 0;
priv->nrfd_mode = 1;
priv->ndac_mode = 1;
priv->dav_tx = 1;
ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_LEVEL_HIGH);
ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_LEVEL_HIGH);
spin_unlock_irqrestore(&priv->rw_lock, flags);
/* wait for the interrupt routines finish their work */
retval = wait_event_interruptible(board->wait,
priv->write_done || (board->status & TIMO));
dbg_printk(3, "awake from wait queue: %d\n", retval);
if (retval == 0) {
if (board->status & TIMO) {
retval = -ETIMEDOUT;
dbg_printk(1, "timeout after %zu/%zu at %d " LINFMT " eoi: %d\n",
priv->w_cnt, length, priv->phase, LINVAL, send_eoi);
} else {
retval = priv->w_cnt;
}
} else {
retval = -ERESTARTSYS;
}
DISABLE_IRQ(priv->irq_NRFD);
DISABLE_IRQ(priv->irq_NDAC);
spin_lock_irqsave(&priv->rw_lock, flags);
priv->w_busy = 0;
gpiod_set_value(DAV, 1); // DIR_WRITE line state
gpiod_set_value(EOI, 1); // De-assert EOI (in case)
spin_unlock_irqrestore(&priv->rw_lock, flags);
write_end:
*bytes_written = priv->w_cnt;
ACT_LED_OFF;
dbg_printk(2, "sent %zu bytes\r\n\r\n", *bytes_written);
priv->phase = 310;
return retval;
}
/***************************************************************************
* *
* WRITE interrupt routine (NRFD line) *
* *
***************************************************************************/
static irqreturn_t bb_NRFD_interrupt(int irq, void *arg)
{
struct gpib_board *board = arg;
struct bb_priv *priv = board->private_data;
unsigned long flags;
int nrfd;
spin_lock_irqsave(&priv->rw_lock, flags);
nrfd = gpiod_get_value(NRFD);
priv->all_irqs++;
dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n",
irq, nrfd, gpiod_get_value(NDAC), board->status, priv->direction,
priv->w_busy, priv->r_busy);
if (priv->nrfd_mode) {
ENABLE_IRQ(priv->irq_NRFD, IRQ_TYPE_EDGE_RISING);
priv->nrfd_mode = 0;
}
if (priv->w_busy == 0) {
dbg_printk(1, "interrupt while idle after %zu/%zu at %d\n",
priv->w_cnt, priv->length, priv->phase);
priv->nrfd_idle++;
goto nrfd_exit; /* idle */
}
if (nrfd == 0) {
dbg_printk(1, "out of order interrupt after %zu/%zu at %d cmd %d " LINFMT ".\n",
priv->w_cnt, priv->length, priv->phase, priv->cmd, LINVAL);
priv->phase = 400;
priv->nrfd_seq++;
goto nrfd_exit;
}
if (!priv->dav_tx) {
dbg_printk(1, "DAV low after %zu/%zu cmd %d " LINFMT ". No action.\n",
priv->w_cnt, priv->length, priv->cmd, LINVAL);
priv->dav_seq++;
goto nrfd_exit;
}
if (priv->w_cnt >= priv->length) { // test for missed NDAC end of transfer
dev_err(board->gpib_dev, "Unexpected NRFD exit\n");
priv->write_done = 1;
priv->w_busy = 0;
wake_up_interruptible(&board->wait);
goto nrfd_exit;
}
dbg_printk(3, "sending %zu\n", priv->w_cnt);
set_data_lines(priv->w_buf[priv->w_cnt++]); // put the data on the lines
if (priv->w_cnt == priv->length && priv->end) {
dbg_printk(3, "Asserting EOI\n");
gpiod_set_value(EOI, 0); // Assert EOI
}
gpiod_set_value(DAV, 0); // Data available
priv->dav_tx = 0;
priv->phase = 410;
nrfd_exit:
spin_unlock_irqrestore(&priv->rw_lock, flags);
return IRQ_HANDLED;
}
/***************************************************************************
* *
* WRITE interrupt routine (NDAC line) *
* *
***************************************************************************/
static irqreturn_t bb_NDAC_interrupt(int irq, void *arg)
{
struct gpib_board *board = arg;
struct bb_priv *priv = board->private_data;
unsigned long flags;
int ndac;
spin_lock_irqsave(&priv->rw_lock, flags);
ndac = gpiod_get_value(NDAC);
priv->all_irqs++;
dbg_printk(3, "> irq: %d NRFD: %d NDAC: %d st: %4lx dir: %d busy: %d:%d\n",
irq, gpiod_get_value(NRFD), ndac, board->status, priv->direction,
priv->w_busy, priv->r_busy);
if (priv->ndac_mode) {
ENABLE_IRQ(priv->irq_NDAC, IRQ_TYPE_EDGE_RISING);
priv->ndac_mode = 0;
}
if (priv->w_busy == 0) {
dbg_printk(1, "interrupt while idle.\n");
priv->ndac_idle++;
goto ndac_exit;
}
if (ndac == 0) {
dbg_printk(1, "out of order interrupt at %zu:%d.\n", priv->w_cnt, priv->phase);
priv->phase = 500;
priv->ndac_seq++;
goto ndac_exit;
}
if (priv->dav_tx) {
dbg_printk(1, "DAV high after %zu/%zu cmd %d " LINFMT ". No action.\n",
priv->w_cnt, priv->length, priv->cmd, LINVAL);
priv->dav_seq++;
goto ndac_exit;
}
dbg_printk(3, "accepted %zu\n", priv->w_cnt - 1);
gpiod_set_value(DAV, 1); // Data not available
priv->dav_tx = 1;
priv->phase = 510;
if (priv->w_cnt >= priv->length) { // test for end of transfer
priv->write_done = 1;
priv->w_busy = 0;
wake_up_interruptible(&board->wait);
}
ndac_exit:
spin_unlock_irqrestore(&priv->rw_lock, flags);
return IRQ_HANDLED;
}
/***************************************************************************
* *
* interrupt routine for SRQ line *
* *
***************************************************************************/
static irqreturn_t bb_SRQ_interrupt(int irq, void *arg)
{
struct gpib_board *board = arg;
int val = gpiod_get_value(SRQ);
dbg_printk(3, "> %d st: %4lx\n", val, board->status);
if (!val)
set_bit(SRQI_NUM, &board->status); /* set_bit() is atomic */
wake_up_interruptible(&board->wait);
return IRQ_HANDLED;
}
static int bb_command(struct gpib_board *board, u8 *buffer,
size_t length, size_t *bytes_written)
{
int ret;
struct bb_priv *priv = board->private_data;
int i;
dbg_printk(2, "%p %p\n", buffer, board->buffer);
/* the _ATN line has already been asserted by bb_take_control() */
priv->cmd = 1;
ret = bb_write(board, buffer, length, 0, bytes_written); // no eoi
for (i = 0; i < length; i++) {
if (buffer[i] == UNT) {
priv->talker_state = talker_idle;
} else {
if (buffer[i] == UNL) {
priv->listener_state = listener_idle;
} else {
if (buffer[i] == (MTA(board->pad))) {
priv->talker_state = talker_addressed;
priv->listener_state = listener_idle;
} else if (buffer[i] == (MLA(board->pad))) {
priv->listener_state = listener_addressed;
priv->talker_state = talker_idle;
}
}
}
}
/* the _ATN line will be released by bb_go_to_stby */
priv->cmd = 0;
return ret;
}
/***************************************************************************
* *
* Buffer print with decode for debug/trace *
* *
***************************************************************************/
static char *cmd_string[32] = {
"", // 0x00
"GTL", // 0x01
"", // 0x02
"", // 0x03
"SDC", // 0x04
"PPC", // 0x05
"", // 0x06
"", // 0x07
"GET", // 0x08
"TCT", // 0x09
"", // 0x0a
"", // 0x0b
"", // 0x0c
"", // 0x0d
"", // 0x0e
"", // 0x0f
"", // 0x10
"LLO", // 0x11
"", // 0x12
"", // 0x13
"DCL", // 0x14
"PPU", // 0x15
"", // 0x16
"", // 0x17
"SPE", // 0x18
"SPD", // 0x19
"", // 0x1a
"", // 0x1b
"", // 0x1c
"", // 0x1d
"", // 0x1e
"CFE" // 0x1f
};
static void bb_buffer_print(struct gpib_board *board, unsigned char *buffer, size_t length,
int cmd, int eoi)
{
int i;
if (cmd) {
dbg_printk(2, "<cmd len %zu>\n", length);
for (i = 0; i < length; i++) {
if (buffer[i] < 0x20) {
dbg_printk(3, "0x%x=%s\n", buffer[i], cmd_string[buffer[i]]);
} else if (buffer[i] == 0x3f) {
dbg_printk(3, "0x%x=%s\n", buffer[i], "UNL");
} else if (buffer[i] == 0x5f) {
dbg_printk(3, "0x%x=%s\n", buffer[i], "UNT");
} else if (buffer[i] < 0x60) {
dbg_printk(3, "0x%x=%s%d\n", buffer[i],
(buffer[i] & 0x40) ? "TLK" : "LSN", buffer[i] & 0x1F);
} else {
dbg_printk(3, "0x%x\n", buffer[i]);
}
}
} else {
dbg_printk(2, "<data len %zu %s>\n", length, (eoi) ? "w.EOI" : " ");
for (i = 0; i < length; i++)
dbg_printk(2, "%3d 0x%x->%c\n", i, buffer[i], printable(buffer[i]));
}
}
/***************************************************************************
* *
* STATUS Management *
* *
***************************************************************************/
static void set_atn(struct gpib_board *board, int atn_asserted)
{
struct bb_priv *priv = board->private_data;
if (priv->listener_state != listener_idle &&
priv->talker_state != talker_idle) {
dev_err(board->gpib_dev, "listener/talker state machine conflict\n");
}
if (atn_asserted) {
if (priv->listener_state == listener_active)
priv->listener_state = listener_addressed;
if (priv->talker_state == talker_active)
priv->talker_state = talker_addressed;
SET_DIR_WRITE(priv); // need to be able to read bus NRFD/NDAC
} else {
if (priv->listener_state == listener_addressed) {
priv->listener_state = listener_active;
SET_DIR_READ(priv); // make sure holdoff is active when we unassert ATN
}
if (priv->talker_state == talker_addressed)
priv->talker_state = talker_active;
}
gpiod_direction_output(_ATN, !atn_asserted);
}
static int bb_take_control(struct gpib_board *board, int synchronous)
{
dbg_printk(2, "%d\n", synchronous);
set_atn(board, 1);
return 0;
}
static int bb_go_to_standby(struct gpib_board *board)
{
dbg_printk(2, "\n");
set_atn(board, 0);
return 0;
}
static int bb_request_system_control(struct gpib_board *board, int request_control)
{
struct bb_priv *priv = board->private_data;
dbg_printk(2, "%d\n", request_control);
if (!request_control)
return -EINVAL;
gpiod_direction_output(REN, 1); /* user space must enable REN if needed */
gpiod_direction_output(IFC, 1); /* user space must toggle IFC if needed */
if (sn7516x)
gpiod_direction_output(DC, 0); /* enable ATN as output on SN75161/2 */
gpiod_direction_input(SRQ);
ENABLE_IRQ(priv->irq_SRQ, IRQ_TYPE_EDGE_FALLING);
return 0;
}
static void bb_interface_clear(struct gpib_board *board, int assert)
{
struct bb_priv *priv = board->private_data;
dbg_printk(2, "%d\n", assert);
if (assert) {
gpiod_direction_output(IFC, 0);
priv->talker_state = talker_idle;
priv->listener_state = listener_idle;
set_bit(CIC_NUM, &board->status);
} else {
gpiod_direction_output(IFC, 1);
}
}
static void bb_remote_enable(struct gpib_board *board, int enable)
{
dbg_printk(2, "%d\n", enable);
if (enable) {
set_bit(REM_NUM, &board->status);
gpiod_direction_output(REN, 0);
} else {
clear_bit(REM_NUM, &board->status);
gpiod_direction_output(REN, 1);
}
}
static int bb_enable_eos(struct gpib_board *board, u8 eos_byte, int compare_8_bits)
{
struct bb_priv *priv = board->private_data;
dbg_printk(2, "%s\n", "EOS_en");
priv->eos = eos_byte;
priv->eos_flags = REOS;
if (compare_8_bits)
priv->eos_flags |= BIN;
return 0;
}
static void bb_disable_eos(struct gpib_board *board)
{
struct bb_priv *priv = board->private_data;
dbg_printk(2, "\n");
priv->eos_flags &= ~REOS;
}
static unsigned int bb_update_status(struct gpib_board *board, unsigned int clear_mask)
{
struct bb_priv *priv = board->private_data;
board->status &= ~clear_mask;
if (gpiod_get_value(SRQ)) /* SRQ asserted low */
clear_bit(SRQI_NUM, &board->status);
else
set_bit(SRQI_NUM, &board->status);
if (gpiod_get_value(_ATN)) /* ATN asserted low */
clear_bit(ATN_NUM, &board->status);
else
set_bit(ATN_NUM, &board->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);
if (priv->listener_state == listener_active ||
priv->listener_state == listener_addressed)
set_bit(LACS_NUM, &board->status);
else
clear_bit(LACS_NUM, &board->status);
dbg_printk(2, "0x%lx mask 0x%x\n", board->status, clear_mask);
return board->status;
}
static int bb_primary_address(struct gpib_board *board, unsigned int address)
{
dbg_printk(2, "%d\n", address);
board->pad = address;
return 0;
}
static int bb_secondary_address(struct gpib_board *board, unsigned int address, int enable)
{
dbg_printk(2, "%d %d\n", address, enable);
if (enable)
board->sad = address;
return 0;
}
static int bb_parallel_poll(struct gpib_board *board, u8 *result)
{
return -ENOENT;
}
static void bb_parallel_poll_configure(struct gpib_board *board, u8 config)
{
}
static void bb_parallel_poll_response(struct gpib_board *board, int ist)
{
}
static void bb_serial_poll_response(struct gpib_board *board, u8 status)
{
}
static u8 bb_serial_poll_status(struct gpib_board *board)
{
return 0; // -ENOENT;
}
static int bb_t1_delay(struct gpib_board *board, unsigned int nano_sec)
{
struct bb_priv *priv = board->private_data;
if (nano_sec <= 350)
priv->t1_delay = 350;
else if (nano_sec <= 1100)
priv->t1_delay = 1100;
else
priv->t1_delay = 2000;
dbg_printk(2, "t1 delay set to %d nanosec\n", priv->t1_delay);
return priv->t1_delay;
}
static void bb_return_to_local(struct gpib_board *board)
{
}
static int bb_line_status(const struct gpib_board *board)
{
int line_status = VALID_ALL;
if (gpiod_get_value(REN) == 0)
line_status |= BUS_REN;
if (gpiod_get_value(IFC) == 0)
line_status |= BUS_IFC;
if (gpiod_get_value(NDAC) == 0)
line_status |= BUS_NDAC;
if (gpiod_get_value(NRFD) == 0)
line_status |= BUS_NRFD;
if (gpiod_get_value(DAV) == 0)
line_status |= BUS_DAV;
if (gpiod_get_value(EOI) == 0)
line_status |= BUS_EOI;
if (gpiod_get_value(_ATN) == 0)
line_status |= BUS_ATN;
if (gpiod_get_value(SRQ) == 0)
line_status |= BUS_SRQ;
dbg_printk(2, "status lines: %4x\n", line_status);
return line_status;
}
/***************************************************************************
* *
* Module Management *
* *
***************************************************************************/
static int allocate_private(struct gpib_board *board)
{
board->private_data = kzalloc_obj(struct bb_priv);
if (!board->private_data)
return -ENOMEM;
return 0;
}
static void free_private(struct gpib_board *board)
{
kfree(board->private_data);
board->private_data = NULL;
}
static int bb_get_irq(struct gpib_board *board, char *name,
struct gpio_desc *gpio, int *irq,
irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags)
{
if (!gpio)
return -1;
gpiod_direction_input(gpio);
*irq = gpiod_to_irq(gpio);
dbg_printk(2, "IRQ %s: %d\n", name, *irq);
if (*irq < 0) {
dev_err(board->gpib_dev, "can't get IRQ for %s\n", name);
return -1;
}
if (request_threaded_irq(*irq, handler, thread_fn, flags, name, board)) {
dev_err(board->gpib_dev, "can't request IRQ for %s %d\n", name, *irq);
*irq = 0;
return -1;
}
DISABLE_IRQ(*irq);
return 0;
}
static void bb_free_irq(struct gpib_board *board, int *irq, char *name)
{
if (*irq) {
free_irq(*irq, board);
dbg_printk(2, "IRQ %d(%s) freed\n", *irq, name);
*irq = 0;
}
}
static void release_gpios(void)
{
int j;
for (j = 0 ; j < NUM_PINS ; j++) {
if (all_descriptors[j]) {
gpiod_put(all_descriptors[j]);
all_descriptors[j] = NULL;
}
}
}
static int allocate_gpios(struct gpib_board *board)
{
int j;
int table_index = 0;
char name[256];
struct gpio_desc *desc;
struct gpiod_lookup_table *lookup_table;
if (!board->gpib_dev) {
pr_err("NULL gpib dev for board\n");
return -ENOENT;
}
lookup_table = lookup_tables[table_index];
lookup_table->dev_id = dev_name(board->gpib_dev);
gpiod_add_lookup_table(lookup_table);
dbg_printk(1, "Allocating gpios using table index %d\n", table_index);
for (j = 0 ; j < NUM_PINS ; j++) {
if (gpios_vector[j] < 0)
continue;
/* name not really used in gpiod_get_index() */
sprintf(name, "GPIO%d", gpios_vector[j]);
try_again:
dbg_printk(1, "Allocating gpio %s pin no %d\n", name, gpios_vector[j]);
desc = gpiod_get_index(board->gpib_dev, name, gpios_vector[j], GPIOD_IN);
if (IS_ERR(desc)) {
gpiod_remove_lookup_table(lookup_table);
table_index++;
lookup_table = lookup_tables[table_index];
if (!lookup_table) {
dev_err(board->gpib_dev, "Unable to obtain gpio descriptor for pin %d error %ld\n",
gpios_vector[j], PTR_ERR(desc));
goto alloc_gpios_fail;
}
dbg_printk(1, "Allocation failed, now using table_index %d\n", table_index);
lookup_table->dev_id = dev_name(board->gpib_dev);
gpiod_add_lookup_table(lookup_table);
goto try_again;
}
all_descriptors[j] = desc;
}
gpiod_remove_lookup_table(lookup_table);
return 0;
alloc_gpios_fail:
release_gpios();
return -1;
}
static void bb_detach(struct gpib_board *board)
{
struct bb_priv *priv = board->private_data;
dbg_printk(2, "Enter with data %p\n", board->private_data);
if (!board->private_data)
return;
bb_free_irq(board, &priv->irq_DAV, NAME "_DAV");
bb_free_irq(board, &priv->irq_NRFD, NAME "_NRFD");
bb_free_irq(board, &priv->irq_NDAC, NAME "_NDAC");
bb_free_irq(board, &priv->irq_SRQ, NAME "_SRQ");
if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */
gpiod_set_value(YOGA_ENABLE, 0);
}
release_gpios();
dbg_printk(2, "detached board: %d\n", board->minor);
dbg_printk(0, "NRFD: idle %d, seq %d, NDAC: idle %d, seq %d DAV: idle %d seq: %d all: %ld",
priv->nrfd_idle, priv->nrfd_seq,
priv->ndac_idle, priv->ndac_seq,
priv->dav_idle, priv->dav_seq, priv->all_irqs);
free_private(board);
}
static int bb_attach(struct gpib_board *board, const struct gpib_board_config *config)
{
struct bb_priv *priv;
int retval;
dbg_printk(2, "%s\n", "Enter ...");
board->status = 0;
retval = allocate_private(board);
if (retval)
return retval;
priv = board->private_data;
priv->direction = -1;
priv->t1_delay = 2000;
priv->listener_state = listener_idle;
priv->talker_state = talker_idle;
sn7516x = sn7516x_used;
if (strcmp(PINMAP_0, pin_map) == 0) {
if (!sn7516x) {
gpios_vector[&(PE) - &all_descriptors[0]] = -1;
gpios_vector[&(DC) - &all_descriptors[0]] = -1;
gpios_vector[&(TE) - &all_descriptors[0]] = -1;
}
} else if (strcmp(PINMAP_1, pin_map) == 0) {
if (!sn7516x) {
gpios_vector[&(PE) - &all_descriptors[0]] = -1;
gpios_vector[&(DC) - &all_descriptors[0]] = -1;
gpios_vector[&(TE) - &all_descriptors[0]] = -1;
}
gpios_vector[&(REN) - &all_descriptors[0]] = 0; /* 27 -> 0 REN on GPIB pin 0 */
} else if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA */
sn7516x = 0;
gpios_vector[&(D03) - &all_descriptors[0]] = YOGA_D03_pin_nr;
gpios_vector[&(D04) - &all_descriptors[0]] = YOGA_D04_pin_nr;
gpios_vector[&(D05) - &all_descriptors[0]] = YOGA_D05_pin_nr;
gpios_vector[&(D06) - &all_descriptors[0]] = YOGA_D06_pin_nr;
gpios_vector[&(PE) - &all_descriptors[0]] = -1;
gpios_vector[&(DC) - &all_descriptors[0]] = -1;
} else {
dev_err(board->gpib_dev, "Unrecognized pin map %s\n", pin_map);
goto bb_attach_fail;
}
dbg_printk(0, "Using pin map \"%s\" %s\n", pin_map, (sn7516x) ?
" with SN7516x driver support" : "");
if (allocate_gpios(board))
goto bb_attach_fail;
/*
* Configure SN7516X control lines.
* drive ATN, IFC and REN as outputs only when master
* i.e. system controller. In this mode can only be the CIC
* When not master then enable device mode ATN, IFC & REN as inputs
*/
if (sn7516x) {
gpiod_direction_output(DC, 0);
gpiod_direction_output(TE, 1);
gpiod_direction_output(PE, 1);
}
/* Set main control lines to a known state */
gpiod_direction_output(IFC, 1);
gpiod_direction_output(REN, 1);
gpiod_direction_output(_ATN, 1);
if (strcmp(PINMAP_2, pin_map) == 0) { /* YOGA: enable level shifters */
gpiod_direction_output(YOGA_ENABLE, 1);
}
spin_lock_init(&priv->rw_lock);
/* request DAV interrupt for read */
if (bb_get_irq(board, NAME "_DAV", DAV, &priv->irq_DAV, bb_DAV_interrupt, NULL,
IRQF_TRIGGER_NONE))
goto bb_attach_fail_r;
/* request NRFD interrupt for write */
if (bb_get_irq(board, NAME "_NRFD", NRFD, &priv->irq_NRFD, bb_NRFD_interrupt, NULL,
IRQF_TRIGGER_NONE))
goto bb_attach_fail_r;
/* request NDAC interrupt for write */
if (bb_get_irq(board, NAME "_NDAC", NDAC, &priv->irq_NDAC, bb_NDAC_interrupt, NULL,
IRQF_TRIGGER_NONE))
goto bb_attach_fail_r;
/* request SRQ interrupt for Service Request */
if (bb_get_irq(board, NAME "_SRQ", SRQ, &priv->irq_SRQ, bb_SRQ_interrupt, NULL,
IRQF_TRIGGER_NONE))
goto bb_attach_fail_r;
dbg_printk(0, "attached board %d\n", board->minor);
goto bb_attach_out;
bb_attach_fail_r:
release_gpios();
bb_attach_fail:
retval = -1;
bb_attach_out:
return retval;
}
static struct gpib_interface bb_interface = {
.name = NAME,
.attach = bb_attach,
.detach = bb_detach,
.read = bb_read,
.write = bb_write,
.command = bb_command,
.take_control = bb_take_control,
.go_to_standby = bb_go_to_standby,
.request_system_control = bb_request_system_control,
.interface_clear = bb_interface_clear,
.remote_enable = bb_remote_enable,
.enable_eos = bb_enable_eos,
.disable_eos = bb_disable_eos,
.parallel_poll = bb_parallel_poll,
.parallel_poll_configure = bb_parallel_poll_configure,
.parallel_poll_response = bb_parallel_poll_response,
.line_status = bb_line_status,
.update_status = bb_update_status,
.primary_address = bb_primary_address,
.secondary_address = bb_secondary_address,
.serial_poll_response = bb_serial_poll_response,
.serial_poll_status = bb_serial_poll_status,
.t1_delay = bb_t1_delay,
.return_to_local = bb_return_to_local,
};
static int __init bb_init_module(void)
{
int result = gpib_register_driver(&bb_interface, THIS_MODULE);
if (result) {
pr_err("gpib_register_driver failed: error = %d\n", result);
return result;
}
return 0;
}
static void __exit bb_exit_module(void)
{
gpib_unregister_driver(&bb_interface);
}
module_init(bb_init_module);
module_exit(bb_exit_module);
/***************************************************************************
* *
* UTILITY Functions *
* *
***************************************************************************/
inline long usec_diff(struct timespec64 *a, struct timespec64 *b)
{
return ((a->tv_sec - b->tv_sec) * 1000000 +
(a->tv_nsec - b->tv_nsec) / 1000);
}
static inline int check_for_eos(struct bb_priv *priv, u8 byte)
{
if (priv->eos_check)
return 0;
if (priv->eos_check_8) {
if (priv->eos == byte)
return 1;
} else {
if (priv->eos_mask_7 == (byte & 0x7f))
return 1;
}
return 0;
}
static void set_data_lines_output(void)
{
gpiod_direction_output(D01, 1);
gpiod_direction_output(D02, 1);
gpiod_direction_output(D03, 1);
gpiod_direction_output(D04, 1);
gpiod_direction_output(D05, 1);
gpiod_direction_output(D06, 1);
gpiod_direction_output(D07, 1);
gpiod_direction_output(D08, 1);
}
static void set_data_lines(u8 byte)
{
gpiod_set_value(D01, !(byte & 0x01));
gpiod_set_value(D02, !(byte & 0x02));
gpiod_set_value(D03, !(byte & 0x04));
gpiod_set_value(D04, !(byte & 0x08));
gpiod_set_value(D05, !(byte & 0x10));
gpiod_set_value(D06, !(byte & 0x20));
gpiod_set_value(D07, !(byte & 0x40));
gpiod_set_value(D08, !(byte & 0x80));
}
static u8 get_data_lines(void)
{
u8 ret;
ret = gpiod_get_value(D01);
ret |= gpiod_get_value(D02) << 1;
ret |= gpiod_get_value(D03) << 2;
ret |= gpiod_get_value(D04) << 3;
ret |= gpiod_get_value(D05) << 4;
ret |= gpiod_get_value(D06) << 5;
ret |= gpiod_get_value(D07) << 6;
ret |= gpiod_get_value(D08) << 7;
return ~ret;
}
static void set_data_lines_input(void)
{
gpiod_direction_input(D01);
gpiod_direction_input(D02);
gpiod_direction_input(D03);
gpiod_direction_input(D04);
gpiod_direction_input(D05);
gpiod_direction_input(D06);
gpiod_direction_input(D07);
gpiod_direction_input(D08);
}
static inline void SET_DIR_WRITE(struct bb_priv *priv)
{
if (priv->direction == DIR_WRITE)
return;
gpiod_direction_input(NRFD);
gpiod_direction_input(NDAC);
set_data_lines_output();
gpiod_direction_output(DAV, 1);
gpiod_direction_output(EOI, 1);
if (sn7516x) {
gpiod_set_value(PE, 1); /* set data lines to transmit on sn75160b */
gpiod_set_value(TE, 1); /* set NDAC and NRFD to receive and DAV to transmit */
}
priv->direction = DIR_WRITE;
}
static inline void SET_DIR_READ(struct bb_priv *priv)
{
if (priv->direction == DIR_READ)
return;
gpiod_direction_input(DAV);
gpiod_direction_input(EOI);
set_data_lines_input();
if (sn7516x) {
gpiod_set_value(PE, 0); /* set data lines to receive on sn75160b */
gpiod_set_value(TE, 0); /* set NDAC and NRFD to transmit and DAV to receive */
}
gpiod_direction_output(NRFD, 0); /* hold off the talker */
gpiod_direction_output(NDAC, 0); /* data not accepted */
priv->direction = DIR_READ;
}