2014-12-05 12:30:48 +01:00
|
|
|
/** Node type: GTFPGA (Xilinx ML507)
|
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2016-01-14 23:16:35 +01:00
|
|
|
* @copyright 2015-2016, Steffen Vogel
|
2016-06-08 23:21:42 +02:00
|
|
|
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
|
2015-08-07 01:11:43 +02:00
|
|
|
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
2015-06-02 21:53:04 +02:00
|
|
|
*********************************************************************************/
|
2015-05-07 13:04:06 +02:00
|
|
|
|
2015-06-03 10:13:35 +02:00
|
|
|
#include <stdio.h>
|
2015-05-07 13:04:06 +02:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
2015-03-30 10:11:31 +02:00
|
|
|
|
2015-06-02 21:53:04 +02:00
|
|
|
#include "gtfpga.h"
|
2016-01-14 23:16:35 +01:00
|
|
|
#include "config.h"
|
2015-06-02 21:53:04 +02:00
|
|
|
#include "utils.h"
|
|
|
|
#include "timing.h"
|
2016-03-18 12:38:28 +01:00
|
|
|
#include "utils.h"
|
2016-06-08 22:44:44 +02:00
|
|
|
#include "kernel.h"
|
2015-03-30 10:11:31 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
static struct pci_access *pacc;
|
|
|
|
|
|
|
|
static void gtfpga_debug(char *msg, ...) {
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, msg);
|
|
|
|
log_vprint(DEBUG, msg, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
2015-03-31 14:28:21 +02:00
|
|
|
|
2015-12-11 12:35:32 +01:00
|
|
|
int gtfpga_init(int argc, char * argv[], config_setting_t *cfg)
|
2015-03-30 10:11:31 +02:00
|
|
|
{
|
2016-03-18 12:38:28 +01:00
|
|
|
if (getuid() != 0)
|
2015-09-30 11:12:16 +02:00
|
|
|
error("The gtfpga node-type requires superuser privileges!");
|
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
pacc = pci_alloc(); /* Get the pci_access structure */
|
2015-08-22 17:42:38 +02:00
|
|
|
if (!pacc)
|
|
|
|
error("Failed to allocate PCI access structure");
|
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
pci_init(pacc); /* Initialize the PCI library */
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
pacc->error = (log_cb_t) error; /* Replace logging and debug functions */
|
|
|
|
pacc->warning = (log_cb_t) warn;
|
|
|
|
pacc->debug = gtfpga_debug;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
pci_scan_bus(pacc); /* We want to get the list of devices */
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
return 0;
|
2015-03-31 14:28:21 +02:00
|
|
|
}
|
2015-03-30 10:11:31 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
int gtfpga_deinit()
|
|
|
|
{
|
|
|
|
pci_cleanup(pacc);
|
2015-05-07 13:04:06 +02:00
|
|
|
|
|
|
|
return 0;
|
2015-03-30 10:11:31 +02:00
|
|
|
}
|
|
|
|
|
2015-11-23 16:42:43 +01:00
|
|
|
int gtfpga_parse(struct node *n, config_setting_t *cfg)
|
2015-03-30 10:11:31 +02:00
|
|
|
{
|
2015-11-29 22:47:57 +01:00
|
|
|
struct gtfpga *g = n->_vd;
|
2015-03-31 14:28:21 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
const char *slot, *id, *err;
|
|
|
|
config_setting_t *cfg_slot, *cfg_id;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
pci_filter_init(NULL, &g->filter);
|
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
cfg_slot = config_setting_get_member(cfg, "slot");
|
|
|
|
if (cfg_slot) {
|
|
|
|
slot = config_setting_get_string(cfg_slot);
|
|
|
|
if (slot) {
|
|
|
|
err = pci_filter_parse_slot(&g->filter, (char*) slot);
|
|
|
|
if (err)
|
2015-03-31 14:28:21 +02:00
|
|
|
cerror(cfg_slot, "%s", err);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cerror(cfg_slot, "Invalid slot format");
|
|
|
|
}
|
2015-03-30 10:11:31 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
cfg_id = config_setting_get_member(cfg, "id");
|
|
|
|
if (cfg_id) {
|
|
|
|
id = config_setting_get_string(cfg_id);
|
|
|
|
if (id) {
|
|
|
|
err = pci_filter_parse_id(&g->filter, (char*) id);
|
|
|
|
if (err)
|
2015-03-31 14:28:21 +02:00
|
|
|
cerror(cfg_id, "%s", err);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cerror(cfg_slot, "Invalid id format");
|
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
if (!config_setting_lookup_float(cfg, "rate", &g->rate))
|
|
|
|
g->rate = 0;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-22 12:58:37 +02:00
|
|
|
char * gtfpga_print(struct node *n)
|
2015-03-31 14:28:21 +02:00
|
|
|
{
|
2015-11-29 22:47:57 +01:00
|
|
|
struct gtfpga *g = n->_vd;
|
2015-05-07 13:04:06 +02:00
|
|
|
|
|
|
|
if (g->dev) {
|
2016-04-16 21:58:13 +02:00
|
|
|
return strf("rate=%.1f slot=%04"PRIx16":%02"PRIx8":%02"PRIx8".%"PRIx8
|
2015-05-07 13:04:06 +02:00
|
|
|
" id=%04"PRIx16":%04"PRIx16" class=%04"PRIx16" irq=%d (%s)", g->rate,
|
|
|
|
g->dev->domain, g->dev->bus, g->dev->dev, g->dev->func, g->dev->vendor_id, g->dev->device_id,
|
|
|
|
g->dev->device_class, g->dev->irq, g->name);
|
|
|
|
}
|
|
|
|
else {
|
2016-04-16 21:58:13 +02:00
|
|
|
return strf("rate=%.1f slot=%02"PRIx8":%02"PRIx8".%"PRIx8" id=%04"PRIx16":%04"PRIx16, g->rate,
|
2015-05-07 13:04:06 +02:00
|
|
|
g->filter.bus, g->filter.device, g->filter.func,
|
|
|
|
g->filter.vendor, g->filter.device);
|
|
|
|
}
|
2015-03-31 14:28:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gtfpga_load_driver(struct pci_dev *d)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char slot[16];
|
2015-09-19 12:14:35 +02:00
|
|
|
|
2016-03-18 12:38:28 +01:00
|
|
|
if (kernel_module_load("uio_pci_generic"))
|
2015-09-19 12:14:35 +02:00
|
|
|
error("Missing kernel module: uio_pci_generic");
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
/* Prepare slot identifier */
|
|
|
|
snprintf(slot, sizeof(slot), "%04x:%02x:%02x.%x",
|
2015-05-07 13:04:06 +02:00
|
|
|
d->domain, d->bus, d->dev, d->func);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
/* Add new ID to uio_pci_generic */
|
2016-03-18 12:38:28 +01:00
|
|
|
f = fopen(SYSFS_PATH "/drivers/uio_pci_generic/new_id", "w");
|
2015-09-19 15:28:28 +02:00
|
|
|
if (!f)
|
2015-03-31 14:28:21 +02:00
|
|
|
serror("Failed to add PCI id to uio_pci_generic driver");
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-06-13 21:43:08 +02:00
|
|
|
debug(DBG_GTFPGA | 5, "Adding ID to uio_pci_generic module: %04x %04x", d->vendor_id, d->device_id);
|
2015-03-31 14:28:21 +02:00
|
|
|
fprintf(f, "%04x %04x", d->vendor_id, d->device_id);
|
|
|
|
fclose(f);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
/* Bind to uio_pci_generic */
|
|
|
|
f = fopen(SYSFS_PATH "/drivers/uio_pci_generic/bind", "w");
|
|
|
|
if (!f)
|
|
|
|
serror("Failed to add PCI id to uio_pci_generic driver");
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-06-13 21:43:08 +02:00
|
|
|
debug(DBG_GTFPGA | 5, "Bind slot to uio_pci_generic module: %s", slot);
|
2015-03-31 14:28:21 +02:00
|
|
|
fprintf(f, "%s\n", slot);
|
|
|
|
fclose(f);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
return 0;
|
2015-03-31 14:28:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_dev * gtfpga_find_device(struct pci_filter *f)
|
|
|
|
{
|
|
|
|
struct pci_dev *d;
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
/* Iterate over all devices */
|
2015-03-31 14:28:21 +02:00
|
|
|
for (d = pacc->devices; d; d = d->next) {
|
2015-05-07 13:04:06 +02:00
|
|
|
if (pci_filter_match(f, d))
|
2015-03-31 14:28:21 +02:00
|
|
|
return d;
|
2015-03-30 10:11:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
static int gtfpga_mmap(struct gtfpga *g)
|
2015-03-30 10:11:31 +02:00
|
|
|
{
|
|
|
|
int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
|
|
|
if (!fd)
|
|
|
|
serror("Failed open()");
|
|
|
|
|
|
|
|
long int addr = g->dev->base_addr[GTFPGA_BAR] & ~0xfff;
|
|
|
|
int size = g->dev->size[GTFPGA_BAR];
|
|
|
|
|
|
|
|
/* mmap() first BAR */
|
2016-06-13 21:43:08 +02:00
|
|
|
debug(DBG_GTFPGA | 5, "Setup mapping: mmap(NULL, %#x, PROT_READ | PROT_WRITE, MAP_SHARED, %u, %#lx)", size, fd, addr);
|
2015-03-30 10:11:31 +02:00
|
|
|
void *map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr);
|
|
|
|
if (map == MAP_FAILED)
|
|
|
|
serror("Failed mmap()");
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
return 0;
|
2015-03-30 10:11:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int gtfpga_open(struct node *n)
|
|
|
|
{
|
2015-11-29 22:47:57 +01:00
|
|
|
struct gtfpga *g = n->_vd;
|
2015-03-30 10:11:31 +02:00
|
|
|
struct pci_dev *dev;
|
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
dev = gtfpga_find_device(&g->filter);
|
2015-03-30 10:11:31 +02:00
|
|
|
if (!dev)
|
2015-05-07 13:04:06 +02:00
|
|
|
error("No GTFPGA card found");
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
g->dev = dev;
|
2015-05-07 13:04:06 +02:00
|
|
|
g->name = alloc(512);
|
2015-03-30 10:11:31 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
gtfpga_load_driver(dev);
|
|
|
|
gtfpga_mmap(g);
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
/* Show some debug infos */
|
|
|
|
pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_CLASS); /* Fill in header info we need */
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
g->name = pci_lookup_name(pacc, g->name, 512, PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-07 13:04:06 +02:00
|
|
|
/* Setup timer */
|
|
|
|
if (g->rate) {
|
2016-01-14 22:57:39 +01:00
|
|
|
g->fd_irq = timerfd_create_rate(g->rate);
|
2015-05-07 13:04:06 +02:00
|
|
|
if (g->fd_irq < 0)
|
|
|
|
serror("Failed to create timer");
|
|
|
|
}
|
2015-09-22 13:20:20 +02:00
|
|
|
else /** @todo implement UIO interrupts */
|
2015-05-07 13:04:06 +02:00
|
|
|
error("UIO irq not implemented yet. Use 'rate' setting");
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-30 10:11:31 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int gtfpga_close(struct node *n)
|
|
|
|
{
|
2015-11-29 22:47:57 +01:00
|
|
|
struct gtfpga *g = n->_vd;
|
2015-03-30 10:11:31 +02:00
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
if (g->map)
|
|
|
|
munmap(g->map, g->dev->size[GTFPGA_BAR]);
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
close(g->fd_mmap);
|
2015-05-07 13:04:06 +02:00
|
|
|
close(g->fd_irq);
|
|
|
|
free(g->name);
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
/** @todo implement */
|
2016-01-14 22:59:57 +01:00
|
|
|
int gtfpga_read(struct node *n, struct pool *pool, int cnt)
|
2015-03-30 10:11:31 +02:00
|
|
|
{
|
2015-11-29 22:47:57 +01:00
|
|
|
struct gtfpga *g = n->_vd;
|
2016-01-14 23:16:35 +01:00
|
|
|
// struct msg *m = pool_getrel(pool, 0);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-01-14 23:16:35 +01:00
|
|
|
/* Wait for IRQ */
|
|
|
|
uint64_t fired;
|
|
|
|
read(g->fd_irq, &fired, sizeof(fired));
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-01-14 23:16:35 +01:00
|
|
|
/* Copy memory mapped data */
|
|
|
|
/** @todo */
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-31 14:28:21 +02:00
|
|
|
/** @todo implement */
|
2016-01-14 22:59:57 +01:00
|
|
|
int gtfpga_write(struct node *n, struct pool *pool, int cnt)
|
2015-03-30 10:11:31 +02:00
|
|
|
{
|
2015-11-29 22:47:57 +01:00
|
|
|
// struct gtfpga *g = n->_vd;
|
2016-01-14 22:59:57 +01:00
|
|
|
// struct msg *m = pool_getrel(pool, 0);
|
2015-10-13 16:14:01 +02:00
|
|
|
|
2016-01-14 23:16:35 +01:00
|
|
|
/* Copy memory mapped data */
|
|
|
|
/** @todo */
|
2015-03-30 10:11:31 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-23 16:44:01 +01:00
|
|
|
static struct node_type vt = {
|
|
|
|
.name = "gtfpga",
|
|
|
|
.description = "GTFPGA PCIe card (libpci)",
|
2016-01-14 22:54:08 +01:00
|
|
|
.vectorize = 1,
|
2015-11-23 16:44:01 +01:00
|
|
|
.parse = gtfpga_parse,
|
|
|
|
.print = gtfpga_print,
|
|
|
|
.open = gtfpga_open,
|
|
|
|
.close = gtfpga_close,
|
|
|
|
.read = gtfpga_read,
|
|
|
|
.write = gtfpga_write,
|
|
|
|
.init = gtfpga_init,
|
|
|
|
.deinit = gtfpga_deinit
|
|
|
|
};
|
|
|
|
|
|
|
|
REGISTER_NODE_TYPE(&vt)
|