diff --git a/fpga/pcie_driver/CMakeLists.txt b/fpga/pcie_driver/CMakeLists.txt index 93c11885..3fa0d939 100644 --- a/fpga/pcie_driver/CMakeLists.txt +++ b/fpga/pcie_driver/CMakeLists.txt @@ -2,7 +2,8 @@ # but it helps in code completion in CLion IDE ADD_LIBRARY(jfjoch_kernel_module STATIC - jfjoch_ioctl.h jfjoch_drv.c jfjoch_drv.h jfjoch_ioctl.h jfjoch_pcie_setup.c jfjoch_memory.c jfjoch_ioctl.c jfjoch_function.c jfjoch_miscdev.c) + jfjoch_ioctl.h jfjoch_drv.c jfjoch_drv.h jfjoch_ioctl.h jfjoch_pcie_setup.c jfjoch_memory.c jfjoch_ioctl.c jfjoch_function.c jfjoch_miscdev.c +jfjoch_sysfs.c) EXECUTE_PROCESS(COMMAND uname -r OUTPUT_VARIABLE KERNEL_RELEASE diff --git a/fpga/pcie_driver/Makefile b/fpga/pcie_driver/Makefile index 5eaa1904..f1e55961 100644 --- a/fpga/pcie_driver/Makefile +++ b/fpga/pcie_driver/Makefile @@ -1,7 +1,7 @@ obj-m += jfjoch.o cflags-m=-std=c99 -jfjoch-y := jfjoch_drv.o jfjoch_ioctl.o jfjoch_memory.o jfjoch_pcie_setup.o jfjoch_function.o jfjoch_miscdev.o +jfjoch-y := jfjoch_drv.o jfjoch_ioctl.o jfjoch_memory.o jfjoch_pcie_setup.o jfjoch_function.o jfjoch_miscdev.o jfjoch_sysfs.o all: make -C /lib/modules/$(shell uname -r)/build M=$(CURDIR) modules diff --git a/fpga/pcie_driver/jfjoch_drv.c b/fpga/pcie_driver/jfjoch_drv.c index dc0a337d..9530b74a 100644 --- a/fpga/pcie_driver/jfjoch_drv.c +++ b/fpga/pcie_driver/jfjoch_drv.c @@ -83,9 +83,16 @@ static int jfjoch_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id jfjoch_setup_cms(drvdata); jfjoch_setup_network(drvdata); + err = jfjoch_register_sysfs(drvdata); + if (err) + goto unregister_sysfs; + dev_info(drvdata->miscdev.this_device, "Jungfraujoch FPGA loaded with FW build: %x", drvdata->git_sha1); return 0; + unregister_sysfs: + jfjoch_unregister_sysfs(drvdata); + deregister_misc: misc_deregister(&drvdata->miscdev); @@ -119,6 +126,7 @@ static void jfjoch_pci_remove(struct pci_dev *pdev) { pci_clear_master(pdev); + jfjoch_unregister_sysfs(drvdata); jfjoch_unregister_misc_dev(pdev); jfjoch_free_phys_continous_buf(pdev); pci_iounmap(pdev, drvdata->bar0); diff --git a/fpga/pcie_driver/jfjoch_drv.h b/fpga/pcie_driver/jfjoch_drv.h index 5582089c..df6fde0f 100644 --- a/fpga/pcie_driver/jfjoch_drv.h +++ b/fpga/pcie_driver/jfjoch_drv.h @@ -142,6 +142,9 @@ void jfjoch_reset(struct jfjoch_drvdata *drvdata); void jfjoch_write_register(struct jfjoch_drvdata *drvdata, uint32_t addr, uint32_t val); uint32_t jfjoch_read_register(struct jfjoch_drvdata *drvdata, uint32_t addr); +int jfjoch_register_sysfs(struct jfjoch_drvdata *drvdata); +void jfjoch_unregister_sysfs(struct jfjoch_drvdata *drvdata); + static const struct file_operations jfjoch_cdev_fileops = { .mmap = jfjoch_cdev_mmap, .unlocked_ioctl = jfjoch_cdev_ioctl, diff --git a/fpga/pcie_driver/jfjoch_miscdev.c b/fpga/pcie_driver/jfjoch_miscdev.c index 03f3f423..32b77598 100644 --- a/fpga/pcie_driver/jfjoch_miscdev.c +++ b/fpga/pcie_driver/jfjoch_miscdev.c @@ -10,7 +10,7 @@ int jfjoch_register_misc_dev(struct pci_dev *pdev) { struct jfjoch_drvdata *drvdata = pci_get_drvdata(pdev); snprintf( drvdata->name, 255, "jfjoch%d", device_index++); - + drvdata->miscdev.parent = &pdev->dev; drvdata->miscdev.mode = 0660; drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; drvdata->miscdev.fops = &jfjoch_cdev_fileops; diff --git a/fpga/pcie_driver/jfjoch_sysfs.c b/fpga/pcie_driver/jfjoch_sysfs.c new file mode 100644 index 00000000..f99d93ef --- /dev/null +++ b/fpga/pcie_driver/jfjoch_sysfs.c @@ -0,0 +1,180 @@ +// Copyright (2019-2023) Paul Scherrer Institute + +#include +#include +#include + +#include "jfjoch_drv.h" + +static ssize_t hbm0_temp_degC_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + unsigned int val = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_HBM_TEMP1_INS_REG); + return scnprintf(buf, PAGE_SIZE, "%d", val); +} + +static ssize_t hbm1_temp_degC_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + unsigned int val = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_HBM_TEMP2_INS_REG); + return scnprintf(buf, PAGE_SIZE, "%d", val); +} + +static ssize_t fpga_temp_degC_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + unsigned int val = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_FPGA_TEMP_INS_REG); + return scnprintf(buf, PAGE_SIZE, "%d", val); +} + +static ssize_t fpga_power_mW_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + u32 fpga_pcie_3p3V_I_mA = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_3V3PEX_I_IN_INS_REG); + u32 fpga_pcie_12V_I_mA = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_12VPEX_I_IN_INS_REG); + u32 fpga_pcie_3p3V_V_mV = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_3V3_PEX_INS_REG); + u32 fpga_pcie_12V_V_mV = ioread32(drvdata->bar0 + CMS_OFFSET + ADDR_CMS_12V_PEX_INS_REG); + u32 power_mW = (fpga_pcie_3p3V_I_mA * fpga_pcie_3p3V_V_mV + fpga_pcie_12V_I_mA * fpga_pcie_12V_V_mV) / 1000; + return scnprintf(buf, PAGE_SIZE, "%d", power_mW); +} + +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + unsigned int val = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_CTRL_REGISTER); + return scnprintf(buf, PAGE_SIZE, "%d", val); +} + +static ssize_t eth_aligned_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + // Need to ask twice to get recent answer + ioread32(drvdata->bar0 + CMAC_OFFSET + 0x0204); + unsigned int val = ioread32(drvdata->bar0 + CMAC_OFFSET + 0x0204) & 0x2; + return scnprintf(buf, PAGE_SIZE, "%d", val); +} + +static ssize_t git_sha1_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + return scnprintf(buf, PAGE_SIZE, "%x", drvdata->git_sha1); +} + +static ssize_t max_modules_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + return scnprintf(buf, PAGE_SIZE, "%x", drvdata->max_modules); +} + +static ssize_t stalls_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + u64 val0 = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_STALLS_HBM_LO); + u64 val1 = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_STALLS_HBM_HI); + u64 val = (val1 << 32) | val0; + return scnprintf(buf, PAGE_SIZE, "%lld", val); +} + +static ssize_t ipv4_addr_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + u32 val = ioread32((drvdata->bar0) + ACTION_CONFIG_OFFSET + ADDR_IPV4_ADDR); + return scnprintf(buf, PAGE_SIZE, "0x%x", val); +} + +static ssize_t mac_addr_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct miscdevice* miscdev = dev_get_drvdata(dev); + struct jfjoch_drvdata* drvdata = container_of(miscdev, struct jfjoch_drvdata, miscdev); + + u64 val0 = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_MAC_ADDR_LO); + u64 val1 = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_MAC_ADDR_HI); + u64 val = (val1 << 32) | val0; + return scnprintf(buf, PAGE_SIZE, "0x%llx", val); +} + +static ssize_t nbuffers_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + return scnprintf(buf, 25, "%d", nbuffer); +} + +static ssize_t release_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + return scnprintf(buf, 25, "%d", RELEASE_LEVEL); +} + +static struct device_attribute device_attrs[] = { + __ATTR_RO(hbm0_temp_degC), + __ATTR_RO(hbm1_temp_degC), + __ATTR_RO(fpga_temp_degC), + __ATTR_RO(fpga_power_mW), + __ATTR_RO(nbuffers), + __ATTR_RO(stalls), + __ATTR_RO(status), + __ATTR_RO(git_sha1), + __ATTR_RO(release), + __ATTR_RO(max_modules), + __ATTR_RO(eth_aligned), + __ATTR_RO(mac_addr), + __ATTR_RO(ipv4_addr) +}; + +int jfjoch_register_sysfs(struct jfjoch_drvdata *drvdata) { + if (!drvdata) + return -EINVAL; + + int i, err; + + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { + err = device_create_file(drvdata->miscdev.this_device, &device_attrs[i]); + if (err) { + dev_err(drvdata->miscdev.this_device, "Failed to create sysfs file"); + break; + } + } + + if (err) { + while (--i >= 0) + device_remove_file(drvdata->miscdev.this_device, + &device_attrs[i]); + } + return err; +} + +void jfjoch_unregister_sysfs(struct jfjoch_drvdata *drvdata) { + int i; + + for (i = 0; i < ARRAY_SIZE(device_attrs); i++) + device_remove_file(drvdata->miscdev.this_device, &device_attrs[i]); +}