staging: New driver: Xillybus generic interface for FPGA

This is the driver for Xillybus, which is a general-purpose interface for
data communication with FPGAs (programmable logic). Please refer to the
README included in this patch for a detailed explanation.

It was previously submitted for misc-devices, but it appears like noone's
willing to review the code (which I can understand, given its magnitude).
Hence submitted as a staging driver.

Signed-off-by: Eli Billauer <eli.billauer@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Eli Billauer 2013-06-24 18:55:47 +03:00 коммит произвёл Greg Kroah-Hartman
Родитель 8079154a1d
Коммит 48bae05074
10 изменённых файлов: 3453 добавлений и 0 удалений

Просмотреть файл

@ -144,4 +144,6 @@ source "drivers/staging/lustre/Kconfig"
source "drivers/staging/btmtk_usb/Kconfig"
source "drivers/staging/xillybus/Kconfig"
endif # STAGING

Просмотреть файл

@ -64,3 +64,4 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_LUSTRE_FS) += lustre/
obj-$(CONFIG_USB_BTMTK) += btmtk_usb/
obj-$(CONFIG_XILLYBUS) += xillybus/

Просмотреть файл

@ -0,0 +1,35 @@
#
# Xillybus devices
#
config XILLYBUS
tristate "Xillybus generic FPGA interface"
depends on PCI || (OF_ADDRESS && OF_DEVICE && OF_IRQ)
default n
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 XILLYBUS && PCI
default n
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 XILLYBUS && OF_ADDRESS && OF_DEVICE && OF_IRQ
default n
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

Просмотреть файл

@ -0,0 +1,7 @@
#
# Makefile for Xillybus driver
#
obj-$(CONFIG_XILLYBUS) += xillybus_core.o
obj-$(CONFIG_XILLYBUS_PCIE) += xillybus_pcie.o
obj-$(CONFIG_XILLYBUS_OF) += xillybus_of.o

Просмотреть файл

@ -0,0 +1,403 @@
==========================================
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
-- Memory management
-- 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.
Memory management
-----------------
The tricky part about the buffer allocation procedure described above is
freeing and unmapping the buffers, in particular if something goes wrong in
the middle, and the allocations need to be rolled back. The three-stage
probing procedure makes this even more crucial, since temporary buffers are
set up and mapped in the first of its two stages.
To keep the code clean from complicated and bug-prone memory release routines,
there are special routines for allocating memory. For example, instead of
calling kzalloc, there's
void *xilly_malloc(struct xilly_cleanup *mem, size_t size)
which effectively allocates a zeroed buffer of size "size". Its first
argument, "mem", is where this allocation is enlisted, so that it's released
when xillybus_do_cleanup() is called with the same "mem" structure.
Two other functions enlist allocations in this structure: xilly_pagealloc()
for page allocations and xilly_map_single_*() for DMA mapping.
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.

Просмотреть файл

@ -0,0 +1,5 @@
TODO:
- have the driver reviewed
Please send any patches and/or comments to Eli Billauer,
<eli.billauer@gmail.com>.

Просмотреть файл

