Commit 04ec4bf1 authored by Federico Vaga's avatar Federico Vaga Committed by Federico Vaga

spec: add UAL implementation

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>

ual: move code to ual repo
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent aed186b9
......@@ -18,6 +18,8 @@ endif
ccflags-y += -I$(src)/include -I$(FMC_DRV)/include
ccflags-y += $(WR_NIC_CFLAGS)
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
ccflags-$(CONFIG_SPEC_UAL) += -DCONFIG_SPEC_UAL
ccflags-$(CONFIG_SPEC_UAL) += -I$(LIBUAL)/kernel
# this is a bad hack. Sometimes we are a submodule, and wr-nic can
......@@ -34,6 +36,14 @@ spec-y += spec-i2c.o
spec-y += spec-vic.o
spec-y += loader-ll.o
spec-y += spec-gpio-no.o
spec-$(CONFIG_SPEC_UAL) += spec-ual.o
spec-$(CONFIG_SPEC_UAL) += ../libual/kernel/ual-cdev.o
spec-$(CONFIG_SPEC_UAL) += ../libual/kernel/ual-common.o
spec-$(CONFIG_SPEC_UAL) += ../libual/kernel/ual-fw.o
spec-$(CONFIG_SPEC_UAL) += ../libual/kernel/ual-irq.o
spec-$(CONFIG_SPEC_UAL) += ../libual/kernel/ual-blk.o
spec-$(CONFIG_SPEC_UAL) += gncore-dma.o
spec-$(CONFIG_SPEC_UAL) += gncore-dma.o
spec-$(CONFIG_GPIOLIB) += spec-gpio.o
wr-nic-y = wr-nic-core.o
......@@ -49,7 +59,11 @@ wr-nic-$(CONFIG_GPIOLIB) += wr-nic-gpio.o
all modules:
ln -sf $(LIBUAL) ../libual
rm -f ../libual/kernel/*.o
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules
rm -f ../libual/kernel/*.o
rm -f ../libual
install modules_install:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules_install
......@@ -59,3 +73,4 @@ clean:
rm -rf *.o *~ .*.cmd *.ko *.mod.c .tmp_versions Module.symvers \
Module.markers modules.order
rm -rf wr_nic/*.o wr_nic/*~
rm -f $(LIBUAL)/kernel/*.o
This diff is collapsed.
/*
* Copyright (C) 2014 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/fmc.h>
#include <linux/workqueue.h>
#define GNCORE_DDR_SIZE 0x10000000 /* 256M */
#define GNCORE_IRQ_DMA_DISABLE_MASK 0x00
#define GNCORE_IRQ_DMA_ENABLE_MASK 0x04
#define GNCORE_IRQ_DMA_MASK_STATUS 0x08
#define GNCORE_IRQ_DMA_SRC 0x0C
#define GNCORE_IRQ_DMA_MASK 0x3
#define GNCORE_IRQ_VIC_CTRL 0x00
#define GNCORE_IRQ_VIC_ENABLE 0x08
#define GNCORE_IRQ_VIC_DISABLE 0x0C
#define GNCORE_IRQ_VIC_STATUS 0x10
/* FIXME enable interrupt source 0 [DMA] (check with Tom Levens is it
* does not work)
*/
#define GNCORE_IRQ_VIC_MASK 0x1
#define GNCORE_IRQ_DMA_DONE 0x1
#define GNCORE_IRQ_DMA_ERR 0x2
#define GNCORE_DMA_CTL 0x00
#define GNCORE_DMA_CTL_SWP 0xC
#define GNCORE_DMA_CTL_ABORT 0x2
#define GNCORE_DMA_CTL_START 0x1
#define GNCORE_DMA_STA 0x04
#define GNCORE_DMA_ADDR 0x08
#define GNCORE_DMA_ADDR_L 0x0C
#define GNCORE_DMA_ADDR_H 0x10
#define GNCORE_DMA_LEN 0x14
#define GNCORE_DMA_NEXT_L 0x18
#define GNCORE_DMA_NEXT_H 0x1C
#define GNCORE_DMA_BR 0x20
#define GNCORE_DMA_BR_DIR 0x2
#define GNCORE_DMA_BR_LAST 0x1
/*
* fa_dma_item: The information about a DMA transfer
* @start_addr: pointer where start to retrieve data from device memory
* @dma_addr_l: low 32bit of the dma address on host memory
* @dma_addr_h: high 32bit of the dma address on host memory
* @dma_len: number of bytes to transfer from device to host
* @next_addr_l: low 32bit of the address of the next memory area to use
* @next_addr_h: high 32bit of the address of the next memory area to use
* @attribute: dma information about data transferm. At the moment it is used
* only to provide the "last item" bit, direction is fixed to
* device->host
*/
struct gncore_dma_item {
uint32_t start_addr; /* 0x00 */
uint32_t dma_addr_l; /* 0x04 */
uint32_t dma_addr_h; /* 0x08 */
uint32_t dma_len; /* 0x0C */
uint32_t next_addr_l; /* 0x10 */
uint32_t next_addr_h; /* 0x14 */
uint32_t attribute; /* 0x18 */
uint32_t reserved; /* ouch */
};
struct gncore {
struct fmc_device *fmc;
unsigned int dma_base_addr;
unsigned int irq_dma_base_addr;
unsigned long flags;
unsigned long int offset;
void *buf;
size_t len;
enum dma_data_direction direction;
void (*dma_over_cb)(void *data, uint32_t status);
struct sg_table sgt;
dma_addr_t dma_list_item;
struct gncore_dma_item *items;
struct spinlock lock;
uint32_t irq_enable_orig; /* original enable status */
struct work_struct dma_work;
void *priv;
};
#define GNCORE_FLAG_DMA_RUNNING (1 << 0)
#define GNCORE_FLAG_DMA_FAIL (1 << 1)
/* Status of the DMA engine */
enum gncore_dma_status {
GNCORE_DMA_IDLE = 0,
GNCORE_DMA_DONE,
GNCORE_DMA_BUSY,
GNCORE_DMA_ERROR,
GNCORE_DMA_ABORT,
};
extern struct gncore *gncore_dma_init(struct fmc_device *fmc);
extern void gncore_dma_exit(struct gncore *gncore);
extern int gncore_dma_run(struct gncore *gncore, void *buf, size_t len,
unsigned long offset, enum dma_data_direction direction,
void (*dma_over_cb)(void *data, uint32_t status));
extern int gncore_dma_is_running(struct gncore *gncore);
extern void gncore_dma_map_page(struct gncore *gncore, int page_idx,
struct gncore_dma_item *item,
dma_addr_t dma_addr, struct scatterlist *sg,
uint32_t dev_mem_off);
......@@ -14,8 +14,11 @@
#include <linux/moduleparam.h>
#include <linux/gpio.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
#include <ual.h>
static int spec_test_irq;
module_param_named(test_irq, spec_test_irq, int, 0444);
......@@ -69,7 +72,7 @@ static int spec_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
fmc_free_sdb_tree(fmc);
fmc->flags &= ~(FMC_DEVICE_HAS_GOLDEN | FMC_DEVICE_HAS_CUSTOM);
ret = spec_load_fpga(spec, fw->data, fw->size);
if (ret <0) {
if (ret < 0) {
dev_err(dev, "write firmware \"%s\": error %i\n", gw, ret);
goto out;
}
......@@ -80,6 +83,9 @@ static int spec_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
out:
release_firmware(fw);
spec_ual_sdb_info(spec);
return ret;
}
......@@ -129,6 +135,8 @@ static irqreturn_t spec_vic_irq_handler(int id, void *data)
return IRQ_HANDLED;
}
static struct fmc_gpio spec_vic_gpio_cfg[] = {
{
.gpio = FMC_GPIO_IRQ(1),
......@@ -175,7 +183,14 @@ static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
ARRAY_SIZE(spec_vic_gpio_cfg));
}
} else {
rv = spec_shared_irq_request(fmc, handler, name, flags);
if (spec->ual) {
/* Put the UAL IRQ catcher in the middle */
spec->ual->tmp_handler = handler;
rv = spec_shared_irq_request(fmc, ual_irq_handler,
name, flags);
} else {
rv = spec_shared_irq_request(fmc, handler, name, flags);
}
pr_debug("Requesting irq '%s' in shared mode (rv %d)\n", name,
rv);
}
......@@ -551,6 +566,9 @@ out_free:
void spec_fmc_destroy(struct spec_dev *spec)
{
/* undo the things in the reverse order, but pin the device first */
if (!spec->fmc)
return;
get_device(&spec->fmc->dev);
spec_gpio_exit(spec->fmc);
fmc_device_unregister(spec->fmc);
......
......@@ -24,12 +24,17 @@
#include "spec.h"
#include "loader-ll.h"
#include <ual.h>
char *spec_fw_name = "fmc/spec-init.bin";
module_param_named(fw_name, spec_fw_name, charp, 0444);
int spec_use_msi = 0;
module_param_named(use_msi, spec_use_msi, int, 0444);
int export_ual = 0;
module_param_named(export_ual, export_ual, int, 0444);
/* Load the FPGA. This bases on loader-ll.c, a kernel/user space thing */
int spec_load_fpga(struct spec_dev *spec, const void *data, int size)
{
......@@ -148,6 +153,15 @@ static int spec_probe(struct pci_dev *pdev,
snprintf(spec->name, SPEC_NAME_LEN, "spec-%04x",
spec->pdev->bus->number << 8 | spec->pdev->devfn);
strncpy(spec->fwname, spec_fw_name, SPEC_FW_NAME_LEN);
if (export_ual) {
spec->ual = ual_create(spec->name, &spec_ual_op, spec);
if (IS_ERR_OR_NULL(spec->ual)) {
spec->ual = NULL;
goto out_unmap;
}
}
/* Done */
pci_set_drvdata(pdev, spec);
......@@ -175,6 +189,8 @@ static void spec_remove(struct pci_dev *pdev)
dev_info(&pdev->dev, "remove\n");
ual_destroy(spec->ual);
spec_fmc_destroy(spec);
for (i = 0; i < 3; i++) {
if (spec->remap[i])
......
/*
* Copyright (C) 2010-2012 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
* License : GPL version 2 or later
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <ual.h>
#include "spec.h"
#include "gncore-dma.h"
#include "loader-ll.h"
void spec_ual_sdb_info(struct spec_dev *spec)
{
int err;
fmc_free_sdb_tree(spec->fmc);
err = fmc_scan_sdb_tree(spec->fmc, 0);
if (err) {
dev_err(&spec->pdev->dev, "Cannot scan SDB: err %d\n", err);
return;
}
fmc_show_sdb_tree(spec->fmc);
if (!spec->ual)
return;
/* DMA irq base address - to avoid conflicts with FPGA driver */
spec->ual->irq_dma_base_addr = fmc_find_sdb_device(spec->fmc->sdb,
0xce42,
0xd5735ab4,
NULL);
spec->priv_dma = gncore_dma_init(spec->fmc);
if (IS_ERR(spec->priv_dma)) {
dev_err(&spec->pdev->dev,
"GNCORE DMA component is not part of the bitstream\n");
spec->priv_dma = NULL;
}
}
static int spec_ual_create(struct ual *ual)
{
struct spec_dev *spec = ual->priv;
spec_ual_sdb_info(spec);
return 0;
}
static void spec_ual_destroy(struct ual *ual)
{
struct spec_dev *spec = ual->priv;
if (spec->priv_dma)
gncore_dma_exit(spec->priv_dma);
}
static int spec_ual_irq_request(struct ual *ual, int src, irq_handler_t h,
char *name, int flags)
{
struct spec_dev *spec = ual->priv;
struct fmc_device *fmc = spec->fmc;
if (!(flags & IRQF_SHARED))
fmc->irq = src; /* VIC */
return fmc->op->irq_request(fmc, h, name, flags);
}
static void spec_ual_irq_free(struct ual *ual, int src)
{
struct spec_dev *spec = ual->priv;
struct fmc_device *fmc = spec->fmc;
if (src)
fmc->irq = src; /* VIC */
fmc->op->irq_free(fmc);
}
static void spec_ual_irq_ack(struct ual *ual)
{
struct spec_dev *spec = ual->priv;
struct fmc_device *fmc = spec->fmc;
fmc->op->irq_ack(fmc);
}
static int spec_ual_irq_is_managed(struct ual *ual, int src)
{
struct spec_dev *spec = ual->priv;
return vic_is_managed(spec->vic, src);
}
static int spec_fmc_reload(struct spec_dev *spec)
{
int err;
/* Remove previous FMC device */
spec_fmc_destroy(spec);
/* Load the golden firmware to allow FMC initialization */
err = spec_load_fpga_file(spec, spec_fw_name);
if (err)
return err;
/* Create a new FMC device */
return spec_fmc_create(spec);
}
static int spec_ual_load_fw_file(struct ual *ual, char *file)
{
struct spec_dev *spec = ual->priv;
int err;
/* Load a new set of FMC devices */
err = spec_fmc_reload(spec);
if (err)
return err;
/* Load the new firmware */
err = spec_load_fpga_file(spec, file);
if (err)
return err;
/* Update SDB */
spec_ual_sdb_info(spec);
return 0;
}
static int spec_ual_load_fw_raw(struct ual *ual, void *buf, size_t len)
{
struct spec_dev *spec = ual->priv;
int err;
/* Load a new set of FMC devices */
err = spec_fmc_reload(spec);
if (err)
goto out;
/* Program FPGA */
dev_info(&spec->pdev->dev, "%s %p %zu\n", __func__, buf, len);
err = spec_load_fpga(spec, buf, len);
if (err)
goto out;
/* Update SDB */
spec_ual_sdb_info(spec);
out:
return 0;
}
static uint32_t spec_ual_readl(struct ual *ual, unsigned long addr)
{
struct spec_dev *spec = ual->priv;
struct fmc_device *fmc = spec->fmc;
return fmc_readl(fmc, addr);
}
static void spec_ual_writel(struct ual *ual, uint32_t val, unsigned long addr)
{
struct spec_dev *spec = ual->priv;
struct fmc_device *fmc = spec->fmc;
fmc_writel(fmc, val, addr);
}
static int spec_ual_trans_is_running(struct ual *ual)
{
struct spec_dev *spec = ual->priv;
if (unlikely(!spec->priv_dma)) {
dev_err(&spec->pdev->dev, "GNCORE component not loaded\n");
return -EPERM;
}
return gncore_dma_is_running(spec->priv_dma);
}
/**
* FIXME we should not do this because it breaks the UAL independency
*/
static void spec_ual_trans_over(void *data, uint32_t status)
{
struct fmc_device *fmc = data;
struct spec_dev *spec = fmc->carrier_data;
struct ual *ual = spec->ual;
wake_up_interruptible(&ual->q_dma);
}
static void spec_ual_trans_run(struct ual *ual,
void (*trans_over)(void *data,
uint32_t status))
{
struct spec_dev *spec = ual->priv;
/* FIXME We are not using trans_over, but we should*/
if (unlikely(!spec->priv_dma)) {
dev_err(&spec->pdev->dev, "GNCORE component not loaded\n");
return;
}
/* Run DMA transfer */
gncore_dma_run(spec->priv_dma, ual->buffer, ual->len, ual->offset,
ual->direction, spec_ual_trans_over);
}
struct ual_op spec_ual_op = {
.create = spec_ual_create,
.destroy = spec_ual_destroy,
.readl = spec_ual_readl,
.writel = spec_ual_writel,
.irq_request = spec_ual_irq_request,
.irq_free = spec_ual_irq_free,
.irq_ack = spec_ual_irq_ack,
.irq_is_managed = spec_ual_irq_is_managed,
.load_fw_file = spec_ual_load_fw_file,
.load_fw_raw = spec_ual_load_fw_raw,
.trans_is_running = spec_ual_trans_is_running,
.trans_run = spec_ual_trans_run,
};
......@@ -132,6 +132,16 @@ irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec)
goto fail;
vec = &vic->vectors[index];
/*
* This 'if' may look like a waste of time and someone will have
* the temptation to optimize this with "#ifdef" or "likely()". But,
* even the oldest branch predictor can easily optimize this in few
* runs.
*/
if (spec->ual)
ual_catch_irq(spec->ual, vec->saved_id);
if (!vec->handler)
goto fail;
......@@ -145,6 +155,7 @@ fail:
}
/* NOTE: this function must be called while holding irq_lock */
/* NOTE: it overrides the previous handler if there */
int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
unsigned long id, irq_handler_t handler)
{
......
......@@ -14,6 +14,9 @@
#include <linux/completion.h>
#include <linux/fmc.h>
#include <linux/gpio.h>
#ifdef CONFIG_SPEC_UAL
#include "ual.h"
#endif
#define PCI_VENDOR_ID_CERN 0x10dc
#define PCI_DEVICE_ID_SPEC 0x018d
......@@ -23,6 +26,7 @@
#define SPEC_DEFAULT_LM32_ADDR 0x80000 /* used if "1" is passed */
#define SPEC_NAME_LEN 10
#define SPEC_FW_NAME_LEN 128
/* Our device structure */
struct spec_dev {
......@@ -39,8 +43,15 @@ struct spec_dev {
spinlock_t irq_lock;
char name[SPEC_NAME_LEN];
char fwname[SPEC_FW_NAME_LEN];
#ifdef CONFIG_SPEC_UAL
struct ual *ual;
#endif
void *priv_dma; /* private data for DMA engine */
};
#define SPEC_FLAG_FAKE_EEPROM 0x00000001
#define SPEC_FLAG_IRQS_REQUESTED 0x00000002
......@@ -156,4 +167,9 @@ void spec_vic_irq_free(struct spec_dev *spec, unsigned long id);
irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec);
extern int vic_is_managed(struct vic_irq_controller *vic, unsigned long id);
#ifdef CONFIG_SPEC_UAL
extern struct ual_op spec_ual_op;
extern void spec_ual_sdb_info(struct spec_dev *spec);
#endif
#endif /* __SPEC_H__ */
......@@ -46,7 +46,7 @@ static int spec_check_id(int bus, int dev)
f=fopen(buf,"r");
if (f==NULL){
fprintf(stderr,"error accessing to file\n");
fprintf(stderr,"error accessing to file: %s\n", strerror(errno));
return -1;
}
fscanf(f, "%x", &device);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment