Commit 7035a37a authored by Federico Vaga's avatar Federico Vaga

sw:drv: re-apply lost changes in sw|hdl merge

something was defentivelly wrong during the merge and I missed it.
This patch re-apply the good code
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent d36a4c14
......@@ -7,13 +7,37 @@ endif
endif
# add versions of used submodules
CONFIG_FPGA_MGR_BACKPORT_INCLUDE := -I$(CONFIG_FPGA_MGR_BACKPORT_PATH_ABS)/include
CONFIG_FPGA_MGR_BACKPORT_INCLUDE += -I$(CONFIG_FPGA_MGR_BACKPORT_PATH_ABS)/include/linux
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS)"
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
ccflags-y += -I$(VMEBRIDGE_ABS)/include -I$(FPGA_MGR_ABS)/include
ccflags-y += -DVERSION=\"$(VERSION)\"
ccflags-y += -Wall -Werror
ccflags-y += -I$(VMEBRIDGE_ABS)/include
ccflags-y += -I$(FPGA_MGR_ABS)/include
ccflags-$(CONFIG_FPGA_MGR_BACKPORT) += -DCONFIG_FPGA_MGR_BACKPORT
ccflags-$(CONFIG_FPGA_MGR_BACKPORT) += $(CONFIG_FPGA_MGR_BACKPORT_INCLUDE)
ccflags-y += -I$(FMC_ABS)/include
ccflags-y += -I$(I2C_ABS)/include
# priority to I2C headers from our sources
LINUXINCLUDE := -I$(FMC_ABS)/include -I$(FMC_ABS)/include/linux -I$(I2C_ABS)/include -I$(I2C_ABS)/include/linux $(LINUXINCLUDE)
ifeq ($(CONFIG_FPGA_MGR_BACKPORT), y)
LINUXINCLUDE := $(CONFIG_FPGA_MGR_BACKPORT_INCLUDE) $(LINUXINCLUDE)
KBUILD_EXTRA_SYMBOLS += $(CONFIG_FPGA_MGR_BACKPORT_PATH_ABS)/drivers/fpga/Module.symvers
endif
KBUILD_EXTRA_SYMBOLS += $(FMC_ABS)/drivers/fmc/Module.symvers
KBUILD_EXTRA_SYMBOLS += $(VMEBRIDGE_ABS)/driver/Module.symvers
KBUILD_EXTRA_SYMBOLS += $(FPGA_MGR_ABS)/drivers/fpga/Module.symvers
KBUILD_EXTRA_SYMBOLS += $(FMC_ABS)/drivers/fmc/Module.symvers
KBUILD_EXTRA_SYMBOLS += $(I2C_BUS)/drivers/i2c/busses/Module.symvers
obj-m := svec.o
obj-m := svec-fmc-carrier.o
svec-objs := svec-core.o
svec-fmc-carrier-objs := svec-core.o
svec-fmc-carrier-objs += svec-core-fpga.o
svec-fmc-carrier-objs += svec-compat.o
-include Makefile.specific
# include parent_common.mk for buildsystem's defines
#use absolute path for REPO_PARENT
REPO_PARENT ?= $(shell /bin/pwd)/../..
-include $(REPO_PARENT)/parent_common.mk
LINUX ?= /lib/modules/$(shell uname -r)/build
KVERSION ?= $(shell uname -r)
LINUX ?= /lib/modules/$(KVERSION)/build
VMEBRIDGE_ABS ?= $(abspath $(VMEBRIDGE))
CONFIG_FPGA_MGR_BACKPORT_PATH_ABS ?= $(abspath $(CONFIG_FPGA_MGR_BACKPORT_PATH))
VMEBRIDGE_ABS ?= $(abspath $(VMEBRIDGE))
FPGA_MGR_ABS ?= $(abspath $(FPGA_MGR))
FMC_ABS ?= $(abspath $(FMC))
I2C_ABS ?= $(abspath $(I2C))
GIT_VERSION = $(shell git describe --dirty --long --tags)
export GIT_VERSION
VERSION = $(shell git describe --dirty --long --tags)
all: modules
.PHONY: all modules clean help install modules_install
modules help install modules_install:
$(MAKE) -C $(LINUX) M=$(shell pwd) GIT_VERSION=$(GIT_VERSION) VMEBRIDGE_ABS=$(VMEBRIDGE_ABS) FPGA_MGR_ABS=$(FPGA_MGR_ABS) $@
$(MAKE) -C $(LINUX) M=$(shell pwd) \
VERSION=$(VERSION) \
VMEBRIDGE_ABS=$(VMEBRIDGE_ABS) \
CONFIG_FPGA_MGR_BACKPORT_PATH_ABS=$(CONFIG_FPGA_MGR_BACKPORT_PATH_ABS) \
CONFIG_FPGA_MGR_BACKPORT=$(CONFIG_FPGA_MGR_BACKPORT) \
FMC_ABS=$(FMC_ABS) \
I2C_ABS=$(I2C_ABS) \
$@
# be able to run the "clean" rule even if $(LINUX) is not valid
clean:
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/version.h>
#include "svec-compat.h"
#if KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE && !defined(CONFIG_FPGA_MGR_BACKPORT)
struct fpga_manager *__fpga_mgr_get(struct device *dev)
{
struct fpga_manager *mgr;
int ret = -ENODEV;
mgr = to_fpga_manager(dev);
if (!mgr)
goto err_dev;
/* Get exclusive use of fpga manager */
if (!mutex_trylock(&mgr->ref_mutex)) {
ret = -EBUSY;
goto err_dev;
}
if (!try_module_get(dev->parent->driver->owner))
goto err_ll_mod;
return mgr;
err_ll_mod:
mutex_unlock(&mgr->ref_mutex);
err_dev:
put_device(dev);
return ERR_PTR(ret);
}
static int fpga_mgr_dev_match(struct device *dev, const void *data)
{
return dev->parent == data;
}
/**
* fpga_mgr_get - get an exclusive reference to a fpga mgr
* @dev:parent device that fpga mgr was registered with
*
* Given a device, get an exclusive reference to a fpga mgr.
*
* Return: fpga manager struct or IS_ERR() condition containing error code.
*/
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
struct class *fpga_mgr_class = (struct class *) kallsyms_lookup_name("fpga_mgr_class");
struct device *mgr_dev;
mgr_dev = class_find_device(fpga_mgr_class, NULL, dev, fpga_mgr_dev_match);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
return __fpga_mgr_get(mgr_dev);
}
#endif
static int __compat_svec_fw_load(struct fpga_manager *mgr, const char *name)
{
#if KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE && !defined(CONFIG_FPGA_MGR_BACKPORT)
#if KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE
return fpga_mgr_firmware_load(mgr, 0, name);
#else
struct fpga_image_info image;
memset(&image, 0, sizeof(image));
return fpga_mgr_firmware_load(mgr, &image, name);
#endif
#else
struct fpga_image_info image;
memset(&image, 0, sizeof(image));
image.firmware_name = (char *)name;
image.dev = mgr->dev.parent;
return fpga_mgr_load(mgr, &image);
#endif
}
int compat_svec_fw_load(struct svec_dev *svec_dev, const char *name)
{
struct fpga_manager *mgr;
int err;
mgr = fpga_mgr_get(&svec_dev->vdev->dev);
if (IS_ERR(mgr))
return -ENODEV;
err = fpga_mgr_lock(mgr);
if (err)
goto out;
err = __compat_svec_fw_load(mgr, name);
fpga_mgr_unlock(mgr);
out:
fpga_mgr_put(mgr);
return err;
}
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __SVEC_COMPAT_H__
#define __SVEC_COMPAT_H__
#include <linux/fpga/fpga-mgr.h>
#include <linux/types.h>
#include <linux/version.h>
#include "svec.h"
#if KERNEL_VERSION(4, 10, 0) <= LINUX_VERSION_CODE
#if KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE
/* So that we select the buffer size because smaller */
#define compat_fpga_ops_initial_header_size .initial_header_size = 0xFFFFFFFF,
#else
#define compat_fpga_ops_initial_header_size .initial_header_size = 0,
#endif
#else
#define compat_fpga_ops_initial_header_size
#endif
#if KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE && !defined(CONFIG_FPGA_MGR_BACKPORT)
#define compat_fpga_ops_groups
#else
#define compat_fpga_ops_groups .groups = NULL,
#endif
#if KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE && !defined(CONFIG_FPGA_MGR_BACKPORT)
struct fpga_image_info;
#endif
int compat_svec_fw_load(struct svec_dev *svec_dev, const char *name);
#if KERNEL_VERSION(3, 11, 0) > LINUX_VERSION_CODE
#define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \
_name##_show, _name##_store)
#define __ATTR_WO(_name) { \
.attr = { .name = __stringify(_name), .mode = S_IWUSR }, \
.store = _name##_store, \
}
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
#endif
#endif /* __SVEC_COMPAT_H__ */
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/mfd/core.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/types.h>
#include <linux/platform_data/i2c-ocores.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/delay.h>
#include "svec.h"
#include "svec-core-fpga.h"
enum svec_fpga_irq_lines {
SVEC_FPGA_IRQ_FMC_I2C = 0,
SVEC_FPGA_IRQ_SPI,
};
enum svec_fpga_csr_offsets {
SVEC_FPGA_CSR_APP_OFF = SVEC_TEMPLATE_REGS_CSR + 0x00,
SVEC_FPGA_CSR_RESETS = SVEC_TEMPLATE_REGS_CSR + 0x04,
SVEC_FPGA_CSR_FMC_PRESENT = SVEC_TEMPLATE_REGS_CSR + 0x08,
SVEC_FPGA_CSR_DDR_STATUS = SVEC_TEMPLATE_REGS_CSR + 0x10,
SVEC_FPGA_CSR_PCB_REV = SVEC_TEMPLATE_REGS_CSR + 0x14,
};
enum svec_fpga_therm_offsets {
SVEC_FPGA_THERM_SERID_MSB = SVEC_TEMPLATE_REGS_THERM_ID + 0x0,
SVEC_FPGA_THERM_SERID_LSB = SVEC_TEMPLATE_REGS_THERM_ID + 0x4,
SVEC_FPGA_THERM_TEMP = SVEC_TEMPLATE_REGS_THERM_ID + 0x8,
};
enum svec_fpga_meta_cap_mask {
SVEC_META_CAP_VIC = BIT(0),
SVEC_META_CAP_THERM = BIT(1),
SVEC_META_CAP_SPI = BIT(2),
SVEC_META_CAP_WR = BIT(3),
SVEC_META_CAP_BLD = BIT(4),
};
static const struct debugfs_reg32 svec_fpga_debugfs_reg32[] = {
{
.name = "Application offset",
.offset = SVEC_FPGA_CSR_APP_OFF,
},
{
.name = "Resets",
.offset = SVEC_FPGA_CSR_RESETS,
},
{
.name = "FMC present",
.offset = SVEC_FPGA_CSR_FMC_PRESENT,
},
{
.name = "PCB revision",
.offset = SVEC_FPGA_CSR_PCB_REV,
},
};
static int svec_fpga_dbg_bld_info(struct seq_file *s, void *offset)
{
struct svec_fpga *svec_fpga = s->private;
struct svec_dev *svec_dev = dev_get_drvdata(svec_fpga->dev.parent);
int off;
if (!(svec_dev->meta.cap & SVEC_META_CAP_BLD)) {
seq_puts(s, "not available\n");
return 0;
}
for (off = SVEC_TEMPLATE_REGS_BUILDINFO;
off < SVEC_TEMPLATE_REGS_BUILDINFO + SVEC_TEMPLATE_REGS_BUILDINFO_SIZE -1;
off++) {
char tmp = ioread8(svec_fpga->fpga + off);
if (!tmp)
return 0;
seq_putc(s, tmp);
}
return 0;
}
static int svec_fpga_dbg_bld_info_open(struct inode *inode,
struct file *file)
{
struct svec_fpga *svec = inode->i_private;
return single_open(file, svec_fpga_dbg_bld_info, svec);
}
static const struct file_operations svec_fpga_dbg_bld_info_ops = {
.owner = THIS_MODULE,
.open = svec_fpga_dbg_bld_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int svec_fpga_dbg_init(struct svec_fpga *svec_fpga)
{
struct svec_dev *svec_dev = dev_get_drvdata(svec_fpga->dev.parent);
int err;
svec_fpga->dbg_dir = debugfs_create_dir(dev_name(&svec_fpga->dev),
svec_dev->dbg_dir);
if (IS_ERR_OR_NULL(svec_fpga->dbg_dir)) {
err = PTR_ERR(svec_fpga->dbg_dir);
dev_err(&svec_fpga->dev,
"Cannot create debugfs directory \"%s\" (%d)\n",
dev_name(&svec_fpga->dev), err);
return err;
}
svec_fpga->dbg_csr_reg.regs = svec_fpga_debugfs_reg32;
svec_fpga->dbg_csr_reg.nregs = ARRAY_SIZE(svec_fpga_debugfs_reg32);
svec_fpga->dbg_csr_reg.base = svec_fpga->fpga;
svec_fpga->dbg_csr = debugfs_create_regset32(SVEC_DBG_CSR_NAME, 0200,
svec_fpga->dbg_dir,
&svec_fpga->dbg_csr_reg);
if (IS_ERR_OR_NULL(svec_fpga->dbg_csr)) {
err = PTR_ERR(svec_fpga->dbg_csr);
dev_warn(&svec_fpga->dev,
"Cannot create debugfs file \"%s\" (%d)\n",
SVEC_DBG_CSR_NAME, err);
goto err;
}
svec_fpga->dbg_bld_info = debugfs_create_file(SVEC_DBG_BLD_INFO_NAME, 0444,
svec_fpga->dbg_dir,
svec_fpga,
&svec_fpga_dbg_bld_info_ops);
if (IS_ERR_OR_NULL(svec_fpga->dbg_bld_info)) {
err = PTR_ERR(svec_fpga->dbg_bld_info);
dev_err(&svec_fpga->dev,
"Cannot create debugfs file \"%s\" (%d)\n",
SVEC_DBG_BLD_INFO_NAME, err);
goto err;
}
return 0;
err:
debugfs_remove_recursive(svec_fpga->dbg_dir);
return err;
}
static void svec_fpga_dbg_exit(struct svec_fpga *svec_fpga)
{
debugfs_remove_recursive(svec_fpga->dbg_dir);
}
static inline uint32_t svec_fpga_csr_app_offset(struct svec_fpga *svec_fpga)
{
return ioread32be(svec_fpga->fpga + SVEC_FPGA_CSR_APP_OFF);
}
static inline uint32_t svec_fpga_csr_pcb_rev(struct svec_fpga *svec_fpga)
{
return ioread32be(svec_fpga->fpga + SVEC_FPGA_CSR_PCB_REV);
}
/* Vector Interrupt Controller */
static struct resource svec_fpga_vic_res[] = {
{
.name = "htvic-mem",
.flags = IORESOURCE_MEM,
.start = SVEC_TEMPLATE_REGS_VIC,
.end = SVEC_TEMPLATE_REGS_VIC,
}, {
.name = "htvic-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 0,
.end = 0,
},
};
static int svec_fpga_vic_init(struct svec_fpga *svec_fpga)
{
struct svec_dev *svec_dev = dev_get_drvdata(svec_fpga->dev.parent);
unsigned long vme_start = vme_resource_start(svec_dev->vdev,
svec_fpga->function_nr);
const unsigned int res_n = ARRAY_SIZE(svec_fpga_vic_res);
struct resource res[ARRAY_SIZE(svec_fpga_vic_res)];
struct platform_device *pdev;
if (!(svec_dev->meta.cap & SVEC_META_CAP_VIC))
return 0;
memcpy(&res, svec_fpga_vic_res, sizeof(svec_fpga_vic_res));
res[0].start += vme_start;
res[0].end += vme_start;
res[1].start = svec_dev->vdev->irq;
res[1].end = res[1].start;
pdev = platform_device_register_resndata(&svec_fpga->dev,
"htvic-svec",
PLATFORM_DEVID_AUTO,
res, res_n,
NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
svec_fpga->vic_pdev = pdev;
return 0;
}
static void svec_fpga_vic_exit(struct svec_fpga *svec_fpga)
{
if (svec_fpga->vic_pdev) {
platform_device_unregister(svec_fpga->vic_pdev);
svec_fpga->vic_pdev = NULL;
}
}
/* MFD devices */
enum svec_fpga_mfd_devs_enum {
SVEC_FPGA_MFD_FMC_I2C = 0,
SVEC_FPGA_MFD_SPI,
};
static struct resource svec_fpga_fmc_i2c_res[] = {
{
.name = "i2c-ocores-mem",
.flags = IORESOURCE_MEM,
.start = SVEC_TEMPLATE_REGS_FMC_I2C,
.end = SVEC_TEMPLATE_REGS_FMC_I2C + SVEC_TEMPLATE_REGS_FMC_I2C_SIZE -1,
}, {
.name = "i2c-ocores-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = SVEC_FPGA_IRQ_FMC_I2C,
.end = SVEC_FPGA_IRQ_FMC_I2C,
},
};
static struct ocores_i2c_platform_data svec_fpga_fmc_i2c_pdata = {
.reg_shift = 2, /* 32bit aligned */
.reg_io_width = 4,
.clock_khz = 62500,
.big_endian = 1,
.num_devices = 0,
.devices = NULL,
};
#if 0
static struct resource svec_fpga_spi_res[] = {
{
.name = "spi-ocores-mem",
.flags = IORESOURCE_MEM,
.start = SVEC_TEMPLATE_REGS_FLASH_SPI,
.end = SVEC_TEMPLATE_REGS_FLASH_SPI + SVEC_TEMPLATE_REGS_FLASH_SPI_SIZE - 1,
}, {
.name = "spi-ocores-irq",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = SVEC_FPGA_IRQ_SPI,
.end = SVEC_FPGA_IRQ_SPI,
},
};
struct flash_platform_data svec_flash_pdata = {
.name = "svec-flash",
.parts = NULL,
.nr_parts = 0,
.type = "m25p32",
};
static struct spi_board_info svec_fpga_spi_devices_info[] = {
{
.modalias = "m25p32",
.max_speed_hz = 75000000,
.chip_select = 0,
.platform_data = &svec_flash_pdata,
}
};
static struct spi_ocores_platform_data svec_fpga_spi_pdata = {
.big_endian = 0,
.clock_hz = 65200000,
.num_devices = ARRAY_SIZE(svec_fpga_spi_devices_info),
.devices = svec_fpga_spi_devices_info,
};
#endif
static const struct mfd_cell svec_fpga_mfd_devs[] = {
[SVEC_FPGA_MFD_FMC_I2C] = {
.name = "i2c-ohwr",
.platform_data = &svec_fpga_fmc_i2c_pdata,
.pdata_size = sizeof(svec_fpga_fmc_i2c_pdata),
.num_resources = ARRAY_SIZE(svec_fpga_fmc_i2c_res),
.resources = svec_fpga_fmc_i2c_res,
},
#if 0
[SVEC_FPGA_MFD_SPI] = {
.name = "spi-ocores",
.platform_data = &svec_fpga_spi_pdata,
.pdata_size = sizeof(svec_fpga_spi_pdata),
.num_resources = ARRAY_SIZE(svec_fpga_spi_res),
.resources = svec_fpga_spi_res,
},
#endif
};
static inline size_t __fpga_mfd_devs_size(void)
{
#define SVEC_FPGA_MFD_DEVS_MAX 2
return (sizeof(struct mfd_cell) * SVEC_FPGA_MFD_DEVS_MAX);
}
static int svec_fpga_devices_init(struct svec_fpga *svec_fpga)
{
struct vme_dev *vdev = to_vme_dev(svec_fpga->dev.parent);
struct mfd_cell *fpga_mfd_devs;
struct irq_domain *vic_domain;
unsigned int n_mfd = 0;
int err;
fpga_mfd_devs = devm_kzalloc(&svec_fpga->dev,
__fpga_mfd_devs_size(),
GFP_KERNEL);
if (!fpga_mfd_devs)
return -ENOMEM;
memcpy(&fpga_mfd_devs[n_mfd],
&svec_fpga_mfd_devs[SVEC_FPGA_MFD_FMC_I2C],
sizeof(fpga_mfd_devs[n_mfd]));
n_mfd++;
vic_domain = irq_find_host((void *)&svec_fpga->vic_pdev->dev);
if (!vic_domain) {
/* Remove IRQ resource from all devices */
fpga_mfd_devs[0].num_resources = 1; /* FMC I2C */
fpga_mfd_devs[1].num_resources = 1; /* SPI */
}
err = mfd_add_devices(&svec_fpga->dev, PLATFORM_DEVID_AUTO,
fpga_mfd_devs, n_mfd,
&vdev->resource[svec_fpga->function_nr],
0, vic_domain);
if (err)
goto err_mfd;
return 0;
err_mfd:
devm_kfree(&svec_fpga->dev, fpga_mfd_devs);
return err;
}
static void svec_fpga_devices_exit(struct svec_fpga *svec_fpga)
{
mfd_remove_devices(&svec_fpga->dev);
}
/* Thermometer */
static ssize_t temperature_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct svec_fpga *svec_fpga = to_svec_fpga(dev);
struct svec_dev *svec_dev = dev_get_drvdata(svec_fpga->dev.parent);
if (svec_dev->meta.cap & SVEC_META_CAP_THERM) {
uint32_t temp = ioread32be(svec_fpga->fpga + SVEC_FPGA_THERM_TEMP);
return snprintf(buf, PAGE_SIZE, "%d.%d C\n",
temp / 16, (temp & 0xF) * 1000 / 16);
} else {
return snprintf(buf, PAGE_SIZE, "-.- C\n");
}
}
static DEVICE_ATTR_RO(temperature);
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct svec_fpga *svec_fpga = to_svec_fpga(dev);
struct svec_dev *svec_dev = dev_get_drvdata(svec_fpga->dev.parent);
if (svec_dev->meta.cap & SVEC_META_CAP_THERM) {
uint32_t msb = ioread32be(svec_fpga->fpga + SVEC_FPGA_THERM_SERID_MSB);
uint32_t lsb = ioread32be(svec_fpga->fpga + SVEC_FPGA_THERM_SERID_LSB);
return snprintf(buf, PAGE_SIZE, "0x%08x%08x\n", msb, lsb);
} else {
return snprintf(buf, PAGE_SIZE, "0x----------------\n");
}
}
static DEVICE_ATTR_RO(serial_number);
static struct attribute *svec_fpga_therm_attrs[] = {
&dev_attr_serial_number.attr,
&dev_attr_temperature.attr,
NULL,
};
static const struct attribute_group svec_fpga_therm_group = {
.name = "thermometer",
.attrs = svec_fpga_therm_attrs,
};
/* CSR attributes */
static ssize_t pcb_rev_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct svec_fpga *svec_fpga = to_svec_fpga(dev);
return snprintf(buf, PAGE_SIZE, "0x%x\n",
svec_fpga_csr_pcb_rev(svec_fpga));
}
static DEVICE_ATTR_RO(pcb_rev);
static ssize_t application_offset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct svec_fpga *svec_fpga = to_svec_fpga(dev);
return snprintf(buf, PAGE_SIZE, "0x%x\n",
svec_fpga_csr_app_offset(svec_fpga));
}
static DEVICE_ATTR_RO(application_offset);
enum svec_fpga_csr_resets {
SVEC_FPGA_CSR_RESETS_ALL = BIT(0),
SVEC_FPGA_CSR_RESETS_APP = BIT(1),
};
static void svec_fpga_app_reset(struct svec_fpga *svec_fpga, bool val)
{
uint32_t resets;
resets = ioread32be(svec_fpga->fpga + SVEC_FPGA_CSR_RESETS);
if (val)
resets |= SVEC_FPGA_CSR_RESETS_APP;
else
resets &= ~SVEC_FPGA_CSR_RESETS_APP;
iowrite32be(resets, svec_fpga->fpga + SVEC_FPGA_CSR_RESETS);
}
static void svec_fpga_app_restart(struct svec_fpga *svec_fpga)
{
svec_fpga_app_reset(svec_fpga, true);
udelay(1);
svec_fpga_app_reset(svec_fpga, false);
udelay(1);
}
static ssize_t reset_app_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct svec_fpga *svec_fpga = to_svec_fpga(dev);
uint32_t resets;
resets = ioread32be(svec_fpga->fpga + SVEC_FPGA_CSR_RESETS);
return snprintf(buf, PAGE_SIZE, "%d\n",
!!(resets & SVEC_FPGA_CSR_RESETS_APP));
}
static ssize_t reset_app_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
long val;
int err;
err = kstrtol(buf, 10, &val);
if (err)
return err;
svec_fpga_app_reset(to_svec_fpga(dev), val);
return count;
}
static DEVICE_ATTR(reset_app, 0644, reset_app_show, reset_app_store);
static struct attribute *svec_fpga_csr_attrs[] = {
&dev_attr_pcb_rev.attr,
&dev_attr_application_offset.attr,
&dev_attr_reset_app.attr,
NULL,
};
static const struct attribute_group svec_fpga_csr_group = {
.attrs = svec_fpga_csr_attrs,
};
/* FMC */
static inline u8 svec_fmc_presence(struct svec_fpga *svec_fpga)
{
u8 presence;
presence = ioread32be(svec_fpga->fpga + SVEC_FPGA_CSR_FMC_PRESENT);
return presence & (BIT(SVEC_FMC_SLOTS) - 1);
}
static int svec_fmc_is_present(struct fmc_carrier *carrier,
struct fmc_slot *slot)
{
struct svec_fpga *svec_fpga = carrier->priv;
return !!(svec_fmc_presence(svec_fpga) & BIT(slot->lun - 1));
}
static const struct fmc_carrier_operations svec_fmc_ops = {
.owner = THIS_MODULE,
.is_present = svec_fmc_is_present,
};
static int svec_i2c_find_adapter(struct device *dev, void *data)
{
struct svec_fpga *svec_fpga = data;
struct i2c_adapter *adap, *adap_parent;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
adap_parent = i2c_parent_is_i2c_adapter(adap);
if (!adap_parent)
return 0;
/* We have a muxed I2C master */
if (&svec_fpga->dev != adap_parent->dev.parent->parent)
return 0;
/* Found! Return the bus ID */
return i2c_adapter_id(adap);
}
/**
* Get the I2C adapter associated with an FMC slot
* @data: data used to find the correct I2C bus
* @slot_nr: FMC slot number
*
* Return: the I2C bus to be used
*/
static int svec_i2c_get_bus(struct svec_fpga *svec_fpga)
{
return i2c_for_each_dev(svec_fpga, svec_i2c_find_adapter);
}
/**
* Create an FMC interface
*/
static int svec_fmc_init(struct svec_fpga *svec_fpga)
{
int err, i;
for (i = 0; i < SVEC_FMC_SLOTS; ++i) {
svec_fpga->slot_info[i].i2c_bus_nr = svec_i2c_get_bus(svec_fpga);
if (svec_fpga->slot_info[i].i2c_bus_nr <= 0)
return -ENODEV;
svec_fpga->slot_info[i].ga = i;
svec_fpga->slot_info[i].lun = i + 1;
}
err = fmc_carrier_register(&svec_fpga->dev, &svec_fmc_ops,
SVEC_FMC_SLOTS, svec_fpga->slot_info,
svec_fpga);
if (err) {
dev_err(svec_fpga->dev.parent,
"Failed to register as FMC carrier\n");
goto err_fmc;
}
return 0;
err_fmc:
return err;
}
static int svec_fmc_exit(struct svec_fpga *svec_fpga)
{
return fmc_carrier_unregister(&svec_fpga->dev);
}
/* FPGA Application */
/**
* Build the platform_device_id->name from metadata
*
* The byte order on SVEC is little endian, but we want to convert it
* in string. Use big-endian read to swap word and get the string order
* from MSB to LSB
*/
static int svec_fpga_app_id_build(struct svec_fpga *svec_fpga,
unsigned long app_off,
char *id, unsigned int size)
{
uint32_t vendor = ioread32be(svec_fpga->fpga + app_off + FPGA_META_VENDOR);
uint32_t device = ioread32be(svec_fpga->fpga + app_off + FPGA_META_DEVICE);
memset(id, 0, size);
if (vendor == 0xFF000000) {
dev_warn(&svec_fpga->dev, "Vendor UUID not supported yet\n");
return -ENODEV;
} else {
snprintf(id, size, "id:%4phN%4phN", &vendor, &device);
}
return 0;
}
static int svec_fpga_app_init(struct svec_fpga *svec_fpga)
{
#define SVEC_FPGA_APP_NAME_MAX 47
#define SVEC_FPGA_APP_IRQ_BASE 6
#define SVEC_FPGA_APP_RES_N (32 - SVEC_FPGA_APP_IRQ_BASE + 1)
struct vme_dev *vdev = to_vme_dev(svec_fpga->dev.parent);
unsigned int res_n = SVEC_FPGA_APP_RES_N;
struct resource res[SVEC_FPGA_APP_RES_N] = {
[0] = {
.name = "app-mem",
.flags = IORESOURCE_MEM,
},
};
struct platform_device *pdev;
struct irq_domain *vic_domain;
char app_name[SVEC_FPGA_APP_NAME_MAX];
unsigned long app_offset;
int err;
app_offset = svec_fpga_csr_app_offset(svec_fpga);
if (!app_offset) {
dev_warn(&svec_fpga->dev, "Application not found\n");
return 0;
}
res[0].start = vme_resource_start(vdev, 0) + app_offset;
res[0].end = vme_resource_end(vdev, 0);
if (svec_fpga->vic_pdev)
vic_domain = irq_find_host((void *)&svec_fpga->vic_pdev->dev);
else
vic_domain = NULL;
if (vic_domain) {
int i, hwirq;
for (i = 1, hwirq = SVEC_FPGA_APP_IRQ_BASE;
i < SVEC_FPGA_APP_RES_N;
++i, ++hwirq) {
res[i].name = "app-irq",
res[i].flags = IORESOURCE_IRQ,
res[i].start = irq_find_mapping(vic_domain, hwirq);
res[i].end = res[1].start;
}
} else {
res_n = 1;
}
err = svec_fpga_app_id_build(svec_fpga, app_offset,
app_name, SVEC_FPGA_APP_NAME_MAX);
if (err)
return err;
svec_fpga_app_restart(svec_fpga);
pdev = platform_device_register_resndata(&svec_fpga->dev,
app_name, PLATFORM_DEVID_AUTO,
res, res_n,
NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
svec_fpga->app_pdev = pdev;
return 0;
}
static void svec_fpga_app_exit(struct svec_fpga *svec_fpga)
{
if (svec_fpga->app_pdev) {
platform_device_unregister(svec_fpga->app_pdev);
svec_fpga->app_pdev = NULL;
}
}
static void svec_fpga_metadata_get(struct svec_meta_id *meta,
void __iomem *fpga)
{
uint32_t *meta_tmp = (uint32_t *)meta;
int i;
for (i = 0; i < sizeof(*meta) / 4; ++i)
meta_tmp[i] = ioread32be(fpga + SVEC_META_BASE + (i * 4));
}
static bool svec_fpga_is_valid(struct svec_dev *svec_dev,
struct svec_meta_id *meta)
{
if ((meta->bom & SVEC_META_BOM_END_MASK) != SVEC_META_BOM_BE) {
dev_err(&svec_dev->vdev->dev,
"Expected Big Endian devices BOM: 0x%x\n",
meta->bom);
return false;
}
if ((meta->bom & SVEC_META_BOM_VER_MASK) != 0) {
dev_err(&svec_dev->vdev->dev,
"Unknow Metadata svecification version BOM: 0x%x\n",
meta->bom);
return false;
}
if (meta->vendor != SVEC_META_VENDOR_ID ||
meta->device != SVEC_META_DEVICE_ID) {
dev_err(&svec_dev->vdev->dev,
"Unknow vendor/device ID: %08x:%08x\n",
meta->vendor, meta->device);
return false;
}
if ((meta->version & SVEC_META_VERSION_MASK) != SVEC_META_VERSION_1_4) {
dev_err(&svec_dev->vdev->dev,
"Unknow version: %08x\n", meta->version);
return false;
}
return true;
}
static void svec_release(struct device *dev)
{
}
static int svec_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return 0;
}
static const struct attribute_group *svec_groups[] = {
&svec_fpga_therm_group,
&svec_fpga_csr_group,
NULL
};
static const struct device_type svec_fpga_type = {
.name = "svec",
.release = svec_release,
.uevent = svec_uevent,
.groups = svec_groups,
};
int svec_fpga_init(struct svec_dev *svec_dev, unsigned int function_nr)
{
struct svec_fpga *svec_fpga;
struct resource *r = &svec_dev->vdev->resource[function_nr];
int err;
svec_fpga = kzalloc(sizeof(*svec_fpga), GFP_KERNEL);
if (!svec_fpga)
return -ENOMEM;
svec_fpga->function_nr = function_nr;
svec_fpga->fpga = ioremap(r->start, resource_size(r));
if (!svec_fpga->fpga) {
err = -ENOMEM;
goto err_map;
}
svec_fpga_metadata_get(&svec_dev->meta, svec_fpga->fpga);
if (!svec_fpga_is_valid(svec_dev, &svec_dev->meta)) {
err = -EINVAL;
goto err_valid;
}
svec_fpga->dev.parent = &svec_dev->vdev->dev;
svec_fpga->dev.driver = svec_dev->vdev->dev.driver;
svec_fpga->dev.type = &svec_fpga_type;
err = dev_set_name(&svec_fpga->dev, "svec-%s",
dev_name(&svec_dev->vdev->dev));
if (err)
goto err_name;
err = device_register(&svec_fpga->dev);
if (err) {
dev_err(&svec_dev->vdev->dev, "Failed to register '%s'\n",
dev_name(&svec_dev->vdev->dev));
goto err_dev;
}
svec_fpga_dbg_init(svec_fpga);
err = svec_fpga_vic_init(svec_fpga);
if (err) {
dev_err(&svec_dev->vdev->dev,
"Failed to initialize VIC %d\n", err);
goto err_vic;
}
err = svec_fpga_devices_init(svec_fpga);
if (err) {
dev_err(&svec_dev->vdev->dev,
"Failed to initialize Devices %d\n", err);
goto err_devs;
}
err = svec_fmc_init(svec_fpga);
if (err) {
dev_err(&svec_dev->vdev->dev,
"Failed to initialize FMC %d\n", err);
goto err_fmc;
}
err = svec_fpga_app_init(svec_fpga);
if (err) {
dev_err(&svec_dev->vdev->dev,
"Failed to initialize APP %d\n", err);
goto err_app;
}
svec_dev->svec_fpga = svec_fpga;
return 0;
err_app:
svec_fmc_exit(svec_fpga);
err_fmc:
svec_fpga_devices_exit(svec_fpga);
err_devs:
svec_fpga_vic_exit(svec_fpga);
err_vic:
return err;
err_dev:
err_name:
err_valid:
iounmap(svec_fpga->fpga);
err_map:
kfree(svec_fpga);
svec_dev->svec_fpga = NULL;
return err;
}
int svec_fpga_exit(struct svec_dev *svec_dev)
{
struct svec_fpga *svec_fpga = svec_dev->svec_fpga;
if (!svec_fpga)
return 0;
svec_fpga_app_exit(svec_fpga);
svec_fmc_exit(svec_fpga);
svec_fpga_devices_exit(svec_fpga);
svec_fpga_vic_exit(svec_fpga);
svec_fpga_dbg_exit(svec_fpga);
device_unregister(&svec_fpga->dev);
iounmap(svec_fpga->fpga);
kfree(svec_fpga);
svec_dev->svec_fpga = NULL;
return 0;
}
#ifndef __CHEBY__SVEC_TEMPLATE_REGS__H__
#define __CHEBY__SVEC_TEMPLATE_REGS__H__
#define SVEC_TEMPLATE_REGS_SIZE 8192
/* a ROM containing the carrier metadata */
#define SVEC_TEMPLATE_REGS_METADATA 0x0UL
#define SVEC_TEMPLATE_REGS_METADATA_SIZE 64
/* carrier and fmc status and control */
#define SVEC_TEMPLATE_REGS_CSR 0x40UL
#define SVEC_TEMPLATE_REGS_CSR_SIZE 32
/* offset to the application metadata */
#define SVEC_TEMPLATE_REGS_CSR_APP_OFFSET 0x40UL
/* global and application resets */
#define SVEC_TEMPLATE_REGS_CSR_RESETS 0x44UL
#define SVEC_TEMPLATE_REGS_CSR_RESETS_GLOBAL 0x1UL
#define SVEC_TEMPLATE_REGS_CSR_RESETS_APPL 0x2UL
/* presence lines for the fmcs */
#define SVEC_TEMPLATE_REGS_CSR_FMC_PRESENCE 0x48UL
/* status of gennum */
#define SVEC_TEMPLATE_REGS_CSR_GN4124_STATUS 0x4cUL
/* status of the ddr3 controller */
#define SVEC_TEMPLATE_REGS_CSR_DDR_STATUS 0x50UL
#define SVEC_TEMPLATE_REGS_CSR_DDR_STATUS_CALIB_DONE 0x1UL
/* pcb revision */
#define SVEC_TEMPLATE_REGS_CSR_PCB_REV 0x54UL
#define SVEC_TEMPLATE_REGS_CSR_PCB_REV_REV_MASK 0xfUL
#define SVEC_TEMPLATE_REGS_CSR_PCB_REV_REV_SHIFT 0
/* Thermometer and unique id */
#define SVEC_TEMPLATE_REGS_THERM_ID 0x70UL
#define SVEC_TEMPLATE_REGS_THERM_ID_SIZE 16
/* i2c controllers to the fmcs */
#define SVEC_TEMPLATE_REGS_FMC_I2C 0x80UL
#define SVEC_TEMPLATE_REGS_FMC_I2C_SIZE 32
/* spi controller to the flash */
#define SVEC_TEMPLATE_REGS_FLASH_SPI 0xa0UL
#define SVEC_TEMPLATE_REGS_FLASH_SPI_SIZE 32
/* dma registers for the gennum core */
#define SVEC_TEMPLATE_REGS_DMA 0xc0UL
#define SVEC_TEMPLATE_REGS_DMA_SIZE 64
/* vector interrupt controller */
#define SVEC_TEMPLATE_REGS_VIC 0x100UL
#define SVEC_TEMPLATE_REGS_VIC_SIZE 256
/* a ROM containing build information */
#define SVEC_TEMPLATE_REGS_BUILDINFO 0x200UL
#define SVEC_TEMPLATE_REGS_BUILDINFO_SIZE 256
/* white-rabbit core registers */
#define SVEC_TEMPLATE_REGS_WRC_REGS 0x1000UL
#define SVEC_TEMPLATE_REGS_WRC_REGS_SIZE 4096
struct svec_template_regs {
/* [0x0]: SUBMAP a ROM containing the carrier metadata */
uint32_t metadata[16];
/* [0x40]: BLOCK carrier and fmc status and control */
struct csr {
/* [0x0]: REG (ro) offset to the application metadata */
uint32_t app_offset;
/* [0x4]: REG (rw) global and application resets */
uint32_t resets;
/* [0x8]: REG (ro) presence lines for the fmcs */
uint32_t fmc_presence;
/* [0xc]: REG (ro) status of gennum */
uint32_t gn4124_status;
/* [0x10]: REG (ro) status of the ddr3 controller */
uint32_t ddr_status;
/* [0x14]: REG (ro) pcb revision */
uint32_t pcb_rev;
/* padding to: 5 words */
uint32_t __padding_0[2];
} csr;
/* padding to: 28 words */
uint32_t __padding_0[4];
/* [0x70]: SUBMAP Thermometer and unique id */
uint32_t therm_id[4];
/* [0x80]: SUBMAP i2c controllers to the fmcs */
uint32_t fmc_i2c[8];
/* [0xa0]: SUBMAP spi controller to the flash */
uint32_t flash_spi[8];
/* [0xc0]: SUBMAP dma registers for the gennum core */
uint32_t dma[16];
/* [0x100]: SUBMAP vector interrupt controller */
uint32_t vic[64];
/* [0x200]: SUBMAP a ROM containing build information */
uint32_t buildinfo[64];
/* padding to: 1024 words */
uint32_t __padding_1[832];
/* [0x1000]: SUBMAP white-rabbit core registers */
uint32_t wrc_regs[1024];
};
#endif /* __CHEBY__SVEC_TEMPLATE_REGS__H__ */
......@@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/fmc.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/jhash.h>
......@@ -31,12 +32,10 @@
#include <linux/fpga/fpga-mgr.h>
#include <vmebus.h>
#include "svec.h"
#include "svec-compat.h"
#include "hw/xloader_regs.h"
#define SVEC_BASE_LOADER 0x70000
static void svec_csr_write(u8 value, void *base, u32 offset)
{
offset -= offset % 4;
......@@ -44,36 +43,147 @@ static void svec_csr_write(u8 value, void *base, u32 offset)
}
/**
* Byte sequence to unlock and clear the Application FPGA
* Load FPGA code
* @svec: SVEC device
* @name: FPGA bitstream file name
*
* Return: 0 on success, otherwise a negative error number
*/
static const uint32_t boot_unlock_sequence[8] = {
0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe
static int svec_fw_load(struct svec_dev *svec_dev, const char *name)
{
int err;
err = svec_fpga_exit(svec_dev);
if (err) {
dev_err(&svec_dev->vdev->dev,
"Cannot remove FPGA device instances. Try to remove them manually and to reload this device instance\n");
return err;
}
mutex_lock(&svec_dev->mtx);
err = compat_svec_fw_load(svec_dev, name);
if (err)
goto out;
err = svec_fpga_init(svec_dev, SVEC_FUNC_NR);
if (err)
dev_warn(&svec_dev->vdev->dev,
"FPGA incorrectly programmed %d\n", err);
out:
mutex_unlock(&svec_dev->mtx);
return err;
}
static ssize_t svec_dbg_fw_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
struct svec_dev *svec_dev = file->private_data;
int err;
err = svec_fw_load(svec_dev, buf);
if (err)
return err;
return count;
}
static int svec_dbg_fw_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static const struct file_operations svec_dbg_fw_ops = {
.owner = THIS_MODULE,
.open = svec_dbg_fw_open,
.write = svec_dbg_fw_write,
};
static int svec_dbg_meta(struct seq_file *s, void *offset)
{
struct svec_dev *svec_dev = s->private;
struct svec_meta_id *meta;
meta = &svec_dev->meta;
seq_printf(s, "'%s':\n", dev_name(&svec_dev->vdev->dev));
seq_puts(s, "Metadata:\n");
seq_printf(s, " - Vendor: 0x%08x\n", meta->vendor);
seq_printf(s, " - Device: 0x%08x\n", meta->device);
seq_printf(s, " - Version: 0x%08x\n", meta->version);
seq_printf(s, " - BOM: 0x%08x\n", meta->bom);
seq_printf(s, " - SourceID: 0x%08x%08x%08x%08x\n",
meta->src[0],
meta->src[1],
meta->src[2],
meta->src[3]);
seq_printf(s, " - CapabilityMask: 0x%08x\n", meta->cap);
seq_printf(s, " - VendorUUID: 0x%08x%08x%08x%08x\n",
meta->uuid[0],
meta->uuid[1],
meta->uuid[2],
meta->uuid[3]);
/**
* struct svec_dev - SVEC instance
* It describes a SVEC device instance.
* @vdev VME device instance
* @bitstream_last_word last data to write into the FPGA
* @bistream_last_word_size last data size to write in the FPGA. This is a dirty
* and ugly hack in order to properly handle a dirty
* and ugly interface. The SVEC bootloader does not
* accept emtpy transfers and neither to declare the
* transmission over without sending data.
* @fpgA_status state of the Application FPGA
* The user must lock the spinlock `lock` when using the following variables in
* this data structure: flags.
*/
struct svec_dev {
struct vme_dev *vdev;
char name[8];
return 0;
}
static int svec_dbg_meta_open(struct inode *inode, struct file *file)
{
struct svec_dev *svec = inode->i_private;
return single_open(file, svec_dbg_meta, svec);
}
uint32_t bitstream_last_word;
uint32_t bitstream_last_word_size;
enum fpga_mgr_states fpga_status;
static const struct file_operations svec_dbg_meta_ops = {
.owner = THIS_MODULE,
.open = svec_dbg_meta_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int svec_dbg_init(struct svec_dev *svec_dev)
{
struct device *dev = &svec_dev->vdev->dev;
svec_dev->dbg_dir = debugfs_create_dir(dev_name(dev), NULL);
if (IS_ERR_OR_NULL(svec_dev->dbg_dir)) {
dev_err(dev, "Cannot create debugfs directory (%ld)\n",
PTR_ERR(svec_dev->dbg_dir));
return PTR_ERR(svec_dev->dbg_dir);
}
svec_dev->dbg_fw = debugfs_create_file(SVEC_DBG_FW_NAME, 0200,
svec_dev->dbg_dir,
svec_dev,
&svec_dbg_fw_ops);
if (IS_ERR_OR_NULL(svec_dev->dbg_fw)) {
dev_err(dev, "Cannot create debugfs file \"%s\" (%ld)\n",
SVEC_DBG_FW_NAME, PTR_ERR(svec_dev->dbg_fw));
return PTR_ERR(svec_dev->dbg_fw);
}
svec_dev->dbg_meta = debugfs_create_file(SVEC_DBG_META_NAME, 0200,
svec_dev->dbg_dir,
svec_dev,
&svec_dbg_meta_ops);
if (IS_ERR_OR_NULL(svec_dev->dbg_meta)) {
dev_err(dev, "Cannot create debugfs file \"%s\" (%ld)\n",
SVEC_DBG_META_NAME, PTR_ERR(svec_dev->dbg_meta));
return PTR_ERR(svec_dev->dbg_meta);
}
return 0;
}
static void svec_dbg_exit(struct svec_dev *svec_dev)
{
debugfs_remove_recursive(svec_dev->dbg_dir);
}
/**
* Writes a "magic" unlock sequence, activating the System FPGA bootloader
......@@ -167,7 +277,7 @@ static int svec_fpga_write_word(struct fpga_manager *mgr,
/**
* It starts the programming procedure
* Start programming procedure
* It is usable only when there is a valid CR/CSR space mapped
* @mgr FPGA manager instance
* Return 0 on success, otherwise a negative errno number.
......@@ -206,7 +316,7 @@ err_reset:
/**
* It starts the programming procedure.
* Stop programming procedure.
* It is usable only when there is a valid CR/CSR space mapped
* @mgr FPGA manager instance
* Return 0 on success, otherwise a negative errno number
......@@ -233,7 +343,11 @@ static int svec_fpga_write_stop(struct fpga_manager *mgr,
svec->bitstream_last_word_size = -1;
/* Two seconds later */
timeout = get_jiffies_64() + usecs_to_jiffies(info->config_complete_timeout_us);
timeout = get_jiffies_64();
if (info->config_complete_timeout_us)
timeout += usecs_to_jiffies(info->config_complete_timeout_us);
else
timeout += usecs_to_jiffies(100);
while (time_before64(get_jiffies_64(), timeout)) {
rval = ioread32be(loader_addr + XLDR_REG_CSR);
if (rval & XLDR_CSR_DONE)
......@@ -357,7 +471,7 @@ static int svec_vme_init(struct svec_dev *svec)
struct vme_dev *vdev = svec->vdev;
int err;
err = vme_csr_enable(vdev, 0);
err = vme_disable_device(vdev);
if (err)
return err;
/* Configure the SVEC VME interface */
......@@ -366,17 +480,14 @@ static int svec_vme_init(struct svec_dev *svec)
svec_csr_write(vdev->irq_level, vdev->map_cr.kernel_va,
SVEC_USER_CSR_INT_LEVEL);
err = vme_csr_enable(vdev, 1);
if (err)
return err;
return 0;
return vme_enable_device(vdev);
}
/**
* It initialize a new SVEC instance
* @pdev correspondend Linux device instance
* @ndev (Deprecated) device number
*
* Return: 0 on success, otherwise a negative number correspondent to an errno
*/
static int svec_probe(struct device *dev, unsigned int ndev)
......@@ -390,36 +501,61 @@ static int svec_probe(struct device *dev, unsigned int ndev)
err = -ENOMEM;
goto err;
}
mutex_init(&svec->mtx);
svec->vdev = vdev;
dev_set_drvdata(dev, svec);
svec_vme_init(svec);
svec->fpga_status = FPGA_MGR_STATE_UNKNOWN;
svec->mgr = fpga_mgr_create(dev, dev_name(dev),
&svec_fpga_ops, svec);
if (!svec->mgr) {
err = -EPERM;
goto err_fpga_new;
}
snprintf(svec->name, 8, "svec.%d", vdev->slot);
err = fpga_mgr_register(&svec->vdev->dev, svec->name,
&svec_fpga_ops, svec);
err = fpga_mgr_register(svec->mgr);
if (err)
goto err_fpga_reg;
svec_vme_init(svec);
svec_dbg_init(svec);
err = svec_fpga_init(svec, SVEC_FUNC_NR);
if (err)
dev_warn(&vdev->dev,
"FPGA incorrectly programmed or empty (%d)\n", err);
return 0;
err_fpga_reg:
fpga_mgr_free(svec->mgr);
err_fpga_new:
dev_set_drvdata(dev, NULL);
kfree(svec);
err:
dev_err(dev, "Failed to register SVEC device\n");
return err;
}
/**
* It removes a SVEC device instance
* @vdev Linux device pointer
* @ndev DEPRECATED Device instance
* Return: 0 on success, otherwise a negative errno number
*/
static int svec_remove(struct device *vdev, unsigned int ndev)
static int svec_remove(struct device *dev, unsigned int ndev)
{
fpga_mgr_unregister(vdev);
struct svec_dev *svec = dev_get_drvdata(dev);
svec_fpga_exit(svec);
svec_dbg_exit(svec);
fpga_mgr_unregister(svec->mgr);
fpga_mgr_free(svec->mgr);
dev_set_drvdata(dev, NULL);
vme_disable_device(svec->vdev);
kfree(svec);
return 0;
}
......@@ -444,7 +580,8 @@ static struct vme_driver svec_driver = {
.probe = svec_probe,
.remove = svec_remove,
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
.name = "svec-fmc-carrier",
},
.id_table = svec_id_table,
};
......@@ -464,9 +601,10 @@ module_init(svec_init);
module_exit(svec_exit);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_AUTHOR("Juan David Gonzalez Cobas <dcobas@cern.ch>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(GIT_VERSION);
MODULE_DESCRIPTION("svec driver");
MODULE_VERSION(VERSION);
MODULE_DESCRIPTION("Driver for the 'Simple VME FMC Carrier' a.k.a. SVEC");
MODULE_SOFTDEP("pre: htvic i2c_mux i2c_ohwr spi-ocores");
ADDITIONAL_VERSIONS;
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __SVEC_H__
#define __SVEC_H__
#include <linux/bitmap.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include <vmebus.h>
#include "svec-core-fpga.h"
#define PCI_VENDOR_ID_CERN (0x10DC)
#define SVEC_BASE_LOADER 0x70000
#define SVEC_FMC_SLOTS 2
/* On FPGA components */
#define SVEC_GOLDEN_ADDR 0x10000
#define SVEC_I2C_SIZE 32
#define SVEC_I2C_ADDR_START 0x14000
#define SVEC_I2C_ADDR_END ((SVEC_I2C_ADDR_START + SVEC_I2C_SIZE) - 1)
/**
* Byte sequence to unlock and clear the Application FPGA
*/
static const uint32_t boot_unlock_sequence[8] = {
0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe
};
enum svec_dev_flags {
SVEC_DEV_F_APP = BIT(0),
};
enum {
/* Metadata */
FPGA_META_VENDOR = 0x00,
FPGA_META_DEVICE = 0x04,
FPGA_META_VERSION = 0x08,
FPGA_META_BOM = 0x0C,
FPGA_META_SRC = 0x10,
FPGA_META_CAP = 0x20,
FPGA_META_UUID = 0x30,
};
enum {
/* Metadata */
SVEC_META_BASE = SVEC_TEMPLATE_REGS_METADATA,
SVEC_META_VENDOR = SVEC_META_BASE + FPGA_META_VENDOR,
SVEC_META_DEVICE = SVEC_META_BASE + FPGA_META_DEVICE,
SVEC_META_VERSION = SVEC_META_BASE + FPGA_META_VERSION,
SVEC_META_BOM = SVEC_META_BASE + FPGA_META_BOM,
SVEC_META_SRC = SVEC_META_BASE + FPGA_META_SRC,
SVEC_META_CAP = SVEC_META_BASE + FPGA_META_CAP,
SVEC_META_UUID = SVEC_META_BASE + FPGA_META_UUID,
};
#define SVEC_META_VENDOR_ID PCI_VENDOR_ID_CERN
#define SVEC_META_DEVICE_ID 0x53564543
//#define SVEC_META_BOM_BE 0xFEFF0000
#define SVEC_META_BOM_BE 0xFFFE0000 /* FIXME */
#define SVEC_META_BOM_END_MASK 0xFFFF0000
#define SVEC_META_BOM_VER_MASK 0x0000FFFF
#define SVEC_META_VERSION_MASK 0xFFFF0000
#define SVEC_META_VERSION_1_4 0x01040000
/**
* struct svec_meta_id Metadata
*/
struct svec_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 SVEC_FUNC_NR 1
#define SVEC_FMC_SLOTS 2
struct svec_fpga {
struct device dev;
unsigned int function_nr;
void __iomem *fpga;
struct platform_device *vic_pdev;
struct platform_device *app_pdev;
struct fmc_slot_info slot_info[SVEC_FMC_SLOTS];
struct dentry *dbg_dir;
#define SVEC_DBG_CSR_NAME "csr_regs"
struct dentry *dbg_csr;
struct debugfs_regset32 dbg_csr_reg;
#define SVEC_DBG_BLD_INFO_NAME "build_info"
struct dentry *dbg_bld_info;
};
static inline struct svec_fpga *to_svec_fpga(struct device *_dev)
{
return container_of(_dev, struct svec_fpga, dev);
}
/**
* struct svec_dev - SVEC instance
* It describes a SVEC device instance.
* @vdev VME device instance
* @flags: flags
* @mgr FPGA manager instance
* @bitstream_last_word last data to write into the FPGA
* @bistream_last_word_size last data size to write in the FPGA. This is a dirty
* and ugly hack in order to properly handle a dirty
* and ugly interface. The SVEC bootloader does not
* accept emtpy transfers and neither to declare the
* transmission over without sending data.
* @fpga_status state of the Application FPGA
* @i2c_adapter the I2C adapter to access the FMC EEPROMs
* The user must lock the spinlock `lock` when using the following variables in
* this data structure: flags.
* @mem: ioremapped memory
*/
struct svec_dev {
struct vme_dev *vdev;
char name[8];
unsigned long flags;
struct svec_meta_id meta;
struct mutex mtx;
struct fpga_manager *mgr;
uint32_t bitstream_last_word;
uint32_t bitstream_last_word_size;
enum fpga_mgr_states fpga_status;
struct platform_device *i2c_pdev;
struct i2c_adapter *i2c_adapter;
struct fmc_slot_info slot_info[SVEC_FMC_SLOTS];
void *mem;
struct dentry *dbg_dir;
#define SVEC_DBG_FW_NAME "fpga_firmware"
struct dentry *dbg_fw;
#define SVEC_DBG_META_NAME "fpga_device_metadata"
struct dentry *dbg_meta;
struct svec_fpga *svec_fpga;
};
extern int svec_fpga_init(struct svec_dev *svec_dev, unsigned int function_nr);
extern int svec_fpga_exit(struct svec_dev *svec_dev);
#endif /* __SVEC_H__ */
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