171 lines
4.4 KiB
C
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);
|