Files
Jungfraujoch/fpga/pcie_driver/jfjoch_drv.c
2024-10-23 19:03:09 +02:00

171 lines
4.4 KiB
C

// Copyright (2019-2023) Paul Scherrer Institute
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/io.h>
#include "jfjoch_drv.h"
MODULE_AUTHOR("Filip Leonarski; Paul Scherrer Institute");
MODULE_DESCRIPTION("Jungfraujoch device module");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0.0-rc.23");
#define PCI_DEV_ID_JUNGFRAUJOCH (0x3450)
static const struct pci_device_id jfjoch_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEV_ID_JUNGFRAUJOCH) },
{ 0, },
};
static struct pci_driver jfjoch_pci_driver;
static const struct file_operations jfjoch_cdev_fileops;
static int jfjoch_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
int err;
struct device *const dev = &pdev->dev;
struct jfjoch_drvdata *drvdata;
struct DataCollectionStatus status;
if ((nbuffer < 64) || (nbuffer > MAX_FPGA_BUFFER)) {
dev_err(dev, "nbuffer parameter must be in range 64-%d\n", MAX_FPGA_BUFFER);
return -EINVAL;
}
// Setup private data structure
drvdata = kzalloc(sizeof(struct jfjoch_drvdata), GFP_KERNEL);
if (drvdata == NULL) {
dev_err(dev, "Failed to allocate driver data\n");
return -ENOMEM;
}
pci_set_drvdata(pdev, drvdata);
drvdata->pdev = pdev;
INIT_KFIFO(drvdata->work_compl);
mutex_init(&drvdata->work_compl_read_mutex);
init_waitqueue_head(&drvdata->work_compl_wait_queue);
atomic_set(&drvdata->active_handles, 0);
spin_lock_init(&drvdata->work_request_submit_spinlock);
// Enable device
err = pci_enable_device(pdev);
if (err) {
dev_err(dev, "Failed to enable PCI device (%d)\n", err);
goto dealloc_private_data;
}
err = jfjoch_map_cfg_bar(pdev);
if (err)
goto disable_device;
err = jfjoch_check_version(pdev);
if (err)
goto unmap_bar;
err = jfjoch_register_misc_dev(pdev);
if (err)
goto unmap_bar;
// Setup DMA on PCIe side
err = jfjoch_setup_pcie_for_dma(pdev);
if (err)
goto deregister_misc;
err = jfjoch_setup_pcie_interrupt(pdev);
if (err)
goto clear_master;
// Allocate memory
err = jfjoch_alloc_phys_continuous_buf(pdev);
if (err)
goto free_interrupts;
jfjoch_get_status(drvdata, &status);
drvdata->git_sha1 = status.git_sha1;
drvdata->max_modules = status.max_modules;
drvdata->fpga_variant = pdev->subsystem_device;
if (status.jfjoch_fpga_variant == JFJOCH_FPGA_VARIANT_100G)
drvdata->eth_links = 1;
else if (status.jfjoch_fpga_variant == JFJOCH_FPGA_VARIANT_8x10G)
drvdata->eth_links = 8;
else {
goto free_phys_continous_buf;
}
jfjoch_setup_cms(drvdata);
jfjoch_setup_network(drvdata);
jfjoch_read_cms_default_config(drvdata);
jfjoch_i2c_si5394_init(drvdata);
err = jfjoch_register_sysfs(drvdata);
if (err)
goto free_phys_continous_buf;
dev_info(drvdata->miscdev.this_device, "Jungfraujoch FPGA loaded with FW build: %x", drvdata->git_sha1);
return 0;
free_phys_continous_buf:
jfjoch_free_phys_continuous_buf(pdev);
free_interrupts:
jfjoch_free_pcie_interrupt(pdev);
clear_master:
pci_clear_master(pdev);
deregister_misc:
misc_deregister(&drvdata->miscdev);
unmap_bar:
pci_iounmap(pdev, drvdata->bar0);
pci_release_region(pdev, 0);
disable_device:
pci_disable_device(pdev);
dealloc_private_data:
kfree(drvdata);
return err;
}
void jfjoch_reset(struct jfjoch_drvdata *drvdata) {
pci_reset_function(drvdata->pdev);
jfjoch_setup_cms(drvdata);
dev_info(drvdata->miscdev.this_device, "Jungfraujoch FPGA restarted");
}
static void jfjoch_pci_remove(struct pci_dev *pdev) {
struct jfjoch_drvdata *drvdata = pci_get_drvdata(pdev);
if (!drvdata)
return;
pci_clear_master(pdev);
jfjoch_free_pcie_interrupt(pdev);
jfjoch_unregister_sysfs(drvdata);
jfjoch_unregister_misc_dev(pdev);
jfjoch_free_phys_continuous_buf(pdev);
pci_iounmap(pdev, drvdata->bar0);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
kfree(drvdata);
}
static struct pci_driver jfjoch_pci_driver = {
.name = "jfjoch",
.id_table = jfjoch_pci_tbl,
.probe = jfjoch_pci_probe,
.remove = jfjoch_pci_remove
};
MODULE_DEVICE_TABLE(pci, jfjoch_pci_tbl);
module_pci_driver(jfjoch_pci_driver);