@ -0,0 +1,185 @@
/*
* 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/list.h>
#include <linux/workqueue.h>
char xillyname[] = "xillybus";
struct xilly_endpoint_hardware;
struct xilly_page {
struct list_head node;
unsigned long addr;
unsigned int order;
};
struct xilly_dma {
struct list_head node;
struct pci_dev *pdev;
struct device *dev;
dma_addr_t dma_addr;
size_t size;
int direction;
};
struct xilly_buffer {
void *addr;
dma_addr_t dma_addr;
int end_offset; /* Counting elements, not bytes */
};
struct xilly_cleanup {
struct list_head to_kfree;
struct list_head to_pagefree;
struct list_head to_unmap;
};
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 resource res; /* OF devices only */
struct xilly_endpoint_hardware *ephw;
struct list_head ep_list;
int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */
u32 *registers;
int fatal_error;
struct mutex register_mutex;
wait_queue_head_t ep_wait;
/* List of memory allocations, to make release easy */
struct xilly_cleanup cleanup;
/* 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 (*sync_single_for_cpu)(struct xilly_endpoint *,
dma_addr_t,
size_t,
int);
void (*sync_single_for_device)(struct xilly_endpoint *,
dma_addr_t,
size_t,
int);
dma_addr_t (*map_single)(struct xilly_cleanup *,
struct xilly_endpoint *,
void *,
size_t,
int);
void (*unmap_single)(struct xilly_dma *entry);
};
irqreturn_t xillybus_isr(int irq, void *data);
void xillybus_do_cleanup(struct xilly_cleanup *mem,
struct xilly_endpoint *endpoint);
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 */

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,210 @@
/*
* 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 "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");
/* Match table for of_platform binding */
static struct of_device_id xillybus_of_match[] = {
{ .compatible = "xlnx,xillybus-1.00.a", },
{}
};
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 dma_addr_t xilly_map_single_of(struct xilly_cleanup *mem,
struct xilly_endpoint *ep,
void *ptr,
size_t size,
int direction
)
{
dma_addr_t addr = 0;
struct xilly_dma *this;
this = kmalloc(sizeof(struct xilly_dma), GFP_KERNEL);
if (!this)
return 0;
addr = dma_map_single(ep->dev, ptr, size, direction);
this->direction = direction;
if (dma_mapping_error(ep->dev, addr)) {
kfree(this);
return 0;
}
this->dma_addr = addr;
this->dev = ep->dev;
this->size = size;
list_add_tail(&this->node, &mem->to_unmap);
return addr;
}
void xilly_unmap_single_of(struct xilly_dma *entry)
{
dma_unmap_single(entry->dev,
entry->dma_addr,
entry->size,
entry->direction);
}
static struct xilly_endpoint_hardware of_hw = {
.owner = THIS_MODULE,
.sync_single_for_cpu = xilly_dma_sync_single_for_cpu_of,
.sync_single_for_device = xilly_dma_sync_single_for_device_of,
.map_single = xilly_map_single_of,
.unmap_single = xilly_unmap_single_of
};
static int xilly_drv_probe(struct platform_device *op)
{
struct device *dev = &op->dev;
struct xilly_endpoint *endpoint;
int rc = 0;
int irq;
endpoint = xillybus_init_endpoint(NULL, dev, &of_hw);
if (!endpoint)
return -ENOMEM;
dev_set_drvdata(dev, endpoint);
rc = of_address_to_resource(dev->of_node, 0, &endpoint->res);
if (rc) {
pr_warn("xillybus: Failed to obtain device tree "
"resource\n");
goto failed_request_regions;
}
if (!request_mem_region(endpoint->res.start,
resource_size(&endpoint->res), xillyname)) {
pr_err("xillybus: request_mem_region failed. Aborting.\n");
rc = -EBUSY;
goto failed_request_regions;
}
endpoint->registers = of_iomap(dev->of_node, 0);
if (!endpoint->registers) {
pr_err("xillybus: Failed to map I/O memory. Aborting.\n");
goto failed_iomap0;
}
irq = irq_of_parse_and_map(dev->of_node, 0);
rc = request_irq(irq, xillybus_isr, 0, xillyname, endpoint);
if (rc) {
pr_err("xillybus: Failed to register IRQ handler. "
"Aborting.\n");
rc = -ENODEV;
goto failed_register_irq;
}
rc = xillybus_endpoint_discovery(endpoint);
if (!rc)
return 0;
free_irq(irq, endpoint);
failed_register_irq:
iounmap(endpoint->registers);
failed_iomap0:
release_mem_region(endpoint->res.start,
resource_size(&endpoint->res));
failed_request_regions:
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
kfree(endpoint);
return rc;
}
static int xilly_drv_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct xilly_endpoint *endpoint = dev_get_drvdata(dev);
int irq = irq_of_parse_and_map(dev->of_node, 0);
xillybus_endpoint_remove(endpoint);
free_irq(irq, endpoint);
iounmap(endpoint->registers);
release_mem_region(endpoint->res.start,
resource_size(&endpoint->res));
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
kfree(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,
},
};
static int __init xillybus_of_init(void)
{
return platform_driver_register(&xillybus_platform_driver);
}
static void __exit xillybus_of_exit(void)
{
platform_driver_unregister(&xillybus_platform_driver);
}
module_init(xillybus_of_init);
module_exit(xillybus_of_exit);

