diff options
Diffstat (limited to 'drivers/staging')
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/xillybus/Kconfig | 33 | ||||
-rw-r--r-- | drivers/staging/xillybus/Makefile | 7 | ||||
-rw-r--r-- | drivers/staging/xillybus/README | 380 | ||||
-rw-r--r-- | drivers/staging/xillybus/TODO | 5 | ||||
-rw-r--r-- | drivers/staging/xillybus/xillybus.h | 160 | ||||
-rw-r--r-- | drivers/staging/xillybus/xillybus_core.c | 2103 | ||||
-rw-r--r-- | drivers/staging/xillybus/xillybus_of.c | 187 | ||||
-rw-r--r-- | drivers/staging/xillybus/xillybus_pcie.c | 228 |
10 files changed, 0 insertions, 3106 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6e2d6fd85b89..e3c1a1fa7b4d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -104,8 +104,6 @@ source "drivers/staging/mt29f_spinand/Kconfig" source "drivers/staging/lustre/Kconfig" -source "drivers/staging/xillybus/Kconfig" - source "drivers/staging/dgnc/Kconfig" source "drivers/staging/dgap/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 74c679e17e77..8e8333f6dd76 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -43,7 +43,6 @@ obj-$(CONFIG_DRM_IMX) += imx-drm/ obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/ obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_LUSTRE_FS) += lustre/ -obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_DGNC) += dgnc/ obj-$(CONFIG_DGAP) += dgap/ obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/ diff --git a/drivers/staging/xillybus/Kconfig b/drivers/staging/xillybus/Kconfig deleted file mode 100644 index b53bdf12da0d..000000000000 --- a/drivers/staging/xillybus/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -# -# Xillybus devices -# - -config XILLYBUS - tristate "Xillybus generic FPGA interface" - depends on PCI || (OF_ADDRESS && OF_IRQ) - select CRC32 - help - Xillybus is a generic interface for peripherals designed on - programmable logic (FPGA). The driver probes the hardware for - its capabilities, and creates device files accordingly. - - If unsure, say N. - -if XILLYBUS - -config XILLYBUS_PCIE - tristate "Xillybus over PCIe" - depends on PCI_MSI - help - Set to M if you want Xillybus to use PCI Express for communicating - with the FPGA. - -config XILLYBUS_OF - tristate "Xillybus over Device Tree" - depends on OF_ADDRESS && OF_IRQ - help - Set to M if you want Xillybus to find its resources from the - Open Firmware Flattened Device Tree. If the target is an embedded - system, say M. - -endif # if XILLYBUS diff --git a/drivers/staging/xillybus/Makefile b/drivers/staging/xillybus/Makefile deleted file mode 100644 index b68b7ebfd381..000000000000 --- a/drivers/staging/xillybus/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for Xillybus driver -# - -obj-$(CONFIG_XILLYBUS) += xillybus_core.o -obj-$(CONFIG_XILLYBUS_PCIE) += xillybus_pcie.o -obj-$(CONFIG_XILLYBUS_OF) += xillybus_of.o diff --git a/drivers/staging/xillybus/README b/drivers/staging/xillybus/README deleted file mode 100644 index 81d111b4dc28..000000000000 --- a/drivers/staging/xillybus/README +++ /dev/null @@ -1,380 +0,0 @@ - - ========================================== - Xillybus driver for generic FPGA interface - ========================================== - -Author: Eli Billauer, Xillybus Ltd. (http://xillybus.com) -Email: eli.billauer@gmail.com or as advertised on Xillybus' site. - -Contents: - - - Introduction - -- Background - -- Xillybus Overview - - - Usage - -- User interface - -- Synchronization - -- Seekable pipes - -- Internals - -- Source code organization - -- Pipe attributes - -- Host never reads from the FPGA - -- Channels, pipes, and the message channel - -- Data streaming - -- Data granularity - -- Probing - -- Buffer allocation - -- The "nonempty" message (supporting poll) - - -INTRODUCTION -============ - -Background ----------- - -An FPGA (Field Programmable Gate Array) is a piece of logic hardware, which -can be programmed to become virtually anything that is usually found as a -dedicated chipset: For instance, a display adapter, network interface card, -or even a processor with its peripherals. FPGAs are the LEGO of hardware: -Based upon certain building blocks, you make your own toys the way you like -them. It's usually pointless to reimplement something that is already -available on the market as a chipset, so FPGAs are mostly used when some -special functionality is needed, and the production volume is relatively low -(hence not justifying the development of an ASIC). - -The challenge with FPGAs is that everything is implemented at a very low -level, even lower than assembly language. In order to allow FPGA designers to -focus on their specific project, and not reinvent the wheel over and over -again, pre-designed building blocks, IP cores, are often used. These are the -FPGA parallels of library functions. IP cores may implement certain -mathematical functions, a functional unit (e.g. a USB interface), an entire -processor (e.g. ARM) or anything that might come handy. Think of them as a -building block, with electrical wires dangling on the sides for connection to -other blocks. - -One of the daunting tasks in FPGA design is communicating with a fullblown -operating system (actually, with the processor running it): Implementing the -low-level bus protocol and the somewhat higher-level interface with the host -(registers, interrupts, DMA etc.) is a project in itself. When the FPGA's -function is a well-known one (e.g. a video adapter card, or a NIC), it can -make sense to design the FPGA's interface logic specifically for the project. -A special driver is then written to present the FPGA as a well-known interface -to the kernel and/or user space. In that case, there is no reason to treat the -FPGA differently than any device on the bus. - -It's however common that the desired data communication doesn't fit any well- -known peripheral function. Also, the effort of designing an elegant -abstraction for the data exchange is often considered too big. In those cases, -a quicker and possibly less elegant solution is sought: The driver is -effectively written as a user space program, leaving the kernel space part -with just elementary data transport. This still requires designing some -interface logic for the FPGA, and write a simple ad-hoc driver for the kernel. - -Xillybus Overview ------------------ - -Xillybus is an IP core and a Linux driver. Together, they form a kit for -elementary data transport between an FPGA and the host, providing pipe-like -data streams with a straightforward user interface. It's intended as a low- -effort solution for mixed FPGA-host projects, for which it makes sense to -have the project-specific part of the driver running in a user-space program. - -Since the communication requirements may vary significantly from one FPGA -project to another (the number of data pipes needed in each direction and -their attributes), there isn't one specific chunk of logic being the Xillybus -IP core. Rather, the IP core is configured and built based upon a -specification given by its end user. - -Xillybus presents independent data streams, which resemble pipes or TCP/IP -communication to the user. At the host side, a character device file is used -just like any pipe file. On the FPGA side, hardware FIFOs are used to stream -the data. This is contrary to a common method of communicating through fixed- -sized buffers (even though such buffers are used by Xillybus under the hood). -There may be more than a hundred of these streams on a single IP core, but -also no more than one, depending on the configuration. - -In order to ease the deployment of the Xillybus IP core, it contains a simple -data structure which completely defines the core's configuration. The Linux -driver fetches this data structure during its initialization process, and sets -up the DMA buffers and character devices accordingly. As a result, a single -driver is used to work out of the box with any Xillybus IP core. - -The data structure just mentioned should not be confused with PCI's -configuration space or the Flattened Device Tree. - -USAGE -===== - -User interface --------------- - -On the host, all interface with Xillybus is done through /dev/xillybus_* -device files, which are generated automatically as the drivers loads. The -names of these files depend on the IP core that is loaded in the FPGA (see -Probing below). To communicate with the FPGA, open the device file that -corresponds to the hardware FIFO you want to send data or receive data from, -and use plain write() or read() calls, just like with a regular pipe. In -particular, it makes perfect sense to go: - -$ cat mydata > /dev/xillybus_thisfifo - -$ cat /dev/xillybus_thatfifo > hisdata - -possibly pressing CTRL-C as some stage, even though the xillybus_* pipes have -the capability to send an EOF (but may not use it). - -The driver and hardware are designed to behave sensibly as pipes, including: - -* Supporting non-blocking I/O (by setting O_NONBLOCK on open() ). - -* Supporting poll() and select(). - -* Being bandwidth efficient under load (using DMA) but also handle small - pieces of data sent across (like TCP/IP) by autoflushing. - -A device file can be read only, write only or bidirectional. Bidirectional -device files are treated like two independent pipes (except for sharing a -"channel" structure in the implementation code). - -Synchronization ---------------- - -Xillybus pipes are configured (on the IP core) to be either synchronous or -asynchronous. For a synchronous pipe, write() returns successfully only after -some data has been submitted and acknowledged by the FPGA. This slows down -bulk data transfers, and is nearly impossible for use with streams that -require data at a constant rate: There is no data transmitted to the FPGA -between write() calls, in particular when the process loses the CPU. - -When a pipe is configured asynchronous, write() returns if there was enough -room in the buffers to store any of the data in the buffers. - -For FPGA to host pipes, asynchronous pipes allow data transfer from the FPGA -as soon as the respective device file is opened, regardless of if the data -has been requested by a read() call. On synchronous pipes, only the amount -of data requested by a read() call is transmitted. - -In summary, for synchronous pipes, data between the host and FPGA is -transmitted only to satisfy the read() or write() call currently handled -by the driver, and those calls wait for the transmission to complete before -returning. - -Note that the synchronization attribute has nothing to do with the possibility -that read() or write() completes less bytes than requested. There is a -separate configuration flag ("allowpartial") that determines whether such a -partial completion is allowed. - -Seekable pipes --------------- - -A synchronous pipe can be configured to have the stream's position exposed -to the user logic at the FPGA. Such a pipe is also seekable on the host API. -With this feature, a memory or register interface can be attached on the -FPGA side to the seekable stream. Reading or writing to a certain address in -the attached memory is done by seeking to the desired address, and calling -read() or write() as required. - - -INTERNALS -========= - -Source code organization ------------------------- - -The Xillybus driver consists of a core module, xillybus_core.c, and modules -that depend on the specific bus interface (xillybus_of.c and xillybus_pcie.c). - -The bus specific modules are those probed when a suitable device is found by -the kernel. Since the DMA mapping and synchronization functions, which are bus -dependent by their nature, are used by the core module, a -xilly_endpoint_hardware structure is passed to the core module on -initialization. This structure is populated with pointers to wrapper functions -which execute the DMA-related operations on the bus. - -Pipe attributes ---------------- - -Each pipe has a number of attributes which are set when the FPGA component -(IP core) is built. They are fetched from the IDT (the data structure which -defines the core's configuration, see Probing below) by xilly_setupchannels() -in xillybus_core.c as follows: - -* is_writebuf: The pipe's direction. A non-zero value means it's an FPGA to - host pipe (the FPGA "writes"). - -* channelnum: The pipe's identification number in communication between the - host and FPGA. - -* format: The underlying data width. See Data Granularity below. - -* allowpartial: A non-zero value means that a read() or write() (whichever - applies) may return with less than the requested number of bytes. The common - choice is a non-zero value, to match standard UNIX behavior. - -* synchronous: A non-zero value means that the pipe is synchronous. See - Syncronization above. - -* bufsize: Each DMA buffer's size. Always a power of two. - -* bufnum: The number of buffers allocated for this pipe. Always a power of two. - -* exclusive_open: A non-zero value forces exclusive opening of the associated - device file. If the device file is bidirectional, and already opened only in - one direction, the opposite direction may be opened once. - -* seekable: A non-zero value indicates that the pipe is seekable. See - Seekable pipes above. - -* supports_nonempty: A non-zero value (which is typical) indicates that the - hardware will send the messages that are necessary to support select() and - poll() for this pipe. - -Host never reads from the FPGA ------------------------------- - -Even though PCI Express is hotpluggable in general, a typical motherboard -doesn't expect a card to go away all of the sudden. But since the PCIe card -is based upon reprogrammable logic, a sudden disappearance from the bus is -quite likely as a result of an accidental reprogramming of the FPGA while the -host is up. In practice, nothing happens immediately in such a situation. But -if the host attempts to read from an address that is mapped to the PCI Express -device, that leads to an immediate freeze of the system on some motherboards, -even though the PCIe standard requires a graceful recovery. - -In order to avoid these freezes, the Xillybus driver refrains completely from -reading from the device's register space. All communication from the FPGA to -the host is done through DMA. In particular, the Interrupt Service Routine -doesn't follow the common practice of checking a status register when it's -invoked. Rather, the FPGA prepares a small buffer which contains short -messages, which inform the host what the interrupt was about. - -This mechanism is used on non-PCIe buses as well for the sake of uniformity. - - -Channels, pipes, and the message channel ----------------------------------------- - -Each of the (possibly bidirectional) pipes presented to the user is allocated -a data channel between the FPGA and the host. The distinction between channels -and pipes is necessary only because of channel 0, which is used for interrupt- -related messages from the FPGA, and has no pipe attached to it. - -Data streaming --------------- - -Even though a non-segmented data stream is presented to the user at both -sides, the implementation relies on a set of DMA buffers which is allocated -for each channel. For the sake of illustration, let's take the FPGA to host -direction: As data streams into the respective channel's interface in the -FPGA, the Xillybus IP core writes it to one of the DMA buffers. When the -buffer is full, the FPGA informs the host about that (appending a -XILLYMSG_OPCODE_RELEASEBUF message channel 0 and sending an interrupt if -necessary). The host responds by making the data available for reading through -the character device. When all data has been read, the host writes on the -the FPGA's buffer control register, allowing the buffer's overwriting. Flow -control mechanisms exist on both sides to prevent underflows and overflows. - -This is not good enough for creating a TCP/IP-like stream: If the data flow -stops momentarily before a DMA buffer is filled, the intuitive expectation is -that the partial data in buffer will arrive anyhow, despite the buffer not -being completed. This is implemented by adding a field in the -XILLYMSG_OPCODE_RELEASEBUF message, through which the FPGA informs not just -which buffer is submitted, but how much data it contains. - -But the FPGA will submit a partially filled buffer only if directed to do so -by the host. This situation occurs when the read() method has been blocking -for XILLY_RX_TIMEOUT jiffies (currently 10 ms), after which the host commands -the FPGA to submit a DMA buffer as soon as it can. This timeout mechanism -balances between bus bandwidth efficiency (preventing a lot of partially -filled buffers being sent) and a latency held fairly low for tails of data. - -A similar setting is used in the host to FPGA direction. The handling of -partial DMA buffers is somewhat different, though. The user can tell the -driver to submit all data it has in the buffers to the FPGA, by issuing a -write() with the byte count set to zero. This is similar to a flush request, -but it doesn't block. There is also an autoflushing mechanism, which triggers -an equivalent flush roughly XILLY_RX_TIMEOUT jiffies after the last write(). -This allows the user to be oblivious about the underlying buffering mechanism -and yet enjoy a stream-like interface. - -Note that the issue of partial buffer flushing is irrelevant for pipes having -the "synchronous" attribute nonzero, since synchronous pipes don't allow data -to lay around in the DMA buffers between read() and write() anyhow. - -Data granularity ----------------- - -The data arrives or is sent at the FPGA as 8, 16 or 32 bit wide words, as -configured by the "format" attribute. Whenever possible, the driver attempts -to hide this when the pipe is accessed differently from its natural alignment. -For example, reading single bytes from a pipe with 32 bit granularity works -with no issues. Writing single bytes to pipes with 16 or 32 bit granularity -will also work, but the driver can't send partially completed words to the -FPGA, so the transmission of up to one word may be held until it's fully -occupied with user data. - -This somewhat complicates the handling of host to FPGA streams, because -when a buffer is flushed, it may contain up to 3 bytes don't form a word in -the FPGA, and hence can't be sent. To prevent loss of data, these leftover -bytes need to be moved to the next buffer. The parts in xillybus_core.c -that mention "leftovers" in some way are related to this complication. - -Probing -------- - -As mentioned earlier, the number of pipes that are created when the driver -loads and their attributes depend on the Xillybus IP core in the FPGA. During -the driver's initialization, a blob containing configuration info, the -Interface Description Table (IDT), is sent from the FPGA to the host. The -bootstrap process is done in three phases: - -1. Acquire the length of the IDT, so a buffer can be allocated for it. This - is done by sending a quiesce command to the device, since the acknowledge - for this command contains the IDT's buffer length. - -2. Acquire the IDT itself. - -3. Create the interfaces according to the IDT. - -Buffer allocation ------------------ - -In order to simplify the logic that prevents illegal boundary crossings of -PCIe packets, the following rule applies: If a buffer is smaller than 4kB, -it must not cross a 4kB boundary. Otherwise, it must be 4kB aligned. The -xilly_setupchannels() functions allocates these buffers by requesting whole -pages from the kernel, and diving them into DMA buffers as necessary. Since -all buffers' sizes are powers of two, it's possible to pack any set of such -buffers, with a maximal waste of one page of memory. - -All buffers are allocated when the driver is loaded. This is necessary, -since large continuous physical memory segments are sometimes requested, -which are more likely to be available when the system is freshly booted. - -The allocation of buffer memory takes place in the same order they appear in -the IDT. The driver relies on a rule that the pipes are sorted with decreasing -buffer size in the IDT. If a requested buffer is larger or equal to a page, -the necessary number of pages is requested from the kernel, and these are -used for this buffer. If the requested buffer is smaller than a page, one -single page is requested from the kernel, and that page is partially used. -Or, if there already is a partially used page at hand, the buffer is packed -into that page. It can be shown that all pages requested from the kernel -(except possibly for the last) are 100% utilized this way. - -The "nonempty" message (supporting poll) ---------------------------------------- - -In order to support the "poll" method (and hence select() ), there is a small -catch regarding the FPGA to host direction: The FPGA may have filled a DMA -buffer with some data, but not submitted that buffer. If the host waited for -the buffer's submission by the FPGA, there would be a possibility that the -FPGA side has sent data, but a select() call would still block, because the -host has not received any notification about this. This is solved with -XILLYMSG_OPCODE_NONEMPTY messages sent by the FPGA when a channel goes from -completely empty to containing some data. - -These messages are used only to support poll() and select(). The IP core can -be configured not to send them for a slight reduction of bandwidth. diff --git a/drivers/staging/xillybus/TODO b/drivers/staging/xillybus/TODO deleted file mode 100644 index 95cfe2f62fcd..000000000000 --- a/drivers/staging/xillybus/TODO +++ /dev/null @@ -1,5 +0,0 @@ -TODO: -- have the driver reviewed - -Please send any patches and/or comments to Eli Billauer, -<eli.billauer@gmail.com>. diff --git a/drivers/staging/xillybus/xillybus.h b/drivers/staging/xillybus/xillybus.h deleted file mode 100644 index b9a9eb6d4f72..000000000000 --- a/drivers/staging/xillybus/xillybus.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * linux/drivers/misc/xillybus.h - * - * Copyright 2011 Xillybus Ltd, http://xillybus.com - * - * Header file for the Xillybus FPGA/host framework. - * - * This program is free software; you can redistribute it and/or modify - * it under the smems of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#ifndef __XILLYBUS_H -#define __XILLYBUS_H - -#include <linux/list.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/interrupt.h> -#include <linux/sched.h> -#include <linux/cdev.h> -#include <linux/spinlock.h> -#include <linux/mutex.h> -#include <linux/workqueue.h> - -struct xilly_endpoint_hardware; - -struct xilly_buffer { - void *addr; - dma_addr_t dma_addr; - int end_offset; /* Counting elements, not bytes */ -}; - -struct xilly_idt_handle { - unsigned char *chandesc; - unsigned char *idt; - int entries; -}; - -/* - * Read-write confusion: wr_* and rd_* notation sticks to FPGA view, so - * wr_* buffers are those consumed by read(), since the FPGA writes to them - * and vice versa. - */ - -struct xilly_channel { - struct xilly_endpoint *endpoint; - int chan_num; - int log2_element_size; - int seekable; - - struct xilly_buffer **wr_buffers; /* FPGA writes, driver reads! */ - int num_wr_buffers; - unsigned int wr_buf_size; /* In bytes */ - int wr_fpga_buf_idx; - int wr_host_buf_idx; - int wr_host_buf_pos; - int wr_empty; - int wr_ready; /* Significant only when wr_empty == 1 */ - int wr_sleepy; - int wr_eof; - int wr_hangup; - spinlock_t wr_spinlock; - struct mutex wr_mutex; - wait_queue_head_t wr_wait; - wait_queue_head_t wr_ready_wait; - int wr_ref_count; - int wr_synchronous; - int wr_allow_partial; - int wr_exclusive_open; - int wr_supports_nonempty; - - struct xilly_buffer **rd_buffers; /* FPGA reads, driver writes! */ - int num_rd_buffers; - unsigned int rd_buf_size; /* In bytes */ - int rd_fpga_buf_idx; - int rd_host_buf_pos; - int rd_host_buf_idx; - int rd_full; - spinlock_t rd_spinlock; - struct mutex rd_mutex; - wait_queue_head_t rd_wait; - int rd_ref_count; - int rd_allow_partial; - int rd_synchronous; - int rd_exclusive_open; - struct delayed_work rd_workitem; - unsigned char rd_leftovers[4]; -}; - -struct xilly_endpoint { - /* - * One of pdev and dev is always NULL, and the other is a valid - * pointer, depending on the type of device - */ - struct pci_dev *pdev; - struct device *dev; - struct xilly_endpoint_hardware *ephw; - - struct list_head ep_list; - int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */ - __iomem void *registers; - int fatal_error; - - struct mutex register_mutex; - wait_queue_head_t ep_wait; - - /* Channels and message handling */ - struct cdev cdev; - - int major; - int lowest_minor; /* Highest minor = lowest_minor + num_channels - 1 */ - - int num_channels; /* EXCLUDING message buffer */ - struct xilly_channel **channels; - int msg_counter; - int failed_messages; - int idtlen; - - u32 *msgbuf_addr; - dma_addr_t msgbuf_dma_addr; - unsigned int msg_buf_size; -}; - -struct xilly_endpoint_hardware { - struct module *owner; - void (*hw_sync_sgl_for_cpu)(struct xilly_endpoint *, - dma_addr_t, - size_t, - int); - void (*hw_sync_sgl_for_device)(struct xilly_endpoint *, - dma_addr_t, - size_t, - int); - int (*map_single)(struct xilly_endpoint *, - void *, - size_t, - int, - dma_addr_t *); -}; - -struct xilly_mapping { - void *device; - dma_addr_t dma_addr; - size_t size; - int direction; -}; - -irqreturn_t xillybus_isr(int irq, void *data); - -struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, - struct device *dev, - struct xilly_endpoint_hardware - *ephw); - -int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint); - -void xillybus_endpoint_remove(struct xilly_endpoint *endpoint); - -#endif /* __XILLYBUS_H */ diff --git a/drivers/staging/xillybus/xillybus_core.c b/drivers/staging/xillybus/xillybus_core.c deleted file mode 100644 index b827fa095f1b..000000000000 --- a/drivers/staging/xillybus/xillybus_core.c +++ /dev/null @@ -1,2103 +0,0 @@ -/* - * 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. - * - * This program is free software; you can redistribute it and/or modify - * it under the smems of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#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/cdev.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" - -MODULE_DESCRIPTION("Xillybus core functions"); -MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); -MODULE_VERSION("1.07"); -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 class *xillybus_class; - -/* - * ep_list_lock is the last lock to be taken; No other lock requests are - * allowed while holding it. It merely protects list_of_endpoints, and not - * the endpoints listed in it. - */ - -static LIST_HEAD(list_of_endpoints); -static struct mutex ep_list_lock; -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); - - ep->ephw->hw_sync_sgl_for_cpu(ep, - 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 { - ep->ephw->hw_sync_sgl_for_device( - ep, - 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; - } - } - - ep->ephw->hw_sync_sgl_for_device(ep, - 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 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 = ep->ephw->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 = channel->rd_buf_size = 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_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->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; - - scan = idt; - idt_handle->idt = idt; - - scan++; /* Skip version number */ - - while ((scan <= end_of_idt) && *scan) { - while ((scan <= end_of_idt) && *scan++) - /* Do nothing, just scan thru string */; - count++; - } - - 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; - } - - endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - 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. Accept anything below 0x82 for now. */ - if (*version > 0x82) { - dev_err(endpoint->dev, - "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgarde. 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 */ - channel->endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - 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) { - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - 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++; - - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - 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; - - 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)) { - channel->endpoint->ephw->hw_sync_sgl_for_cpu( - channel->endpoint, - 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) { - channel->endpoint->ephw->hw_sync_sgl_for_device( - channel->endpoint, - 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) -{ - int rc = 0; - unsigned long flags; - int minor = iminor(inode); - int major = imajor(inode); - struct xilly_endpoint *ep_iter, *endpoint = NULL; - struct xilly_channel *channel; - - mutex_lock(&ep_list_lock); - - list_for_each_entry(ep_iter, &list_of_endpoints, ep_list) { - if ((ep_iter->major == major) && - (minor >= ep_iter->lowest_minor) && - (minor < (ep_iter->lowest_minor + - ep_iter->num_channels))) { - endpoint = ep_iter; - break; - } - } - mutex_unlock(&ep_list_lock); - - if (!endpoint) { - pr_err("xillybus: open() failed to find a device for major=%d and minor=%d\n", - major, minor); - return -ENODEV; - } - - if (endpoint->fatal_error) - return -EIO; - - channel = endpoint->channels[1 + minor - endpoint->lowest_minor]; - 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 unsigned int xillybus_poll(struct file *filp, poll_table *wait) -{ - struct xilly_channel *channel = filp->private_data; - unsigned int 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 |= POLLIN | POLLRDNORM; - - if (channel->wr_hangup) - /* - * Not POLLHUP, because its behavior is in the - * mist, and POLLIN does what we want: Wake up - * the read file descriptor so it sees EOF. - */ - mask |= POLLIN | POLLRDNORM; - 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 |= POLLOUT | POLLWRNORM; - spin_unlock_irqrestore(&channel->rd_spinlock, flags); - } - - if (channel->endpoint->fatal_error) - mask |= POLLERR; - - 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, -}; - -static int xillybus_init_chrdev(struct xilly_endpoint *endpoint, - const unsigned char *idt) -{ - int rc; - dev_t dev; - int devnum, i, minor, major; - char devname[48]; - struct device *device; - - rc = alloc_chrdev_region(&dev, 0, /* minor start */ - endpoint->num_channels, - xillyname); - if (rc) { - dev_warn(endpoint->dev, "Failed to obtain major/minors"); - return rc; - } - - endpoint->major = major = MAJOR(dev); - endpoint->lowest_minor = minor = MINOR(dev); - - cdev_init(&endpoint->cdev, &xillybus_fops); - endpoint->cdev.owner = endpoint->ephw->owner; - rc = cdev_add(&endpoint->cdev, MKDEV(major, minor), - endpoint->num_channels); - if (rc) { - dev_warn(endpoint->dev, "Failed to add cdev. Aborting.\n"); - goto unregister_chrdev; - } - - idt++; - - for (i = minor, devnum = 0; - devnum < endpoint->num_channels; - devnum++, i++) { - snprintf(devname, sizeof(devname)-1, "xillybus_%s", idt); - - devname[sizeof(devname)-1] = 0; /* Should never matter */ - - while (*idt++) - /* Skip to next */; - - device = device_create(xillybus_class, - NULL, - MKDEV(major, i), - NULL, - "%s", devname); - - if (IS_ERR(device)) { - dev_warn(endpoint->dev, - "Failed to create %s device. Aborting.\n", - devname); - rc = -ENODEV; - goto unroll_device_create; - } - } - - dev_info(endpoint->dev, "Created %d device files.\n", - endpoint->num_channels); - return 0; /* succeed */ - -unroll_device_create: - devnum--; i--; - for (; devnum >= 0; devnum--, i--) - device_destroy(xillybus_class, MKDEV(major, i)); - - cdev_del(&endpoint->cdev); -unregister_chrdev: - unregister_chrdev_region(MKDEV(major, minor), endpoint->num_channels); - - return rc; -} - -static void xillybus_cleanup_chrdev(struct xilly_endpoint *endpoint) -{ - int minor; - - for (minor = endpoint->lowest_minor; - minor < (endpoint->lowest_minor + endpoint->num_channels); - minor++) - device_destroy(xillybus_class, MKDEV(endpoint->major, minor)); - cdev_del(&endpoint->cdev); - unregister_chrdev_region(MKDEV(endpoint->major, - endpoint->lowest_minor), - endpoint->num_channels); - - dev_info(endpoint->dev, "Removed %d device files.\n", - endpoint->num_channels); -} - -struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev, - struct device *dev, - struct xilly_endpoint_hardware - *ephw) -{ - struct xilly_endpoint *endpoint; - - endpoint = devm_kzalloc(dev, sizeof(*endpoint), GFP_KERNEL); - if (!endpoint) - return NULL; - - endpoint->pdev = pdev; - endpoint->dev = dev; - endpoint->ephw = ephw; - 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; - - /* - * endpoint is now completely configured. We put it on the list - * available to open() before registering the char device(s) - */ - - mutex_lock(&ep_list_lock); - list_add_tail(&endpoint->ep_list, &list_of_endpoints); - mutex_unlock(&ep_list_lock); - - rc = xillybus_init_chrdev(endpoint, idt_handle.idt); - if (rc) - goto failed_chrdevs; - - devres_release_group(dev, bootstrap_resources); - - return 0; - -failed_chrdevs: - mutex_lock(&ep_list_lock); - list_del(&endpoint->ep_list); - mutex_unlock(&ep_list_lock); - -failed_idt: - xilly_quiesce(endpoint); - flush_workqueue(xillybus_wq); - - return rc; -} -EXPORT_SYMBOL(xillybus_endpoint_discovery); - -void xillybus_endpoint_remove(struct xilly_endpoint *endpoint) -{ - xillybus_cleanup_chrdev(endpoint); - - mutex_lock(&ep_list_lock); - list_del(&endpoint->ep_list); - mutex_unlock(&ep_list_lock); - - 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) -{ - mutex_init(&ep_list_lock); - - xillybus_class = class_create(THIS_MODULE, xillyname); - if (IS_ERR(xillybus_class)) - return PTR_ERR(xillybus_class); - - xillybus_wq = alloc_workqueue(xillyname, 0, 0); - if (!xillybus_wq) { - class_destroy(xillybus_class); - return -ENOMEM; - } - - return 0; -} - -static void __exit xillybus_exit(void) -{ - /* flush_workqueue() was called for each endpoint released */ - destroy_workqueue(xillybus_wq); - - class_destroy(xillybus_class); -} - -module_init(xillybus_init); -module_exit(xillybus_exit); diff --git a/drivers/staging/xillybus/xillybus_of.c b/drivers/staging/xillybus/xillybus_of.c deleted file mode 100644 index 1ca0c7a4f1be..000000000000 --- a/drivers/staging/xillybus/xillybus_of.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * linux/drivers/misc/xillybus_of.c - * - * Copyright 2011 Xillybus Ltd, http://xillybus.com - * - * Driver for the Xillybus FPGA/host framework using Open Firmware. - * - * This program is free software; you can redistribute it and/or modify - * it under the smems of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#include <linux/module.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_platform.h> -#include <linux/err.h> -#include "xillybus.h" - -MODULE_DESCRIPTION("Xillybus driver for Open Firmware"); -MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); -MODULE_VERSION("1.06"); -MODULE_ALIAS("xillybus_of"); -MODULE_LICENSE("GPL v2"); - -static const char xillyname[] = "xillybus_of"; - -/* Match table for of_platform binding */ -static struct of_device_id xillybus_of_match[] = { - { .compatible = "xillybus,xillybus-1.00.a", }, - { .compatible = "xlnx,xillybus-1.00.a", }, /* Deprecated */ - {} -}; - -MODULE_DEVICE_TABLE(of, xillybus_of_match); - -static void xilly_dma_sync_single_for_cpu_of(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_cpu(ep->dev, dma_handle, size, direction); -} - -static void xilly_dma_sync_single_for_device_of(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - dma_sync_single_for_device(ep->dev, dma_handle, size, direction); -} - -static void xilly_dma_sync_single_nop(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ -} - -static void xilly_of_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_of(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; - int rc; - - 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; - - rc = devm_add_action(ep->dev, xilly_of_unmap, this); - - if (rc) { - dma_unmap_single(ep->dev, addr, size, direction); - kfree(this); - return rc; - } - - return 0; -} - -static struct xilly_endpoint_hardware of_hw = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_of, - .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_of, - .map_single = xilly_map_single_of, -}; - -static struct xilly_endpoint_hardware of_hw_coherent = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_nop, - .hw_sync_sgl_for_device = xilly_dma_sync_single_nop, - .map_single = xilly_map_single_of, -}; - -static int xilly_drv_probe(struct platform_device *op) -{ - struct device *dev = &op->dev; - struct xilly_endpoint *endpoint; - int rc; - int irq; - struct resource res; - struct xilly_endpoint_hardware *ephw = &of_hw; - - if (of_property_read_bool(dev->of_node, "dma-coherent")) - ephw = &of_hw_coherent; - - endpoint = xillybus_init_endpoint(NULL, dev, ephw); - - if (!endpoint) - return -ENOMEM; - - dev_set_drvdata(dev, endpoint); - - rc = of_address_to_resource(dev->of_node, 0, &res); - endpoint->registers = devm_ioremap_resource(dev, &res); - - if (IS_ERR(endpoint->registers)) - return PTR_ERR(endpoint->registers); - - irq = irq_of_parse_and_map(dev->of_node, 0); - - rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint); - - if (rc) { - dev_err(endpoint->dev, - "Failed to register IRQ handler. Aborting.\n"); - return -ENODEV; - } - - return xillybus_endpoint_discovery(endpoint); -} - -static int xilly_drv_remove(struct platform_device *op) -{ - struct device *dev = &op->dev; - struct xilly_endpoint *endpoint = dev_get_drvdata(dev); - - xillybus_endpoint_remove(endpoint); - - return 0; -} - -static struct platform_driver xillybus_platform_driver = { - .probe = xilly_drv_probe, - .remove = xilly_drv_remove, - .driver = { - .name = xillyname, - .owner = THIS_MODULE, - .of_match_table = xillybus_of_match, - }, -}; - -module_platform_driver(xillybus_platform_driver); diff --git a/drivers/staging/xillybus/xillybus_pcie.c b/drivers/staging/xillybus/xillybus_pcie.c deleted file mode 100644 index d8266bc2ae35..000000000000 --- a/drivers/staging/xillybus/xillybus_pcie.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * linux/drivers/misc/xillybus_pcie.c - * - * Copyright 2011 Xillybus Ltd, http://xillybus.com - * - * Driver for the Xillybus FPGA/host framework using PCI Express. - * - * This program is free software; you can redistribute it and/or modify - * it under the smems of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/pci-aspm.h> -#include <linux/slab.h> -#include "xillybus.h" - -MODULE_DESCRIPTION("Xillybus driver for PCIe"); -MODULE_AUTHOR("Eli Billauer, Xillybus Ltd."); -MODULE_VERSION("1.06"); -MODULE_ALIAS("xillybus_pcie"); -MODULE_LICENSE("GPL v2"); - -#define PCI_DEVICE_ID_XILLYBUS 0xebeb - -#define PCI_VENDOR_ID_ALTERA 0x1172 -#define PCI_VENDOR_ID_ACTEL 0x11aa -#define PCI_VENDOR_ID_LATTICE 0x1204 - -static const char xillyname[] = "xillybus_pcie"; - -static const struct pci_device_id xillyids[] = { - {PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XILLYBUS)}, - {PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_XILLYBUS)}, - {PCI_DEVICE(PCI_VENDOR_ID_ACTEL, PCI_DEVICE_ID_XILLYBUS)}, - {PCI_DEVICE(PCI_VENDOR_ID_LATTICE, PCI_DEVICE_ID_XILLYBUS)}, - { /* End: all zeroes */ } -}; - -static int xilly_pci_direction(int direction) -{ - switch (direction) { - case DMA_TO_DEVICE: - return PCI_DMA_TODEVICE; - case DMA_FROM_DEVICE: - return PCI_DMA_FROMDEVICE; - default: - return PCI_DMA_BIDIRECTIONAL; - } -} - -static void xilly_dma_sync_single_for_cpu_pci(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - pci_dma_sync_single_for_cpu(ep->pdev, - dma_handle, - size, - xilly_pci_direction(direction)); -} - -static void xilly_dma_sync_single_for_device_pci(struct xilly_endpoint *ep, - dma_addr_t dma_handle, - size_t size, - int direction) -{ - pci_dma_sync_single_for_device(ep->pdev, - dma_handle, - size, - xilly_pci_direction(direction)); -} - -static void xilly_pci_unmap(void *ptr) -{ - struct xilly_mapping *data = ptr; - - pci_unmap_single(data->device, data->dma_addr, - data->size, data->direction); - - kfree(ptr); -} - -/* - * Map either through the PCI DMA mapper or the non_PCI one. Behind the - * scenes exactly the same functions are called with the same parameters, - * but that can change. - */ - -static int xilly_map_single_pci(struct xilly_endpoint *ep, - void *ptr, - size_t size, - int direction, - dma_addr_t *ret_dma_handle - ) -{ - int pci_direction; - dma_addr_t addr; - struct xilly_mapping *this; - int rc; - - this = kzalloc(sizeof(*this), GFP_KERNEL); - if (!this) - return -ENOMEM; - - pci_direction = xilly_pci_direction(direction); - - addr = pci_map_single(ep->pdev, ptr, size, pci_direction); - - if (pci_dma_mapping_error(ep->pdev, addr)) { - kfree(this); - return -ENODEV; - } - - this->device = ep->pdev; - this->dma_addr = addr; - this->size = size; - this->direction = pci_direction; - - *ret_dma_handle = addr; - - rc = devm_add_action(ep->dev, xilly_pci_unmap, this); - if (rc) { - pci_unmap_single(ep->pdev, addr, size, pci_direction); - kfree(this); - return rc; - } - - return 0; -} - -static struct xilly_endpoint_hardware pci_hw = { - .owner = THIS_MODULE, - .hw_sync_sgl_for_cpu = xilly_dma_sync_single_for_cpu_pci, - .hw_sync_sgl_for_device = xilly_dma_sync_single_for_device_pci, - .map_single = xilly_map_single_pci, -}; - -static int xilly_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct xilly_endpoint *endpoint; - int rc; - - endpoint = xillybus_init_endpoint(pdev, &pdev->dev, &pci_hw); - - if (!endpoint) - return -ENOMEM; - - pci_set_drvdata(pdev, endpoint); - - rc = pcim_enable_device(pdev); - if (rc) { - dev_err(endpoint->dev, - "pcim_enable_device() failed. Aborting.\n"); - return rc; - } - - /* L0s has caused packet drops. No power saving, thank you. */ - - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S); - - if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - dev_err(endpoint->dev, - "Incorrect BAR configuration. Aborting.\n"); - return -ENODEV; - } - - rc = pcim_iomap_regions(pdev, 0x01, xillyname); - if (rc) { - dev_err(endpoint->dev, - "pcim_iomap_regions() failed. Aborting.\n"); - return rc; - } - - endpoint->registers = pcim_iomap_table(pdev)[0]; - - pci_set_master(pdev); - - /* Set up a single MSI interrupt */ - if (pci_enable_msi(pdev)) { - dev_err(endpoint->dev, - "Failed to enable MSI interrupts. Aborting.\n"); - return -ENODEV; - } - rc = devm_request_irq(&pdev->dev, pdev->irq, xillybus_isr, 0, - xillyname, endpoint); - if (rc) { - dev_err(endpoint->dev, - "Failed to register MSI handler. Aborting.\n"); - return -ENODEV; - } - - /* - * In theory, an attempt to set the DMA mask to 64 and dma_using_dac=1 - * is the right thing. But some unclever PCIe drivers report it's OK - * when the hardware drops those 64-bit PCIe packets. So trust - * nobody and use 32 bits DMA addressing in any case. - */ - - if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { - endpoint->dma_using_dac = 0; - } else { - dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); - return -ENODEV; - } - - return xillybus_endpoint_discovery(endpoint); -} - -static void xilly_remove(struct pci_dev *pdev) -{ - struct xilly_endpoint *endpoint = pci_get_drvdata(pdev); - - xillybus_endpoint_remove(endpoint); -} - -MODULE_DEVICE_TABLE(pci, xillyids); - -static struct pci_driver xillybus_driver = { - .name = xillyname, - .id_table = xillyids, - .probe = xilly_probe, - .remove = xilly_remove, -}; - -module_pci_driver(xillybus_driver); |