Commit 156b2ea2 authored by Federico Vaga's avatar Federico Vaga

kernel: allow userspace to program FPGA

For the time being you must write the bitstream in a single write. For
example:

    dd if=/home/fvaga/spec-fmc-adc-v4.0.bin of=/dev/spec-0700 obs=5000000
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 12dc2b34
......@@ -39,11 +39,40 @@ static int spec_validate(struct fmc_device *fmc, struct fmc_driver *drv)
return -ENOENT;
}
static int spec_reprogram_raw(struct fmc_device *fmc, struct fmc_driver *drv,
void *gw, unsigned long len)
{
struct spec_dev *spec = fmc->carrier_data;
struct device *dev = fmc->hwdev;
int ret;
if (!gw || !len) {
dev_err(dev, "Invalid firmware buffer - buf: %p len: %ld\n",
gw, len);
return -EINVAL;
}
if (!drv)
dev_info(dev, "Carrier FPGA re-program\n");
fmc_free_sdb_tree(fmc);
fmc->flags &= ~FMC_DEVICE_HAS_GOLDEN;
ret = spec_load_fpga(spec, gw, len);
if (ret < 0) {
dev_err(dev, "writing firmware: error %i\n", ret);
return ret;
}
fmc->flags |= FMC_DEVICE_HAS_CUSTOM;
return 0;
}
static int spec_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
char *gw)
{
const struct firmware *fw;
struct spec_dev *spec = fmc->carrier_data;
struct device *dev = fmc->hwdev;
int ret;
......@@ -66,17 +95,15 @@ static int spec_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
dev_warn(dev, "request firmware \"%s\": error %i\n", gw, ret);
goto out;
}
fmc_free_sdb_tree(fmc);
fmc->flags &= ~(FMC_DEVICE_HAS_GOLDEN | FMC_DEVICE_HAS_CUSTOM);
ret = spec_load_fpga(spec, fw->data, fw->size);
ret = spec_reprogram_raw(fmc, drv, (void *)fw->data, fw->size);
if (ret <0) {
dev_err(dev, "write firmware \"%s\": error %i\n", gw, ret);
goto out;
}
if (gw == spec_fw_name)
fmc->flags |= FMC_DEVICE_HAS_GOLDEN;
else
fmc->flags |= FMC_DEVICE_HAS_CUSTOM;
out:
release_firmware(fw);
......@@ -370,6 +397,7 @@ static int spec_write_ee(struct fmc_device *fmc, int pos,
static struct fmc_operations spec_fmc_operations = {
/* no readl/writel because we have the base pointer */
.validate = spec_validate,
.reprogram_raw = spec_reprogram_raw,
.reprogram = spec_reprogram,
.irq_request = spec_irq_request,
.irq_ack = spec_irq_ack,
......@@ -491,7 +519,7 @@ static int check_golden(struct fmc_device *fmc)
}
int spec_fmc_create(struct spec_dev *spec)
int spec_fmc_create(struct spec_dev *spec, struct fmc_gateware *gw)
{
struct fmc_device *fmc;
struct pci_dev *pdev;
......@@ -534,10 +562,12 @@ int spec_fmc_create(struct spec_dev *spec)
ret = spec_irq_init(fmc);
if (ret)
goto out_free;
ret = fmc_device_register(fmc);
ret = fmc_device_register_gw(fmc, gw);
if (ret)
goto out_irq;
spec_gpio_init(fmc); /* May fail, we don't care */
spec->flags |= SPEC_FLAG_FMC_REGISTERED;
return ret;
out_irq:
......@@ -550,6 +580,8 @@ out_free:
void spec_fmc_destroy(struct spec_dev *spec)
{
if (!(spec->flags & SPEC_FLAG_FMC_REGISTERED))
return;
/* undo the things in the reverse order, but pin the device first */
get_device(&spec->fmc->dev);
spec_gpio_exit(spec->fmc);
......@@ -558,4 +590,5 @@ void spec_fmc_destroy(struct spec_dev *spec)
spec_i2c_exit(spec->fmc);
put_device(&spec->fmc->dev);
spec->fmc = NULL;
spec->flags &= ~SPEC_FLAG_FMC_REGISTERED;
}
......@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <asm/unaligned.h>
#include <linux/version.h>
#include <linux/fs.h>
#include "spec.h"
#include "loader-ll.h"
......@@ -87,6 +88,87 @@ int spec_load_fpga_file(struct spec_dev *spec, char *name)
return err;
}
static int spec_reconfigure(struct spec_dev *spec, struct fmc_gateware *gw)
{
int ret;
if (spec->flags & SPEC_FLAG_FMC_REGISTERED)
spec_fmc_destroy(spec);
/* Load the golden FPGA binary to read the eeprom */
ret = spec_load_fpga_file(spec, spec_fw_name);
if (ret)
return ret;
return spec_fmc_create(spec, gw);
}
/* * * * * * MISC DEVICE * * * * * */
static int spec_mdev_simple_open(struct inode *inode, struct file *file)
{
struct miscdevice *mdev_ptr = file->private_data;
file->private_data = container_of(mdev_ptr, struct spec_dev, mdev);
return 0;
}
static ssize_t spec_mdev_write_raw(struct file *f, const char __user *buf,
size_t count, loff_t *offp)
{
struct spec_dev *spec = f->private_data;
struct fmc_gateware gw;
int err = 0;
if (!count)
return -EINVAL;
/* Copy FPGA bitstream to kernel space */
gw.len = count;
gw.bitstream = vmalloc(count);
if (!gw.bitstream)
return -ENOMEM;
if (copy_from_user(gw.bitstream, buf, gw.len)) {
err = -EFAULT;
goto out;
}
dev_dbg(&spec->pdev->dev, "writing FPGA %p %ld (%zu + %lld)\n",
gw.bitstream, gw.len, count, *offp);
/* Program FPGA */
err = spec_reconfigure(spec, &gw);
if (err)
dev_err(&spec->pdev->dev,
"Manually program FPGA bitstream from buffer: fail\n");
else
dev_info(&spec->pdev->dev,
"Manually program FPGA bitstream from buffer: success\n");
out:
vfree(gw.bitstream);
return err ? err : count;
}
static const struct file_operations spec_fops = {
.owner = THIS_MODULE,
.open = spec_mdev_simple_open,
.write = spec_mdev_write_raw,
};
static int spec_create_misc_device(struct spec_dev *spec)
{
spec->mdev.minor = MISC_DYNAMIC_MINOR;
spec->mdev.fops = &spec_fops;
spec->mdev.name = spec->name;
return misc_register(&spec->mdev);
}
static void spec_destroy_misc_device(struct spec_dev *spec)
{
misc_deregister(&spec->mdev);
}
/* * * * * * END MISC DEVICE * * * * */
static int spec_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
......@@ -142,12 +224,7 @@ static int spec_probe(struct pci_dev *pdev,
gennum_mask_val(spec, 0xfc0, 0xfc0, GNGPIO_DIRECTION_MODE); /* input */
gennum_writel(spec, 0xffff, GNGPIO_INT_MASK_SET); /* disable */
/* Load the golden FPGA binary to read the eeprom */
ret = spec_load_fpga_file(spec, spec_fw_name);
if (ret)
goto out_unmap;
ret = spec_fmc_create(spec);
ret = spec_reconfigure(spec, NULL);
if (ret)
goto out_unmap;
......@@ -156,8 +233,17 @@ static int spec_probe(struct pci_dev *pdev,
/* Done */
pci_set_drvdata(pdev, spec);
ret = spec_create_misc_device(spec);
if (ret) {
dev_err(&spec->pdev->dev, "Error creating misc device\n");
goto failed_misc;
}
return 0;
failed_misc:
spec_fmc_destroy(spec);
out_unmap:
for (i = 0; i < 3; i++) {
if (spec->remap[i])
......@@ -180,6 +266,7 @@ static void spec_remove(struct pci_dev *pdev)
dev_info(&pdev->dev, "remove\n");
spec_destroy_misc_device(spec);
spec_fmc_destroy(spec);
for (i = 0; i < 3; i++) {
if (spec->remap[i])
......
......@@ -9,6 +9,7 @@
*/
#ifndef __SPEC_H__
#define __SPEC_H__
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/completion.h>
......@@ -37,12 +38,14 @@ struct spec_dev {
struct gpio_chip *gpio;
struct vic_irq_controller *vic;
spinlock_t irq_lock;
struct miscdevice mdev;
char name[SPEC_NAME_LEN];
};
#define SPEC_FLAG_FAKE_EEPROM 0x00000001
#define SPEC_FLAG_IRQS_REQUESTED 0x00000002
#define SPEC_FLAG_FMC_REGISTERED 0x00000004
/* Registers for GN4124 access */
enum {
......@@ -129,7 +132,7 @@ extern char *spec_fw_name;
extern int spec_use_msi;
/* Functions in spec-fmc.c, used by spec-pci.c */
extern int spec_fmc_create(struct spec_dev *spec);
extern int spec_fmc_create(struct spec_dev *spec, struct fmc_gateware *gw);
extern void spec_fmc_destroy(struct spec_dev *spec);
/* Functions in spec-i2c.c, used by spec-fmc.c */
......
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