Просмотреть файл

@ -0,0 +1,260 @@
/*
* 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 DEFINE_PCI_DEVICE_TABLE(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));
}
/*
* 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 dma_addr_t xilly_map_single_pci(struct xilly_cleanup *mem,
struct xilly_endpoint *ep,
void *ptr,
size_t size,
int direction
)
{
dma_addr_t addr = 0;
struct xilly_dma *this;
int pci_direction;
this = kmalloc(sizeof(struct xilly_dma), GFP_KERNEL);
if (!this)
return 0;
pci_direction = xilly_pci_direction(direction);
addr = pci_map_single(ep->pdev, ptr, size, pci_direction);
this->direction = pci_direction;
if (pci_dma_mapping_error(ep->pdev, addr)) {
kfree(this);
return 0;
}
this->dma_addr = addr;
this->pdev = ep->pdev;
this->size = size;
list_add_tail(&this->node, &mem->to_unmap);
return addr;
}
void xilly_unmap_single_pci(struct xilly_dma *entry)
{
pci_unmap_single(entry->pdev,
entry->dma_addr,
entry->size,
entry->direction);
}
static struct xilly_endpoint_hardware pci_hw = {
.owner = THIS_MODULE,
.sync_single_for_cpu = xilly_dma_sync_single_for_cpu_pci,
.sync_single_for_device = xilly_dma_sync_single_for_device_pci,
.map_single = xilly_map_single_pci,
.unmap_single = xilly_unmap_single_pci
};
static int xilly_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct xilly_endpoint *endpoint;
int rc = 0;
endpoint = xillybus_init_endpoint(pdev, NULL, &pci_hw);
if (!endpoint)
return -ENOMEM;
pci_set_drvdata(pdev, endpoint);
rc = pci_enable_device(pdev);
/* L0s has caused packet drops. No power saving, thank you. */
pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S);
if (rc) {
pr_err("xillybus: pci_enable_device() failed. "
"Aborting.\n");
goto no_enable;
}
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
pr_err("xillybus: Incorrect BAR configuration. "
"Aborting.\n");
rc = -ENODEV;
goto bad_bar;
}
rc = pci_request_regions(pdev, xillyname);
if (rc) {
pr_err("xillybus: pci_request_regions() failed. "
"Aborting.\n");
goto failed_request_regions;
}
endpoint->registers = pci_iomap(pdev, 0, 128);
if (!endpoint->registers) {
pr_err("xillybus: Failed to map BAR 0. Aborting.\n");
goto failed_iomap0;
}
pci_set_master(pdev);
/* Set up a single MSI interrupt */
if (pci_enable_msi(pdev)) {
pr_err("xillybus: Failed to enable MSI interrupts. "
"Aborting.\n");
rc = -ENODEV;
goto failed_enable_msi;
}
rc = request_irq(pdev->irq, xillybus_isr, 0, xillyname, endpoint);
if (rc) {
pr_err("xillybus: Failed to register MSI handler. "
"Aborting.\n");
rc = -ENODEV;
goto failed_register_msi;
}
/*
* 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 {
pr_err("xillybus: Failed to set DMA mask. "
"Aborting.\n");
rc = -ENODEV;
goto failed_dmamask;
}
rc = xillybus_endpoint_discovery(endpoint);
if (!rc)
return 0;
failed_dmamask:
free_irq(pdev->irq, endpoint);
failed_register_msi:
pci_disable_msi(pdev);
failed_enable_msi:
/* pci_clear_master(pdev); Nobody else seems to do this */
pci_iounmap(pdev, endpoint->registers);
failed_iomap0:
pci_release_regions(pdev);
failed_request_regions:
bad_bar:
pci_disable_device(pdev);
no_enable:
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
kfree(endpoint);
return rc;
}
static void xilly_remove(struct pci_dev *pdev)
{
struct xilly_endpoint *endpoint = pci_get_drvdata(pdev);
xillybus_endpoint_remove(endpoint);
free_irq(pdev->irq, endpoint);
pci_disable_msi(pdev);
pci_iounmap(pdev, endpoint->registers);
pci_release_regions(pdev);
pci_disable_device(pdev);
xillybus_do_cleanup(&endpoint->cleanup, endpoint);
kfree(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);