Commit 061bbd7e authored by Federico Vaga's avatar Federico Vaga

Merge branch 'release/v2.1.0' into master

parents f63c5572 0c8a72ce
...@@ -6,11 +6,35 @@ ...@@ -6,11 +6,35 @@
Changelog Changelog
========= =========
2.0.0 - 2020-08-20 2.1.0 - 2020-11-09
================== ==================
Fixed Fixed
----- -----
- program 2 or more SPEC FPGAs in parallel. There is a bug in the - hdl: cross-page DMA failure
- sw: DMA pool memory leak
- sw: fix concurrent DMA tasklet
Changed
-------
- tst: keep the DMA interface open while testing to avoid continuos
memory re-allocation
Added
-----
- sw: tool to firmware version inspection
- sw: FLASH partitions
2.0.2 - 2020-09-29
==================
Fixed
-----
- hdl: L2P DMA issues reported with slower hosts
2.0.1 - 2020-08-20
==================
Fixed
-----
- sw: program 2 or more SPEC FPGAs in parallel. There is a bug in the
GN412x chip that we fixed in software by serializing any attempt of GN412x chip that we fixed in software by serializing any attempt of
parallel programming parallel programming
......
Subproject commit 258eb8e00f99f795fe9b98840b01ac4a8b92ec94 Subproject commit 347e0de1e0d91834d298a146569530b71adeb33a
Subproject commit e763762405dd5274d342285dbc64683221f1fb15 Subproject commit c629364388453726da401909b5154306ab4e6930
...@@ -604,7 +604,7 @@ begin -- architecture top ...@@ -604,7 +604,7 @@ begin -- architecture top
metadata_data <= x"53504543"; metadata_data <= x"53504543";
when x"2" => when x"2" =>
-- Version -- Version
metadata_data <= x"02000000"; metadata_data <= x"02000002";
when x"3" => when x"3" =>
-- BOM -- BOM
metadata_data <= x"fffe0000"; metadata_data <= x"fffe0000";
......
...@@ -7,11 +7,17 @@ import pytest ...@@ -7,11 +7,17 @@ import pytest
from PySPEC import PySPEC from PySPEC import PySPEC
@pytest.fixture(scope="function") @pytest.fixture(scope="module")
def spec(): def spec():
spec_dev = PySPEC(pytest.pci_id) spec_dev = PySPEC(pytest.pci_id)
yield spec_dev yield spec_dev
@pytest.fixture(scope="class")
def dma():
spec = PySPEC(pytest.pci_id)
with spec.dma() as spec_dma:
yield spec_dma
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption("--pci-id", parser.addoption("--pci-id",
required=True, help="SPEC PCI Identifier") required=True, help="SPEC PCI Identifier")
......
This diff is collapsed.
-include Makefile.specific -include Makefile.specific
-include $(REPO_PARENT)/parent_common.mk -include $(REPO_PARENT)/parent_common.mk
DIRS = kernel DIRS = kernel tools
.PHONY: all clean modules install modules_install coccicheck $(DIRS) .PHONY: all clean modules install modules_install coccicheck $(DIRS)
......
/*
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __LINUX_UAPI_SPEC_H
#define __LINUX_UAPI_SPEC_H
#ifndef __KERNEL__
#include <stdint.h>
#endif
#define SPEC_FMC_SLOTS 1
/* On FPGA components */
#define PCI_VENDOR_ID_CERN (0x10DC)
#define PCI_DEVICE_ID_SPEC_45T (0x018D)
#define PCI_DEVICE_ID_SPEC_100T (0x01A2)
#define PCI_DEVICE_ID_SPEC_150T (0x01A3)
#define PCI_VENDOR_ID_GENNUM (0x1A39)
#define PCI_DEVICE_ID_GN4124 (0x0004)
#define GN4124_GPIO_MAX 16
#define GN4124_GPIO_BOOTSEL0 15
#define GN4124_GPIO_BOOTSEL1 14
#define GN4124_GPIO_SPRI_DIN 13
#define GN4124_GPIO_SPRI_FLASH_CS 12
#define GN4124_GPIO_IRQ0 9
#define GN4124_GPIO_IRQ1 8
#define GN4124_GPIO_SCL 5
#define GN4124_GPIO_SDA 4
#define SPEC_DDR_SIZE (256 * 1024 * 1024)
#define SPEC_META_VENDOR_ID PCI_VENDOR_ID_CERN
#define SPEC_META_DEVICE_ID 0x53504543
#define SPEC_META_BOM_LE 0xFFFE0000
#define SPEC_META_BOM_END_MASK 0xFFFF0000
#define SPEC_META_BOM_VER_MASK 0x0000FFFF
#define SPEC_META_VERSION_MASK 0xFFFF0000
#define SPEC_META_VERSION_2_0 0x02000000
/**
* struct spec_meta_id Metadata
*/
struct spec_meta_id {
uint32_t vendor;
uint32_t device;
uint32_t version;
uint32_t bom;
uint32_t src[4];
uint32_t cap;
uint32_t uuid[4];
};
#define SPEC_META_VERSION_MAJ(_v) ((_v >> 24) & 0xFF)
#define SPEC_META_VERSION_MIN(_v) ((_v >> 16) & 0xFF)
#define SPEC_META_VERSION_PATCH(_v) (_v & 0xFFFF)
#endif /* __LINUX_UAPI_SPEC_H */
...@@ -15,6 +15,7 @@ ccflags-y += -I$(FPGA_MGR_ABS)/include ...@@ -15,6 +15,7 @@ ccflags-y += -I$(FPGA_MGR_ABS)/include
ccflags-$(CONFIG_FPGA_MGR_BACKPORT) += -DCONFIG_FPGA_MGR_BACKPORT ccflags-$(CONFIG_FPGA_MGR_BACKPORT) += -DCONFIG_FPGA_MGR_BACKPORT
ccflags-y += -I$(FMC_ABS)/include ccflags-y += -I$(FMC_ABS)/include
ccflags-y += -I$(SPI_ABS)/include ccflags-y += -I$(SPI_ABS)/include
ccflags-y += -I$(src)/../include
# priority to I2C, FMC headers from our sources # priority to I2C, FMC headers from our sources
LINUXINCLUDE := -I$(FMC_ABS)/include -I$(FMC_ABS)/include/linux -I$(I2C_ABS)/include -I$(I2C_ABS)/include/linux $(LINUXINCLUDE) LINUXINCLUDE := -I$(FMC_ABS)/include -I$(FMC_ABS)/include/linux -I$(I2C_ABS)/include -I$(I2C_ABS)/include/linux $(LINUXINCLUDE)
......
...@@ -156,13 +156,43 @@ static void gn412x_gpio_reg_write(struct gpio_chip *chip, ...@@ -156,13 +156,43 @@ static void gn412x_gpio_reg_write(struct gpio_chip *chip,
gn412x_iowrite32(gn412x, regval, reg); gn412x_iowrite32(gn412x, regval, reg);
} }
/**
* Enable Internal Gennum error's interrupts
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_enable_err(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg |= GNINT_STAT_ERR_ALL;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
/**
* disable Internal Gennum error's interrupts
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_disable_err(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg &= ~GNINT_STAT_ERR_ALL;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
/** /**
* Enable GPIO interrupts * Enable GPIO interrupts
* @gn412x gn412x device * @gn412x gn412x device
* *
* Return: 0 on success, otherwise a negative error number * Return: 0 on success, otherwise a negative error number
*/ */
static void gn412x_gpio_int_cfg_enable(struct gn412x_gpio_dev *gn412x) static void gn412x_gpio_int_cfg_enable_gpio(struct gn412x_gpio_dev *gn412x)
{ {
uint32_t int_cfg; uint32_t int_cfg;
...@@ -175,7 +205,7 @@ static void gn412x_gpio_int_cfg_enable(struct gn412x_gpio_dev *gn412x) ...@@ -175,7 +205,7 @@ static void gn412x_gpio_int_cfg_enable(struct gn412x_gpio_dev *gn412x)
* Disable GPIO interrupts from a single configuration space * Disable GPIO interrupts from a single configuration space
* @gn412x gn412x device * @gn412x gn412x device
*/ */
static void gn412x_gpio_int_cfg_disable(struct gn412x_gpio_dev *gn412x) static void gn412x_gpio_int_cfg_disable_gpio(struct gn412x_gpio_dev *gn412x)
{ {
uint32_t int_cfg; uint32_t int_cfg;
...@@ -435,7 +465,7 @@ static irqreturn_t gn412x_gpio_irq_handler_t(int irq, void *arg) ...@@ -435,7 +465,7 @@ static irqreturn_t gn412x_gpio_irq_handler_t(int irq, void *arg)
out_enable_irq: out_enable_irq:
/* Re-enable the GPIO interrupts, we are done here */ /* Re-enable the GPIO interrupts, we are done here */
gn412x_gpio_int_cfg_enable(gn412x); gn412x_gpio_int_cfg_enable_gpio(gn412x);
return ret; return ret;
} }
...@@ -454,6 +484,13 @@ static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg) ...@@ -454,6 +484,13 @@ static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg)
if (unlikely(int_stat & GNINT_STAT_SW_ALL)) /* only for testing */ if (unlikely(int_stat & GNINT_STAT_SW_ALL)) /* only for testing */
return spec_irq_sw_handler(irq, gn412x); return spec_irq_sw_handler(irq, gn412x);
if (WARN(int_stat & GNINT_STAT_ERR_ALL, "GN412x ERROR 0x%08x",
int_stat)) {
gn412x_iowrite32(gn412x, int_stat & GNINT_STAT_ERR_ALL,
GNINT_STAT);
return IRQ_HANDLED;
}
/* /*
* Do not listen to new interrupts while handling the current GPIOs. * Do not listen to new interrupts while handling the current GPIOs.
* This may take a while since the chain behind each GPIO can be long. * This may take a while since the chain behind each GPIO can be long.
...@@ -462,8 +499,10 @@ static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg) ...@@ -462,8 +499,10 @@ static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg)
* devices sharing the same IRQ to wait for us; just to play safe, * devices sharing the same IRQ to wait for us; just to play safe,
* let's disable interrupts. Within the thread we will re-enable them * let's disable interrupts. Within the thread we will re-enable them
* when we are ready (like IRQF_ONESHOT). * when we are ready (like IRQF_ONESHOT).
*
* We keep the error interrupts enabled
*/ */
gn412x_gpio_int_cfg_disable(gn412x); gn412x_gpio_int_cfg_disable_gpio(gn412x);
return IRQ_WAKE_THREAD; return IRQ_WAKE_THREAD;
} }
...@@ -521,7 +560,8 @@ static int gn412x_gpio_probe(struct platform_device *pdev) ...@@ -521,7 +560,8 @@ static int gn412x_gpio_probe(struct platform_device *pdev)
} }
gn412x_iowrite32(gn412x, 0, GNGPIO_BYPASS_MODE); gn412x_iowrite32(gn412x, 0, GNGPIO_BYPASS_MODE);
gn412x_gpio_int_cfg_disable(gn412x); gn412x_gpio_int_cfg_disable_err(gn412x);
gn412x_gpio_int_cfg_disable_gpio(gn412x);
gn412x_iowrite32(gn412x, 0xFFFF, GNGPIO_INT_MASK_SET); gn412x_iowrite32(gn412x, 0xFFFF, GNGPIO_INT_MASK_SET);
gn412x->irqchip.name = "GN412X-GPIO", gn412x->irqchip.name = "GN412X-GPIO",
...@@ -594,7 +634,8 @@ static int gn412x_gpio_probe(struct platform_device *pdev) ...@@ -594,7 +634,8 @@ static int gn412x_gpio_probe(struct platform_device *pdev)
goto err_req; goto err_req;
} }
gn412x_gpio_int_cfg_enable(gn412x); gn412x_gpio_int_cfg_enable_err(gn412x);
gn412x_gpio_int_cfg_enable_gpio(gn412x);
gn412x_dbg_init(gn412x); gn412x_dbg_init(gn412x);
...@@ -621,7 +662,9 @@ static int gn412x_gpio_remove(struct platform_device *pdev) ...@@ -621,7 +662,9 @@ static int gn412x_gpio_remove(struct platform_device *pdev)
gn412x_dbg_exit(gn412x); gn412x_dbg_exit(gn412x);
gn412x_gpio_int_cfg_disable(gn412x); gn412x_gpio_int_cfg_disable_gpio(gn412x);
gn412x_gpio_int_cfg_disable_err(gn412x);
free_irq(platform_get_irq(pdev, 0), gn412x); free_irq(platform_get_irq(pdev, 0), gn412x);
gn412x_gpio_irq_set_nested_thread_all(gn412x, false); gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
......
...@@ -7,8 +7,22 @@ ...@@ -7,8 +7,22 @@
#define __GN412X_H__ #define __GN412X_H__
#define GNINT_STAT_GPIO BIT(15) #define GNINT_STAT_GPIO BIT(15)
#define GNINT_STAT_ALI6 BIT(14)
#define GNINT_STAT_ALI5 BIT(13)
#define GNINT_STAT_ALI4 BIT(12)
#define GNINT_STAT_ALI3 BIT(11)
#define GNINT_STAT_ALI2 BIT(10)
#define GNINT_STAT_ALI1 BIT(9)
#define GNINT_STAT_ALI0 BIT(8)
#define GNINT_STAT_SW0 BIT(2) #define GNINT_STAT_SW0 BIT(2)
#define GNINT_STAT_SW1 BIT(3) #define GNINT_STAT_SW1 BIT(3)
#define GNINT_STAT_ERR_ALL (GNINT_STAT_ALI0 | \
GNINT_STAT_ALI1 | \
GNINT_STAT_ALI2 | \
GNINT_STAT_ALI3 | \
GNINT_STAT_ALI4 | \
GNINT_STAT_ALI5 | \
GNINT_STAT_ALI6)
#define GNINT_STAT_SW_ALL (GNINT_STAT_SW0 | GNINT_STAT_SW1) #define GNINT_STAT_SW_ALL (GNINT_STAT_SW0 | GNINT_STAT_SW1)
/* Registers for GN4124 access */ /* Registers for GN4124 access */
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/mtd/partitions.h>
#include "linux/printk.h" #include "linux/printk.h"
#include "spec.h" #include "spec.h"
...@@ -646,10 +647,23 @@ static struct resource spec_fpga_spi_res[] = { ...@@ -646,10 +647,23 @@ static struct resource spec_fpga_spi_res[] = {
}, },
}; };
static struct mtd_partition spec_flash_parts[] = {
{
.name = "AFPGA",
.offset = 0x00000000,
.size = 5 * SZ_1M,
}, {
.name = "AFPGA_DATA",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
};
struct flash_platform_data spec_flash_pdata = { struct flash_platform_data spec_flash_pdata = {
.name = "spec-flash", .name = "spec-flash",
.parts = NULL, .parts = spec_flash_parts,
.nr_parts = 0, .nr_parts = ARRAY_SIZE(spec_flash_parts),
.type = "m25p32", .type = "m25p32",
}; };
......
...@@ -783,6 +783,6 @@ MODULE_VERSION(VERSION); ...@@ -783,6 +783,6 @@ MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the 'Simple PCIe FMC Carrier' a.k.a. SPEC"); MODULE_DESCRIPTION("Driver for the 'Simple PCIe FMC Carrier' a.k.a. SPEC");
MODULE_DEVICE_TABLE(pci, spec_pci_tbl); MODULE_DEVICE_TABLE(pci, spec_pci_tbl);
MODULE_SOFTDEP("pre: gn412x_gpio gn412x_fcl htvic spec_gn412x_dma i2c_mux i2c-ocores spi-ocores"); MODULE_SOFTDEP("pre: gn412x_gpio gn412x_fcl htvic spec_gn412x_dma i2c_mux i2c-ocores spi-ocores m25p80");
ADDITIONAL_VERSIONS; ADDITIONAL_VERSIONS;
...@@ -310,25 +310,17 @@ static void gn412x_dma_ctrl_swapping(struct gn412x_dma_device *gn412x_dma, ...@@ -310,25 +310,17 @@ static void gn412x_dma_ctrl_swapping(struct gn412x_dma_device *gn412x_dma,
static enum gn412x_dma_state gn412x_dma_state(struct gn412x_dma_device *gn412x_dma) static enum gn412x_dma_state gn412x_dma_state(struct gn412x_dma_device *gn412x_dma)
{ {
return ioread32(gn412x_dma->addr + GN412X_DMA_STAT); return ioread32(gn412x_dma->addr + GN412X_DMA_STAT) & 0x3;
} }
static bool gn412x_dma_is_busy(struct gn412x_dma_device *gn412x_dma) static bool gn412x_dma_is_busy(struct gn412x_dma_device *gn412x_dma)
{ {
uint32_t status; return gn412x_dma_state(gn412x_dma) == GN412X_DMA_STAT_BUSY;
status = ioread32(gn412x_dma->addr + GN412X_DMA_STAT);
return status & GN412X_DMA_STAT_BUSY;
} }
static bool gn412x_dma_is_abort(struct gn412x_dma_device *gn412x_dma) static bool gn412x_dma_is_abort(struct gn412x_dma_device *gn412x_dma)
{ {
uint32_t status; return gn412x_dma_state(gn412x_dma) == GN412X_DMA_STAT_ABORTED;
status = ioread32(gn412x_dma->addr + GN412X_DMA_STAT);
return status & GN412X_DMA_STAT_ABORTED;
} }
static void gn412x_dma_irq_ack(struct gn412x_dma_device *gn412x_dma) static void gn412x_dma_irq_ack(struct gn412x_dma_device *gn412x_dma)
...@@ -535,6 +527,32 @@ err: ...@@ -535,6 +527,32 @@ err:
return NULL; return NULL;
} }
static void gn412x_dma_tx_free(struct gn412x_dma_tx *tx)
{
struct gn412x_dma_device *gn412x_dma;
int i;
if (unlikely(!tx))
return;
gn412x_dma = to_gn412x_dma_device(tx->tx.chan->device);
for (i = 0; i < tx->sg_len; ++i) {
dma_addr_t phys;
dev_dbg(&gn412x_dma->pdev->dev,
"Release TX (%p) DMA desc %d\n", tx, i);
if (i == 0) {
phys = tx->tx.phys;
} else {
phys = tx->sgl_hw[i - 1]->next_addr_h;
phys <<= 32;
phys |= tx->sgl_hw[i - 1]->next_addr_l;
}
dma_pool_free(gn412x_dma->pool, tx->sgl_hw[i], phys);
}
kfree(tx->sgl_hw);
kfree(tx);
}
static void gn412x_dma_schedule_next(struct gn412x_dma_chan *gn412x_dma_chan) static void gn412x_dma_schedule_next(struct gn412x_dma_chan *gn412x_dma_chan)
{ {
unsigned long flags; unsigned long flags;
...@@ -609,27 +627,33 @@ static int gn412x_dma_slave_config(struct dma_chan *chan, ...@@ -609,27 +627,33 @@ static int gn412x_dma_slave_config(struct dma_chan *chan,
static int gn412x_dma_terminate_all(struct dma_chan *chan) static int gn412x_dma_terminate_all(struct dma_chan *chan)
{ {
struct gn412x_dma_chan *gn412x_dma_chan = to_gn412x_dma_chan(chan);
struct gn412x_dma_device *gn412x_dma; struct gn412x_dma_device *gn412x_dma;
struct gn412x_dma_tx *tx; struct gn412x_dma_tx *tx, *tx_tmp;
unsigned long flags;
gn412x_dma = to_gn412x_dma_device(chan->device); gn412x_dma = to_gn412x_dma_device(chan->device);
gn412x_dma_ctrl_abort(gn412x_dma);
/* FIXME remove all pending */
if (!gn412x_dma_is_abort(gn412x_dma)) {
dev_err(&gn412x_dma->pdev->dev,
"Failed to abort DMA transfer\n");
return -EINVAL;
}
tx = to_gn412x_dma_chan(chan)->tx_curr; spin_lock_irqsave(&gn412x_dma_chan->lock, flags);
if (tx && tx->tx.callback_result) { list_for_each_entry_safe(tx, tx_tmp,
const struct dmaengine_result result = { &gn412x_dma_chan->pending_list, list) {
.result = DMA_TRANS_ABORTED, list_del(&tx->list);
.residue = 0, gn412x_dma_tx_free(tx);
};
tx->tx.callback_result(tx->tx.callback_param, &result);
} }
tx = gn412x_dma_chan->tx_curr;
if (tx) {
gn412x_dma_ctrl_abort(gn412x_dma);
gn412x_dma_chan->tx_curr = NULL;
if (tx->tx.callback_result && gn412x_dma_is_abort(gn412x_dma)) {
const struct dmaengine_result result = {
.result = DMA_TRANS_ABORTED,
.residue = 0,
};
tx->tx.callback_result(tx->tx.callback_param, &result);
}
gn412x_dma_tx_free(tx);
}
spin_unlock_irqrestore(&gn412x_dma_chan->lock, flags);
return 0; return 0;
} }
...@@ -656,13 +680,13 @@ static int gn412x_dma_device_control(struct dma_chan *chan, ...@@ -656,13 +680,13 @@ static int gn412x_dma_device_control(struct dma_chan *chan,
} }
#endif #endif
static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg) static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg)
{ {
struct gn412x_dma_device *gn412x_dma = arg; struct gn412x_dma_device *gn412x_dma = arg;
struct gn412x_dma_chan *chan = &gn412x_dma->chan; struct gn412x_dma_chan *chan = &gn412x_dma->chan;
struct gn412x_dma_tx *tx; struct gn412x_dma_tx *tx;
unsigned long flags; unsigned long flags;
unsigned int i;
enum gn412x_dma_state state; enum gn412x_dma_state state;
/* FIXME check for spurious - need HDL fix */ /* FIXME check for spurious - need HDL fix */
...@@ -683,6 +707,7 @@ static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg) ...@@ -683,6 +707,7 @@ static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg)
spin_unlock_irqrestore(&chan->lock, flags); spin_unlock_irqrestore(&chan->lock, flags);
state = gn412x_dma_state(gn412x_dma); state = gn412x_dma_state(gn412x_dma);
gn412x_dma_schedule_next(chan);
switch (state) { switch (state) {
case GN412X_DMA_STAT_IDLE: case GN412X_DMA_STAT_IDLE:
dma_cookie_complete(&tx->tx); dma_cookie_complete(&tx->tx);
...@@ -706,7 +731,8 @@ static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg) ...@@ -706,7 +731,8 @@ static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg)
tx->tx.callback_result(tx->tx.callback_param, &result); tx->tx.callback_result(tx->tx.callback_param, &result);
} }
dev_err(&gn412x_dma->pdev->dev, "DMA transfer failed: error\n"); dev_err(&gn412x_dma->pdev->dev,
"DMA transfer failed: error\n");
break; break;
default: default:
dev_err(&gn412x_dma->pdev->dev, dev_err(&gn412x_dma->pdev->dev,
...@@ -716,12 +742,7 @@ static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg) ...@@ -716,12 +742,7 @@ static irqreturn_t gn412x_dma_irq_handler(int irq, void *arg)
} }
/* Clean up memory */ /* Clean up memory */
for (i = 0; i < tx->sg_len; ++i) gn412x_dma_tx_free(tx);
dma_pool_free(gn412x_dma->pool, tx->sgl_hw[i], tx->tx.phys);
kfree(tx->sgl_hw);
kfree(tx);
gn412x_dma_schedule_next(chan);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -779,6 +800,8 @@ static int gn412x_dma_engine_init(struct gn412x_dma_device *gn412x_dma, ...@@ -779,6 +800,8 @@ static int gn412x_dma_engine_init(struct gn412x_dma_device *gn412x_dma,
dma->dev = parent; dma->dev = parent;
if (dma_set_mask(dma->dev, DMA_BIT_MASK(64))) { if (dma_set_mask(dma->dev, DMA_BIT_MASK(64))) {
dev_warn(dma->dev, "64-bit DMA addressing not available\n");
/* Check if hardware supports 32-bit DMA */ /* Check if hardware supports 32-bit DMA */
if (dma_set_mask(dma->dev, DMA_BIT_MASK(32))) { if (dma_set_mask(dma->dev, DMA_BIT_MASK(32))) {
dev_err(dma->dev, dev_err(dma->dev,
......
...@@ -15,32 +15,11 @@ ...@@ -15,32 +15,11 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/fmc.h> #include <linux/fmc.h>
#include <uapi/linux/spec.h>
#include "gn412x.h" #include "gn412x.h"
#include "spec-core-fpga.h" #include "spec-core-fpga.h"
#define SPEC_FMC_SLOTS 1
/* On FPGA components */
#define PCI_VENDOR_ID_CERN (0x10DC)
#define PCI_DEVICE_ID_SPEC_45T (0x018D)
#define PCI_DEVICE_ID_SPEC_100T (0x01A2)
#define PCI_DEVICE_ID_SPEC_150T (0x01A3)
#define PCI_VENDOR_ID_GENNUM (0x1A39)
#define PCI_DEVICE_ID_GN4124 (0x0004)
#define GN4124_GPIO_MAX 16
#define GN4124_GPIO_BOOTSEL0 15
#define GN4124_GPIO_BOOTSEL1 14
#define GN4124_GPIO_SPRI_DIN 13
#define GN4124_GPIO_SPRI_FLASH_CS 12
#define GN4124_GPIO_IRQ0 9
#define GN4124_GPIO_IRQ1 8
#define GN4124_GPIO_SCL 5
#define GN4124_GPIO_SDA 4
#define SPEC_DDR_SIZE (256 * 1024 * 1024)
/** /**
* @SPEC_FPGA_SELECT_FPGA_FLASH: (default) the FPGA is an SPI master that can * @SPEC_FPGA_SELECT_FPGA_FLASH: (default) the FPGA is an SPI master that can
* access the flash (at boot it takes its * access the flash (at boot it takes its
...@@ -79,27 +58,6 @@ enum { ...@@ -79,27 +58,6 @@ enum {
SPEC_META_UUID = SPEC_META_BASE + FPGA_META_UUID, SPEC_META_UUID = SPEC_META_BASE + FPGA_META_UUID,
}; };
#define SPEC_META_VENDOR_ID PCI_VENDOR_ID_CERN
#define SPEC_META_DEVICE_ID 0x53504543
#define SPEC_META_BOM_LE 0xFFFE0000
#define SPEC_META_BOM_END_MASK 0xFFFF0000
#define SPEC_META_BOM_VER_MASK 0x0000FFFF
#define SPEC_META_VERSION_MASK 0xFFFF0000
#define SPEC_META_VERSION_2_0 0x02000000
/**
* struct spec_meta_id Metadata
*/
struct spec_meta_id {
uint32_t vendor;
uint32_t device;
uint32_t version;
uint32_t bom;
uint32_t src[4];
uint32_t cap;
uint32_t uuid[4];
};
/** /**
* struct spec_fpga - it contains data to handle the FPGA * struct spec_fpga - it contains data to handle the FPGA
* *
......
spec-firmware-version
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# Copyright (C) 2020 CERN
# If it exists includes Makefile.specific. In this Makefile, you should put
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
DESTDIR ?= /usr/local/
TOOLS = spec-firmware-version
INCLUDE := -I. -I../kernel -I../include/uapi
CFLAGS = -ggdb $(INCLUDE) -Wall -Werror $(EXTRACFLAGS)
GIT_VERSION := $(shell git describe --dirty --long --tags)
CFLAGS += -DGIT_VERSION="\"$(GIT_VERSION)\""
CPPCHECK ?= cppcheck
all: $(TOOLS)
$(TOOLS):
clean:
rm -f $(TOOLS)
install:
install -d $(DESTDIR)/bin
install -D $(TOOLS) $(DESTDIR)/bin
cppcheck:
$(CPPCHECK) -q $(INCLUDE) --suppress=missingIncludeSystem --enable=all *.c *.h --error-exitcode=1
.PHONY=cppcheck
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include <libgen.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/spec.h>
#include <spec-core-fpga.h>
static const char git_version[] = "git version: " GIT_VERSION;
static char *name;
static bool singleline = false;
static unsigned int verbose;
static void help(void)
{
fprintf(stderr, "%s [options]\n"
"\tPrint the firmware version\n"
"\t-p <PCIID>\n"
"\t-b print spec-base\n"
"\t-a print spec-application\n"
"\t-1 print on a single line\n"
"\t-V print version\n"
"\t-h print help\n",
name);
}
static void print_version(void)
{
printf("%s version %s\n", name, git_version);
}
static void cleanup(void)
{
if (name)
free(name);
}
static const char *bom_to_str(uint32_t bom)
{
if ((bom & SPEC_META_BOM_END_MASK) == SPEC_META_BOM_LE)
return "little-endian";
else
return "wrong";
}
static void print_meta_id_one(struct spec_meta_id *rom)
{
fprintf(stdout, "0x%08x,0x%08x,%u.%u.%u",
rom->vendor,
rom->device,
SPEC_META_VERSION_MAJ(rom->version),
SPEC_META_VERSION_MIN(rom->version),
SPEC_META_VERSION_PATCH(rom->version));
if (verbose > 1) {
fprintf(stdout, ",%08x%08x%08x%08x,%s,%08x%08x%08x%08x",
rom->src[0], rom->src[1], rom->src[2], rom->src[3],
bom_to_str(rom->bom),
rom->uuid[0], rom->uuid[1], rom->uuid[2], rom->uuid[3]);
}
fputc('\n', stdout);
}
static void print_meta_id(struct spec_meta_id *rom)
{
fputc('\n', stdout);
fprintf(stdout, " vendor : 0x%08x\n", rom->vendor);
fprintf(stdout, " device : 0x%08x\n", rom->device);
fprintf(stdout, " version : %u.%u.%u\n",
SPEC_META_VERSION_MAJ(rom->version),
SPEC_META_VERSION_MIN(rom->version),
SPEC_META_VERSION_PATCH(rom->version));
if (verbose > 1) {
fprintf(stdout, " byte-order : %s\n", bom_to_str(rom->bom));
fprintf(stdout, " sources : %08x%08x%08x%08x\n",
rom->src[0], rom->src[1], rom->src[2], rom->src[3]);
fprintf(stdout, " capabilities : 0x%08x\n", rom->cap);
fprintf(stdout, " UUID : %08x%08x%08x%08x\n",
rom->uuid[0], rom->uuid[1],
rom->uuid[2], rom->uuid[3]);
}
}
static int print_base_meta_id(int fd)
{
struct spec_meta_id *rom;
rom = mmap(NULL, sizeof(*rom), PROT_READ, MAP_SHARED, fd,
SPEC_BASE_REGS_METADATA);
if ((long)rom == -1) {
fputs("Failed while reading SPEC-BASE FPGA ROM\n", stderr);
return -1;
}
fputs("spec-base: ", stdout);
if (singleline)
print_meta_id_one(rom);
else
print_meta_id(rom);
munmap(rom, sizeof(*rom));
return 0;
}
static off_t app_meta_id_offset(int fd)
{
off_t offset;
void *regs;
regs = mmap(NULL, 0x100, PROT_READ, MAP_SHARED, fd, 0);
if ((long)regs == -1)
return -1;
offset = *((uint32_t *)(regs + SPEC_BASE_REGS_CSR_APP_OFFSET));
munmap(regs, 0x100);
return offset;
}
static int print_app_meta_id(int fd)
{
struct spec_meta_id *rom;
off_t offset;
offset = app_meta_id_offset(fd);
if (offset < 0) {
fputs("Can't get spec-app offset\n", stderr);
return -1;
}
if (offset == 0) {
fputs("spec-application:\n None\n", stderr);
return 0;
}
rom = mmap(NULL, sizeof(*rom), PROT_READ, MAP_SHARED, fd, offset);
if ((long)rom == -1) {
fputs("Failed while reading SPEC-APP FPGA ROM\n", stderr);
return -1;
}
fputs("spec-application: ", stdout);
if (singleline)
print_meta_id_one(rom);
else
print_meta_id(rom);
munmap(rom, sizeof(*rom));
return 0;
}
#define PCIID_STR_LEN 16
int main(int argc, char *argv[])
{
bool base = false, app = false;
int err;
int fd;
char path[128];
char opt;
char pciid_str[PCIID_STR_LEN] = "\0";
name = strndup(basename(argv[0]), 64);
if (!name)
exit(EXIT_FAILURE);
err = atexit(cleanup);
if (err)
exit(EXIT_FAILURE);
while ((opt = getopt(argc, argv, "h?Vvp:ba1")) != -1) {
switch (opt) {
case 'h':
case '?':
help();
exit(EXIT_SUCCESS);
case 'V':
print_version();
exit(EXIT_SUCCESS);
case 'p':
strncpy(pciid_str, optarg, PCIID_STR_LEN);
break;
case 'a':
app = true;
break;
case 'b':
base = true;
break;
case 'v':
verbose++;
break;
case '1':
singleline = true;
break;
}
}
if (strlen(pciid_str) == 0) {
fputs("PCI ID is mandatory\n", stderr);
help();
exit(EXIT_FAILURE);
}
snprintf(path, 128, "/sys/bus/pci/devices/%s/resource0", pciid_str);
fd = open(path, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can't open \"%s\": %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
if (base)
err = print_base_meta_id(fd);
if (app)
err = print_app_meta_id(fd);
close(fd);
exit(err ? EXIT_FAILURE : EXIT_SUCCESS);
}
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