linux/drivers/char/xillybus/xillybus_core.c

1992 lines
50 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/misc/xillybus_core.c
*
* Copyright 2011 Xillybus Ltd, http://xillybus.com
*
* Driver for the Xillybus FPGA/host framework.
*
* This driver interfaces with a special IP core in an FPGA, setting up
* a pipe between a hardware FIFO in the programmable logic and a device
* file in the host. The number of such pipes and their attributes are
* set up on the logic. This driver detects these automatically and
* creates the device files accordingly.
*/
#include <linux/list.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/crc32.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "xillybus.h"
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
#include "xillybus_class.h"
MODULE_DESCRIPTION("Xillybus core functions");
MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
MODULE_ALIAS("xillybus_core");
MODULE_LICENSE("GPL v2");
/* General timeout is 100 ms, rx timeout is 10 ms */
#define XILLY_RX_TIMEOUT (10*HZ/1000)
#define XILLY_TIMEOUT (100*HZ/1000)
#define fpga_msg_ctrl_reg 0x0008
#define fpga_dma_control_reg 0x0020
#define fpga_dma_bufno_reg 0x0024
#define fpga_dma_bufaddr_lowaddr_reg 0x0028
#define fpga_dma_bufaddr_highaddr_reg 0x002c
#define fpga_buf_ctrl_reg 0x0030
#define fpga_buf_offset_reg 0x0034
#define fpga_endian_reg 0x0040
#define XILLYMSG_OPCODE_RELEASEBUF 1
#define XILLYMSG_OPCODE_QUIESCEACK 2
#define XILLYMSG_OPCODE_FIFOEOF 3
#define XILLYMSG_OPCODE_FATAL_ERROR 4
#define XILLYMSG_OPCODE_NONEMPTY 5
static const char xillyname[] = "xillybus";
static struct workqueue_struct *xillybus_wq;
/*
* Locking scheme: Mutexes protect invocations of character device methods.
* If both locks are taken, wr_mutex is taken first, rd_mutex second.
*
* wr_spinlock protects wr_*_buf_idx, wr_empty, wr_sleepy, wr_ready and the
* buffers' end_offset fields against changes made by IRQ handler (and in
* theory, other file request handlers, but the mutex handles that). Nothing
* else.
* They are held for short direct memory manipulations. Needless to say,
* no mutex locking is allowed when a spinlock is held.
*
* rd_spinlock does the same with rd_*_buf_idx, rd_empty and end_offset.
*
* register_mutex is endpoint-specific, and is held when non-atomic
* register operations are performed. wr_mutex and rd_mutex may be
* held when register_mutex is taken, but none of the spinlocks. Note that
* register_mutex doesn't protect against sporadic buf_ctrl_reg writes
* which are unrelated to buf_offset_reg, since they are harmless.
*
* Blocking on the wait queues is allowed with mutexes held, but not with
* spinlocks.
*
* Only interruptible blocking is allowed on mutexes and wait queues.
*
* All in all, the locking order goes (with skips allowed, of course):
* wr_mutex -> rd_mutex -> register_mutex -> wr_spinlock -> rd_spinlock
*/
static void malformed_message(struct xilly_endpoint *endpoint, u32 *buf)
{
int opcode;
int msg_channel, msg_bufno, msg_data, msg_dir;
opcode = (buf[0] >> 24) & 0xff;
msg_dir = buf[0] & 1;
msg_channel = (buf[0] >> 1) & 0x7ff;
msg_bufno = (buf[0] >> 12) & 0x3ff;
msg_data = buf[1] & 0xfffffff;
dev_warn(endpoint->dev,
"Malformed message (skipping): opcode=%d, channel=%03x, dir=%d, bufno=%03x, data=%07x\n",
opcode, msg_channel, msg_dir, msg_bufno, msg_data);
}
/*
* xillybus_isr assumes the interrupt is allocated exclusively to it,
* which is the natural case MSI and several other hardware-oriented
* interrupts. Sharing is not allowed.
*/
irqreturn_t xillybus_isr(int irq, void *data)
{
struct xilly_endpoint *ep = data;
u32 *buf;
unsigned int buf_size;
int i;
int opcode;
unsigned int msg_channel, msg_bufno, msg_data, msg_dir;
struct xilly_channel *channel;
buf = ep->msgbuf_addr;
buf_size = ep->msg_buf_size/sizeof(u32);
dma_sync_single_for_cpu(ep->dev, ep->msgbuf_dma_addr,
ep->msg_buf_size, DMA_FROM_DEVICE);
for (i = 0; i < buf_size; i += 2) {
if (((buf[i+1] >> 28) & 0xf) != ep->msg_counter) {
malformed_message(ep, &buf[i]);
dev_warn(ep->dev,
"Sending a NACK on counter %x (instead of %x) on entry %d\n",
((buf[i+1] >> 28) & 0xf),
ep->msg_counter,
i/2);
if (++ep->failed_messages > 10) {
dev_err(ep->dev,
"Lost sync with interrupt messages. Stopping.\n");
} else {
dma_sync_single_for_device(ep->dev,
ep->msgbuf_dma_addr,
ep->msg_buf_size,
DMA_FROM_DEVICE);
iowrite32(0x01, /* Message NACK */
ep->registers + fpga_msg_ctrl_reg);
}
return IRQ_HANDLED;
} else if (buf[i] & (1 << 22)) /* Last message */
break;
}
if (i >= buf_size) {
dev_err(ep->dev, "Bad interrupt message. Stopping.\n");
return IRQ_HANDLED;
}
buf_size = i + 2;
for (i = 0; i < buf_size; i += 2) { /* Scan through messages */
opcode = (buf[i] >> 24) & 0xff;
msg_dir = buf[i] & 1;
msg_channel = (buf[i] >> 1) & 0x7ff;
msg_bufno = (buf[i] >> 12) & 0x3ff;
msg_data = buf[i+1] & 0xfffffff;
switch (opcode) {
case XILLYMSG_OPCODE_RELEASEBUF:
if ((msg_channel > ep->num_channels) ||
(msg_channel == 0)) {
malformed_message(ep, &buf[i]);
break;
}
channel = ep->channels[msg_channel];
if (msg_dir) { /* Write channel */
if (msg_bufno >= channel->num_wr_buffers) {
malformed_message(ep, &buf[i]);
break;
}
spin_lock(&channel->wr_spinlock);
channel->wr_buffers[msg_bufno]->end_offset =
msg_data;
channel->wr_fpga_buf_idx = msg_bufno;
channel->wr_empty = 0;
channel->wr_sleepy = 0;
spin_unlock(&channel->wr_spinlock);
wake_up_interruptible(&channel->wr_wait);
} else {
/* Read channel */
if (msg_bufno >= channel->num_rd_buffers) {
malformed_message(ep, &buf[i]);
break;
}
spin_lock(&channel->rd_spinlock);
channel->rd_fpga_buf_idx = msg_bufno;
channel->rd_full = 0;
spin_unlock(&channel->rd_spinlock);
wake_up_interruptible(&channel->rd_wait);
if (!channel->rd_synchronous)
queue_delayed_work(
xillybus_wq,
&channel->rd_workitem,
XILLY_RX_TIMEOUT);
}
break;
case XILLYMSG_OPCODE_NONEMPTY:
if ((msg_channel > ep->num_channels) ||
(msg_channel == 0) || (!msg_dir) ||
!ep->channels[msg_channel]->wr_supports_nonempty) {
malformed_message(ep, &buf[i]);
break;
}
channel = ep->channels[msg_channel];
if (msg_bufno >= channel->num_wr_buffers) {
malformed_message(ep, &buf[i]);
break;
}
spin_lock(&channel->wr_spinlock);
if (msg_bufno == channel->wr_host_buf_idx)
channel->wr_ready = 1;
spin_unlock(&channel->wr_spinlock);
wake_up_interruptible(&channel->wr_ready_wait);
break;
case XILLYMSG_OPCODE_QUIESCEACK:
ep->idtlen = msg_data;
wake_up_interruptible(&ep->ep_wait);
break;
case XILLYMSG_OPCODE_FIFOEOF:
if ((msg_channel > ep->num_channels) ||
(msg_channel == 0) || (!msg_dir) ||
!ep->channels[msg_channel]->num_wr_buffers) {
malformed_message(ep, &buf[i]);
break;
}
channel = ep->channels[msg_channel];
spin_lock(&channel->wr_spinlock);
channel->wr_eof = msg_bufno;
channel->wr_sleepy = 0;
channel->wr_hangup = channel->wr_empty &&
(channel->wr_host_buf_idx == msg_bufno);
spin_unlock(&channel->wr_spinlock);
wake_up_interruptible(&channel->wr_wait);
break;
case XILLYMSG_OPCODE_FATAL_ERROR:
ep->fatal_error = 1;
wake_up_interruptible(&ep->ep_wait); /* For select() */
dev_err(ep->dev,
"FPGA reported a fatal error. This means that the low-level communication with the device has failed. This hardware problem is most likely unrelated to Xillybus (neither kernel module nor FPGA core), but reports are still welcome. All I/O is aborted.\n");
break;
default:
malformed_message(ep, &buf[i]);
break;
}
}
dma_sync_single_for_device(ep->dev, ep->msgbuf_dma_addr,
ep->msg_buf_size, DMA_FROM_DEVICE);
ep->msg_counter = (ep->msg_counter + 1) & 0xf;
ep->failed_messages = 0;
iowrite32(0x03, ep->registers + fpga_msg_ctrl_reg); /* Message ACK */
return IRQ_HANDLED;
}
EXPORT_SYMBOL(xillybus_isr);
/*
* A few trivial memory management functions.
* NOTE: These functions are used only on probe and remove, and therefore
* no locks are applied!
*/
static void xillybus_autoflush(struct work_struct *work);
struct xilly_alloc_state {
void *salami;
int left_of_salami;
int nbuffer;
enum dma_data_direction direction;
u32 regdirection;
};
static void xilly_unmap(void *ptr)
{
struct xilly_mapping *data = ptr;
dma_unmap_single(data->device, data->dma_addr,
data->size, data->direction);
kfree(ptr);
}
static int xilly_map_single(struct xilly_endpoint *ep,
void *ptr,
size_t size,
int direction,
dma_addr_t *ret_dma_handle
)
{
dma_addr_t addr;
struct xilly_mapping *this;
this = kzalloc(sizeof(*this), GFP_KERNEL);
if (!this)
return -ENOMEM;
addr = dma_map_single(ep->dev, ptr, size, direction);
if (dma_mapping_error(ep->dev, addr)) {
kfree(this);
return -ENODEV;
}
this->device = ep->dev;
this->dma_addr = addr;
this->size = size;
this->direction = direction;
*ret_dma_handle = addr;
return devm_add_action_or_reset(ep->dev, xilly_unmap, this);
}
static int xilly_get_dma_buffers(struct xilly_endpoint *ep,
struct xilly_alloc_state *s,
struct xilly_buffer **buffers,
int bufnum, int bytebufsize)
{
int i, rc;
dma_addr_t dma_addr;
struct device *dev = ep->dev;
struct xilly_buffer *this_buffer = NULL; /* Init to silence warning */
if (buffers) { /* Not the message buffer */
this_buffer = devm_kcalloc(dev, bufnum,
sizeof(struct xilly_buffer),
GFP_KERNEL);
if (!this_buffer)
return -ENOMEM;
}
for (i = 0; i < bufnum; i++) {
/*
* Buffers are expected in descending size order, so there
* is either enough space for this buffer or none at all.
*/
if ((s->left_of_salami < bytebufsize) &&
(s->left_of_salami > 0)) {
dev_err(ep->dev,
"Corrupt buffer allocation in IDT. Aborting.\n");
return -ENODEV;
}
if (s->left_of_salami == 0) {
int allocorder, allocsize;
allocsize = PAGE_SIZE;
allocorder = 0;
while (bytebufsize > allocsize) {
allocsize *= 2;
allocorder++;
}
s->salami = (void *) devm_get_free_pages(
dev,
GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO,
allocorder);
if (!s->salami)
return -ENOMEM;
s->left_of_salami = allocsize;
}
rc = xilly_map_single(ep, s->salami,
bytebufsize, s->direction,
&dma_addr);
if (rc)
return rc;
iowrite32((u32) (dma_addr & 0xffffffff),
ep->registers + fpga_dma_bufaddr_lowaddr_reg);
iowrite32(((u32) ((((u64) dma_addr) >> 32) & 0xffffffff)),
ep->registers + fpga_dma_bufaddr_highaddr_reg);
if (buffers) { /* Not the message buffer */
this_buffer->addr = s->salami;
this_buffer->dma_addr = dma_addr;
buffers[i] = this_buffer++;
iowrite32(s->regdirection | s->nbuffer++,
ep->registers + fpga_dma_bufno_reg);
} else {
ep->msgbuf_addr = s->salami;
ep->msgbuf_dma_addr = dma_addr;
ep->msg_buf_size = bytebufsize;
iowrite32(s->regdirection,
ep->registers + fpga_dma_bufno_reg);
}
s->left_of_salami -= bytebufsize;
s->salami += bytebufsize;
}
return 0;
}
static int xilly_setupchannels(struct xilly_endpoint *ep,
unsigned char *chandesc,
int entries)
{
struct device *dev = ep->dev;
int i, entry, rc;
struct xilly_channel *channel;
int channelnum, bufnum, bufsize, format, is_writebuf;
int bytebufsize;
int synchronous, allowpartial, exclusive_open, seekable;
int supports_nonempty;
int msg_buf_done = 0;
struct xilly_alloc_state rd_alloc = {
.salami = NULL,
.left_of_salami = 0,
.nbuffer = 1,
.direction = DMA_TO_DEVICE,
.regdirection = 0,
};
struct xilly_alloc_state wr_alloc = {
.salami = NULL,
.left_of_salami = 0,
.nbuffer = 1,
.direction = DMA_FROM_DEVICE,
.regdirection = 0x80000000,
};
channel = devm_kcalloc(dev, ep->num_channels,
sizeof(struct xilly_channel), GFP_KERNEL);
if (!channel)
return -ENOMEM;
ep->channels = devm_kcalloc(dev, ep->num_channels + 1,
sizeof(struct xilly_channel *),
GFP_KERNEL);
if (!ep->channels)
return -ENOMEM;
ep->channels[0] = NULL; /* Channel 0 is message buf. */
/* Initialize all channels with defaults */
for (i = 1; i <= ep->num_channels; i++) {
channel->wr_buffers = NULL;
channel->rd_buffers = NULL;
channel->num_wr_buffers = 0;
channel->num_rd_buffers = 0;
channel->wr_fpga_buf_idx = -1;
channel->wr_host_buf_idx = 0;
channel->wr_host_buf_pos = 0;
channel->wr_empty = 1;
channel->wr_ready = 0;
channel->wr_sleepy = 1;
channel->rd_fpga_buf_idx = 0;
channel->rd_host_buf_idx = 0;
channel->rd_host_buf_pos = 0;
channel->rd_full = 0;
channel->wr_ref_count = 0;
channel->rd_ref_count = 0;
spin_lock_init(&channel->wr_spinlock);
spin_lock_init(&channel->rd_spinlock);
mutex_init(&channel->wr_mutex);
mutex_init(&channel->rd_mutex);
init_waitqueue_head(&channel->rd_wait);
init_waitqueue_head(&channel->wr_wait);
init_waitqueue_head(&channel->wr_ready_wait);
INIT_DELAYED_WORK(&channel->rd_workitem, xillybus_autoflush);
channel->endpoint = ep;
channel->chan_num = i;
channel->log2_element_size = 0;
ep->channels[i] = channel++;
}
for (entry = 0; entry < entries; entry++, chandesc += 4) {
struct xilly_buffer **buffers = NULL;
is_writebuf = chandesc[0] & 0x01;
channelnum = (chandesc[0] >> 1) | ((chandesc[1] & 0x0f) << 7);
format = (chandesc[1] >> 4) & 0x03;
allowpartial = (chandesc[1] >> 6) & 0x01;
synchronous = (chandesc[1] >> 7) & 0x01;
bufsize = 1 << (chandesc[2] & 0x1f);
bufnum = 1 << (chandesc[3] & 0x0f);
exclusive_open = (chandesc[2] >> 7) & 0x01;
seekable = (chandesc[2] >> 6) & 0x01;
supports_nonempty = (chandesc[2] >> 5) & 0x01;
if ((channelnum > ep->num_channels) ||
((channelnum == 0) && !is_writebuf)) {
dev_err(ep->dev,
"IDT requests channel out of range. Aborting.\n");
return -ENODEV;
}
channel = ep->channels[channelnum]; /* NULL for msg channel */
if (!is_writebuf || channelnum > 0) {
channel->log2_element_size = ((format > 2) ?
2 : format);
bytebufsize = bufsize *
(1 << channel->log2_element_size);
buffers = devm_kcalloc(dev, bufnum,
sizeof(struct xilly_buffer *),
GFP_KERNEL);
if (!buffers)
return -ENOMEM;
} else {
bytebufsize = bufsize << 2;
}
if (!is_writebuf) {
channel->num_rd_buffers = bufnum;
channel->rd_buf_size = bytebufsize;
channel->rd_allow_partial = allowpartial;
channel->rd_synchronous = synchronous;
channel->rd_exclusive_open = exclusive_open;
channel->seekable = seekable;
channel->rd_buffers = buffers;
rc = xilly_get_dma_buffers(ep, &rd_alloc, buffers,
bufnum, bytebufsize);
} else if (channelnum > 0) {
channel->num_wr_buffers = bufnum;
channel->wr_buf_size = bytebufsize;
channel->seekable = seekable;
channel->wr_supports_nonempty = supports_nonempty;
channel->wr_allow_partial = allowpartial;
channel->wr_synchronous = synchronous;
channel->wr_exclusive_open = exclusive_open;
channel->wr_buffers = buffers;
rc = xilly_get_dma_buffers(ep, &wr_alloc, buffers,
bufnum, bytebufsize);
} else {
rc = xilly_get_dma_buffers(ep, &wr_alloc, NULL,
bufnum, bytebufsize);
msg_buf_done++;
}
if (rc)
return -ENOMEM;
}
if (!msg_buf_done) {
dev_err(ep->dev,
"Corrupt IDT: No message buffer. Aborting.\n");
return -ENODEV;
}
return 0;
}
static int xilly_scan_idt(struct xilly_endpoint *endpoint,
struct xilly_idt_handle *idt_handle)
{
int count = 0;
unsigned char *idt = endpoint->channels[1]->wr_buffers[0]->addr;
unsigned char *end_of_idt = idt + endpoint->idtlen - 4;
unsigned char *scan;
int len;
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
scan = idt + 1;
idt_handle->names = scan;
while ((scan <= end_of_idt) && *scan) {
while ((scan <= end_of_idt) && *scan++)
/* Do nothing, just scan thru string */;
count++;
}
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
idt_handle->names_len = scan - idt_handle->names;
scan++;
if (scan > end_of_idt) {
dev_err(endpoint->dev,
"IDT device name list overflow. Aborting.\n");
return -ENODEV;
}
idt_handle->chandesc = scan;
len = endpoint->idtlen - (3 + ((int) (scan - idt)));
if (len & 0x03) {
dev_err(endpoint->dev,
"Corrupt IDT device name list. Aborting.\n");
return -ENODEV;
}
idt_handle->entries = len >> 2;
endpoint->num_channels = count;
return 0;
}
static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
{
struct xilly_channel *channel;
unsigned char *version;
long t;
channel = endpoint->channels[1]; /* This should be generated ad-hoc */
channel->wr_sleepy = 1;
iowrite32(1 |
(3 << 24), /* Opcode 3 for channel 0 = Send IDT */
endpoint->registers + fpga_buf_ctrl_reg);
t = wait_event_interruptible_timeout(channel->wr_wait,
(!channel->wr_sleepy),
XILLY_TIMEOUT);
if (t <= 0) {
dev_err(endpoint->dev, "Failed to obtain IDT. Aborting.\n");
if (endpoint->fatal_error)
return -EIO;
return -ENODEV;
}
dma_sync_single_for_cpu(channel->endpoint->dev,
channel->wr_buffers[0]->dma_addr,
channel->wr_buf_size,
DMA_FROM_DEVICE);
if (channel->wr_buffers[0]->end_offset != endpoint->idtlen) {
dev_err(endpoint->dev,
"IDT length mismatch (%d != %d). Aborting.\n",
channel->wr_buffers[0]->end_offset, endpoint->idtlen);
return -ENODEV;
}
if (crc32_le(~0, channel->wr_buffers[0]->addr,
endpoint->idtlen+1) != 0) {
dev_err(endpoint->dev, "IDT failed CRC check. Aborting.\n");
return -ENODEV;
}
version = channel->wr_buffers[0]->addr;
/* Check version number. Reject anything above 0x82. */
if (*version > 0x82) {
dev_err(endpoint->dev,
"No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgrade. Aborting.\n",
*version);
return -ENODEV;
}
return 0;
}
static ssize_t xillybus_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *f_pos)
{
ssize_t rc;
unsigned long flags;
int bytes_done = 0;
int no_time_left = 0;
long deadline, left_to_sleep;
struct xilly_channel *channel = filp->private_data;
int empty, reached_eof, exhausted, ready;
/* Initializations are there only to silence warnings */
int howmany = 0, bufpos = 0, bufidx = 0, bufferdone = 0;
int waiting_bufidx;
if (channel->endpoint->fatal_error)
return -EIO;
deadline = jiffies + 1 + XILLY_RX_TIMEOUT;
rc = mutex_lock_interruptible(&channel->wr_mutex);
if (rc)
return rc;
while (1) { /* Note that we may drop mutex within this loop */
int bytes_to_do = count - bytes_done;
spin_lock_irqsave(&channel->wr_spinlock, flags);
empty = channel->wr_empty;
ready = !empty || channel->wr_ready;
if (!empty) {
bufidx = channel->wr_host_buf_idx;
bufpos = channel->wr_host_buf_pos;
howmany = ((channel->wr_buffers[bufidx]->end_offset
+ 1) << channel->log2_element_size)
- bufpos;
/* Update wr_host_* to its post-operation state */
if (howmany > bytes_to_do) {
bufferdone = 0;
howmany = bytes_to_do;
channel->wr_host_buf_pos += howmany;
} else {
bufferdone = 1;
channel->wr_host_buf_pos = 0;
if (bufidx == channel->wr_fpga_buf_idx) {
channel->wr_empty = 1;
channel->wr_sleepy = 1;
channel->wr_ready = 0;
}
if (bufidx >= (channel->num_wr_buffers - 1))
channel->wr_host_buf_idx = 0;
else
channel->wr_host_buf_idx++;
}
}
/*
* Marking our situation after the possible changes above,
* for use after releasing the spinlock.
*
* empty = empty before change
* exhasted = empty after possible change
*/
reached_eof = channel->wr_empty &&
(channel->wr_host_buf_idx == channel->wr_eof);
channel->wr_hangup = reached_eof;
exhausted = channel->wr_empty;
waiting_bufidx = channel->wr_host_buf_idx;
spin_unlock_irqrestore(&channel->wr_spinlock, flags);
if (!empty) { /* Go on, now without the spinlock */
if (bufpos == 0) /* Position zero means it's virgin */
dma_sync_single_for_cpu(channel->endpoint->dev,
channel->wr_buffers[bufidx]->dma_addr,
channel->wr_buf_size,
DMA_FROM_DEVICE);
if (copy_to_user(
userbuf,
channel->wr_buffers[bufidx]->addr
+ bufpos, howmany))
rc = -EFAULT;
userbuf += howmany;
bytes_done += howmany;
if (bufferdone) {
dma_sync_single_for_device(channel->endpoint->dev,
channel->wr_buffers[bufidx]->dma_addr,
channel->wr_buf_size,
DMA_FROM_DEVICE);
/*
* Tell FPGA the buffer is done with. It's an
* atomic operation to the FPGA, so what
* happens with other channels doesn't matter,
* and the certain channel is protected with
* the channel-specific mutex.
*/
iowrite32(1 | (channel->chan_num << 1) |
(bufidx << 12),
channel->endpoint->registers +
fpga_buf_ctrl_reg);
}
if (rc) {
mutex_unlock(&channel->wr_mutex);
return rc;
}
}
/* This includes a zero-count return = EOF */
if ((bytes_done >= count) || reached_eof)
break;
if (!exhausted)
continue; /* More in RAM buffer(s)? Just go on. */
if ((bytes_done > 0) &&
(no_time_left ||
(channel->wr_synchronous && channel->wr_allow_partial)))
break;
/*
* Nonblocking read: The "ready" flag tells us that the FPGA
* has data to send. In non-blocking mode, if it isn't on,
* just return. But if there is, we jump directly to the point
* where we ask for the FPGA to send all it has, and wait
* until that data arrives. So in a sense, we *do* block in
* nonblocking mode, but only for a very short time.
*/
if (!no_time_left && (filp->f_flags & O_NONBLOCK)) {
if (bytes_done > 0)
break;
if (ready)
goto desperate;
rc = -EAGAIN;
break;
}
if (!no_time_left || (bytes_done > 0)) {
/*
* Note that in case of an element-misaligned read
* request, offsetlimit will include the last element,
* which will be partially read from.
*/
int offsetlimit = ((count - bytes_done) - 1) >>
channel->log2_element_size;
int buf_elements = channel->wr_buf_size >>
channel->log2_element_size;
/*
* In synchronous mode, always send an offset limit.
* Just don't send a value too big.
*/
if (channel->wr_synchronous) {
/* Don't request more than one buffer */
if (channel->wr_allow_partial &&
(offsetlimit >= buf_elements))
offsetlimit = buf_elements - 1;
/* Don't request more than all buffers */
if (!channel->wr_allow_partial &&
(offsetlimit >=
(buf_elements * channel->num_wr_buffers)))
offsetlimit = buf_elements *
channel->num_wr_buffers - 1;
}
/*
* In asynchronous mode, force early flush of a buffer
* only if that will allow returning a full count. The
* "offsetlimit < ( ... )" rather than "<=" excludes
* requesting a full buffer, which would obviously
* cause a buffer transmission anyhow
*/
if (channel->wr_synchronous ||
(offsetlimit < (buf_elements - 1))) {
mutex_lock(&channel->endpoint->register_mutex);
iowrite32(offsetlimit,
channel->endpoint->registers +
fpga_buf_offset_reg);
iowrite32(1 | (channel->chan_num << 1) |
(2 << 24) | /* 2 = offset limit */
(waiting_bufidx << 12),
channel->endpoint->registers +
fpga_buf_ctrl_reg);
mutex_unlock(&channel->endpoint->
register_mutex);
}
}
/*
* If partial completion is disallowed, there is no point in
* timeout sleeping. Neither if no_time_left is set and
* there's no data.
*/
if (!channel->wr_allow_partial ||
(no_time_left && (bytes_done == 0))) {
/*
* This do-loop will run more than once if another
* thread reasserted wr_sleepy before we got the mutex
* back, so we try again.
*/
do {
mutex_unlock(&channel->wr_mutex);
if (wait_event_interruptible(
channel->wr_wait,
(!channel->wr_sleepy)))
goto interrupted;
if (mutex_lock_interruptible(
&channel->wr_mutex))
goto interrupted;
} while (channel->wr_sleepy);
continue;
interrupted: /* Mutex is not held if got here */
if (channel->endpoint->fatal_error)
return -EIO;
if (bytes_done)
return bytes_done;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN; /* Don't admit snoozing */
return -EINTR;
}
left_to_sleep = deadline - ((long) jiffies);
/*
* If our time is out, skip the waiting. We may miss wr_sleepy
* being deasserted but hey, almost missing the train is like
* missing it.
*/
if (left_to_sleep > 0) {
left_to_sleep =
wait_event_interruptible_timeout(
channel->wr_wait,
(!channel->wr_sleepy),
left_to_sleep);
if (left_to_sleep > 0) /* wr_sleepy deasserted */
continue;
if (left_to_sleep < 0) { /* Interrupt */
mutex_unlock(&channel->wr_mutex);
if (channel->endpoint->fatal_error)
return -EIO;
if (bytes_done)
return bytes_done;
return -EINTR;
}
}
desperate:
no_time_left = 1; /* We're out of sleeping time. Desperate! */
if (bytes_done == 0) {
/*
* Reaching here means that we allow partial return,
* that we've run out of time, and that we have
* nothing to return.
* So tell the FPGA to send anything it has or gets.
*/
iowrite32(1 | (channel->chan_num << 1) |
(3 << 24) | /* Opcode 3, flush it all! */
(waiting_bufidx << 12),
channel->endpoint->registers +
fpga_buf_ctrl_reg);
}
/*
* Reaching here means that we *do* have data in the buffer,
* but the "partial" flag disallows returning less than
* required. And we don't have as much. So loop again,
* which is likely to end up blocking indefinitely until
* enough data has arrived.
*/
}
mutex_unlock(&channel->wr_mutex);
if (channel->endpoint->fatal_error)
return -EIO;
if (rc)
return rc;
return bytes_done;
}
/*
* The timeout argument takes values as follows:
* >0 : Flush with timeout
* ==0 : Flush, and wait idefinitely for the flush to complete
* <0 : Autoflush: Flush only if there's a single buffer occupied
*/
static int xillybus_myflush(struct xilly_channel *channel, long timeout)
{
int rc;
unsigned long flags;
int end_offset_plus1;
int bufidx, bufidx_minus1;
int i;
int empty;
int new_rd_host_buf_pos;
if (channel->endpoint->fatal_error)
return -EIO;
rc = mutex_lock_interruptible(&channel->rd_mutex);
if (rc)
return rc;
/*
* Don't flush a closed channel. This can happen when the work queued
* autoflush thread fires off after the file has closed. This is not
* an error, just something to dismiss.
*/
if (!channel->rd_ref_count)
goto done;
bufidx = channel->rd_host_buf_idx;
bufidx_minus1 = (bufidx == 0) ?
channel->num_rd_buffers - 1 :
bufidx - 1;
end_offset_plus1 = channel->rd_host_buf_pos >>
channel->log2_element_size;
new_rd_host_buf_pos = channel->rd_host_buf_pos -
(end_offset_plus1 << channel->log2_element_size);
/* Submit the current buffer if it's nonempty */
if (end_offset_plus1) {
unsigned char *tail = channel->rd_buffers[bufidx]->addr +
(end_offset_plus1 << channel->log2_element_size);
/* Copy unflushed data, so we can put it in next buffer */
for (i = 0; i < new_rd_host_buf_pos; i++)
channel->rd_leftovers[i] = *tail++;
spin_lock_irqsave(&channel->rd_spinlock, flags);
/* Autoflush only if a single buffer is occupied */
if ((timeout < 0) &&
(channel->rd_full ||
(bufidx_minus1 != channel->rd_fpga_buf_idx))) {
spin_unlock_irqrestore(&channel->rd_spinlock, flags);
/*
* A new work item may be queued by the ISR exactly
* now, since the execution of a work item allows the
* queuing of a new one while it's running.
*/
goto done;
}
/* The 4th element is never needed for data, so it's a flag */
channel->rd_leftovers[3] = (new_rd_host_buf_pos != 0);
/* Set up rd_full to reflect a certain moment's state */
if (bufidx == channel->rd_fpga_buf_idx)
channel->rd_full = 1;
spin_unlock_irqrestore(&channel->rd_spinlock, flags);
if (bufidx >= (channel->num_rd_buffers - 1))
channel->rd_host_buf_idx = 0;
else
channel->rd_host_buf_idx++;
dma_sync_single_for_device(channel->endpoint->dev,
channel->rd_buffers[bufidx]->dma_addr,
channel->rd_buf_size,
DMA_TO_DEVICE);
mutex_lock(&channel->endpoint->register_mutex);
iowrite32(end_offset_plus1 - 1,
channel->endpoint->registers + fpga_buf_offset_reg);
iowrite32((channel->chan_num << 1) | /* Channel ID */
(2 << 24) | /* Opcode 2, submit buffer */
(bufidx << 12),
channel->endpoint->registers + fpga_buf_ctrl_reg);
mutex_unlock(&channel->endpoint->register_mutex);
} else if (bufidx == 0) {
bufidx = channel->num_rd_buffers - 1;
} else {
bufidx--;
}
channel->rd_host_buf_pos = new_rd_host_buf_pos;
if (timeout < 0)
goto done; /* Autoflush */
/*
* bufidx is now the last buffer written to (or equal to
* rd_fpga_buf_idx if buffer was never written to), and
* channel->rd_host_buf_idx the one after it.
*
* If bufidx == channel->rd_fpga_buf_idx we're either empty or full.
*/
while (1) { /* Loop waiting for draining of buffers */
spin_lock_irqsave(&channel->rd_spinlock, flags);
if (bufidx != channel->rd_fpga_buf_idx)
channel->rd_full = 1; /*
* Not really full,
* but needs waiting.
*/
empty = !channel->rd_full;
spin_unlock_irqrestore(&channel->rd_spinlock, flags);
if (empty)
break;
/*
* Indefinite sleep with mutex taken. With data waiting for
* flushing user should not be surprised if open() for write
* sleeps.
*/
if (timeout == 0)
wait_event_interruptible(channel->rd_wait,
(!channel->rd_full));
else if (wait_event_interruptible_timeout(
channel->rd_wait,
(!channel->rd_full),
timeout) == 0) {
dev_warn(channel->endpoint->dev,
"Timed out while flushing. Output data may be lost.\n");
rc = -ETIMEDOUT;
break;
}
if (channel->rd_full) {
rc = -EINTR;
break;
}
}
done:
mutex_unlock(&channel->rd_mutex);
if (channel->endpoint->fatal_error)
return -EIO;
return rc;
}
static int xillybus_flush(struct file *filp, fl_owner_t id)
{
if (!(filp->f_mode & FMODE_WRITE))
return 0;
return xillybus_myflush(filp->private_data, HZ); /* 1 second timeout */
}
static void xillybus_autoflush(struct work_struct *work)
{
struct delayed_work *workitem = container_of(
work, struct delayed_work, work);
struct xilly_channel *channel = container_of(
workitem, struct xilly_channel, rd_workitem);
int rc;
rc = xillybus_myflush(channel, -1);
if (rc == -EINTR)
dev_warn(channel->endpoint->dev,
"Autoflush failed because work queue thread got a signal.\n");
else if (rc)
dev_err(channel->endpoint->dev,
"Autoflush failed under weird circumstances.\n");
}
static ssize_t xillybus_write(struct file *filp, const char __user *userbuf,
size_t count, loff_t *f_pos)
{
ssize_t rc;
unsigned long flags;
int bytes_done = 0;
struct xilly_channel *channel = filp->private_data;
int full, exhausted;
/* Initializations are there only to silence warnings */
int howmany = 0, bufpos = 0, bufidx = 0, bufferdone = 0;
int end_offset_plus1 = 0;
if (channel->endpoint->fatal_error)
return -EIO;
rc = mutex_lock_interruptible(&channel->rd_mutex);
if (rc)
return rc;
while (1) {
int bytes_to_do = count - bytes_done;
spin_lock_irqsave(&channel->rd_spinlock, flags);
full = channel->rd_full;
if (!full) {
bufidx = channel->rd_host_buf_idx;
bufpos = channel->rd_host_buf_pos;
howmany = channel->rd_buf_size - bufpos;
/*
* Update rd_host_* to its state after this operation.
* count=0 means committing the buffer immediately,
* which is like flushing, but not necessarily block.
*/
if ((howmany > bytes_to_do) &&
(count ||
((bufpos >> channel->log2_element_size) == 0))) {
bufferdone = 0;
howmany = bytes_to_do;
channel->rd_host_buf_pos += howmany;
} else {
bufferdone = 1;
if (count) {
end_offset_plus1 =
channel->rd_buf_size >>
channel->log2_element_size;
channel->rd_host_buf_pos = 0;
} else {
unsigned char *tail;
int i;
howmany = 0;
end_offset_plus1 = bufpos >>
channel->log2_element_size;
channel->rd_host_buf_pos -=
end_offset_plus1 <<
channel->log2_element_size;
tail = channel->
rd_buffers[bufidx]->addr +
(end_offset_plus1 <<
channel->log2_element_size);
for (i = 0;
i < channel->rd_host_buf_pos;
i++)
channel->rd_leftovers[i] =
*tail++;
}
if (bufidx == channel->rd_fpga_buf_idx)
channel->rd_full = 1;
if (bufidx >= (channel->num_rd_buffers - 1))
channel->rd_host_buf_idx = 0;
else
channel->rd_host_buf_idx++;
}
}
/*
* Marking our situation after the possible changes above,
* for use after releasing the spinlock.
*
* full = full before change
* exhasted = full after possible change
*/
exhausted = channel->rd_full;
spin_unlock_irqrestore(&channel->rd_spinlock, flags);
if (!full) { /* Go on, now without the spinlock */
unsigned char *head =
channel->rd_buffers[bufidx]->addr;
int i;
if ((bufpos == 0) || /* Zero means it's virgin */
(channel->rd_leftovers[3] != 0)) {
dma_sync_single_for_cpu(channel->endpoint->dev,
channel->rd_buffers[bufidx]->dma_addr,
channel->rd_buf_size,
DMA_TO_DEVICE);
/* Virgin, but leftovers are due */
for (i = 0; i < bufpos; i++)
*head++ = channel->rd_leftovers[i];
channel->rd_leftovers[3] = 0; /* Clear flag */
}
if (copy_from_user(
channel->rd_buffers[bufidx]->addr + bufpos,
userbuf, howmany))
rc = -EFAULT;
userbuf += howmany;
bytes_done += howmany;
if (bufferdone) {
dma_sync_single_for_device(channel->endpoint->dev,
channel->rd_buffers[bufidx]->dma_addr,
channel->rd_buf_size,
DMA_TO_DEVICE);
mutex_lock(&channel->endpoint->register_mutex);
iowrite32(end_offset_plus1 - 1,
channel->endpoint->registers +
fpga_buf_offset_reg);
iowrite32((channel->chan_num << 1) |
(2 << 24) | /* 2 = submit buffer */
(bufidx << 12),
channel->endpoint->registers +
fpga_buf_ctrl_reg);
mutex_unlock(&channel->endpoint->
register_mutex);
channel->rd_leftovers[3] =
(channel->rd_host_buf_pos != 0);
}
if (rc) {
mutex_unlock(&channel->rd_mutex);
if (channel->endpoint->fatal_error)
return -EIO;
if (!channel->rd_synchronous)
queue_delayed_work(
xillybus_wq,
&channel->rd_workitem,
XILLY_RX_TIMEOUT);
return rc;
}
}
if (bytes_done >= count)
break;
if (!exhausted)
continue; /* If there's more space, just go on */
if ((bytes_done > 0) && channel->rd_allow_partial)
break;
/*
* Indefinite sleep with mutex taken. With data waiting for
* flushing, user should not be surprised if open() for write
* sleeps.
*/
if (filp->f_flags & O_NONBLOCK) {
rc = -EAGAIN;
break;
}
if (wait_event_interruptible(channel->rd_wait,
(!channel->rd_full))) {
mutex_unlock(&channel->rd_mutex);
if (channel->endpoint->fatal_error)
return -EIO;
if (bytes_done)
return bytes_done;
return -EINTR;
}
}
mutex_unlock(&channel->rd_mutex);
if (!channel->rd_synchronous)
queue_delayed_work(xillybus_wq,
&channel->rd_workitem,
XILLY_RX_TIMEOUT);
if (channel->endpoint->fatal_error)
return -EIO;
if (rc)
return rc;
if ((channel->rd_synchronous) && (bytes_done > 0)) {
rc = xillybus_myflush(filp->private_data, 0); /* No timeout */
if (rc && (rc != -EINTR))
return rc;
}
return bytes_done;
}
static int xillybus_open(struct inode *inode, struct file *filp)
{
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
int rc;
unsigned long flags;
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
struct xilly_endpoint *endpoint;
struct xilly_channel *channel;
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
int index;
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
rc = xillybus_find_inode(inode, (void **)&endpoint, &index);
if (rc)
return rc;
if (endpoint->fatal_error)
return -EIO;
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
channel = endpoint->channels[1 + index];
filp->private_data = channel;
/*
* It gets complicated because:
* 1. We don't want to take a mutex we don't have to
* 2. We don't want to open one direction if the other will fail.
*/
if ((filp->f_mode & FMODE_READ) && (!channel->num_wr_buffers))
return -ENODEV;
if ((filp->f_mode & FMODE_WRITE) && (!channel->num_rd_buffers))
return -ENODEV;
if ((filp->f_mode & FMODE_READ) && (filp->f_flags & O_NONBLOCK) &&
(channel->wr_synchronous || !channel->wr_allow_partial ||
!channel->wr_supports_nonempty)) {
dev_err(endpoint->dev,
"open() failed: O_NONBLOCK not allowed for read on this device\n");
return -ENODEV;
}
if ((filp->f_mode & FMODE_WRITE) && (filp->f_flags & O_NONBLOCK) &&
(channel->rd_synchronous || !channel->rd_allow_partial)) {
dev_err(endpoint->dev,
"open() failed: O_NONBLOCK not allowed for write on this device\n");
return -ENODEV;
}
/*
* Note: open() may block on getting mutexes despite O_NONBLOCK.
* This shouldn't occur normally, since multiple open of the same
* file descriptor is almost always prohibited anyhow
* (*_exclusive_open is normally set in real-life systems).
*/
if (filp->f_mode & FMODE_READ) {
rc = mutex_lock_interruptible(&channel->wr_mutex);
if (rc)
return rc;
}
if (filp->f_mode & FMODE_WRITE) {
rc = mutex_lock_interruptible(&channel->rd_mutex);
if (rc)
goto unlock_wr;
}
if ((filp->f_mode & FMODE_READ) &&
(channel->wr_ref_count != 0) &&
(channel->wr_exclusive_open)) {
rc = -EBUSY;
goto unlock;
}
if ((filp->f_mode & FMODE_WRITE) &&
(channel->rd_ref_count != 0) &&
(channel->rd_exclusive_open)) {
rc = -EBUSY;
goto unlock;
}
if (filp->f_mode & FMODE_READ) {
if (channel->wr_ref_count == 0) { /* First open of file */
/* Move the host to first buffer */
spin_lock_irqsave(&channel->wr_spinlock, flags);
channel->wr_host_buf_idx = 0;
channel->wr_host_buf_pos = 0;
channel->wr_fpga_buf_idx = -1;
channel->wr_empty = 1;
channel->wr_ready = 0;
channel->wr_sleepy = 1;
channel->wr_eof = -1;
channel->wr_hangup = 0;
spin_unlock_irqrestore(&channel->wr_spinlock, flags);
iowrite32(1 | (channel->chan_num << 1) |
(4 << 24) | /* Opcode 4, open channel */
((channel->wr_synchronous & 1) << 23),
channel->endpoint->registers +
fpga_buf_ctrl_reg);
}
channel->wr_ref_count++;
}
if (filp->f_mode & FMODE_WRITE) {
if (channel->rd_ref_count == 0) { /* First open of file */
/* Move the host to first buffer */
spin_lock_irqsave(&channel->rd_spinlock, flags);
channel->rd_host_buf_idx = 0;
channel->rd_host_buf_pos = 0;
channel->rd_leftovers[3] = 0; /* No leftovers. */
channel->rd_fpga_buf_idx = channel->num_rd_buffers - 1;
channel->rd_full = 0;
spin_unlock_irqrestore(&channel->rd_spinlock, flags);
iowrite32((channel->chan_num << 1) |
(4 << 24), /* Opcode 4, open channel */
channel->endpoint->registers +
fpga_buf_ctrl_reg);
}
channel->rd_ref_count++;
}
unlock:
if (filp->f_mode & FMODE_WRITE)
mutex_unlock(&channel->rd_mutex);
unlock_wr:
if (filp->f_mode & FMODE_READ)
mutex_unlock(&channel->wr_mutex);
if (!rc && (!channel->seekable))
return nonseekable_open(inode, filp);
return rc;
}
static int xillybus_release(struct inode *inode, struct file *filp)
{
unsigned long flags;
struct xilly_channel *channel = filp->private_data;
int buf_idx;
int eof;
if (channel->endpoint->fatal_error)
return -EIO;
if (filp->f_mode & FMODE_WRITE) {
mutex_lock(&channel->rd_mutex);
channel->rd_ref_count--;
if (channel->rd_ref_count == 0) {
/*
* We rely on the kernel calling flush()
* before we get here.
*/
iowrite32((channel->chan_num << 1) | /* Channel ID */
(5 << 24), /* Opcode 5, close channel */
channel->endpoint->registers +
fpga_buf_ctrl_reg);
}
mutex_unlock(&channel->rd_mutex);
}
if (filp->f_mode & FMODE_READ) {
mutex_lock(&channel->wr_mutex);
channel->wr_ref_count--;
if (channel->wr_ref_count == 0) {
iowrite32(1 | (channel->chan_num << 1) |
(5 << 24), /* Opcode 5, close channel */
channel->endpoint->registers +
fpga_buf_ctrl_reg);
/*
* This is crazily cautious: We make sure that not
* only that we got an EOF (be it because we closed
* the channel or because of a user's EOF), but verify
* that it's one beyond the last buffer arrived, so
* we have no leftover buffers pending before wrapping
* up (which can only happen in asynchronous channels,
* BTW)
*/
while (1) {
spin_lock_irqsave(&channel->wr_spinlock,
flags);
buf_idx = channel->wr_fpga_buf_idx;
eof = channel->wr_eof;
channel->wr_sleepy = 1;
spin_unlock_irqrestore(&channel->wr_spinlock,
flags);
/*
* Check if eof points at the buffer after
* the last one the FPGA submitted. Note that
* no EOF is marked by negative eof.
*/
buf_idx++;
if (buf_idx == channel->num_wr_buffers)
buf_idx = 0;
if (buf_idx == eof)
break;
/*
* Steal extra 100 ms if awaken by interrupt.
* This is a simple workaround for an
* interrupt pending when entering, which would
* otherwise result in declaring the hardware
* non-responsive.
*/
if (wait_event_interruptible(
channel->wr_wait,
(!channel->wr_sleepy)))
msleep(100);
if (channel->wr_sleepy) {
mutex_unlock(&channel->wr_mutex);
dev_warn(channel->endpoint->dev,
"Hardware failed to respond to close command, therefore left in messy state.\n");
return -EINTR;
}
}
}
mutex_unlock(&channel->wr_mutex);
}
return 0;
}
static loff_t xillybus_llseek(struct file *filp, loff_t offset, int whence)
{
struct xilly_channel *channel = filp->private_data;
loff_t pos = filp->f_pos;
int rc = 0;
/*
* Take both mutexes not allowing interrupts, since it seems like
* common applications don't expect an -EINTR here. Besides, multiple
* access to a single file descriptor on seekable devices is a mess
* anyhow.
*/
if (channel->endpoint->fatal_error)
return -EIO;
mutex_lock(&channel->wr_mutex);
mutex_lock(&channel->rd_mutex);
switch (whence) {
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos += offset;
break;
case SEEK_END:
pos = offset; /* Going to the end => to the beginning */
break;
default:
rc = -EINVAL;
goto end;
}
/* In any case, we must finish on an element boundary */
if (pos & ((1 << channel->log2_element_size) - 1)) {
rc = -EINVAL;
goto end;
}
mutex_lock(&channel->endpoint->register_mutex);
iowrite32(pos >> channel->log2_element_size,
channel->endpoint->registers + fpga_buf_offset_reg);
iowrite32((channel->chan_num << 1) |
(6 << 24), /* Opcode 6, set address */
channel->endpoint->registers + fpga_buf_ctrl_reg);
mutex_unlock(&channel->endpoint->register_mutex);
end:
mutex_unlock(&channel->rd_mutex);
mutex_unlock(&channel->wr_mutex);
if (rc) /* Return error after releasing mutexes */
return rc;
filp->f_pos = pos;
/*
* Since seekable devices are allowed only when the channel is
* synchronous, we assume that there is no data pending in either
* direction (which holds true as long as no concurrent access on the
* file descriptor takes place).
* The only thing we may need to throw away is leftovers from partial
* write() flush.
*/
channel->rd_leftovers[3] = 0;
return pos;
}
static __poll_t xillybus_poll(struct file *filp, poll_table *wait)
{
struct xilly_channel *channel = filp->private_data;
__poll_t mask = 0;
unsigned long flags;
poll_wait(filp, &channel->endpoint->ep_wait, wait);
/*
* poll() won't play ball regarding read() channels which
* aren't asynchronous and support the nonempty message. Allowing
* that will create situations where data has been delivered at
* the FPGA, and users expecting select() to wake up, which it may
* not.
*/
if (!channel->wr_synchronous && channel->wr_supports_nonempty) {
poll_wait(filp, &channel->wr_wait, wait);
poll_wait(filp, &channel->wr_ready_wait, wait);
spin_lock_irqsave(&channel->wr_spinlock, flags);
if (!channel->wr_empty || channel->wr_ready)
mask |= EPOLLIN | EPOLLRDNORM;
if (channel->wr_hangup)
/*
* Not EPOLLHUP, because its behavior is in the
* mist, and EPOLLIN does what we want: Wake up
* the read file descriptor so it sees EOF.
*/
mask |= EPOLLIN | EPOLLRDNORM;
spin_unlock_irqrestore(&channel->wr_spinlock, flags);
}
/*
* If partial data write is disallowed on a write() channel,
* it's pointless to ever signal OK to write, because is could
* block despite some space being available.
*/
if (channel->rd_allow_partial) {
poll_wait(filp, &channel->rd_wait, wait);
spin_lock_irqsave(&channel->rd_spinlock, flags);
if (!channel->rd_full)
mask |= EPOLLOUT | EPOLLWRNORM;
spin_unlock_irqrestore(&channel->rd_spinlock, flags);
}
if (channel->endpoint->fatal_error)
mask |= EPOLLERR;
return mask;
}
static const struct file_operations xillybus_fops = {
.owner = THIS_MODULE,
.read = xillybus_read,
.write = xillybus_write,
.open = xillybus_open,
.flush = xillybus_flush,
.release = xillybus_release,
.llseek = xillybus_llseek,
.poll = xillybus_poll,
};
struct xilly_endpoint *xillybus_init_endpoint(struct device *dev)
{
struct xilly_endpoint *endpoint;
endpoint = devm_kzalloc(dev, sizeof(*endpoint), GFP_KERNEL);
if (!endpoint)
return NULL;
endpoint->dev = dev;
endpoint->msg_counter = 0x0b;
endpoint->failed_messages = 0;
endpoint->fatal_error = 0;
init_waitqueue_head(&endpoint->ep_wait);
mutex_init(&endpoint->register_mutex);
return endpoint;
}
EXPORT_SYMBOL(xillybus_init_endpoint);
static int xilly_quiesce(struct xilly_endpoint *endpoint)
{
long t;
endpoint->idtlen = -1;
iowrite32((u32) (endpoint->dma_using_dac & 0x0001),
endpoint->registers + fpga_dma_control_reg);
t = wait_event_interruptible_timeout(endpoint->ep_wait,
(endpoint->idtlen >= 0),
XILLY_TIMEOUT);
if (t <= 0) {
dev_err(endpoint->dev,
"Failed to quiesce the device on exit.\n");
return -ENODEV;
}
return 0;
}
int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
{
int rc;
long t;
void *bootstrap_resources;
int idtbuffersize = (1 << PAGE_SHIFT);
struct device *dev = endpoint->dev;
/*
* The bogus IDT is used during bootstrap for allocating the initial
* message buffer, and then the message buffer and space for the IDT
* itself. The initial message buffer is of a single page's size, but
* it's soon replaced with a more modest one (and memory is freed).
*/
unsigned char bogus_idt[8] = { 1, 224, (PAGE_SHIFT)-2, 0,
3, 192, PAGE_SHIFT, 0 };
struct xilly_idt_handle idt_handle;
/*
* Writing the value 0x00000001 to Endianness register signals which
* endianness this processor is using, so the FPGA can swap words as
* necessary.
*/
iowrite32(1, endpoint->registers + fpga_endian_reg);
/* Bootstrap phase I: Allocate temporary message buffer */
bootstrap_resources = devres_open_group(dev, NULL, GFP_KERNEL);
if (!bootstrap_resources)
return -ENOMEM;
endpoint->num_channels = 0;
rc = xilly_setupchannels(endpoint, bogus_idt, 1);
if (rc)
return rc;
/* Clear the message subsystem (and counter in particular) */
iowrite32(0x04, endpoint->registers + fpga_msg_ctrl_reg);
endpoint->idtlen = -1;
/*
* Set DMA 32/64 bit mode, quiesce the device (?!) and get IDT
* buffer size.
*/
iowrite32((u32) (endpoint->dma_using_dac & 0x0001),
endpoint->registers + fpga_dma_control_reg);
t = wait_event_interruptible_timeout(endpoint->ep_wait,
(endpoint->idtlen >= 0),
XILLY_TIMEOUT);
if (t <= 0) {
dev_err(endpoint->dev, "No response from FPGA. Aborting.\n");
return -ENODEV;
}
/* Enable DMA */
iowrite32((u32) (0x0002 | (endpoint->dma_using_dac & 0x0001)),
endpoint->registers + fpga_dma_control_reg);
/* Bootstrap phase II: Allocate buffer for IDT and obtain it */
while (endpoint->idtlen >= idtbuffersize) {
idtbuffersize *= 2;
bogus_idt[6]++;
}
endpoint->num_channels = 1;
rc = xilly_setupchannels(endpoint, bogus_idt, 2);
if (rc)
goto failed_idt;
rc = xilly_obtain_idt(endpoint);
if (rc)
goto failed_idt;
rc = xilly_scan_idt(endpoint, &idt_handle);
if (rc)
goto failed_idt;
devres_close_group(dev, bootstrap_resources);
/* Bootstrap phase III: Allocate buffers according to IDT */
rc = xilly_setupchannels(endpoint,
idt_handle.chandesc,
idt_handle.entries);
if (rc)
goto failed_idt;
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
rc = xillybus_init_chrdev(dev, &xillybus_fops,
endpoint->owner, endpoint,
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
idt_handle.names,
idt_handle.names_len,
endpoint->num_channels,
xillyname, false);
if (rc)
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
goto failed_idt;
devres_release_group(dev, bootstrap_resources);
return 0;
failed_idt:
xilly_quiesce(endpoint);
flush_workqueue(xillybus_wq);
return rc;
}
EXPORT_SYMBOL(xillybus_endpoint_discovery);
void xillybus_endpoint_remove(struct xilly_endpoint *endpoint)
{
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
xillybus_cleanup_chrdev(endpoint, endpoint->dev);
xilly_quiesce(endpoint);
/*
* Flushing is done upon endpoint release to prevent access to memory
* just about to be released. This makes the quiesce complete.
*/
flush_workqueue(xillybus_wq);
}
EXPORT_SYMBOL(xillybus_endpoint_remove);
static int __init xillybus_init(void)
{
xillybus_wq = alloc_workqueue(xillyname, 0, 0);
char: xillybus: Move class-related functions to new xillybus_class.c This patch is a preparation for adding another related driver, XillyUSB. In order to share some code between the existing Xillybus driver and the one to be added, some functions are moved to xillybus_class.c XILLYBUS_CLASS is added to Kconfig and is common to all drivers in this group. The relation with the existing XILLYBUS symbol is "select" rather than "depends on" XILLYBUS_CLASS, or else "make olddefconfig" will silently turn off XILLYBUS, which is currently enabled in several distributions. XILLYBUS_CLASS doesn't depend on anything else, hence using it with "select" poses no risk for a broken configuration. After the future addition of the XillyUSB module, the tree of symbols will be as follows: XILLYBUS_CLASS --+-- XILLYBUS --+-- XILLYBUS_PCIE | | | +-- XILLYBUS_OF | +-- XILLYUSB XILLYBUS is for drivers based upon memory registers + DMA-based interfaces, and it's combined with XILLYBUS_PCIE and/or XILLYBUS_OF. XILLYUSB is for the USB variant only. Or a more detailed, bottom-up outline: * CONFIG_XILLYBUS_PCIE -> xillybus_pcie.c: Functions related to PCIe. * CONFIG_XILLYBUS_OF -> xillybus_of.c: Functions related to Xillybus as a peripheral on an FPGA / Processor combo chip. * CONFIG_XILLYBUS -> xillybus_core.c: Functions that are common to the two above, mainly access to the peripheral with memory-mapped registers and DMA. * CONFIG_XILLYUSB -> xillyusb.c: The driver for the USB variant, accesses the peripheral through the USB framework. * CONFIG_XILLYBUS_CLASS -> xillybus_class.c: The new module, which contains the class and API parts that would otherwise appear both in xillybus_core.c and xillyusb.c. Contains utility functions for the two latter. And since I'm at it, comments on the module names are added in the Kconfig's help part. The functions are exported with the non-GPL EXPORT_SYMBOL (a matter of taste). Signed-off-by: Eli Billauer <eli.billauer@gmail.com> Link: https://lore.kernel.org/r/20210526100311.56327-2-eli.billauer@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-05-26 13:03:10 +03:00
if (!xillybus_wq)
return -ENOMEM;
return 0;
}
static void __exit xillybus_exit(void)
{
/* flush_workqueue() was called for each endpoint released */
destroy_workqueue(xillybus_wq);
}
module_init(xillybus_init);
module_exit(xillybus_exit);