From 1f0a4021f79428f5ef1969a4d5c09ef9ff9320df Mon Sep 17 00:00:00 2001 From: Raghav Rao Date: Thu, 18 Sep 2025 20:44:55 +0000 Subject: [PATCH 1/6] IGNORE THIS COMMIT: [PDDF] multifpgapci gpio module (PR sonic-net#23669) --- platform/pddf/i2c/debian/rules | 2 +- .../modules/include/pddf_multifpgapci_defs.h | 5 + .../include/pddf_multifpgapci_gpio_defs.h | 84 ++++++++ .../pddf/i2c/modules/multifpgapci/Makefile | 2 +- .../driver/pddf_multifpgapci_driver.c | 92 ++++++--- .../i2c/modules/multifpgapci/gpio/Makefile | 6 + .../modules/multifpgapci/gpio/driver/Makefile | 4 + .../driver/pddf_multifpgapci_gpio_driver.c | 145 +++++++++++++ .../gpio/pddf_multifpgapci_gpio_module.c | 190 ++++++++++++++++++ platform/pddf/i2c/utils/pddfparse.py | 27 +++ 10 files changed, 532 insertions(+), 25 deletions(-) create mode 100644 platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h create mode 100644 platform/pddf/i2c/modules/multifpgapci/gpio/Makefile create mode 100644 platform/pddf/i2c/modules/multifpgapci/gpio/driver/Makefile create mode 100644 platform/pddf/i2c/modules/multifpgapci/gpio/driver/pddf_multifpgapci_gpio_driver.c create mode 100644 platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c diff --git a/platform/pddf/i2c/debian/rules b/platform/pddf/i2c/debian/rules index f62a480aa2f1..9d78317880fc 100755 --- a/platform/pddf/i2c/debian/rules +++ b/platform/pddf/i2c/debian/rules @@ -19,7 +19,7 @@ PACKAGE_PRE_NAME := sonic-platform-pddf KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/i2c fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver +MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver MODULE_DIR:= modules UTILS_DIR := utils SERVICE_DIR := service diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h index c724ae0ccccb..c18931954533 100644 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h @@ -21,6 +21,7 @@ #include "linux/types.h" #include +#include "pddf_multifpgapci_gpio_defs.h" #include "pddf_multifpgapci_i2c_defs.h" #define NAME_SIZE 32 @@ -37,6 +38,10 @@ struct pddf_multifpgapci_drvdata { struct kobject *i2c_kobj; struct i2c_adapter_drvdata i2c_adapter_drvdata; bool i2c_adapter_drvdata_initialized; + // gpio + struct kobject *gpio_kobj; + struct gpio_chip_drvdata gpio_chip_drvdata; + bool gpio_chip_drvdata_initialized; }; // FPGA diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h new file mode 100644 index 000000000000..855dedf6cfbd --- /dev/null +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h @@ -0,0 +1,84 @@ +/* + * Copyright 2025 Nexthop Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PDDF_MULTIFPGAPCI_GPIO_DEFS_H__ +#define __PDDF_MULTIFPGAPCI_GPIO_DEFS_H__ + +#include +#include +#include +#include +#include + +#include "pddf_client_defs.h" + +#define MAX_MULTIFPGAPCI_GPIO_LINES 64 + +struct pddf_multifpgapci_gpio_line_pdata { + // Offset within FPGA + uint32_t offset; + uint32_t bit; + // GPIO_LINE_DIRECTION_IN = 0 or GPIO_LINE_DIRECTION_OUT = 1 + int direction; +}; + +struct pddf_multifpgapci_gpio_chip_pdata { + // The number of GPIOs handled by this controller. + int ngpio; + struct pci_dev *fpga; + struct pddf_multifpgapci_gpio_line_pdata + chan_data[MAX_MULTIFPGAPCI_GPIO_LINES]; +}; + +struct gpio_line_attrs { + PDDF_ATTR attr_bit; + PDDF_ATTR attr_offset; + PDDF_ATTR attr_direction; + PDDF_ATTR attr_create; +}; + +#define NUM_GPIO_LINE_ATTRS (sizeof(struct gpio_line_attrs) / sizeof(PDDF_ATTR)) + +struct gpio_chip_attrs { + struct gpio_line_attrs line_attrs; + PDDF_ATTR attr_ngpio; + PDDF_ATTR attr_create; +}; + +#define NUM_GPIO_CHIP_ATTRS \ + ((sizeof(struct gpio_chip_attrs) - sizeof(struct gpio_line_attrs)) / \ + sizeof(PDDF_ATTR)) + +struct gpio_chip_drvdata { + // pdata is passed to gpio platform driver. + struct pddf_multifpgapci_gpio_chip_pdata pdata; + // temp_line_data is mutated by sysfs attrs and copied to pdata. + struct pddf_multifpgapci_gpio_line_pdata temp_line_data; + // sysfs attrs + struct gpio_chip_attrs attrs; + struct kobject *line_kobj; + struct attribute *gpio_line_attrs[NUM_GPIO_LINE_ATTRS + 1]; + struct attribute_group gpio_line_attr_group; + struct attribute *gpio_chip_attrs[NUM_GPIO_CHIP_ATTRS + 1]; + struct attribute_group gpio_chip_attr_group; +}; + +extern int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, + struct kobject *kobj); + +// Only called if multifpgapci_gpio_module_init succeeded +extern void pddf_multifpgapci_gpio_module_exit(struct pci_dev *pci_dev, + struct kobject *kobj); + +#endif diff --git a/platform/pddf/i2c/modules/multifpgapci/Makefile b/platform/pddf/i2c/modules/multifpgapci/Makefile index 697b7878cb9e..0e96bd0fe83c 100644 --- a/platform/pddf/i2c/modules/multifpgapci/Makefile +++ b/platform/pddf/i2c/modules/multifpgapci/Makefile @@ -1,4 +1,4 @@ -obj-m := driver/ i2c/ +obj-m := driver/ gpio/ i2c/ obj-m += pddf_multifpgapci_module.o ccflags-y:= -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c b/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c index 4cdd51aa11f1..45511260c62c 100644 --- a/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c +++ b/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c @@ -51,6 +51,7 @@ #include "pddf_client_defs.h" #include "pddf_multifpgapci_defs.h" +#include "pddf_multifpgapci_gpio_defs.h" #include "pddf_multifpgapci_i2c_defs.h" #define BDF_NAME_SIZE 32 @@ -86,6 +87,9 @@ struct pddf_attrs { PDDF_ATTR attr_dev_ops; }; +#define NUM_FPGA_ATTRS \ + (sizeof(struct pddf_attrs) / sizeof(PDDF_ATTR)) + struct pddf_multi_fpgapci_ops_t pddf_multi_fpgapci_ops; EXPORT_SYMBOL(pddf_multi_fpgapci_ops); @@ -95,10 +99,16 @@ struct fpga_data_node { char dev_name[DEVICE_NAME_SIZE]; // device_name as defined in pddf-device.json. struct kobject *kobj; struct pci_dev *dev; - struct pddf_attrs attrs; void __iomem *fpga_ctl_addr; unsigned long bar_start; struct list_head list; + + // sysfs attrs + struct pddf_attrs attrs; + struct attribute *fpga_attrs[NUM_FPGA_ATTRS + 1]; + struct attribute_group fpga_attr_group; + bool fpga_attr_group_initialized; + bool pddf_clients_data_group_initialized; }; LIST_HEAD(fpga_list); @@ -165,12 +175,22 @@ void free_pci_drvdata(struct pci_dev *pci_dev) } KOBJ_FREE(pci_drvdata->i2c_kobj); + if (pci_drvdata->gpio_chip_drvdata_initialized) { + pddf_multifpgapci_gpio_module_exit(pci_dev, + pci_drvdata->gpio_kobj); + } + KOBJ_FREE(pci_drvdata->gpio_kobj); + pci_set_drvdata(pci_dev, NULL); } void free_sysfs_attr_groups(struct fpga_data_node *node) { - sysfs_remove_group(node->kobj, &pddf_clients_data_group); + if (node->fpga_attr_group_initialized) + sysfs_remove_group(node->kobj, &node->fpga_attr_group); + + if (node->pddf_clients_data_group_initialized) + sysfs_remove_group(node->kobj, &pddf_clients_data_group); } void delete_fpga_data_node(const char *bdf) @@ -181,11 +201,9 @@ void delete_fpga_data_node(const char *bdf) list_for_each_entry_safe(node, tmp, &fpga_list, list) { if (strcmp(node->bdf, bdf) == 0) { - KOBJ_FREE(node->kobj); - free_pci_drvdata(node->dev); free_sysfs_attr_groups(node); - + KOBJ_FREE(node->kobj); list_del(&node->list); kfree(node); break; @@ -290,6 +308,23 @@ static int pddf_pci_add_fpga(char *bdf, struct pci_dev *dev) } pci_drvdata->i2c_adapter_drvdata_initialized = true; + pci_drvdata->gpio_kobj = kobject_create_and_add("gpio", fpga_data->kobj); + if (!pci_drvdata->gpio_kobj) { + pddf_dbg(MULTIFPGA, KERN_ERR "%s create gpio kobj failed\n", + __FUNCTION__); + ret = -ENOMEM; + goto free_i2c_dirs; + } + + ret = pddf_multifpgapci_gpio_module_init(dev, pci_drvdata->gpio_kobj); + if (ret) { + pddf_dbg(MULTIFPGA, + KERN_ERR "%s create add gpio module failed %d\n", + __FUNCTION__, ret); + goto free_gpio_kobj; + } + pci_drvdata->gpio_chip_drvdata_initialized = true; + fpga_data->dev = dev; PDDF_DATA_ATTR( @@ -298,40 +333,50 @@ static int pddf_pci_add_fpga(char *bdf, struct pci_dev *dev) fpga_data->attrs.attr_dev_ops = attr_dev_ops; - struct attribute *attrs_fpgapci[] = { - &fpga_data->attrs.attr_dev_ops.dev_attr.attr, - NULL, - }; + fpga_data->fpga_attrs[0] = &fpga_data->attrs.attr_dev_ops.dev_attr.attr; + fpga_data->fpga_attrs[1] = NULL; - struct attribute_group attr_group_fpgapci = { - .attrs = attrs_fpgapci, - }; - - mutex_lock(&fpga_list_lock); - list_add(&fpga_data->list, &fpga_list); - mutex_unlock(&fpga_list_lock); + fpga_data->fpga_attr_group.attrs = fpga_data->fpga_attrs; - ret = sysfs_create_group(fpga_data->kobj, &attr_group_fpgapci); + ret = sysfs_create_group(fpga_data->kobj, &fpga_data->fpga_attr_group); if (ret) { pddf_dbg(MULTIFPGA, KERN_ERR "%s create fpga sysfs attributes failed\n", __FUNCTION__); - return ret; + goto free_gpio_dirs; } + fpga_data->fpga_attr_group_initialized = true; ret = sysfs_create_group(fpga_data->kobj, &pddf_clients_data_group); if (ret) { pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] sysfs_create_group failed: %d\n", + KERN_ERR "[%s] create pddf_clients_data_group failed: %d\n", __FUNCTION__, ret); goto free_fpga_attr_group; } + fpga_data->pddf_clients_data_group_initialized = true; + + mutex_lock(&fpga_list_lock); + list_add(&fpga_data->list, &fpga_list); + mutex_unlock(&fpga_list_lock); return 0; free_fpga_attr_group: - sysfs_remove_group(fpga_data->kobj, &attr_group_fpgapci); + fpga_data->fpga_attr_group_initialized = false; + sysfs_remove_group(fpga_data->kobj, &fpga_data->fpga_attr_group); + +free_gpio_dirs: + pci_drvdata->gpio_chip_drvdata_initialized = false; + pddf_multifpgapci_gpio_module_exit(dev, pci_drvdata->gpio_kobj); + +free_gpio_kobj: + kobject_put(pci_drvdata->gpio_kobj); + +free_i2c_dirs: + pci_drvdata->i2c_adapter_drvdata_initialized = false; + pddf_multifpgapci_i2c_module_exit(dev, pci_drvdata->i2c_kobj); free_i2c_kobj: kobject_put(pci_drvdata->i2c_kobj); @@ -571,7 +616,7 @@ static int pddf_multifpgapci_probe(struct pci_dev *dev, KERN_ERR "[%s] pci_enable_device failed. dev:%s err:%#x\n", __FUNCTION__, pci_name(dev), err); - return err; + goto error_enable_dev; } // Enable DMA @@ -595,6 +640,7 @@ static int pddf_multifpgapci_probe(struct pci_dev *dev, KERN_ERR "[%s] couldn't allocate pci_privdata memory\n", __FUNCTION__); + err = -ENOMEM; goto error_pci_req; } @@ -605,10 +651,10 @@ static int pddf_multifpgapci_probe(struct pci_dev *dev, return 0; -// ERROR HANDLING error_pci_req: pci_disable_device(dev); - return -ENODEV; +error_enable_dev: + return err; } // Initialize the driver module (but not any device) and register diff --git a/platform/pddf/i2c/modules/multifpgapci/gpio/Makefile b/platform/pddf/i2c/modules/multifpgapci/gpio/Makefile new file mode 100644 index 000000000000..df693bb03753 --- /dev/null +++ b/platform/pddf/i2c/modules/multifpgapci/gpio/Makefile @@ -0,0 +1,6 @@ +obj-m := driver/ + +TARGET = pddf_multifpgapci_gpio_module +obj-m += $(TARGET).o + +ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/gpio/driver/Makefile b/platform/pddf/i2c/modules/multifpgapci/gpio/driver/Makefile new file mode 100644 index 000000000000..7aaa1fd018f8 --- /dev/null +++ b/platform/pddf/i2c/modules/multifpgapci/gpio/driver/Makefile @@ -0,0 +1,4 @@ +TARGET = pddf_multifpgapci_gpio_driver_module +obj-m := pddf_multifpgapci_gpio_driver.o + +ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/gpio/driver/pddf_multifpgapci_gpio_driver.c b/platform/pddf/i2c/modules/multifpgapci/gpio/driver/pddf_multifpgapci_gpio_driver.c new file mode 100644 index 000000000000..c1464c68bec9 --- /dev/null +++ b/platform/pddf/i2c/modules/multifpgapci/gpio/driver/pddf_multifpgapci_gpio_driver.c @@ -0,0 +1,145 @@ +/* + * Copyright 2025 Nexthop Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "pddf_client_defs.h" +#include "pddf_multifpgapci_defs.h" +#include "pddf_multifpgapci_gpio_defs.h" + +struct pddf_multifpgapci_gpio { + struct pddf_multifpgapci_gpio_chip_pdata pdata; + struct mutex lock; + struct gpio_chip chip; +}; + +static int pddf_multifpgapci_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct pddf_multifpgapci_gpio *gpio = gpiochip_get_data(chip); + return gpio->pdata.chan_data[offset].direction; +} + +static int pddf_multifpgapci_gpio_get(struct gpio_chip *chip, + unsigned int offset) +{ + int status = 0; + struct pddf_multifpgapci_gpio *gpio = gpiochip_get_data(chip); + struct pddf_multifpgapci_gpio_line_pdata line = + gpio->pdata.chan_data[offset]; + int temp; + + mutex_lock(&gpio->lock); + status = ptr_multifpgapci_readpci(gpio->pdata.fpga, line.offset, &temp); + mutex_unlock(&gpio->lock); + + if (status < 0) + return status; + + return !!(temp & BIT(line.bit)); +} + +static int pddf_multifpgapci_gpio_set_internal(struct gpio_chip *chip, + unsigned int offset, int value) +{ + int status = 0; + struct pddf_multifpgapci_gpio *gpio = gpiochip_get_data(chip); + struct pddf_multifpgapci_gpio_line_pdata line = + gpio->pdata.chan_data[offset]; + int temp; + + mutex_lock(&gpio->lock); + status = ptr_multifpgapci_readpci(gpio->pdata.fpga, line.offset, &temp); + if (status) + goto exit; + temp &= ~BIT(line.bit); + temp |= (!!value) << line.bit; + status = ptr_multifpgapci_writepci(gpio->pdata.fpga, temp, line.offset); +exit: + if (status) + printk(KERN_ERR "%s: Error status = %d", __FUNCTION__, status); + + mutex_unlock(&gpio->lock); + + return status; +} + +static void pddf_multifpgapci_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + pddf_multifpgapci_gpio_set_internal(chip, offset, value); +} + +static int pddf_multifpgapci_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + struct pddf_multifpgapci_gpio *gpio = gpiochip_get_data(chip); + gpio->pdata.chan_data[offset].direction = GPIO_LINE_DIRECTION_IN; + return 0; +} + +static int pddf_multifpgapci_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct pddf_multifpgapci_gpio *gpio = gpiochip_get_data(chip); + gpio->pdata.chan_data[offset].direction = GPIO_LINE_DIRECTION_OUT; + return pddf_multifpgapci_gpio_set_internal(chip, offset, value); +} + +static const struct gpio_chip template_chip = { + .label = "pddf-multifpgapci-gpio", + .owner = THIS_MODULE, + .direction_input = pddf_multifpgapci_gpio_direction_input, + .direction_output = pddf_multifpgapci_gpio_direction_output, + .get_direction = pddf_multifpgapci_gpio_get_direction, + .get = pddf_multifpgapci_gpio_get, + .set = pddf_multifpgapci_gpio_set, + .base = -1, +}; + +static int pddf_multifpgapci_gpio_probe(struct platform_device *pdev) +{ + struct pddf_multifpgapci_gpio *gpio; + struct pddf_multifpgapci_gpio_chip_pdata *pdata = + dev_get_platdata(&pdev->dev); + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->pdata = *pdata; + mutex_init(&gpio->lock); + + gpio->chip = template_chip; + gpio->chip.parent = pdev->dev.parent; + gpio->chip.ngpio = pdata->ngpio; + + return devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); +} + +static struct platform_driver pddf_multifpgapci_gpio_driver = { + .driver = { + .name = "pddf-multifpgapci-gpio", + }, + .probe = pddf_multifpgapci_gpio_probe, +}; +module_platform_driver(pddf_multifpgapci_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nexthop Systems"); +MODULE_DESCRIPTION("PDDF Driver for Multiple PCI FPGA GPIOs."); diff --git a/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c b/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c new file mode 100644 index 000000000000..56e984c3b35e --- /dev/null +++ b/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c @@ -0,0 +1,190 @@ +/* + * Copyright 2025 Nexthop Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pddf_client_defs.h" +#include "pddf_multifpgapci_defs.h" +#include "pddf_multifpgapci_gpio_defs.h" + +static ssize_t create_chip(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + struct pddf_multifpgapci_gpio_chip_pdata *pdata = + (struct pddf_multifpgapci_gpio_chip_pdata *)_ptr->addr; + struct platform_device *pdev; + + if (strncmp(buf, "init", strlen("init")) != 0) { + pddf_dbg(FPGA, + KERN_ERR + "[%s] Unexpected input: %s - Expected input: init\n", + __FUNCTION__, buf); + return -EINVAL; + } + + pdev = platform_device_register_data(&pdata->fpga->dev, + "pddf-multifpgapci-gpio", + PLATFORM_DEVID_AUTO, pdata, + sizeof(*pdata)); + if (!pdev) { + pddf_dbg(FPGA, + KERN_ERR "[%s] error allocating platform device\n", + __FUNCTION__); + return -ENOMEM; + } + + return count; +} + +static ssize_t create_line(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + struct gpio_chip_drvdata *node = (struct gpio_chip_drvdata *)_ptr->addr; + + if (strncmp(buf, "init", strlen("init")) != 0) { + pddf_dbg(FPGA, + KERN_ERR + "[%s] Unexpected input: %s - Expected input: init\n", + __FUNCTION__, buf); + return -EINVAL; + } + + if (node->pdata.ngpio >= MAX_MULTIFPGAPCI_GPIO_LINES) { + pddf_dbg(FPGA, KERN_ERR "[%s] Cannot exceed %d GPIO lines\n", + __FUNCTION__, MAX_MULTIFPGAPCI_GPIO_LINES); + return -EINVAL; + } + + node->pdata.chan_data[node->pdata.ngpio++] = node->temp_line_data; + + return count; +} + +int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, + struct kobject *kobj) +{ + struct pddf_multifpgapci_drvdata *pci_drvdata = + dev_get_drvdata(&pci_dev->dev); + struct gpio_chip_drvdata *gpio_drvdata = + &pci_drvdata->gpio_chip_drvdata; + int err; + + gpio_drvdata->pdata.fpga = pci_dev; + + PDDF_DATA_ATTR( + ngpio, S_IRUGO, show_pddf_data, NULL, PDDF_UINT32, + sizeof(uint32_t), (void *)&gpio_drvdata->pdata.ngpio, NULL); + PDDF_DATA_ATTR( + create_chip, S_IWUSR, NULL, create_chip, PDDF_CHAR, + 32, (void *)gpio_drvdata, NULL); + + gpio_drvdata->attrs.attr_ngpio = attr_ngpio; + gpio_drvdata->attrs.attr_create = attr_create_chip; + + gpio_drvdata->gpio_chip_attrs[0] = + &gpio_drvdata->attrs.attr_ngpio.dev_attr.attr; + gpio_drvdata->gpio_chip_attrs[1] = + &gpio_drvdata->attrs.attr_create.dev_attr.attr; + gpio_drvdata->gpio_chip_attrs[2] = NULL; + + gpio_drvdata->gpio_chip_attr_group.attrs = + gpio_drvdata->gpio_chip_attrs; + + err = sysfs_create_group(kobj, &gpio_drvdata->gpio_chip_attr_group); + if (err) + goto return_err; + + gpio_drvdata->line_kobj = kobject_create_and_add("line", kobj); + if (!gpio_drvdata->line_kobj) { + err = -ENOMEM; + goto remove_gpio_chip_attr_group; + } + + PDDF_DATA_ATTR( + bit, S_IWUSR | S_IRUGO, show_pddf_data, store_pddf_data, + PDDF_INT_HEX, sizeof(uint32_t), + (void *)&gpio_drvdata->temp_line_data.bit, NULL); + PDDF_DATA_ATTR( + offset, S_IWUSR | S_IRUGO, show_pddf_data, store_pddf_data, + PDDF_INT_HEX, sizeof(uint32_t), + (void *)&gpio_drvdata->temp_line_data.offset, NULL); + PDDF_DATA_ATTR( + direction, S_IWUSR | S_IRUGO, show_pddf_data, store_pddf_data, + PDDF_INT_HEX, sizeof(uint32_t), + (void *)&gpio_drvdata->temp_line_data.direction, NULL); + PDDF_DATA_ATTR( + create_line, S_IWUSR, NULL, create_line, PDDF_CHAR, 32, + (void *)&gpio_drvdata->pdata, NULL); + + gpio_drvdata->attrs.line_attrs.attr_bit = attr_bit; + gpio_drvdata->attrs.line_attrs.attr_offset = attr_offset; + gpio_drvdata->attrs.line_attrs.attr_direction = attr_direction; + gpio_drvdata->attrs.line_attrs.attr_create = attr_create_line; + + gpio_drvdata->gpio_line_attrs[0] = + &gpio_drvdata->attrs.line_attrs.attr_bit.dev_attr.attr; + gpio_drvdata->gpio_line_attrs[1] = + &gpio_drvdata->attrs.line_attrs.attr_offset.dev_attr.attr; + gpio_drvdata->gpio_line_attrs[2] = + &gpio_drvdata->attrs.line_attrs.attr_direction.dev_attr.attr; + gpio_drvdata->gpio_line_attrs[3] = + &gpio_drvdata->attrs.line_attrs.attr_create.dev_attr.attr; + gpio_drvdata->gpio_line_attrs[4] = NULL; + + gpio_drvdata->gpio_line_attr_group.attrs = + gpio_drvdata->gpio_line_attrs; + + err = sysfs_create_group(gpio_drvdata->line_kobj, + &gpio_drvdata->gpio_line_attr_group); + if (err) + goto free_gpio_line_kobj; + + return 0; + +free_gpio_line_kobj: + kobject_put(gpio_drvdata->line_kobj); + +remove_gpio_chip_attr_group: + sysfs_remove_group(kobj, &gpio_drvdata->gpio_chip_attr_group); + +return_err: + return err; +} +EXPORT_SYMBOL(pddf_multifpgapci_gpio_module_init); + +void pddf_multifpgapci_gpio_module_exit(struct pci_dev *pci_dev, + struct kobject *kobj) +{ + struct pddf_multifpgapci_drvdata *pci_drvdata = + pci_get_drvdata(pci_dev); + struct gpio_chip_drvdata *gpio_drvdata = + &pci_drvdata->gpio_chip_drvdata; + sysfs_remove_group(gpio_drvdata->line_kobj, + &gpio_drvdata->gpio_line_attr_group); + kobject_put(gpio_drvdata->line_kobj); + sysfs_remove_group(kobj, &gpio_drvdata->gpio_chip_attr_group); +}; +EXPORT_SYMBOL(pddf_multifpgapci_gpio_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nexthop Systems"); +MODULE_DESCRIPTION("PDDF Platform Data for Multiple PCI FPGA GPIOs."); diff --git a/platform/pddf/i2c/utils/pddfparse.py b/platform/pddf/i2c/utils/pddfparse.py index cdbba2d53aa8..7b4e04d7c17a 100755 --- a/platform/pddf/i2c/utils/pddfparse.py +++ b/platform/pddf/i2c/utils/pddfparse.py @@ -544,6 +544,33 @@ def create_multifpgapci_device(self, dev, ops): if ret != 0: return create_ret.append(ret) + if 'gpio' in dev: + ret = self.create_gpio_device(bdf, dev['gpio'], ops) + if ret != 0: + return create_ret.append(ret) + + return create_ret.append(ret) + + def create_gpio_device(self, bdf, gpio_dev, ops): + create_ret = [] + ret = 0 + + for line in gpio_dev.keys(): + for attr in gpio_dev[line]['attr_list']: + ret = self.create_device(attr, "pddf/devices/multifpgapci/{}/gpio/line".format(bdf), ops) + if ret != 0: + return create_ret.append(ret) + + cmd = "echo 'init' > /sys/kernel/pddf/devices/multifpgapci/{}/gpio/line/create_line".format(bdf) + ret = self.runcmd(cmd) + if ret != 0: + return create_ret.append(ret) + + cmd = "echo 'init' > /sys/kernel/pddf/devices/multifpgapci/{}/gpio/create_chip".format(bdf) + ret = self.runcmd(cmd) + if ret != 0: + return create_ret.append(ret) + return create_ret.append(ret) ################################################################################################################################# From fb1dd0bcdf151bcb55cac1368488e2580a99c4d8 Mon Sep 17 00:00:00 2001 From: Raghav Rao Date: Thu, 24 Jul 2025 01:53:29 +0000 Subject: [PATCH 2/6] multifpgapci support for multiple protocols --- .../pddf/pddf-device.json | 3 + .../common/modules/pddf_custom_fpga_algo.c | 41 +- .../modules/include/pddf_multifpgapci_defs.h | 34 +- .../include/pddf_multifpgapci_gpio_defs.h | 1 + .../include/pddf_multifpgapci_i2c_defs.h | 17 +- .../pddf/i2c/modules/multifpgapci/Makefile | 2 +- .../driver/pddf_multifpgapci_driver.c | 673 ++++++++++++++---- .../gpio/pddf_multifpgapci_gpio_module.c | 183 ++++- .../i2c/pddf_multifpgapci_i2c_module.c | 202 +++++- 9 files changed, 912 insertions(+), 244 deletions(-) diff --git a/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json b/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json index 28ee96ddfb08..c40123ce1f15 100644 --- a/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json +++ b/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json @@ -37,6 +37,9 @@ "optoe" ], "pddf_kos": [ + "pddf_multifpgapci_i2c_module", + "pddf_multifpgapci_gpio_module", + "pddf_multifpgapci_spi_module", "pddf_client_module", "pddf_multifpgapci_driver", "pddf_multifpgapci_module", diff --git a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_fpga_algo.c b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_fpga_algo.c index 4c855f787202..b5801b92c5a1 100644 --- a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_fpga_algo.c +++ b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_fpga_algo.c @@ -540,32 +540,25 @@ static int fpgai2c_init(struct fpgalogic_i2c *i2c) static int adap_data_init(struct i2c_adapter *adap, int index) { - struct pddf_multifpgapci_drvdata *pci_privdata = 0; - pci_privdata = (struct pddf_multifpgapci_drvdata*) dev_get_drvdata(adap->dev.parent); - - if (pci_privdata == 0) { - printk("[%s]: ERROR pci_privdata is 0\n", __FUNCTION__); + struct pci_dev *dev = to_pci_dev(adap->dev.parent); + struct i2c_adapter_data i2c_data; + int error = pddf_multifpgapci_i2c_get_adapter_data(dev, &i2c_data); + if (error) { + printk("[%s]: ERROR getting i2c adapter_data: %d\n", __FUNCTION__, error); return -1; } - if (pci_privdata->i2c_adapter_drvdata_initialized != 1) { - printk("[%s]: ERROR i2c_adapter_drvdata is not initialized\n", __FUNCTION__); - return -1; - } - struct i2c_adapter_drvdata *i2c_privdata = &pci_privdata->i2c_adapter_drvdata; - - int i2c_ch_index = index + i2c_privdata->virt_bus; + int i2c_ch_index = index + i2c_data.virt_bus; - pddf_dbg(FPGA, KERN_INFO "[%s] index: [%d] fpga_data__base_addr:0x%08lx" - " fpgapci_bar_len:0x%08lx fpga_i2c_ch_base_addr:0x%08lx ch_ssize=0x%x supported_i2c_ch=%d", - __FUNCTION__, i2c_ch_index, pci_privdata->fpga_data_base_addr, - pci_privdata->bar_length, i2c_privdata->ch_base_addr, - i2c_privdata->ch_size, i2c_privdata->num_virt_ch); + pddf_dbg(FPGA, KERN_INFO "[%s] index: [%d] pci_dev: [%s]" + " fpga_i2c_ch_base_addr:0x%08lx ch_ssize=0x%x supported_i2c_ch=%d", + __FUNCTION__, i2c_ch_index, pci_name(dev), + i2c_data.ch_base_addr, i2c_data.ch_size, i2c_data.num_virt_ch); - if (index >= i2c_privdata->num_virt_ch - || i2c_privdata->num_virt_ch > I2C_PCI_MAX_BUS){ + if (index >= i2c_data.num_virt_ch + || i2c_data.num_virt_ch > I2C_PCI_MAX_BUS){ printk("[%s]: ERROR i2c_ch_index=%d max_ch_index=%d out of range: %d\n", - __FUNCTION__, i2c_ch_index, i2c_privdata->num_virt_ch, I2C_PCI_MAX_BUS); + __FUNCTION__, i2c_ch_index, i2c_data.num_virt_ch, I2C_PCI_MAX_BUS); return -1; } @@ -574,8 +567,8 @@ static int adap_data_init(struct i2c_adapter *adap, int index) #else memset(&fpgalogic_i2c[i2c_ch_index], 0, sizeof(fpgalogic_i2c[0])); #endif - fpgalogic_i2c[i2c_ch_index].base = i2c_privdata->ch_base_addr + - index * i2c_privdata->ch_size; + fpgalogic_i2c[i2c_ch_index].base = i2c_data.ch_base_addr + + index * i2c_data.ch_size; mutex_init(&fpgalogic_i2c[i2c_ch_index].lock); fpgai2c_init(&fpgalogic_i2c[i2c_ch_index]); @@ -597,7 +590,7 @@ static int pddf_i2c_multifpgapci_add_numbered_bus_default (struct i2c_adapter *a /* * FPGAPCI APIs */ -static int board_i2c_fpgapci_read(const char *bdf, uint32_t offset) +int board_i2c_fpgapci_read(const char *bdf, uint32_t offset) { if (get_fpga_ctl_addr == NULL) { printk(KERN_ERR "get_fpga_ctl_addr function not available\n"); @@ -615,7 +608,7 @@ static int board_i2c_fpgapci_read(const char *bdf, uint32_t offset) } -static int board_i2c_fpgapci_write(const char *bdf, uint32_t offset, uint32_t value) +int board_i2c_fpgapci_write(const char *bdf, uint32_t offset, uint32_t value) { if (get_fpga_ctl_addr == NULL) { printk(KERN_ERR "get_fpga_ctl_addr function not available\n"); diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h index c18931954533..ba5c9ee35866 100644 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h @@ -25,23 +25,19 @@ #include "pddf_multifpgapci_i2c_defs.h" #define NAME_SIZE 32 + +#ifndef KOBJ_FREE #define KOBJ_FREE(obj) \ if (obj) \ kobject_put(obj); +#endif struct pddf_multifpgapci_drvdata { struct pci_dev *pci_dev; resource_size_t bar_start; void *__iomem fpga_data_base_addr; - // i2c size_t bar_length; - struct kobject *i2c_kobj; - struct i2c_adapter_drvdata i2c_adapter_drvdata; - bool i2c_adapter_drvdata_initialized; - // gpio - struct kobject *gpio_kobj; - struct gpio_chip_drvdata gpio_chip_drvdata; - bool gpio_chip_drvdata_initialized; + bool bar_initialized; }; // FPGA @@ -54,9 +50,31 @@ struct pddf_multi_fpgapci_ops_t { int (*post_device_operation)(struct pci_dev *); }; +// Protocol function pointer types +typedef int (*attach_fn)(struct pci_dev *pci_dev, struct kobject *kobj); +typedef void (*detach_fn)(struct pci_dev *pci_dev, struct kobject *kobj); +typedef void (*map_bar_fn)(struct pci_dev *pci_dev, void *__iomem bar_base, + unsigned long bar_start, unsigned long bar_len); +typedef void (*unmap_bar_fn)(struct pci_dev *pci_dev, void *__iomem bar_base, + unsigned long bar_start, unsigned long bar_len); + +// Protocol operations structure +struct protocol_ops { + attach_fn attach; + detach_fn detach; + map_bar_fn map_bar; + unmap_bar_fn unmap_bar; + const char *name; +}; + extern struct pddf_multi_fpgapci_ops_t pddf_multi_fpgapci_ops; extern int (*ptr_multifpgapci_readpci)(struct pci_dev *, uint32_t, uint32_t *); extern int (*ptr_multifpgapci_writepci)(struct pci_dev *, uint32_t, uint32_t); +extern int multifpgapci_register_protocol(const char *name, + struct protocol_ops *ops); +extern void multifpgapci_unregister_protocol(const char *name); +extern unsigned long multifpgapci_get_pci_dev_index(struct pci_dev *pci_dev); + #endif diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h index 855dedf6cfbd..e9a8a6f74df3 100644 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_gpio_defs.h @@ -61,6 +61,7 @@ struct gpio_chip_attrs { sizeof(PDDF_ATTR)) struct gpio_chip_drvdata { + struct kobject *gpio_kobj; // pdata is passed to gpio platform driver. struct pddf_multifpgapci_gpio_chip_pdata pdata; // temp_line_data is mutated by sysfs attrs and copied to pdata. diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_i2c_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_i2c_defs.h index a2618f383386..2afac4468b1d 100644 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_i2c_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_i2c_defs.h @@ -44,7 +44,18 @@ struct i2c_adapter_sysfs_vals { uint32_t num_virt_ch; }; +struct i2c_adapter_data { + int virt_bus; + void *__iomem ch_base_addr; + int ch_size; + int num_virt_ch; +}; + struct i2c_adapter_drvdata { + struct pci_dev *pci_dev; + size_t bar_length; + struct kobject *i2c_kobj; + // temp_sysfs_vals store temporary values provided by sysfs, // which are eventually copied/saved to I2C adapter platform data. struct i2c_adapter_sysfs_vals temp_sysfs_vals; @@ -70,4 +81,8 @@ extern int pddf_multifpgapci_i2c_module_init(struct pci_dev *pci_dev, extern void pddf_multifpgapci_i2c_module_exit(struct pci_dev *pci_dev, struct kobject *kobj); -#endif \ No newline at end of file +extern int +pddf_multifpgapci_i2c_get_adapter_data(struct pci_dev *pci_dev, + struct i2c_adapter_data *data); + +#endif diff --git a/platform/pddf/i2c/modules/multifpgapci/Makefile b/platform/pddf/i2c/modules/multifpgapci/Makefile index 0e96bd0fe83c..ad6cac982ccf 100644 --- a/platform/pddf/i2c/modules/multifpgapci/Makefile +++ b/platform/pddf/i2c/modules/multifpgapci/Makefile @@ -1,4 +1,4 @@ obj-m := driver/ gpio/ i2c/ obj-m += pddf_multifpgapci_module.o -ccflags-y:= -I$(M)/modules/include +ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c b/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c index 45511260c62c..bf0a06bfeeaa 100644 --- a/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c +++ b/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -51,17 +50,12 @@ #include "pddf_client_defs.h" #include "pddf_multifpgapci_defs.h" -#include "pddf_multifpgapci_gpio_defs.h" -#include "pddf_multifpgapci_i2c_defs.h" #define BDF_NAME_SIZE 32 #define DEVICE_NAME_SIZE 32 #define DEBUG 0 #define DRIVER_NAME "pddf_multifpgapci" #define MAX_PCI_NUM_BARS 6 -#define KOBJ_FREE(obj) \ - if (obj) \ - kobject_put(obj); extern void add_device_table(char *name, void *ptr); extern void* get_device_table(char *name); @@ -113,6 +107,40 @@ struct fpga_data_node { LIST_HEAD(fpga_list); +// PCI devices for a protocol +struct protocol_pci_entry { + struct list_head list; + struct pci_dev *pci_dev; +}; + +// Protocol module registered with the driver +struct protocol_module { + struct list_head list; + char name[32]; + struct protocol_ops *ops; + struct list_head + pci_devices; // List of PCI devices this protocol is initialized on + struct mutex lock; +}; + +// Structure for collecting items needed to call into protocol modules without locks +struct fpga_work_item { + struct list_head list; + struct pci_dev *pci_dev; + struct kobject *kobj; + attach_fn attach; + detach_fn detach; + map_bar_fn map_bar; + unmap_bar_fn unmap_bar; + void __iomem *bar_base; + unsigned long bar_start; + unsigned long bar_len; +}; + +static LIST_HEAD(protocol_modules); +static DEFINE_MUTEX(protocol_modules_lock); +static int num_protocols = 0; + struct mutex fpga_list_lock; static ssize_t dev_operation(struct device *dev, struct device_attribute *da, @@ -122,14 +150,31 @@ static int map_bars(const char *bdf, struct pci_dev *dev); static void map_entire_bar(unsigned long barStart, unsigned long barLen, struct pddf_multifpgapci_drvdata *pci_privdata, - struct fpga_data_node *fpga_data, - struct i2c_adapter_drvdata *i2c_privdata); + struct fpga_data_node *fpga_data); static int pddf_multifpgapci_probe(struct pci_dev *dev, const struct pci_device_id *id); static void pddf_multifpgapci_remove(struct pci_dev *dev); static void free_bars(struct pddf_multifpgapci_drvdata *pci_privdata, struct pci_dev *dev); +static int protocol_add_pci_dev(struct protocol_module *proto, + struct pci_dev *pci_dev); +static int protocol_remove_pci_dev(struct protocol_module *proto, + struct pci_dev *pci_dev); +static void run_attach(struct pci_dev *pci_dev, struct kobject *kobj); +static void attach_protocols_for_fpga(struct pci_dev *pci_dev, + struct kobject *kobj); +static void run_detach(struct pci_dev *pci_dev, struct kobject *kobj); +static void detach_protocols_for_fpga(struct pci_dev *pci_dev, + struct kobject *kobj); +static void attach_protocols_for_all_fpgas(struct protocol_module *proto); +static void detach_protocols_for_all_fpgas(struct protocol_module *proto); +static void run_map_bar(struct pci_dev *pci_dev, void __iomem *bar_base, + unsigned long bar_start, unsigned long bar_len); +static void run_bar_op_for_all_fpgas(struct protocol_module *proto, bool map); +static void run_unmap_bar(struct pci_dev *pci_dev, void __iomem *bar_base, + unsigned long bar_start, unsigned long bar_len); + int default_multifpgapci_readpci(struct pci_dev *pci_dev, uint32_t offset, uint32_t *output) { @@ -141,6 +186,16 @@ int default_multifpgapci_readpci(struct pci_dev *pci_dev, uint32_t offset, struct pddf_multifpgapci_drvdata *pci_drvdata = dev_get_drvdata(&pci_dev->dev); + if (!pci_drvdata) { + pddf_dbg(MULTIFPGA, KERN_ERR "%s pci_drvdata is NULL\n", + __FUNCTION__); + return -ENODEV; + } + if (!pci_drvdata->bar_initialized) { + pddf_dbg(MULTIFPGA, KERN_ERR "%s pci bar not initialized\n", + __FUNCTION__); + return -ENODEV; + } *output = ioread32(pci_drvdata->fpga_data_base_addr + offset); return 0; @@ -157,6 +212,16 @@ int default_multifpgapci_writepci(struct pci_dev *pci_dev, uint32_t val, struct pddf_multifpgapci_drvdata *pci_drvdata = dev_get_drvdata(&pci_dev->dev); + if (!pci_drvdata) { + pddf_dbg(MULTIFPGA, KERN_ERR "%s pci_drvdata is NULL\n", + __FUNCTION__); + return -ENODEV; + } + if (!pci_drvdata->bar_initialized) { + pddf_dbg(MULTIFPGA, KERN_ERR "%s pci bar not initialized\n", + __FUNCTION__); + return -ENODEV; + } iowrite32(val, pci_drvdata->fpga_data_base_addr + offset); return 0; @@ -164,23 +229,6 @@ int default_multifpgapci_writepci(struct pci_dev *pci_dev, uint32_t val, void free_pci_drvdata(struct pci_dev *pci_dev) { - struct pddf_multifpgapci_drvdata *pci_drvdata = - pci_get_drvdata(pci_dev); - if (!pci_drvdata) - return; - - if (pci_drvdata->i2c_adapter_drvdata_initialized) { - pddf_multifpgapci_i2c_module_exit(pci_dev, - pci_drvdata->i2c_kobj); - } - KOBJ_FREE(pci_drvdata->i2c_kobj); - - if (pci_drvdata->gpio_chip_drvdata_initialized) { - pddf_multifpgapci_gpio_module_exit(pci_dev, - pci_drvdata->gpio_kobj); - } - KOBJ_FREE(pci_drvdata->gpio_kobj); - pci_set_drvdata(pci_dev, NULL); } @@ -195,41 +243,51 @@ void free_sysfs_attr_groups(struct fpga_data_node *node) void delete_fpga_data_node(const char *bdf) { - struct fpga_data_node *node, *tmp; + struct fpga_data_node *node, *tmp_node; + struct fpga_data_node *found_node = NULL; + // Find and remove from global list by holding the lock so that + // all further cleanup can be performed without the need for a lock. mutex_lock(&fpga_list_lock); - - list_for_each_entry_safe(node, tmp, &fpga_list, list) { + list_for_each_entry_safe(node, tmp_node, &fpga_list, list) { if (strcmp(node->bdf, bdf) == 0) { - free_pci_drvdata(node->dev); - free_sysfs_attr_groups(node); - KOBJ_FREE(node->kobj); list_del(&node->list); - kfree(node); + found_node = node; break; } } - mutex_unlock(&fpga_list_lock); + + if (!found_node) + return; + + // Cleanup + detach_protocols_for_fpga(found_node->dev, found_node->kobj); + free_pci_drvdata(found_node->dev); + free_sysfs_attr_groups(found_node); + KOBJ_FREE(found_node->kobj); + kfree(found_node); } void delete_all_fpga_data_nodes(void) { struct fpga_data_node *node, *tmp; + struct list_head local_list; + // Clear the global list after copying over the pointer mutex_lock(&fpga_list_lock); + local_list = fpga_list; + INIT_LIST_HEAD(&fpga_list); + mutex_unlock(&fpga_list_lock); - list_for_each_entry_safe(node, tmp, &fpga_list, list) { - KOBJ_FREE(node->kobj); - + // Work on the local copy without the need for a lock + list_for_each_entry_safe(node, tmp, &local_list, list) { + detach_protocols_for_fpga(node->dev, node->kobj); free_pci_drvdata(node->dev); free_sysfs_attr_groups(node); - - list_del(&node->list); + KOBJ_FREE(node->kobj); kfree(node); } - - mutex_unlock(&fpga_list_lock); } struct fpga_data_node *get_fpga_data_node(const char *bdf) @@ -271,11 +329,11 @@ EXPORT_SYMBOL(get_fpga_ctl_addr); static int pddf_pci_add_fpga(char *bdf, struct pci_dev *dev) { + pddf_dbg(MULTIFPGA, KERN_INFO "%s ..\n", __FUNCTION__); int ret = 0; - struct pddf_multifpgapci_drvdata *pci_drvdata = - dev_get_drvdata(&dev->dev); struct fpga_data_node *fpga_data = kzalloc(sizeof(*fpga_data), GFP_KERNEL); + if (!fpga_data) { return -ENOMEM; } @@ -283,110 +341,48 @@ static int pddf_pci_add_fpga(char *bdf, struct pci_dev *dev) strscpy(fpga_data->bdf, bdf, NAME_SIZE); fpga_data->kobj = kobject_create_and_add(bdf, multifpgapci_kobj); - if (!fpga_data->kobj) { - pddf_dbg(MULTIFPGA, KERN_ERR "%s create fpga kobj failed\n", - __FUNCTION__); ret = -ENOMEM; goto free_fpga_data; } - pci_drvdata->i2c_kobj = kobject_create_and_add("i2c", fpga_data->kobj); - if (!pci_drvdata->i2c_kobj) { - pddf_dbg(MULTIFPGA, KERN_ERR "[%s] create i2c kobj failed\n", - __FUNCTION__); - ret = -ENOMEM; - goto free_fpga_kobj; - } - - ret = pddf_multifpgapci_i2c_module_init(dev, pci_drvdata->i2c_kobj); - if (ret) { - pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] create i2c module failed %d\n", - __FUNCTION__, ret); - goto free_i2c_kobj; - } - pci_drvdata->i2c_adapter_drvdata_initialized = true; - - pci_drvdata->gpio_kobj = kobject_create_and_add("gpio", fpga_data->kobj); - if (!pci_drvdata->gpio_kobj) { - pddf_dbg(MULTIFPGA, KERN_ERR "%s create gpio kobj failed\n", - __FUNCTION__); - ret = -ENOMEM; - goto free_i2c_dirs; - } - - ret = pddf_multifpgapci_gpio_module_init(dev, pci_drvdata->gpio_kobj); - if (ret) { - pddf_dbg(MULTIFPGA, - KERN_ERR "%s create add gpio module failed %d\n", - __FUNCTION__, ret); - goto free_gpio_kobj; - } - pci_drvdata->gpio_chip_drvdata_initialized = true; - fpga_data->dev = dev; PDDF_DATA_ATTR( dev_ops, S_IWUSR | S_IRUGO, NULL, dev_operation, PDDF_CHAR, NAME_SIZE, NULL, (void *)&pddf_data); + // Setup sysfs attributes fpga_data->attrs.attr_dev_ops = attr_dev_ops; fpga_data->fpga_attrs[0] = &fpga_data->attrs.attr_dev_ops.dev_attr.attr; fpga_data->fpga_attrs[1] = NULL; - fpga_data->fpga_attr_group.attrs = fpga_data->fpga_attrs; - ret = sysfs_create_group(fpga_data->kobj, &fpga_data->fpga_attr_group); + // Add to FPGA list + mutex_lock(&fpga_list_lock); + list_add(&fpga_data->list, &fpga_list); + mutex_unlock(&fpga_list_lock); - if (ret) { - pddf_dbg(MULTIFPGA, - KERN_ERR "%s create fpga sysfs attributes failed\n", - __FUNCTION__); - goto free_gpio_dirs; - } - fpga_data->fpga_attr_group_initialized = true; + // Attach all registered protocols to this new FPGA + attach_protocols_for_fpga(dev, fpga_data->kobj); - ret = sysfs_create_group(fpga_data->kobj, &pddf_clients_data_group); + ret = sysfs_create_group(fpga_data->kobj, &fpga_data->fpga_attr_group); if (ret) { pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] create pddf_clients_data_group failed: %d\n", + KERN_ERR "[%s] sysfs_create_group failed: %d\n", __FUNCTION__, ret); - goto free_fpga_attr_group; + goto free_fpga_kobj; } - fpga_data->pddf_clients_data_group_initialized = true; - - mutex_lock(&fpga_list_lock); - list_add(&fpga_data->list, &fpga_list); - mutex_unlock(&fpga_list_lock); + fpga_data->fpga_attr_group_initialized = true; return 0; -free_fpga_attr_group: - fpga_data->fpga_attr_group_initialized = false; - sysfs_remove_group(fpga_data->kobj, &fpga_data->fpga_attr_group); - -free_gpio_dirs: - pci_drvdata->gpio_chip_drvdata_initialized = false; - pddf_multifpgapci_gpio_module_exit(dev, pci_drvdata->gpio_kobj); - -free_gpio_kobj: - kobject_put(pci_drvdata->gpio_kobj); - -free_i2c_dirs: - pci_drvdata->i2c_adapter_drvdata_initialized = false; - pddf_multifpgapci_i2c_module_exit(dev, pci_drvdata->i2c_kobj); - -free_i2c_kobj: - kobject_put(pci_drvdata->i2c_kobj); - free_fpga_kobj: + attach_protocols_for_fpga(dev, fpga_data->kobj); kobject_put(fpga_data->kobj); - free_fpga_data: kfree(fpga_data); - return ret; } @@ -398,6 +394,7 @@ ssize_t dev_operation(struct device *dev, struct device_attribute *da, struct pci_dev *pci_dev = NULL; if (strncmp(buf, "fpgapci_init", strlen("fpgapci_init")) == 0) { + pddf_dbg(MULTIFPGA, KERN_INFO "%s ..\n", __FUNCTION__); int err = 0; struct pddf_multifpgapci_drvdata *pci_privdata = 0; const char *bdf = dev->kobj.name; @@ -500,6 +497,7 @@ static int map_bars(const char *bdf, struct pddf_multifpgapci_drvdata *pci_privdata, struct pci_dev *dev) { + pddf_dbg(MULTIFPGA, KERN_INFO "%s - %s\n", __FUNCTION__, pci_name(dev)); unsigned long barFlags, barStart, barEnd, barLen; int i; @@ -532,39 +530,35 @@ static int map_bars(const char *bdf, } } - struct i2c_adapter_drvdata *i2c_privdata = - &pci_privdata->i2c_adapter_drvdata; if (FPGAPCI_BAR_INDEX != -1) { - map_entire_bar(barStart, barLen, pci_privdata, fpga_node, - i2c_privdata); + map_entire_bar(barStart, barLen, pci_privdata, fpga_node); fpga_node->bar_start = barStart; pci_privdata->bar_start = barStart; + pci_privdata->bar_initialized = true; } else { pddf_dbg(MULTIFPGA, KERN_INFO "[%s] Failed to find BAR\n", __FUNCTION__); return -1; } - pddf_dbg( - MULTIFPGA, - KERN_INFO - "[%s] fpga_ctl_addr:0x%p fpga_data__base_addr:0x%p" - " bar_index[%d] fpgapci_bar_len:0x%08lx fpga_i2c_ch_base_addr:0x%p supported_i2c_ch=%d" - " barStart: 0x%08lx\n", - __FUNCTION__, fpga_node->fpga_ctl_addr, - pci_privdata->fpga_data_base_addr, FPGAPCI_BAR_INDEX, - pci_privdata->bar_length, i2c_privdata->ch_base_addr, - i2c_privdata->num_virt_ch, barStart); + pddf_dbg(MULTIFPGA, + KERN_INFO + "[%s] fpga_ctl_addr:0x%p fpga_data__base_addr:0x%p" + " bar_index[%d] fpgapci_bar_len:0x%08lx barStart: 0x%08lx\n", + __FUNCTION__, fpga_node->fpga_ctl_addr, + pci_privdata->fpga_data_base_addr, FPGAPCI_BAR_INDEX, + pci_privdata->bar_length, barStart); return 0; } static void map_entire_bar(unsigned long barStart, unsigned long barLen, struct pddf_multifpgapci_drvdata *pci_privdata, - struct fpga_data_node *fpga_node, - struct i2c_adapter_drvdata *i2c_privdata) + struct fpga_data_node *fpga_node) { void __iomem *bar_base; + pddf_dbg(MULTIFPGA, KERN_INFO "%s - %s\n", __FUNCTION__, + pci_name(fpga_node->dev)); bar_base = ioremap_cache(barStart, barLen); @@ -572,24 +566,19 @@ static void map_entire_bar(unsigned long barStart, unsigned long barLen, pci_privdata->fpga_data_base_addr = bar_base; fpga_node->fpga_ctl_addr = pci_privdata->fpga_data_base_addr; - // i2c specific data store - struct i2c_adapter_sysfs_vals *i2c_pddf_data = - &i2c_privdata->temp_sysfs_vals; - - i2c_privdata->virt_bus = i2c_pddf_data->virt_bus; - i2c_privdata->ch_base_addr = bar_base + i2c_pddf_data->ch_base_offset; - i2c_privdata->num_virt_ch = i2c_pddf_data->num_virt_ch; - i2c_privdata->ch_size = i2c_pddf_data->ch_size; + // Notify all protocols about BAR mapping + run_map_bar(fpga_node->dev, bar_base, barStart, barLen); } static void free_bars(struct pddf_multifpgapci_drvdata *pci_privdata, struct pci_dev *dev) { - struct i2c_adapter_drvdata *i2c_privdata = - &pci_privdata->i2c_adapter_drvdata; - pci_iounmap(dev, (void __iomem *)pci_privdata->bar_start); + // Notify all protocols about BAR unmapping before freeing + run_unmap_bar(dev, pci_privdata->fpga_data_base_addr, + pci_privdata->bar_start, pci_privdata->bar_length); + + pci_iounmap(dev, pci_privdata->fpga_data_base_addr); pci_privdata->fpga_data_base_addr = NULL; - i2c_privdata->ch_base_addr = NULL; } static int pddf_multifpgapci_probe(struct pci_dev *dev, @@ -698,8 +687,8 @@ static void pddf_multifpgapci_remove(struct pci_dev *dev) pci_privdata = (struct pddf_multifpgapci_drvdata *)dev_get_drvdata(&dev->dev); - if (pci_privdata == 0) { - pddf_dbg(MULTIFPGA, KERN_ERR "[%s]: pci_privdata is 0\n", + if (!pci_privdata) { + pddf_dbg(MULTIFPGA, KERN_ERR "[%s]: pci_privdata is NULL\n", __FUNCTION__); return; } @@ -711,6 +700,393 @@ static void pddf_multifpgapci_remove(struct pci_dev *dev) kfree(pci_privdata); } +// Add cleanup function for module exit +static void cleanup_all_protocols(void) +{ + struct protocol_module *proto, *tmp_proto; + struct list_head local_list; + + // Move the list to a local one to be able to process without lock + mutex_lock(&protocol_modules_lock); + local_list = protocol_modules; + INIT_LIST_HEAD(&protocol_modules); + mutex_unlock(&protocol_modules_lock); + + // Work on local copy without any locks + list_for_each_entry_safe(proto, tmp_proto, &local_list, list) { + detach_protocols_for_all_fpgas(proto); + mutex_destroy(&proto->lock); + kfree(proto); + } +} + +// Add PCI device to protocol's list - returns 1 if added, 0 if already exists +static int protocol_add_pci_dev(struct protocol_module *proto, + struct pci_dev *pci_dev) +{ + struct protocol_pci_entry *entry; + int added = 0; + + mutex_lock(&proto->lock); + + // Check if already exists + list_for_each_entry(entry, &proto->pci_devices, list) { + if (entry->pci_dev == pci_dev) { + goto unlock; // Already exists + } + } + + // Add new entry + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry) { + entry->pci_dev = pci_dev; + list_add(&entry->list, &proto->pci_devices); + added = 1; + } + +unlock: + mutex_unlock(&proto->lock); + return added; +} + +// Remove PCI device from protocol's list - returns 1 if removed, 0 if not found +static int protocol_remove_pci_dev(struct protocol_module *proto, + struct pci_dev *pci_dev) +{ + struct protocol_pci_entry *entry, *tmp; + int removed = 0; + + mutex_lock(&proto->lock); + list_for_each_entry_safe(entry, tmp, &proto->pci_devices, list) { + if (entry->pci_dev == pci_dev) { + list_del(&entry->list); + kfree(entry); + removed = 1; + break; + } + } + mutex_unlock(&proto->lock); + + return removed; +} + +static void run_attach(struct pci_dev *pci_dev, struct kobject *kobj) +{ + struct protocol_module *proto; + attach_fn *attach; + int i = 0; + + attach = kzalloc(sizeof(*attach) * num_protocols, GFP_KERNEL); + if (!attach) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "%s failed to allocate memory for attach array\n", + __FUNCTION__); + return; + } + + mutex_lock(&protocol_modules_lock); + list_for_each_entry(proto, &protocol_modules, list) { + if (protocol_add_pci_dev(proto, pci_dev) && + proto->ops->attach) { + attach[i++] = proto->ops->attach; + } + } + mutex_unlock(&protocol_modules_lock); + + // Now run the attach without lock + for (int j = 0; j < i; j++) { + int ret = attach[j](pci_dev, kobj); + if (ret) { + pddf_dbg(MULTIFPGA, + KERN_ERR "Protocol attach failed on %s: %d\n", + pci_name(pci_dev), ret); + } + } + kfree(attach); +} + +static void attach_protocols_for_fpga(struct pci_dev *pci_dev, + struct kobject *kobj) +{ + if (num_protocols > 0) { + run_attach(pci_dev, kobj); + } +} + +static void run_detach(struct pci_dev *pci_dev, struct kobject *kobj) +{ + struct protocol_module *proto; + detach_fn *detach; + int i = 0; + + detach = kzalloc(sizeof(*detach) * num_protocols, GFP_KERNEL); + if (!detach) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "%s failed to allocate memory for detach array\n", + __FUNCTION__); + return; + } + mutex_lock(&protocol_modules_lock); + list_for_each_entry(proto, &protocol_modules, list) { + if (protocol_remove_pci_dev(proto, pci_dev) && + proto->ops->detach) { + detach[i++] = proto->ops->detach; + } + } + mutex_unlock(&protocol_modules_lock); + + for (int j = 0; j < i; j++) { + detach[j](pci_dev, kobj); + } + kfree(detach); +} + +static void detach_protocols_for_fpga(struct pci_dev *pci_dev, + struct kobject *kobj) +{ + if (num_protocols > 0) { + run_detach(pci_dev, kobj); + } +} + +static void attach_protocols_for_all_fpgas(struct protocol_module *proto) +{ + struct fpga_data_node *fpga_node; + struct fpga_work_item *work_item, *tmp; + struct list_head work_list; + + INIT_LIST_HEAD(&work_list); + + // Build work list under lock + mutex_lock(&fpga_list_lock); + list_for_each_entry(fpga_node, &fpga_list, list) { + work_item = kzalloc(sizeof(*work_item), GFP_KERNEL); + if (work_item) { + work_item->pci_dev = fpga_node->dev; + work_item->kobj = fpga_node->kobj; + list_add(&work_item->list, &work_list); + } + } + mutex_unlock(&fpga_list_lock); + + // Execute work items without locks + list_for_each_entry_safe(work_item, tmp, &work_list, list) { + attach_protocols_for_fpga(work_item->pci_dev, work_item->kobj); + list_del(&work_item->list); + kfree(work_item); + } +} + +static void detach_protocols_for_all_fpgas(struct protocol_module *proto) +{ + struct protocol_pci_entry *pci_entry, *tmp_pci; + + // No locking needed - proto is already yanked out of the global list + list_for_each_entry_safe(pci_entry, tmp_pci, &proto->pci_devices, + list) { + if (proto->ops->detach) { + struct fpga_data_node *fpga_node = get_fpga_data_node( + pci_name(pci_entry->pci_dev)); + if (fpga_node) { + proto->ops->detach(pci_entry->pci_dev, + fpga_node->kobj); + } + } + kfree(pci_entry); + } +} + +static void run_map_bar(struct pci_dev *pci_dev, void __iomem *bar_base, + unsigned long bar_start, unsigned long bar_len) +{ + struct protocol_module *proto; + map_bar_fn *map_bar; + int i = 0; + pddf_dbg(MULTIFPGA, KERN_INFO "%s - %s\n", __FUNCTION__, + pci_name(pci_dev)); + + map_bar = kzalloc(sizeof(*map_bar) * num_protocols, GFP_KERNEL); + if (!map_bar) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "%s failed to allocate memory for map_bar array\n", + __FUNCTION__); + return; + } + + mutex_lock(&protocol_modules_lock); + list_for_each_entry(proto, &protocol_modules, list) { + pddf_dbg(MULTIFPGA, KERN_INFO "%s - protocol %s\n", + __FUNCTION__, proto->name); + if (proto->ops->map_bar) { + map_bar[i++] = proto->ops->map_bar; + } + } + mutex_unlock(&protocol_modules_lock); + + // Execute map_bar calls without locks + for (int j = 0; j < i; j++) { + pddf_dbg(MULTIFPGA, KERN_INFO "%s - map_bar %s\n", __FUNCTION__, + pci_name(pci_dev)); + map_bar[j](pci_dev, bar_base, bar_start, bar_len); + } + kfree(map_bar); +} + +static void run_bar_op_for_all_fpgas(struct protocol_module *proto, bool map) +{ + struct fpga_data_node *fpga_node; + struct fpga_work_item *work_item, *tmp; + struct list_head work_list; + + INIT_LIST_HEAD(&work_list); + + // Build work list under lock + mutex_lock(&fpga_list_lock); + list_for_each_entry(fpga_node, &fpga_list, list) { + work_item = kzalloc(sizeof(*work_item), GFP_KERNEL); + if (work_item) { + work_item->pci_dev = fpga_node->dev; + work_item->kobj = fpga_node->kobj; + work_item->map_bar = proto->ops->map_bar; + // Get bar length from pci_privdata + struct pddf_multifpgapci_drvdata *pci_privdata = + dev_get_drvdata(&fpga_node->dev->dev); + if (pci_privdata) { + work_item->bar_start = pci_privdata->bar_start; + work_item->bar_base = + pci_privdata->fpga_data_base_addr; + work_item->bar_len = pci_privdata->bar_length; + } + list_add(&work_item->list, &work_list); + } + } + mutex_unlock(&fpga_list_lock); + + // Execute work items without locks + list_for_each_entry_safe(work_item, tmp, &work_list, list) { + if (work_item->map_bar) { + if (map) { + work_item->map_bar(work_item->pci_dev, + work_item->bar_base, + work_item->bar_start, + work_item->bar_len); + } else { + work_item->unmap_bar(work_item->pci_dev, + work_item->bar_base, + work_item->bar_start, + work_item->bar_len); + } + } + list_del(&work_item->list); + kfree(work_item); + } +} + +static void run_unmap_bar(struct pci_dev *pci_dev, void __iomem *bar_base, + unsigned long bar_start, unsigned long bar_len) +{ + struct protocol_module *proto; + unmap_bar_fn *unmap_bar; + int i = 0; + + unmap_bar = kzalloc(sizeof(*unmap_bar) * num_protocols, GFP_KERNEL); + if (!unmap_bar) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "%s failed to allocate memory for unmap_bar array\n", + __FUNCTION__); + return; + } + + mutex_lock(&protocol_modules_lock); + list_for_each_entry(proto, &protocol_modules, list) { + if (proto->ops->unmap_bar) { + unmap_bar[i++] = proto->ops->unmap_bar; + } + } + mutex_unlock(&protocol_modules_lock); + + // Execute unmap_bar calls without locks + for (int j = 0; j < i; j++) { + unmap_bar[j](pci_dev, bar_base, bar_start, bar_len); + } + kfree(unmap_bar); +} + +int multifpgapci_register_protocol(const char *name, struct protocol_ops *ops) +{ + struct protocol_module *proto; + + proto = kzalloc(sizeof(*proto), GFP_KERNEL); + if (!proto) + return -ENOMEM; + + strscpy(proto->name, name, sizeof(proto->name)); + proto->ops = ops; + INIT_LIST_HEAD(&proto->pci_devices); + mutex_init(&proto->lock); + + // Add to registry + mutex_lock(&protocol_modules_lock); + list_add(&proto->list, &protocol_modules); + num_protocols++; + mutex_unlock(&protocol_modules_lock); + + // Attach protocol to all existing FPGAs + attach_protocols_for_all_fpgas(proto); + + // Map BARs for all existing FPGAs + run_bar_op_for_all_fpgas(proto, true); + + pddf_dbg(MULTIFPGA, KERN_INFO "Registered protocol: %s\n", name); + return 0; +} +EXPORT_SYMBOL(multifpgapci_register_protocol); + +void multifpgapci_unregister_protocol(const char *name) +{ + struct protocol_module *proto, *tmp_proto; + struct protocol_module *found_proto = NULL; + + // Find and remove protocol from registry + mutex_lock(&protocol_modules_lock); + list_for_each_entry_safe(proto, tmp_proto, &protocol_modules, list) { + if (strcmp(proto->name, name) == 0) { + list_del(&proto->list); + found_proto = proto; + num_protocols--; + break; + } + } + mutex_unlock(&protocol_modules_lock); + + if (!found_proto) + return; + + // Unmap BARs for all FPGAs first + run_bar_op_for_all_fpgas(found_proto, false); + + // Detach protocol from all FPGAs without locks + detach_protocols_for_all_fpgas(found_proto); + mutex_destroy(&found_proto->lock); + kfree(found_proto); + pddf_dbg(MULTIFPGA, KERN_INFO "Unregistered protocol: %s\n", name); +} +EXPORT_SYMBOL(multifpgapci_unregister_protocol); + +unsigned long multifpgapci_get_pci_dev_index(struct pci_dev *pci_dev) +{ + unsigned int domain = pci_domain_nr(pci_dev->bus); + unsigned int bus = pci_dev->bus->number; + unsigned int slot = PCI_SLOT(pci_dev->devfn); + unsigned int func = PCI_FUNC(pci_dev->devfn); + return (domain << 16) | (bus << 8) | (slot << 3) | func; +} +EXPORT_SYMBOL(multifpgapci_get_pci_dev_index); + int __init pddf_multifpgapci_driver_init(void) { pddf_dbg(MULTIFPGA, KERN_INFO "%s ..\n", __FUNCTION__); @@ -722,6 +1098,9 @@ void __exit pddf_multifpgapci_driver_exit(void) { pddf_dbg(MULTIFPGA, KERN_INFO "%s ..\n", __FUNCTION__); + // Cleanup all protocols first + cleanup_all_protocols(); + if (driver_registered) { // Unregister this driver from the PCI bus driver pci_unregister_driver(&pddf_multifpgapci_driver); @@ -729,7 +1108,7 @@ void __exit pddf_multifpgapci_driver_exit(void) } delete_all_fpga_data_nodes(); - KOBJ_FREE(multifpgapci_kobj) + KOBJ_FREE(multifpgapci_kobj); return; } diff --git a/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c b/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c index 56e984c3b35e..5d94f499bc18 100644 --- a/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c +++ b/platform/pddf/i2c/modules/multifpgapci/gpio/pddf_multifpgapci_gpio_module.c @@ -24,14 +24,26 @@ #include "pddf_multifpgapci_defs.h" #include "pddf_multifpgapci_gpio_defs.h" +DEFINE_XARRAY(gpio_drvdata_map); + static ssize_t create_chip(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; - struct pddf_multifpgapci_gpio_chip_pdata *pdata = - (struct pddf_multifpgapci_gpio_chip_pdata *)_ptr->addr; + struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; + struct gpio_chip_drvdata *gpio_drvdata; struct platform_device *pdev; + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + gpio_drvdata = xa_load(&gpio_drvdata_map, dev_index); + if (!gpio_drvdata) { + pddf_dbg(FPGA, + KERN_ERR + "[%s] unable to find gpio data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return -ENODEV; + } + if (strncmp(buf, "init", strlen("init")) != 0) { pddf_dbg(FPGA, KERN_ERR @@ -40,10 +52,11 @@ static ssize_t create_chip(struct device *dev, struct device_attribute *da, return -EINVAL; } - pdev = platform_device_register_data(&pdata->fpga->dev, + pdev = platform_device_register_data(&gpio_drvdata->pdata.fpga->dev, "pddf-multifpgapci-gpio", - PLATFORM_DEVID_AUTO, pdata, - sizeof(*pdata)); + PLATFORM_DEVID_AUTO, + &gpio_drvdata->pdata, + sizeof(gpio_drvdata->pdata)); if (!pdev) { pddf_dbg(FPGA, KERN_ERR "[%s] error allocating platform device\n", @@ -58,7 +71,18 @@ static ssize_t create_line(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; - struct gpio_chip_drvdata *node = (struct gpio_chip_drvdata *)_ptr->addr; + struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; + struct gpio_chip_drvdata *node; + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + node = xa_load(&gpio_drvdata_map, dev_index); + if (!node) { + pddf_dbg(FPGA, + KERN_ERR + "[%s] unable to find gpio data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return -ENODEV; + } if (strncmp(buf, "init", strlen("init")) != 0) { pddf_dbg(FPGA, @@ -79,14 +103,28 @@ static ssize_t create_line(struct device *dev, struct device_attribute *da, return count; } -int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, - struct kobject *kobj) +static int pddf_multifpgapci_gpio_attach(struct pci_dev *pci_dev, + struct kobject *kobj) { - struct pddf_multifpgapci_drvdata *pci_drvdata = - dev_get_drvdata(&pci_dev->dev); - struct gpio_chip_drvdata *gpio_drvdata = - &pci_drvdata->gpio_chip_drvdata; + struct gpio_chip_drvdata *gpio_drvdata; int err; + pddf_dbg(MULTIFPGA, KERN_INFO "%s start\n", __FUNCTION__); + + gpio_drvdata = kzalloc(sizeof(struct gpio_chip_drvdata), GFP_KERNEL); + if (!gpio_drvdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] failed to allocate drvdata for %s\n", + __FUNCTION__, pci_name(pci_dev)); + return -ENOMEM; + } + + gpio_drvdata->gpio_kobj = kobject_create_and_add("gpio", kobj); + if (!gpio_drvdata->gpio_kobj) { + pddf_dbg(MULTIFPGA, KERN_ERR "[%s] create gpio kobj failed\n", + __FUNCTION__); + err = -ENOMEM; + goto return_err; + } gpio_drvdata->pdata.fpga = pci_dev; @@ -95,7 +133,7 @@ int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, sizeof(uint32_t), (void *)&gpio_drvdata->pdata.ngpio, NULL); PDDF_DATA_ATTR( create_chip, S_IWUSR, NULL, create_chip, PDDF_CHAR, - 32, (void *)gpio_drvdata, NULL); + 32, (void *)pci_dev, NULL); gpio_drvdata->attrs.attr_ngpio = attr_ngpio; gpio_drvdata->attrs.attr_create = attr_create_chip; @@ -109,12 +147,22 @@ int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, gpio_drvdata->gpio_chip_attr_group.attrs = gpio_drvdata->gpio_chip_attrs; - err = sysfs_create_group(kobj, &gpio_drvdata->gpio_chip_attr_group); - if (err) - goto return_err; + err = sysfs_create_group(gpio_drvdata->gpio_kobj, + &gpio_drvdata->gpio_chip_attr_group); + if (err) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s] unable create sysfs files for device %s - error %d\n", + __FUNCTION__, pci_name(pci_dev), err); + goto remove_gpio_kobj; + } - gpio_drvdata->line_kobj = kobject_create_and_add("line", kobj); + gpio_drvdata->line_kobj = + kobject_create_and_add("line", gpio_drvdata->gpio_kobj); if (!gpio_drvdata->line_kobj) { + pddf_dbg(MULTIFPGA, KERN_ERR "[%s] create line kobj failed\n", + __FUNCTION__); err = -ENOMEM; goto remove_gpio_chip_attr_group; } @@ -132,8 +180,8 @@ int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, PDDF_INT_HEX, sizeof(uint32_t), (void *)&gpio_drvdata->temp_line_data.direction, NULL); PDDF_DATA_ATTR( - create_line, S_IWUSR, NULL, create_line, PDDF_CHAR, 32, - (void *)&gpio_drvdata->pdata, NULL); + create_line, S_IWUSR, NULL, create_line, + PDDF_CHAR, 32, (void *)pci_dev, NULL); gpio_drvdata->attrs.line_attrs.attr_bit = attr_bit; gpio_drvdata->attrs.line_attrs.attr_offset = attr_offset; @@ -155,35 +203,104 @@ int pddf_multifpgapci_gpio_module_init(struct pci_dev *pci_dev, err = sysfs_create_group(gpio_drvdata->line_kobj, &gpio_drvdata->gpio_line_attr_group); - if (err) + if (err) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s] unable to create sysfs files for group %s - error %d\n", + __FUNCTION__, pci_name(pci_dev), err); goto free_gpio_line_kobj; + } + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + xa_store(&gpio_drvdata_map, dev_index, gpio_drvdata, GFP_KERNEL); + pddf_dbg(MULTIFPGA, KERN_INFO "%s done!\n", __FUNCTION__); return 0; free_gpio_line_kobj: kobject_put(gpio_drvdata->line_kobj); remove_gpio_chip_attr_group: - sysfs_remove_group(kobj, &gpio_drvdata->gpio_chip_attr_group); + sysfs_remove_group(gpio_drvdata->gpio_kobj, + &gpio_drvdata->gpio_chip_attr_group); + +remove_gpio_kobj: + kobject_put(gpio_drvdata->gpio_kobj); return_err: return err; } -EXPORT_SYMBOL(pddf_multifpgapci_gpio_module_init); -void pddf_multifpgapci_gpio_module_exit(struct pci_dev *pci_dev, - struct kobject *kobj) +static void pddf_multifpgapci_gpio_detach(struct pci_dev *pci_dev, + struct kobject *kobj) { - struct pddf_multifpgapci_drvdata *pci_drvdata = - pci_get_drvdata(pci_dev); - struct gpio_chip_drvdata *gpio_drvdata = - &pci_drvdata->gpio_chip_drvdata; - sysfs_remove_group(gpio_drvdata->line_kobj, - &gpio_drvdata->gpio_line_attr_group); - kobject_put(gpio_drvdata->line_kobj); - sysfs_remove_group(kobj, &gpio_drvdata->gpio_chip_attr_group); + struct gpio_chip_drvdata *gpio_drvdata; + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + gpio_drvdata = xa_load(&gpio_drvdata_map, dev_index); + if (!gpio_drvdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find gpio module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + + if (gpio_drvdata->line_kobj) { + sysfs_remove_group(gpio_drvdata->line_kobj, + &gpio_drvdata->gpio_line_attr_group); + kobject_put(gpio_drvdata->line_kobj); + gpio_drvdata->line_kobj = NULL; + } + if (gpio_drvdata->gpio_kobj) { + sysfs_remove_group(gpio_drvdata->gpio_kobj, + &gpio_drvdata->gpio_chip_attr_group); + kobject_put(gpio_drvdata->gpio_kobj); + gpio_drvdata->gpio_kobj = NULL; + } + + kfree(gpio_drvdata); + xa_erase(&gpio_drvdata_map, (unsigned long)pci_dev); +} + +static void pddf_multifpgapci_gpio_map_bar(struct pci_dev *pci_dev, + void __iomem *bar_base, + unsigned long bar_start, + unsigned long bar_len) +{ +} + +static void pddf_multifpgapci_gpio_unmap_bar(struct pci_dev *pci_dev, + void __iomem *bar_base, + unsigned long bar_start, + unsigned long bar_len) +{ +} + +static struct protocol_ops gpio_protocol_ops = { + .attach = pddf_multifpgapci_gpio_attach, + .detach = pddf_multifpgapci_gpio_detach, + .map_bar = pddf_multifpgapci_gpio_map_bar, + .unmap_bar = pddf_multifpgapci_gpio_unmap_bar, + .name = "gpio", }; -EXPORT_SYMBOL(pddf_multifpgapci_gpio_module_exit); + +static int __init pddf_multifpgapci_gpio_init(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "Loading GPIO protocol module\n"); + xa_init(&gpio_drvdata_map); + return multifpgapci_register_protocol("gpio", &gpio_protocol_ops); +} + +static void __exit pddf_multifpgapci_gpio_exit(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "Unloading GPIO protocol module\n"); + multifpgapci_unregister_protocol("gpio"); + xa_destroy(&gpio_drvdata_map); +} + +module_init(pddf_multifpgapci_gpio_init); +module_exit(pddf_multifpgapci_gpio_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Nexthop Systems"); diff --git a/platform/pddf/i2c/modules/multifpgapci/i2c/pddf_multifpgapci_i2c_module.c b/platform/pddf/i2c/modules/multifpgapci/i2c/pddf_multifpgapci_i2c_module.c index b825693029f8..3a5fc1534203 100644 --- a/platform/pddf/i2c/modules/multifpgapci/i2c/pddf_multifpgapci_i2c_module.c +++ b/platform/pddf/i2c/modules/multifpgapci/i2c/pddf_multifpgapci_i2c_module.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -26,6 +27,8 @@ #include "pddf_multifpgapci_defs.h" #include "pddf_multifpgapci_i2c_defs.h" +DEFINE_XARRAY(i2c_drvdata_map); + int (*pddf_i2c_multifpgapci_add_numbered_bus)(struct i2c_adapter *, int) = NULL; EXPORT_SYMBOL(pddf_i2c_multifpgapci_add_numbered_bus); @@ -50,10 +53,21 @@ ssize_t new_i2c_adapter(struct device *dev, struct device_attribute *da, struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; - struct pddf_multifpgapci_drvdata *pci_drvdata = - pci_get_drvdata(pci_dev); - struct i2c_adapter_drvdata *i2c_privdata = - &pci_drvdata->i2c_adapter_drvdata; + struct i2c_adapter_drvdata *i2c_privdata; + + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + i2c_privdata = xa_load(&i2c_drvdata_map, dev_index); + if (!i2c_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to retrieve i2c_privdata for device %s", + __FUNCTION__, pci_name(pci_dev)); + return -ENODEV; + } + struct i2c_adapter *i2c_adapters = i2c_privdata->i2c_adapters; if (i2c_privdata->i2c_adapter_registered[index]) { @@ -116,14 +130,16 @@ ssize_t del_i2c_adapter(struct device *dev, struct device_attribute *da, } struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; - struct i2c_adapter_drvdata *i2c_privdata = - (struct i2c_adapter_drvdata *)_ptr->addr; - struct i2c_adapter *i2c_adapters = i2c_privdata->i2c_adapters; + struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; + struct i2c_adapter_drvdata *i2c_privdata; - if (!i2c_privdata->i2c_adapter_registered[index]) { + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + i2c_privdata = xa_load(&i2c_drvdata_map, dev_index); + if (!i2c_privdata) { pddf_dbg(MULTIFPGA, - KERN_ERR "%s: I2C Adapter %d doesn't exist\n", - __FUNCTION__, index); + KERN_ERR + "[%s] unable to retrieve i2c_privdata for device %s", + __FUNCTION__, pci_name(pci_dev)); return -ENODEV; } @@ -131,24 +147,61 @@ ssize_t del_i2c_adapter(struct device *dev, struct device_attribute *da, KERN_INFO "[%s] Attempting delete of bus index: %d\n", __FUNCTION__, index); - i2c_del_adapter(&i2c_adapters[index]); + i2c_del_adapter(&i2c_privdata->i2c_adapters[index]); i2c_privdata->i2c_adapter_registered[index] = false; return count; } -int pddf_multifpgapci_i2c_module_init(struct pci_dev *pci_dev, - struct kobject *kobj) +int pddf_multifpgapci_i2c_get_adapter_data(struct pci_dev *pci_dev, + struct i2c_adapter_data *data) +{ + if (!data) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] NULL i2c_adapter_data pointers for device %s", + __FUNCTION__, pci_name(pci_dev)); + return -EINVAL; + } + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + struct i2c_adapter_drvdata *i2c_privdata = + xa_load(&i2c_drvdata_map, dev_index); + if (!i2c_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to retrieve i2c_privdata for device %s", + __FUNCTION__, pci_name(pci_dev)); + return -ENODEV; + } + data->virt_bus = i2c_privdata->virt_bus; + data->ch_base_addr = i2c_privdata->ch_base_addr; + data->ch_size = i2c_privdata->ch_size; + data->num_virt_ch = i2c_privdata->num_virt_ch; + return 0; +} +EXPORT_SYMBOL(pddf_multifpgapci_i2c_get_adapter_data); + +static int pddf_multifpgapci_i2c_attach(struct pci_dev *pci_dev, + struct kobject *kobj) { pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, pci_name(pci_dev)); - struct pddf_multifpgapci_drvdata *pci_drvdata = - pci_get_drvdata(pci_dev); - struct i2c_adapter_drvdata *i2c_privdata = - &pci_drvdata->i2c_adapter_drvdata; + struct i2c_adapter_drvdata *i2c_privdata; int err; + i2c_privdata = kzalloc(sizeof(struct i2c_adapter_drvdata), GFP_KERNEL); + if (!i2c_privdata) { + return -ENOMEM; + } + i2c_privdata->pci_dev = pci_dev; + + i2c_privdata->i2c_kobj = kobject_create_and_add("i2c", kobj); + if (!i2c_privdata->i2c_kobj) { + pddf_dbg(MULTIFPGA, KERN_ERR "[%s] create i2c kobj failed\n", + __FUNCTION__); + return -ENOMEM; + } + memset(i2c_privdata->i2c_adapter_registered, 0, sizeof(i2c_privdata->i2c_adapter_registered)); @@ -173,8 +226,8 @@ int pddf_multifpgapci_i2c_module_init(struct pci_dev *pci_dev, new_i2c_adapter, PDDF_CHAR, NAME_SIZE, (void *)pci_dev, NULL); PDDF_DATA_ATTR( del_i2c_adapter, S_IWUSR | S_IRUGO, show_pddf_data, - del_i2c_adapter, PDDF_CHAR, NAME_SIZE, (void *)i2c_privdata, NULL); - + del_i2c_adapter, PDDF_CHAR, NAME_SIZE, (void *)pci_dev, NULL); + i2c_privdata->attrs.attr_virt_bus = attr_virt_bus; i2c_privdata->attrs.attr_ch_base_offset = attr_ch_base_offset; i2c_privdata->attrs.attr_ch_size = attr_ch_size; @@ -200,26 +253,38 @@ int pddf_multifpgapci_i2c_module_init(struct pci_dev *pci_dev, i2c_privdata->i2c_adapter_attr_group.attrs = i2c_privdata->i2c_adapter_attrs; - err = sysfs_create_group(kobj, &i2c_privdata->i2c_adapter_attr_group); + err = sysfs_create_group(i2c_privdata->i2c_kobj, + &i2c_privdata->i2c_adapter_attr_group); if (err) { pddf_dbg(MULTIFPGA, KERN_ERR "[%s] sysfs_create_group error, status: %d\n", __FUNCTION__, err); return err; } + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + xa_store(&i2c_drvdata_map, dev_index, i2c_privdata, GFP_KERNEL); return 0; } -EXPORT_SYMBOL(pddf_multifpgapci_i2c_module_init); -void pddf_multifpgapci_i2c_module_exit(struct pci_dev *pci_dev, - struct kobject *kobj) +static void pddf_multifpgapci_i2c_detach(struct pci_dev *pci_dev, + struct kobject *kobj) { - struct pddf_multifpgapci_drvdata *pci_drvdata = - pci_get_drvdata(pci_dev); - struct i2c_adapter_drvdata *i2c_privdata = - &pci_drvdata->i2c_adapter_drvdata; + struct i2c_adapter_drvdata *i2c_privdata; int i; + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + i2c_privdata = xa_load(&i2c_drvdata_map, dev_index); + if (!i2c_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find i2c module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + for (i = 0; i < I2C_PCI_MAX_BUS; i++) { if (i2c_privdata->i2c_adapter_registered[i]) { pddf_dbg(MULTIFPGA, @@ -229,10 +294,87 @@ void pddf_multifpgapci_i2c_module_exit(struct pci_dev *pci_dev, i2c_del_adapter(&i2c_privdata->i2c_adapters[i]); } } - sysfs_remove_group(kobj, &i2c_privdata->i2c_adapter_attr_group); + if (i2c_privdata->i2c_kobj) { + sysfs_remove_group(i2c_privdata->i2c_kobj, + &i2c_privdata->i2c_adapter_attr_group); + kobject_put(i2c_privdata->i2c_kobj); + i2c_privdata->i2c_kobj = NULL; + } + xa_erase(&i2c_drvdata_map, (unsigned long)pci_dev); +} + +static void pddf_multifpgapci_i2c_map_bar(struct pci_dev *pci_dev, + void __iomem *bar_base, + unsigned long bar_start, + unsigned long bar_len) +{ + struct i2c_adapter_drvdata *i2c_privdata; + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + i2c_privdata = xa_load(&i2c_drvdata_map, dev_index); + if (!i2c_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find i2c module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + // i2c specific data store + struct i2c_adapter_sysfs_vals *i2c_pddf_data = + &i2c_privdata->temp_sysfs_vals; + + i2c_privdata->virt_bus = i2c_pddf_data->virt_bus; + i2c_privdata->ch_base_addr = bar_base + i2c_pddf_data->ch_base_offset; + i2c_privdata->num_virt_ch = i2c_pddf_data->num_virt_ch; + i2c_privdata->ch_size = i2c_pddf_data->ch_size; +} + +static void pddf_multifpgapci_i2c_unmap_bar(struct pci_dev *pci_dev, + void __iomem *bar_base, + unsigned long bar_start, + unsigned long bar_len) +{ + struct i2c_adapter_drvdata *i2c_privdata; + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + i2c_privdata = xa_load(&i2c_drvdata_map, dev_index); + if (!i2c_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find i2c module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + i2c_privdata->ch_base_addr = NULL; +} + +static struct protocol_ops i2c_protocol_ops = { + .attach = pddf_multifpgapci_i2c_attach, + .detach = pddf_multifpgapci_i2c_detach, + .map_bar = pddf_multifpgapci_i2c_map_bar, + .unmap_bar = pddf_multifpgapci_i2c_unmap_bar, + .name = "i2c", }; -EXPORT_SYMBOL(pddf_multifpgapci_i2c_module_exit); + +static int __init pddf_multifpgapci_i2c_init(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "Loading I2C protocol module\n"); + xa_init(&i2c_drvdata_map); + return multifpgapci_register_protocol("i2c", &i2c_protocol_ops); +} + +static void __exit pddf_multifpgapci_i2c_exit(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "Unloading I2C protocol module\n"); + multifpgapci_unregister_protocol("i2c"); + xa_destroy(&i2c_drvdata_map); +} + +module_init(pddf_multifpgapci_i2c_init); +module_exit(pddf_multifpgapci_i2c_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Nexthop Systems"); -MODULE_DESCRIPTION("PDDF Platform Data for Multiple PCI FPGA I2C adapters."); \ No newline at end of file +MODULE_DESCRIPTION("PDDF Platform Data for Multiple PCI FPGA I2C adapters."); From d457bf7a60b8b68ce001b35316dcab4dede267c1 Mon Sep 17 00:00:00 2001 From: Roy Wen Date: Mon, 22 Sep 2025 15:33:41 -0400 Subject: [PATCH 3/6] add mdio protocol support --- .../common/modules/Makefile | 1 + .../common/modules/pddf_custom_mdio_algo.c | 230 ++++++++ platform/pddf/i2c/debian/rules | 2 +- .../modules/include/pddf_multifpgapci_defs.h | 1 + .../include/pddf_multifpgapci_mdio_defs.h | 79 +++ .../pddf/i2c/modules/multifpgapci/Makefile | 2 +- .../i2c/modules/multifpgapci/mdio/Makefile | 3 + .../mdio/pddf_multifpgapci_mdio_module.c | 501 ++++++++++++++++++ platform/pddf/i2c/utils/pddfparse.py | 33 +- 9 files changed, 842 insertions(+), 10 deletions(-) create mode 100644 platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c create mode 100644 platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h create mode 100644 platform/pddf/i2c/modules/multifpgapci/mdio/Makefile create mode 100644 platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c diff --git a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile index 21768fdd4e4e..ec945895a024 100644 --- a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile +++ b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile @@ -1,4 +1,5 @@ obj-m := pddf_custom_fpga_algo.o +obj-m += pddf_custom_mdio_algo.o obj-m += nh_tda38740.o obj-m += nh_isl68137.o obj-m += nh_pmbus_core.o diff --git a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c new file mode 100644 index 000000000000..dd3faab80175 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2025 Nexthop Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../../pddf/i2c/modules/include/pddf_multifpgapci_defs.h" +#include "../../../../pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h" + +// MDIO Control Register +#define FPGA_MDIO_CTRL_REG_OFFSET 0x0000 + +// Bit definitions for MDIO Control Register +#define FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS 31 +#define FPGA_MDIO_CTRL_READ_CMD_BIT_POS 30 + +#define FPGA_MDIO_CTRL_OP_SHIFT 26 +#define FPGA_MDIO_CTRL_OP_MASK 0x3 + +#define FPGA_MDIO_OP_ADDRESS_VAL 0x0 +#define FPGA_MDIO_OP_WRITE_VAL 0x1 +#define FPGA_MDIO_OP_READ_VAL 0x3 +#define FPGA_MDIO_OP_POSTREAD_VAL 0x2 + +#define FPGA_MDIO_CTRL_PHY_ADDR_SHIFT 21 +#define FPGA_MDIO_CTRL_DEV_ADDR_SHIFT 16 +#define FPGA_MDIO_CTRL_DATA_SHIFT 0 +#define FPGA_MDIO_CTRL_DATA_MASK 0xFFFF + +// MDIO Read Data Register +#define FPGA_MDIO_READ_DATA_REG_OFFSET 0x0004 + +// Bit definitions for MDIO Read Data Register +#define FPGA_MDIO_READ_DATA_VALID_BIT BIT(31) +#define FPGA_MDIO_READ_DATA_BUSY_BIT BIT(30) +#define FPGA_MDIO_READ_DATA_MASK 0xFFFF + +#define MDIO_DEVAD_SHIFT 16 + +static int fpga_mdio_wait_for_idle(struct fpga_mdio_priv *priv) +{ + // Timeout after 100ms if busy bit has not cleared + unsigned long timeout = jiffies + msecs_to_jiffies(100); + uint32_t read_data_reg; + + do { + read_data_reg = ioread32(priv->reg_base + + FPGA_MDIO_READ_DATA_REG_OFFSET); + if (!(read_data_reg & FPGA_MDIO_READ_DATA_BUSY_BIT)) { + // Busy bit is cleared, transaction is complete + return 0; + } + usleep_range(5, 10); + } while (time_before(jiffies, timeout)); + + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s]: MDIO transaction timed out waiting for busy bit to clear\n", + __FUNCTION__); + return -ETIMEDOUT; +} + +static int fpga_mdio_do_transaction(struct fpga_mdio_priv *priv, int phy_addr, + int dev_addr, uint32_t op_val, + uint16_t data_val, uint32_t cmd_bit) +{ + uint32_t cmd_val = + (BIT(cmd_bit) | (op_val << FPGA_MDIO_CTRL_OP_SHIFT) | + ((phy_addr & 0x1F) << FPGA_MDIO_CTRL_PHY_ADDR_SHIFT) | + ((dev_addr & 0x1F) << FPGA_MDIO_CTRL_DEV_ADDR_SHIFT) | + ((data_val & FPGA_MDIO_CTRL_DATA_MASK) + << FPGA_MDIO_CTRL_DATA_SHIFT)); + + iowrite32(cmd_val, priv->reg_base + FPGA_MDIO_CTRL_REG_OFFSET); + int ret = fpga_mdio_wait_for_idle(priv); + if (ret < 0) + return ret; + + iowrite32(0x0, priv->reg_base + FPGA_MDIO_CTRL_REG_OFFSET); + return fpga_mdio_wait_for_idle(priv); +} + +int fpga_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) +{ + struct fpga_mdio_priv *priv = bus->priv; + int ret = -EIO; + + if (!priv || !priv->reg_base) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s]: Bus private data (reg_base) not properly set for bus %s\n", + __FUNCTION__, dev_name(&bus->dev)); + return -EINVAL; + } + + // Extract the device and reg addresses from reg_num + int dev_addr = (reg_num >> MDIO_DEVAD_SHIFT) & 0x1F; + int c45_reg_addr = reg_num & 0xFFFF; + + mutex_lock(&priv->lock); + + // Clause 45 Handling + + // Step 1: Write the register address + ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, + FPGA_MDIO_OP_ADDRESS_VAL, c45_reg_addr, + FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS); + if (ret < 0) + goto out; + + // Step 2: Read the data from the register + ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, + FPGA_MDIO_OP_READ_VAL, 0, + FPGA_MDIO_CTRL_READ_CMD_BIT_POS); + if (ret < 0) + goto out; + + // Poll for Read Data Valid and read data + unsigned long timeout = jiffies + msecs_to_jiffies(100); + do { + uint32_t read_data_reg = ioread32( + priv->reg_base + FPGA_MDIO_READ_DATA_REG_OFFSET); + if (read_data_reg & FPGA_MDIO_READ_DATA_VALID_BIT) { + ret = (read_data_reg >> FPGA_MDIO_CTRL_DATA_SHIFT) & + FPGA_MDIO_READ_DATA_MASK; + goto out; + } + usleep_range(10, 20); + } while (time_before(jiffies, timeout)); + + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s]: C45 READ data not valid within timeout for bus %s, PHY 0x%x, MMD 0x%x\n", + __FUNCTION__, dev_name(&bus->dev), phy_addr, dev_addr); + ret = -ETIMEDOUT; + +out: + mutex_unlock(&priv->lock); + return ret; +} +EXPORT_SYMBOL(fpga_mdio_read); + +int fpga_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, + uint16_t val) +{ + struct fpga_mdio_priv *priv = bus->priv; + uint32_t cmd_val; + int ret; + + if (!priv || !priv->reg_base) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s]: Bus private data (reg_base) not properly set for bus %s\n", + __FUNCTION__, dev_name(&bus->dev)); + return -EINVAL; + } + + // Extract the device and reg addresses from reg_num + int dev_addr = (reg_num >> MDIO_DEVAD_SHIFT) & 0x1F; + int c45_reg_addr = reg_num & 0xFFFF; + + mutex_lock(&priv->lock); + + // Clause 45 Handling + + // Step 1: Write the register address + ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, + FPGA_MDIO_OP_ADDRESS_VAL, c45_reg_addr, + FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS); + if (ret < 0) + goto out; + + // Step 2: Write the data to the register + ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, + FPGA_MDIO_OP_WRITE_VAL, val, + FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS); + if (ret < 0) + goto out; + +out: + mutex_unlock(&priv->lock); + return 0; +} +EXPORT_SYMBOL(fpga_mdio_write); + +static struct mdio_fpga_ops fpga_algo_ops_instance = { + .read = fpga_mdio_read, + .write = fpga_mdio_write, +}; + +static int __init pddf_custom_mdio_algo_init(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "[%s]\n", __FUNCTION__); + mdio_fpga_algo_ops = &fpga_algo_ops_instance; + return 0; +} + +static void __exit pddf_custom_mdio_algo_exit(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "[%s]\n", __FUNCTION__); + mdio_fpga_algo_ops = NULL; + return; +} + +module_init(pddf_custom_mdio_algo_init); +module_exit(pddf_custom_mdio_algo_exit); + +MODULE_DESCRIPTION("Custom algorithm for Nexthop FPGAPCIe MDIO implementation"); +MODULE_VERSION("1.0.0"); +MODULE_LICENSE("GPL"); diff --git a/platform/pddf/i2c/debian/rules b/platform/pddf/i2c/debian/rules index 9d78317880fc..a982a7f58606 100755 --- a/platform/pddf/i2c/debian/rules +++ b/platform/pddf/i2c/debian/rules @@ -19,7 +19,7 @@ PACKAGE_PRE_NAME := sonic-platform-pddf KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver +MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c multifpgapci/mdio fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver MODULE_DIR:= modules UTILS_DIR := utils SERVICE_DIR := service diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h index ba5c9ee35866..07d420dbb6b8 100644 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h @@ -23,6 +23,7 @@ #include "pddf_multifpgapci_gpio_defs.h" #include "pddf_multifpgapci_i2c_defs.h" +#include "pddf_multifpgapci_mdio_defs.h" #define NAME_SIZE 32 diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h new file mode 100644 index 000000000000..6829b259550c --- /dev/null +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Nexthop Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PDDF_MULTIFPGAPCI_MDIO_DEFS_H__ +#define __PDDF_MULTIFPGAPCI_MDIO_DEFS_H__ + +#include "linux/types.h" +#include +#include +#include +#include + +#include "pddf_client_defs.h" + +#define MDIO_MAX_BUS 512 + +struct mdio_bus_attrs { + PDDF_ATTR attr_ch_base_offset; + PDDF_ATTR attr_ch_size; + PDDF_ATTR attr_num_virt_ch; + PDDF_ATTR attr_new_mdio_bus; + PDDF_ATTR attr_del_mdio_bus; +}; + +#define NUM_MDIO_BUS_ATTRS (sizeof(struct mdio_bus_attrs) / sizeof(PDDF_ATTR)) + +struct mdio_bus_sysfs_vals { + uint32_t ch_base_offset; + uint32_t ch_size; + uint32_t num_virt_ch; +}; + +struct mdio_bus_drvdata { + struct pci_dev *pci_dev; + size_t bar_length; + struct kobject *mdio_kobj; + + // temp_sysfs_vals store temporary values provided by sysfs, + // which are eventually copied/saved to MDIO bus platform data. + struct mdio_bus_sysfs_vals temp_sysfs_vals; + + // platform data + struct mii_bus *mdio_buses[MDIO_MAX_BUS]; + bool mdio_bus_registered[MDIO_MAX_BUS]; + void *__iomem ch_base_addr; + int ch_size; + int num_virt_ch; + + // sysfs attrs + struct mdio_bus_attrs attrs; + struct attribute *mdio_bus_attrs[NUM_MDIO_BUS_ATTRS + 1]; + struct attribute_group mdio_bus_attr_group; +}; + +struct fpga_mdio_priv { + void __iomem *reg_base; // Base address for this MDIO instance + struct mutex lock; // Mutex for this MDIO instance + int last_read_value; +}; + +struct mdio_fpga_ops { + int (*read)(struct mii_bus *bus, int phy_id, int regnum); + int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val); +}; + +extern struct mdio_fpga_ops *mdio_fpga_algo_ops; + +#endif diff --git a/platform/pddf/i2c/modules/multifpgapci/Makefile b/platform/pddf/i2c/modules/multifpgapci/Makefile index ad6cac982ccf..e862c936d69d 100644 --- a/platform/pddf/i2c/modules/multifpgapci/Makefile +++ b/platform/pddf/i2c/modules/multifpgapci/Makefile @@ -1,4 +1,4 @@ -obj-m := driver/ gpio/ i2c/ +obj-m := driver/ gpio/ i2c/ mdio/ obj-m += pddf_multifpgapci_module.o ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/mdio/Makefile b/platform/pddf/i2c/modules/multifpgapci/mdio/Makefile new file mode 100644 index 000000000000..efbc19465c55 --- /dev/null +++ b/platform/pddf/i2c/modules/multifpgapci/mdio/Makefile @@ -0,0 +1,3 @@ +obj-m := pddf_multifpgapci_mdio_module.o + +ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c b/platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c new file mode 100644 index 000000000000..98b49b0bb75f --- /dev/null +++ b/platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c @@ -0,0 +1,501 @@ +/* + * Copyright 2025 Nexthop Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Description: + * PDDF MULTIFPGAPCI kernel module for registering MDIO buses. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pddf_multifpgapci_defs.h" +#include "pddf_multifpgapci_mdio_defs.h" + +DEFINE_XARRAY(mdio_drvdata_map); + +struct mdio_fpga_ops *mdio_fpga_algo_ops = NULL; +EXPORT_SYMBOL_GPL(mdio_fpga_algo_ops); + +static ssize_t mdio_access_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t mdio_access_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count); + +static DEVICE_ATTR_RW(mdio_access); + +static ssize_t mdio_access_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct mii_bus *bus = to_mii_bus(dev); + struct fpga_mdio_priv *priv = bus->priv; + char op[10]; + int phy_addr, reg_num, val; + int ret = -EINVAL; + + int num_args = + sscanf(buf, "%9s %i %i %i", op, &phy_addr, ®_num, &val); + if (num_args < 1) { + pddf_dbg(MULTIFPGA, KERN_ERR + "Invalid MDIO access format. No command provided.\n"); + return -EINVAL; + } + + if (strcmp(op, "write") == 0) { + // MDIO write + if (num_args < 4) { + pddf_dbg( + MULTIFPGA, KERN_ERR + "Invalid MDIO write access format. Expected 'write '\n"); + return -EINVAL; + } + ret = mdio_fpga_algo_ops->write(bus, phy_addr, reg_num, val); + if (ret == 0) { + return count; + } else { + pddf_dbg(MULTIFPGA, + KERN_ERR + "MDIO write failed for phy=%d, reg=0x%x\n", + phy_addr, reg_num); + return ret; + } + } else if (strcmp(op, "read") == 0) { + // MDIO read - the result is stored in priv->last_read_value. + if (num_args < 3) { + pddf_dbg( + MULTIFPGA, KERN_ERR + "Invalid MDIO read access format. Expected 'read '\n"); + return -EINVAL; + } + ret = mdio_fpga_algo_ops->read(bus, phy_addr, reg_num); + if (ret >= 0) { + priv->last_read_value = ret; + return count; + } else { + pddf_dbg(MULTIFPGA, + KERN_ERR + "MDIO read failed for phy=%d, reg=0x%x\n", + phy_addr, reg_num); + return ret; + } + } else { + pddf_dbg(MULTIFPGA, KERN_ERR "Invalid MDIO operation: %s\n", + op); + return -EINVAL; + } + + return ret; +} + +// The show function returns the value of the last read operation to user space. +// This is triggered by a cat command on the mdio_access file. +static ssize_t mdio_access_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mii_bus *bus = to_mii_bus(dev); + struct fpga_mdio_priv *priv = bus->priv; + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", priv->last_read_value); +} + +ssize_t new_mdio_bus(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int index, err; + struct mii_bus *new_bus = NULL; + struct fpga_mdio_priv *algo_priv; + + err = kstrtoint(buf, 10, &index); + if (err != 0) { + pddf_dbg(MULTIFPGA, KERN_ERR "Error converting string: %d\n", + err); + return -EINVAL; + } + + if (index < 0 || index >= MDIO_MAX_BUS) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] MDIO bus %d out of range [0, %d)\n", + __FUNCTION__, index, MDIO_MAX_BUS); + return -ENODEV; + } + + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; + struct mdio_bus_drvdata *mdio_privdata; + + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); + if (!mdio_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to retrieve mdio_privdata for device %s", + __FUNCTION__, pci_name(pci_dev)); + return -ENODEV; + } + + if (mdio_privdata->mdio_bus_registered[index]) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] MDIO bus %d already registered\n", + __FUNCTION__, index); + return -ENODEV; + } + + new_bus = mdiobus_alloc(); + if (!new_bus) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] Failed to allocate MDIO bus %d\n", + __FUNCTION__, index); + return -ENOMEM; + } + + // Allocate the private data for the MDIO algorithm + algo_priv = kzalloc(sizeof(*algo_priv), GFP_KERNEL); + if (!algo_priv) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s] Failed to allocate FPGA MDIO algo private data\n", + __FUNCTION__); + mdiobus_free(new_bus); + return -ENOMEM; + } + algo_priv->reg_base = + mdio_privdata->ch_base_addr + index * mdio_privdata->ch_size; + mutex_init(&algo_priv->lock); + new_bus->priv = algo_priv; + + mdio_privdata->mdio_buses[index] = new_bus; + + new_bus->name = "pci-mdio-bus"; + snprintf(new_bus->id, MII_BUS_ID_SIZE, "pci-mdio-%d", index); + + if (!mdio_fpga_algo_ops || !mdio_fpga_algo_ops->read || + !mdio_fpga_algo_ops->write) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "[%s] MDIO FPGA algorithm module not loaded or incomplete!\n", + __FUNCTION__); + goto err_cleanup; + } + + new_bus->read = mdio_fpga_algo_ops->read; + new_bus->write = mdio_fpga_algo_ops->write; + new_bus->owner = THIS_MODULE; + new_bus->parent = &pci_dev->dev; + + err = mdiobus_register(new_bus); + if (err != 0) { + pddf_dbg(MULTIFPGA, KERN_ERR "Could not register MDIO bus %d\n", + index); + goto err_cleanup; + } + mdio_privdata->mdio_bus_registered[index] = true; + + // Attach the new sysfs path for user-space access + err = device_create_file(&new_bus->dev, &dev_attr_mdio_access); + if (err) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "Failed to create sysfs file for MDIO bus %s: %d\n", + dev_name(&new_bus->dev), err); + goto err_cleanup_registered; + } + + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] Registered MDIO bus id: %s\n", + __FUNCTION__, dev_name(&new_bus->dev)); + + return count; + +err_cleanup_registered: + mdiobus_unregister(new_bus); + +err_cleanup: + if (new_bus) { + mdiobus_free(new_bus); + mdio_privdata->mdio_buses[index] = NULL; + } + if (algo_priv) { + kfree(algo_priv); + } + mdio_privdata->mdio_bus_registered[index] = false; + return err; +} + +ssize_t del_mdio_bus(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int index, error; + + error = kstrtoint(buf, 10, &index); + if (error != 0) { + pddf_dbg(MULTIFPGA, KERN_ERR "Error converting string: %d\n", + error); + return -EINVAL; + } + + if (index < 0 || index >= MDIO_MAX_BUS) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] MDIO bus %d out of range [0, %d)\n", + __FUNCTION__, index, MDIO_MAX_BUS); + return -ENODEV; + } + + struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; + struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; + struct mdio_bus_drvdata *mdio_privdata; + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); + if (!mdio_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to retrieve mdio_privdata for device %s", + __FUNCTION__, pci_name(pci_dev)); + return -ENODEV; + } + + struct mii_bus *bus_to_unregister = mdio_privdata->mdio_buses[index]; + + if (!bus_to_unregister) { + pddf_dbg( + MULTIFPGA, + KERN_ERR + "%s: MDIO bus %d marked registered but poiner is NULL\n", + __FUNCTION__, index); + return -ENODEV; + } + + pddf_dbg(MULTIFPGA, + KERN_INFO "[%s] Attempting to unregister MDIO bus: %d\n", + __FUNCTION__, index); + + // Remove the custom sysfs file + device_remove_file(&bus_to_unregister->dev, &dev_attr_mdio_access); + + mdiobus_unregister(bus_to_unregister); + mdiobus_free(bus_to_unregister); + + mdio_privdata->mdio_bus_registered[index] = false; + mdio_privdata->mdio_buses[index] = NULL; + + return count; +} + +int pddf_multifpgapci_mdio_attach(struct pci_dev *pci_dev, struct kobject *kobj) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + struct mdio_bus_drvdata *mdio_privdata; + int err; + + mdio_privdata = kzalloc(sizeof(struct mdio_bus_drvdata), GFP_KERNEL); + if (!mdio_privdata) { + return -ENOMEM; + } + mdio_privdata->pci_dev = pci_dev; + + mdio_privdata->mdio_kobj = kobject_create_and_add("mdio", kobj); + if (!mdio_privdata->mdio_kobj) { + pddf_dbg(MULTIFPGA, KERN_ERR "[%s] create mdio kobj failed\n", + __FUNCTION__); + err = -ENOMEM; + goto free_privdata; + } + + memset(mdio_privdata->mdio_bus_registered, 0, + sizeof(mdio_privdata->mdio_bus_registered)); + + PDDF_DATA_ATTR(ch_base_offset, S_IWUSR | S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_UINT32, sizeof(uint32_t), + (void *)&mdio_privdata->temp_sysfs_vals.ch_base_offset, + NULL); + + PDDF_DATA_ATTR(ch_size, S_IWUSR | S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_UINT32, sizeof(uint32_t), + (void *)&mdio_privdata->temp_sysfs_vals.ch_size, NULL); + + PDDF_DATA_ATTR(num_virt_ch, S_IWUSR | S_IRUGO, show_pddf_data, + store_pddf_data, PDDF_UINT32, sizeof(uint32_t), + (void *)&mdio_privdata->temp_sysfs_vals.num_virt_ch, + NULL); + + PDDF_DATA_ATTR(new_mdio_bus, S_IWUSR | S_IRUGO, show_pddf_data, + new_mdio_bus, PDDF_CHAR, NAME_SIZE, (void *)pci_dev, + NULL); + + PDDF_DATA_ATTR(del_mdio_bus, S_IWUSR | S_IRUGO, show_pddf_data, + del_mdio_bus, PDDF_CHAR, NAME_SIZE, (void *)pci_dev, + NULL); + + mdio_privdata->attrs.attr_ch_base_offset = attr_ch_base_offset; + mdio_privdata->attrs.attr_ch_size = attr_ch_size; + mdio_privdata->attrs.attr_num_virt_ch = attr_num_virt_ch; + mdio_privdata->attrs.attr_new_mdio_bus = attr_new_mdio_bus; + mdio_privdata->attrs.attr_del_mdio_bus = attr_del_mdio_bus; + + struct attribute *mdio_bus_attrs[NUM_MDIO_BUS_ATTRS + 1] = { + &mdio_privdata->attrs.attr_ch_base_offset.dev_attr.attr, + &mdio_privdata->attrs.attr_ch_size.dev_attr.attr, + &mdio_privdata->attrs.attr_num_virt_ch.dev_attr.attr, + &mdio_privdata->attrs.attr_new_mdio_bus.dev_attr.attr, + &mdio_privdata->attrs.attr_del_mdio_bus.dev_attr.attr, + NULL, + }; + + memcpy(mdio_privdata->mdio_bus_attrs, mdio_bus_attrs, + sizeof(mdio_privdata->mdio_bus_attrs)); + + mdio_privdata->mdio_bus_attr_group.attrs = + mdio_privdata->mdio_bus_attrs; + + err = sysfs_create_group(mdio_privdata->mdio_kobj, + &mdio_privdata->mdio_bus_attr_group); + if (err) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] sysfs_create_group error, status: %d\n", + __FUNCTION__, err); + goto free_kobj; + } + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + xa_store(&mdio_drvdata_map, dev_index, mdio_privdata, GFP_KERNEL); + + return 0; + +free_kobj: + kobject_put(mdio_privdata->mdio_kobj); +free_privdata: + kfree(mdio_privdata); + return err; +} + +static void pddf_multifpgapci_mdio_detach(struct pci_dev *pci_dev, + struct kobject *kobj) +{ + struct mdio_bus_drvdata *mdio_privdata; + int i; + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); + if (!mdio_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find mdio module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + + for (i = 0; i < MDIO_MAX_BUS; i++) { + if (mdio_privdata->mdio_bus_registered[i]) { + pddf_dbg(MULTIFPGA, + KERN_INFO "[%s] unregistering MDIO bus: %s\n", + __FUNCTION__, + mdio_privdata->mdio_buses[i]->name); + mdiobus_unregister(mdio_privdata->mdio_buses[i]); + } + } + if (mdio_privdata->mdio_kobj) { + sysfs_remove_group(mdio_privdata->mdio_kobj, + &mdio_privdata->mdio_bus_attr_group); + kobject_put(mdio_privdata->mdio_kobj); + mdio_privdata->mdio_kobj = NULL; + } + xa_erase(&mdio_drvdata_map, (unsigned long)pci_dev); +} + +static void pddf_multifpgapci_mdio_map_bar(struct pci_dev *pci_dev, + void __iomem *bar_base, + unsigned long bar_start, + unsigned long bar_len) +{ + struct mdio_bus_drvdata *mdio_privdata; + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); + if (!mdio_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find mdio module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + struct mdio_bus_sysfs_vals *mdio_pddf_data = + &mdio_privdata->temp_sysfs_vals; + + mdio_privdata->ch_base_addr = bar_base + mdio_pddf_data->ch_base_offset; + mdio_privdata->num_virt_ch = mdio_pddf_data->num_virt_ch; + mdio_privdata->ch_size = mdio_pddf_data->ch_size; +} + +static void pddf_multifpgapci_mdio_unmap_bar(struct pci_dev *pci_dev, + void __iomem *bar_base, + unsigned long bar_start, + unsigned long bar_len) +{ + struct mdio_bus_drvdata *mdio_privdata; + pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, + pci_name(pci_dev)); + unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); + mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); + if (!mdio_privdata) { + pddf_dbg(MULTIFPGA, + KERN_ERR + "[%s] unable to find mdio module data for device %s\n", + __FUNCTION__, pci_name(pci_dev)); + return; + } + mdio_privdata->ch_base_addr = NULL; +} + +static struct protocol_ops mdio_protocol_ops = { + .attach = pddf_multifpgapci_mdio_attach, + .detach = pddf_multifpgapci_mdio_detach, + .map_bar = pddf_multifpgapci_mdio_map_bar, + .unmap_bar = pddf_multifpgapci_mdio_unmap_bar, + .name = "mdio", +}; + +static int __init pddf_multifpgapci_mdio_init(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "Loading MDIO protocol module\n"); + xa_init(&mdio_drvdata_map); + return multifpgapci_register_protocol("mdio", &mdio_protocol_ops); +} + +static void __exit pddf_multifpgapci_mdio_exit(void) +{ + pddf_dbg(MULTIFPGA, KERN_INFO "Unloading MDIO protocol module\n"); + multifpgapci_unregister_protocol("mdio"); + xa_destroy(&mdio_drvdata_map); +} + +module_init(pddf_multifpgapci_mdio_init); +module_exit(pddf_multifpgapci_mdio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nexthop Systems"); +MODULE_DESCRIPTION( + "PDDF MULTIFPGAPCI kernel module for registering MDIO buses."); diff --git a/platform/pddf/i2c/utils/pddfparse.py b/platform/pddf/i2c/utils/pddfparse.py index 7b4e04d7c17a..6b164f8ed2c1 100755 --- a/platform/pddf/i2c/utils/pddfparse.py +++ b/platform/pddf/i2c/utils/pddfparse.py @@ -530,7 +530,13 @@ def create_multifpgapci_device(self, dev, ops): ret = self.create_device(dev['i2c']['dev_attr'], "pddf/devices/multifpgapci/{}/i2c".format(bdf), ops) if ret != 0: return create_ret.append(ret) - + + # MDIO specific data store + if 'mdio' in dev: + ret = self.create_device(dev['mdio']['dev_attr'], "pddf/devices/multifpgapci/{}/mdio".format(bdf), ops) + if ret != 0: + return create_ret.append(ret) + # TODO: add GPIO & SPI specific data stores cmd = "echo 'fpgapci_init' > /sys/kernel/pddf/devices/multifpgapci/{}/dev_ops".format(bdf) @@ -544,6 +550,11 @@ def create_multifpgapci_device(self, dev, ops): if ret != 0: return create_ret.append(ret) + if 'mdio' in dev: + ret = self.create_mdio_bus(bdf, dev['mdio'], ops) + if ret != 0: + return create_ret.append(ret) + if 'gpio' in dev: ret = self.create_gpio_device(bdf, dev['gpio'], ops) if ret != 0: @@ -551,27 +562,33 @@ def create_multifpgapci_device(self, dev, ops): return create_ret.append(ret) - def create_gpio_device(self, bdf, gpio_dev, ops): - create_ret = [] - ret = 0 + def create_mdio_bus(self, bdf, mdio_dev, ops): + for bus in range(int(mdio_dev['dev_attr']['num_virt_ch'], 16)): + cmd = "echo {} > /sys/kernel/pddf/devices/multifpgapci/{}/mdio/new_mdio_bus".format(bus, bdf) + ret = self.runcmd(cmd) + if ret != 0: + return ret + return 0 + + def create_gpio_device(self, bdf, gpio_dev, ops): for line in gpio_dev.keys(): for attr in gpio_dev[line]['attr_list']: ret = self.create_device(attr, "pddf/devices/multifpgapci/{}/gpio/line".format(bdf), ops) if ret != 0: - return create_ret.append(ret) + return ret cmd = "echo 'init' > /sys/kernel/pddf/devices/multifpgapci/{}/gpio/line/create_line".format(bdf) ret = self.runcmd(cmd) if ret != 0: - return create_ret.append(ret) + return ret cmd = "echo 'init' > /sys/kernel/pddf/devices/multifpgapci/{}/gpio/create_chip".format(bdf) ret = self.runcmd(cmd) if ret != 0: - return create_ret.append(ret) + return ret - return create_ret.append(ret) + return 0 ################################################################################################################################# # DELETE DEFS From 5c1eaa190600b28f12958306038e629beab27700 Mon Sep 17 00:00:00 2001 From: Roy Wen Date: Thu, 23 Oct 2025 21:40:39 -0400 Subject: [PATCH 4/6] remove spi module from pddf json --- device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json.j2 | 1 - 1 file changed, 1 deletion(-) diff --git a/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json.j2 b/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json.j2 index eb16f9b71d90..7110e34d06af 100644 --- a/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json.j2 +++ b/device/nexthop/x86_64-nexthop_4010-r0/pddf/pddf-device.json.j2 @@ -39,7 +39,6 @@ "pddf_kos": [ "pddf_multifpgapci_i2c_module", "pddf_multifpgapci_gpio_module", - "pddf_multifpgapci_spi_module", "pddf_client_module", "pddf_multifpgapci_driver", "pddf_multifpgapci_module", From 3eae40b686cb3df43e52dd3aab77da672bd7c3ce Mon Sep 17 00:00:00 2001 From: Nate White Date: Fri, 24 Oct 2025 11:59:39 +0000 Subject: [PATCH 5/6] Add missing sysfs_create_group --- .../multifpgapci/driver/pddf_multifpgapci_driver.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c b/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c index 9e81bba08a49..a43dd8d5c73a 100644 --- a/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c +++ b/platform/pddf/i2c/modules/multifpgapci/driver/pddf_multifpgapci_driver.c @@ -376,8 +376,20 @@ static int pddf_pci_add_fpga(char *bdf, struct pci_dev *dev) } fpga_data->fpga_attr_group_initialized = true; + ret = sysfs_create_group(fpga_data->kobj, &pddf_clients_data_group); + if (ret) { + pddf_dbg(MULTIFPGA, + KERN_ERR "[%s] sysfs_create_group failed: %d\n", + __FUNCTION__, ret); + goto free_fpga_attr_group; + } + fpga_data->pddf_clients_data_group_initialized = true; + return 0; +free_fpga_attr_group: + sysfs_remove_group(fpga_data->kobj, &fpga_data->fpga_attr_group); + fpga_data->fpga_attr_group_initialized = false; free_fpga_kobj: attach_protocols_for_fpga(dev, fpga_data->kobj); kobject_put(fpga_data->kobj); From c94cf8da6bf63b3405efbd9f4bf57cb1336604b2 Mon Sep 17 00:00:00 2001 From: Lotus Fenn Date: Mon, 27 Oct 2025 04:36:29 +0000 Subject: [PATCH 6/6] Revert "add mdio protocol support" This reverts commit d457bf7a60b8b68ce001b35316dcab4dede267c1. --- .../common/modules/Makefile | 1 - .../common/modules/pddf_custom_mdio_algo.c | 230 -------- platform/pddf/i2c/debian/rules | 2 +- .../modules/include/pddf_multifpgapci_defs.h | 1 - .../include/pddf_multifpgapci_mdio_defs.h | 79 --- .../pddf/i2c/modules/multifpgapci/Makefile | 2 +- .../i2c/modules/multifpgapci/mdio/Makefile | 3 - .../mdio/pddf_multifpgapci_mdio_module.c | 501 ------------------ platform/pddf/i2c/utils/pddfparse.py | 41 +- 9 files changed, 3 insertions(+), 857 deletions(-) delete mode 100644 platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c delete mode 100644 platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h delete mode 100644 platform/pddf/i2c/modules/multifpgapci/mdio/Makefile delete mode 100644 platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c diff --git a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile index ec945895a024..21768fdd4e4e 100644 --- a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile +++ b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/Makefile @@ -1,5 +1,4 @@ obj-m := pddf_custom_fpga_algo.o -obj-m += pddf_custom_mdio_algo.o obj-m += nh_tda38740.o obj-m += nh_isl68137.o obj-m += nh_pmbus_core.o diff --git a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c b/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c deleted file mode 100644 index dd3faab80175..000000000000 --- a/platform/broadcom/sonic-platform-modules-nexthop/common/modules/pddf_custom_mdio_algo.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2025 Nexthop Systems Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#define __STDC_WANT_LIB_EXT1__ 1 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../../../pddf/i2c/modules/include/pddf_multifpgapci_defs.h" -#include "../../../../pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h" - -// MDIO Control Register -#define FPGA_MDIO_CTRL_REG_OFFSET 0x0000 - -// Bit definitions for MDIO Control Register -#define FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS 31 -#define FPGA_MDIO_CTRL_READ_CMD_BIT_POS 30 - -#define FPGA_MDIO_CTRL_OP_SHIFT 26 -#define FPGA_MDIO_CTRL_OP_MASK 0x3 - -#define FPGA_MDIO_OP_ADDRESS_VAL 0x0 -#define FPGA_MDIO_OP_WRITE_VAL 0x1 -#define FPGA_MDIO_OP_READ_VAL 0x3 -#define FPGA_MDIO_OP_POSTREAD_VAL 0x2 - -#define FPGA_MDIO_CTRL_PHY_ADDR_SHIFT 21 -#define FPGA_MDIO_CTRL_DEV_ADDR_SHIFT 16 -#define FPGA_MDIO_CTRL_DATA_SHIFT 0 -#define FPGA_MDIO_CTRL_DATA_MASK 0xFFFF - -// MDIO Read Data Register -#define FPGA_MDIO_READ_DATA_REG_OFFSET 0x0004 - -// Bit definitions for MDIO Read Data Register -#define FPGA_MDIO_READ_DATA_VALID_BIT BIT(31) -#define FPGA_MDIO_READ_DATA_BUSY_BIT BIT(30) -#define FPGA_MDIO_READ_DATA_MASK 0xFFFF - -#define MDIO_DEVAD_SHIFT 16 - -static int fpga_mdio_wait_for_idle(struct fpga_mdio_priv *priv) -{ - // Timeout after 100ms if busy bit has not cleared - unsigned long timeout = jiffies + msecs_to_jiffies(100); - uint32_t read_data_reg; - - do { - read_data_reg = ioread32(priv->reg_base + - FPGA_MDIO_READ_DATA_REG_OFFSET); - if (!(read_data_reg & FPGA_MDIO_READ_DATA_BUSY_BIT)) { - // Busy bit is cleared, transaction is complete - return 0; - } - usleep_range(5, 10); - } while (time_before(jiffies, timeout)); - - pddf_dbg( - MULTIFPGA, - KERN_ERR - "[%s]: MDIO transaction timed out waiting for busy bit to clear\n", - __FUNCTION__); - return -ETIMEDOUT; -} - -static int fpga_mdio_do_transaction(struct fpga_mdio_priv *priv, int phy_addr, - int dev_addr, uint32_t op_val, - uint16_t data_val, uint32_t cmd_bit) -{ - uint32_t cmd_val = - (BIT(cmd_bit) | (op_val << FPGA_MDIO_CTRL_OP_SHIFT) | - ((phy_addr & 0x1F) << FPGA_MDIO_CTRL_PHY_ADDR_SHIFT) | - ((dev_addr & 0x1F) << FPGA_MDIO_CTRL_DEV_ADDR_SHIFT) | - ((data_val & FPGA_MDIO_CTRL_DATA_MASK) - << FPGA_MDIO_CTRL_DATA_SHIFT)); - - iowrite32(cmd_val, priv->reg_base + FPGA_MDIO_CTRL_REG_OFFSET); - int ret = fpga_mdio_wait_for_idle(priv); - if (ret < 0) - return ret; - - iowrite32(0x0, priv->reg_base + FPGA_MDIO_CTRL_REG_OFFSET); - return fpga_mdio_wait_for_idle(priv); -} - -int fpga_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) -{ - struct fpga_mdio_priv *priv = bus->priv; - int ret = -EIO; - - if (!priv || !priv->reg_base) { - pddf_dbg( - MULTIFPGA, - KERN_ERR - "[%s]: Bus private data (reg_base) not properly set for bus %s\n", - __FUNCTION__, dev_name(&bus->dev)); - return -EINVAL; - } - - // Extract the device and reg addresses from reg_num - int dev_addr = (reg_num >> MDIO_DEVAD_SHIFT) & 0x1F; - int c45_reg_addr = reg_num & 0xFFFF; - - mutex_lock(&priv->lock); - - // Clause 45 Handling - - // Step 1: Write the register address - ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, - FPGA_MDIO_OP_ADDRESS_VAL, c45_reg_addr, - FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS); - if (ret < 0) - goto out; - - // Step 2: Read the data from the register - ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, - FPGA_MDIO_OP_READ_VAL, 0, - FPGA_MDIO_CTRL_READ_CMD_BIT_POS); - if (ret < 0) - goto out; - - // Poll for Read Data Valid and read data - unsigned long timeout = jiffies + msecs_to_jiffies(100); - do { - uint32_t read_data_reg = ioread32( - priv->reg_base + FPGA_MDIO_READ_DATA_REG_OFFSET); - if (read_data_reg & FPGA_MDIO_READ_DATA_VALID_BIT) { - ret = (read_data_reg >> FPGA_MDIO_CTRL_DATA_SHIFT) & - FPGA_MDIO_READ_DATA_MASK; - goto out; - } - usleep_range(10, 20); - } while (time_before(jiffies, timeout)); - - pddf_dbg( - MULTIFPGA, - KERN_ERR - "[%s]: C45 READ data not valid within timeout for bus %s, PHY 0x%x, MMD 0x%x\n", - __FUNCTION__, dev_name(&bus->dev), phy_addr, dev_addr); - ret = -ETIMEDOUT; - -out: - mutex_unlock(&priv->lock); - return ret; -} -EXPORT_SYMBOL(fpga_mdio_read); - -int fpga_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, - uint16_t val) -{ - struct fpga_mdio_priv *priv = bus->priv; - uint32_t cmd_val; - int ret; - - if (!priv || !priv->reg_base) { - pddf_dbg( - MULTIFPGA, - KERN_ERR - "[%s]: Bus private data (reg_base) not properly set for bus %s\n", - __FUNCTION__, dev_name(&bus->dev)); - return -EINVAL; - } - - // Extract the device and reg addresses from reg_num - int dev_addr = (reg_num >> MDIO_DEVAD_SHIFT) & 0x1F; - int c45_reg_addr = reg_num & 0xFFFF; - - mutex_lock(&priv->lock); - - // Clause 45 Handling - - // Step 1: Write the register address - ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, - FPGA_MDIO_OP_ADDRESS_VAL, c45_reg_addr, - FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS); - if (ret < 0) - goto out; - - // Step 2: Write the data to the register - ret = fpga_mdio_do_transaction(priv, phy_addr, dev_addr, - FPGA_MDIO_OP_WRITE_VAL, val, - FPGA_MDIO_CTRL_WRITE_CMD_BIT_POS); - if (ret < 0) - goto out; - -out: - mutex_unlock(&priv->lock); - return 0; -} -EXPORT_SYMBOL(fpga_mdio_write); - -static struct mdio_fpga_ops fpga_algo_ops_instance = { - .read = fpga_mdio_read, - .write = fpga_mdio_write, -}; - -static int __init pddf_custom_mdio_algo_init(void) -{ - pddf_dbg(MULTIFPGA, KERN_INFO "[%s]\n", __FUNCTION__); - mdio_fpga_algo_ops = &fpga_algo_ops_instance; - return 0; -} - -static void __exit pddf_custom_mdio_algo_exit(void) -{ - pddf_dbg(MULTIFPGA, KERN_INFO "[%s]\n", __FUNCTION__); - mdio_fpga_algo_ops = NULL; - return; -} - -module_init(pddf_custom_mdio_algo_init); -module_exit(pddf_custom_mdio_algo_exit); - -MODULE_DESCRIPTION("Custom algorithm for Nexthop FPGAPCIe MDIO implementation"); -MODULE_VERSION("1.0.0"); -MODULE_LICENSE("GPL"); diff --git a/platform/pddf/i2c/debian/rules b/platform/pddf/i2c/debian/rules index a982a7f58606..9d78317880fc 100755 --- a/platform/pddf/i2c/debian/rules +++ b/platform/pddf/i2c/debian/rules @@ -19,7 +19,7 @@ PACKAGE_PRE_NAME := sonic-platform-pddf KVERSION ?= $(shell uname -r) KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR:= $(shell pwd) -MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c multifpgapci/mdio fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver +MODULE_DIRS:= client cpld cpld/driver cpldmux cpldmux/driver fpgai2c fpgai2c/driver fpgapci fpgapci/driver fpgapci/algos multifpgapci multifpgapci/driver multifpgapci/gpio multifpgapci/gpio/driver multifpgapci/i2c fan fan/driver mux gpio led psu psu/driver sysstatus xcvr xcvr/driver MODULE_DIR:= modules UTILS_DIR := utils SERVICE_DIR := service diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h index 07d420dbb6b8..ba5c9ee35866 100644 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h +++ b/platform/pddf/i2c/modules/include/pddf_multifpgapci_defs.h @@ -23,7 +23,6 @@ #include "pddf_multifpgapci_gpio_defs.h" #include "pddf_multifpgapci_i2c_defs.h" -#include "pddf_multifpgapci_mdio_defs.h" #define NAME_SIZE 32 diff --git a/platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h b/platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h deleted file mode 100644 index 6829b259550c..000000000000 --- a/platform/pddf/i2c/modules/include/pddf_multifpgapci_mdio_defs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2025 Nexthop Systems Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __PDDF_MULTIFPGAPCI_MDIO_DEFS_H__ -#define __PDDF_MULTIFPGAPCI_MDIO_DEFS_H__ - -#include "linux/types.h" -#include -#include -#include -#include - -#include "pddf_client_defs.h" - -#define MDIO_MAX_BUS 512 - -struct mdio_bus_attrs { - PDDF_ATTR attr_ch_base_offset; - PDDF_ATTR attr_ch_size; - PDDF_ATTR attr_num_virt_ch; - PDDF_ATTR attr_new_mdio_bus; - PDDF_ATTR attr_del_mdio_bus; -}; - -#define NUM_MDIO_BUS_ATTRS (sizeof(struct mdio_bus_attrs) / sizeof(PDDF_ATTR)) - -struct mdio_bus_sysfs_vals { - uint32_t ch_base_offset; - uint32_t ch_size; - uint32_t num_virt_ch; -}; - -struct mdio_bus_drvdata { - struct pci_dev *pci_dev; - size_t bar_length; - struct kobject *mdio_kobj; - - // temp_sysfs_vals store temporary values provided by sysfs, - // which are eventually copied/saved to MDIO bus platform data. - struct mdio_bus_sysfs_vals temp_sysfs_vals; - - // platform data - struct mii_bus *mdio_buses[MDIO_MAX_BUS]; - bool mdio_bus_registered[MDIO_MAX_BUS]; - void *__iomem ch_base_addr; - int ch_size; - int num_virt_ch; - - // sysfs attrs - struct mdio_bus_attrs attrs; - struct attribute *mdio_bus_attrs[NUM_MDIO_BUS_ATTRS + 1]; - struct attribute_group mdio_bus_attr_group; -}; - -struct fpga_mdio_priv { - void __iomem *reg_base; // Base address for this MDIO instance - struct mutex lock; // Mutex for this MDIO instance - int last_read_value; -}; - -struct mdio_fpga_ops { - int (*read)(struct mii_bus *bus, int phy_id, int regnum); - int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val); -}; - -extern struct mdio_fpga_ops *mdio_fpga_algo_ops; - -#endif diff --git a/platform/pddf/i2c/modules/multifpgapci/Makefile b/platform/pddf/i2c/modules/multifpgapci/Makefile index e862c936d69d..ad6cac982ccf 100644 --- a/platform/pddf/i2c/modules/multifpgapci/Makefile +++ b/platform/pddf/i2c/modules/multifpgapci/Makefile @@ -1,4 +1,4 @@ -obj-m := driver/ gpio/ i2c/ mdio/ +obj-m := driver/ gpio/ i2c/ obj-m += pddf_multifpgapci_module.o ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/mdio/Makefile b/platform/pddf/i2c/modules/multifpgapci/mdio/Makefile deleted file mode 100644 index efbc19465c55..000000000000 --- a/platform/pddf/i2c/modules/multifpgapci/mdio/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-m := pddf_multifpgapci_mdio_module.o - -ccflags-y := -I$(M)/modules/include diff --git a/platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c b/platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c deleted file mode 100644 index 98b49b0bb75f..000000000000 --- a/platform/pddf/i2c/modules/multifpgapci/mdio/pddf_multifpgapci_mdio_module.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright 2025 Nexthop Systems Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Description: - * PDDF MULTIFPGAPCI kernel module for registering MDIO buses. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pddf_multifpgapci_defs.h" -#include "pddf_multifpgapci_mdio_defs.h" - -DEFINE_XARRAY(mdio_drvdata_map); - -struct mdio_fpga_ops *mdio_fpga_algo_ops = NULL; -EXPORT_SYMBOL_GPL(mdio_fpga_algo_ops); - -static ssize_t mdio_access_show(struct device *dev, - struct device_attribute *attr, char *buf); -static ssize_t mdio_access_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count); - -static DEVICE_ATTR_RW(mdio_access); - -static ssize_t mdio_access_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct mii_bus *bus = to_mii_bus(dev); - struct fpga_mdio_priv *priv = bus->priv; - char op[10]; - int phy_addr, reg_num, val; - int ret = -EINVAL; - - int num_args = - sscanf(buf, "%9s %i %i %i", op, &phy_addr, ®_num, &val); - if (num_args < 1) { - pddf_dbg(MULTIFPGA, KERN_ERR - "Invalid MDIO access format. No command provided.\n"); - return -EINVAL; - } - - if (strcmp(op, "write") == 0) { - // MDIO write - if (num_args < 4) { - pddf_dbg( - MULTIFPGA, KERN_ERR - "Invalid MDIO write access format. Expected 'write '\n"); - return -EINVAL; - } - ret = mdio_fpga_algo_ops->write(bus, phy_addr, reg_num, val); - if (ret == 0) { - return count; - } else { - pddf_dbg(MULTIFPGA, - KERN_ERR - "MDIO write failed for phy=%d, reg=0x%x\n", - phy_addr, reg_num); - return ret; - } - } else if (strcmp(op, "read") == 0) { - // MDIO read - the result is stored in priv->last_read_value. - if (num_args < 3) { - pddf_dbg( - MULTIFPGA, KERN_ERR - "Invalid MDIO read access format. Expected 'read '\n"); - return -EINVAL; - } - ret = mdio_fpga_algo_ops->read(bus, phy_addr, reg_num); - if (ret >= 0) { - priv->last_read_value = ret; - return count; - } else { - pddf_dbg(MULTIFPGA, - KERN_ERR - "MDIO read failed for phy=%d, reg=0x%x\n", - phy_addr, reg_num); - return ret; - } - } else { - pddf_dbg(MULTIFPGA, KERN_ERR "Invalid MDIO operation: %s\n", - op); - return -EINVAL; - } - - return ret; -} - -// The show function returns the value of the last read operation to user space. -// This is triggered by a cat command on the mdio_access file. -static ssize_t mdio_access_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mii_bus *bus = to_mii_bus(dev); - struct fpga_mdio_priv *priv = bus->priv; - - return scnprintf(buf, PAGE_SIZE, "0x%x\n", priv->last_read_value); -} - -ssize_t new_mdio_bus(struct device *dev, struct device_attribute *da, - const char *buf, size_t count) -{ - int index, err; - struct mii_bus *new_bus = NULL; - struct fpga_mdio_priv *algo_priv; - - err = kstrtoint(buf, 10, &index); - if (err != 0) { - pddf_dbg(MULTIFPGA, KERN_ERR "Error converting string: %d\n", - err); - return -EINVAL; - } - - if (index < 0 || index >= MDIO_MAX_BUS) { - pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] MDIO bus %d out of range [0, %d)\n", - __FUNCTION__, index, MDIO_MAX_BUS); - return -ENODEV; - } - - struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; - struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; - struct mdio_bus_drvdata *mdio_privdata; - - pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, - pci_name(pci_dev)); - - unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); - mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); - if (!mdio_privdata) { - pddf_dbg(MULTIFPGA, - KERN_ERR - "[%s] unable to retrieve mdio_privdata for device %s", - __FUNCTION__, pci_name(pci_dev)); - return -ENODEV; - } - - if (mdio_privdata->mdio_bus_registered[index]) { - pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] MDIO bus %d already registered\n", - __FUNCTION__, index); - return -ENODEV; - } - - new_bus = mdiobus_alloc(); - if (!new_bus) { - pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] Failed to allocate MDIO bus %d\n", - __FUNCTION__, index); - return -ENOMEM; - } - - // Allocate the private data for the MDIO algorithm - algo_priv = kzalloc(sizeof(*algo_priv), GFP_KERNEL); - if (!algo_priv) { - pddf_dbg( - MULTIFPGA, - KERN_ERR - "[%s] Failed to allocate FPGA MDIO algo private data\n", - __FUNCTION__); - mdiobus_free(new_bus); - return -ENOMEM; - } - algo_priv->reg_base = - mdio_privdata->ch_base_addr + index * mdio_privdata->ch_size; - mutex_init(&algo_priv->lock); - new_bus->priv = algo_priv; - - mdio_privdata->mdio_buses[index] = new_bus; - - new_bus->name = "pci-mdio-bus"; - snprintf(new_bus->id, MII_BUS_ID_SIZE, "pci-mdio-%d", index); - - if (!mdio_fpga_algo_ops || !mdio_fpga_algo_ops->read || - !mdio_fpga_algo_ops->write) { - pddf_dbg( - MULTIFPGA, - KERN_ERR - "[%s] MDIO FPGA algorithm module not loaded or incomplete!\n", - __FUNCTION__); - goto err_cleanup; - } - - new_bus->read = mdio_fpga_algo_ops->read; - new_bus->write = mdio_fpga_algo_ops->write; - new_bus->owner = THIS_MODULE; - new_bus->parent = &pci_dev->dev; - - err = mdiobus_register(new_bus); - if (err != 0) { - pddf_dbg(MULTIFPGA, KERN_ERR "Could not register MDIO bus %d\n", - index); - goto err_cleanup; - } - mdio_privdata->mdio_bus_registered[index] = true; - - // Attach the new sysfs path for user-space access - err = device_create_file(&new_bus->dev, &dev_attr_mdio_access); - if (err) { - pddf_dbg(MULTIFPGA, - KERN_ERR - "Failed to create sysfs file for MDIO bus %s: %d\n", - dev_name(&new_bus->dev), err); - goto err_cleanup_registered; - } - - pddf_dbg(MULTIFPGA, KERN_INFO "[%s] Registered MDIO bus id: %s\n", - __FUNCTION__, dev_name(&new_bus->dev)); - - return count; - -err_cleanup_registered: - mdiobus_unregister(new_bus); - -err_cleanup: - if (new_bus) { - mdiobus_free(new_bus); - mdio_privdata->mdio_buses[index] = NULL; - } - if (algo_priv) { - kfree(algo_priv); - } - mdio_privdata->mdio_bus_registered[index] = false; - return err; -} - -ssize_t del_mdio_bus(struct device *dev, struct device_attribute *da, - const char *buf, size_t count) -{ - int index, error; - - error = kstrtoint(buf, 10, &index); - if (error != 0) { - pddf_dbg(MULTIFPGA, KERN_ERR "Error converting string: %d\n", - error); - return -EINVAL; - } - - if (index < 0 || index >= MDIO_MAX_BUS) { - pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] MDIO bus %d out of range [0, %d)\n", - __FUNCTION__, index, MDIO_MAX_BUS); - return -ENODEV; - } - - struct pddf_data_attribute *_ptr = (struct pddf_data_attribute *)da; - struct pci_dev *pci_dev = (struct pci_dev *)_ptr->addr; - struct mdio_bus_drvdata *mdio_privdata; - - unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); - mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); - if (!mdio_privdata) { - pddf_dbg(MULTIFPGA, - KERN_ERR - "[%s] unable to retrieve mdio_privdata for device %s", - __FUNCTION__, pci_name(pci_dev)); - return -ENODEV; - } - - struct mii_bus *bus_to_unregister = mdio_privdata->mdio_buses[index]; - - if (!bus_to_unregister) { - pddf_dbg( - MULTIFPGA, - KERN_ERR - "%s: MDIO bus %d marked registered but poiner is NULL\n", - __FUNCTION__, index); - return -ENODEV; - } - - pddf_dbg(MULTIFPGA, - KERN_INFO "[%s] Attempting to unregister MDIO bus: %d\n", - __FUNCTION__, index); - - // Remove the custom sysfs file - device_remove_file(&bus_to_unregister->dev, &dev_attr_mdio_access); - - mdiobus_unregister(bus_to_unregister); - mdiobus_free(bus_to_unregister); - - mdio_privdata->mdio_bus_registered[index] = false; - mdio_privdata->mdio_buses[index] = NULL; - - return count; -} - -int pddf_multifpgapci_mdio_attach(struct pci_dev *pci_dev, struct kobject *kobj) -{ - pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, - pci_name(pci_dev)); - struct mdio_bus_drvdata *mdio_privdata; - int err; - - mdio_privdata = kzalloc(sizeof(struct mdio_bus_drvdata), GFP_KERNEL); - if (!mdio_privdata) { - return -ENOMEM; - } - mdio_privdata->pci_dev = pci_dev; - - mdio_privdata->mdio_kobj = kobject_create_and_add("mdio", kobj); - if (!mdio_privdata->mdio_kobj) { - pddf_dbg(MULTIFPGA, KERN_ERR "[%s] create mdio kobj failed\n", - __FUNCTION__); - err = -ENOMEM; - goto free_privdata; - } - - memset(mdio_privdata->mdio_bus_registered, 0, - sizeof(mdio_privdata->mdio_bus_registered)); - - PDDF_DATA_ATTR(ch_base_offset, S_IWUSR | S_IRUGO, show_pddf_data, - store_pddf_data, PDDF_UINT32, sizeof(uint32_t), - (void *)&mdio_privdata->temp_sysfs_vals.ch_base_offset, - NULL); - - PDDF_DATA_ATTR(ch_size, S_IWUSR | S_IRUGO, show_pddf_data, - store_pddf_data, PDDF_UINT32, sizeof(uint32_t), - (void *)&mdio_privdata->temp_sysfs_vals.ch_size, NULL); - - PDDF_DATA_ATTR(num_virt_ch, S_IWUSR | S_IRUGO, show_pddf_data, - store_pddf_data, PDDF_UINT32, sizeof(uint32_t), - (void *)&mdio_privdata->temp_sysfs_vals.num_virt_ch, - NULL); - - PDDF_DATA_ATTR(new_mdio_bus, S_IWUSR | S_IRUGO, show_pddf_data, - new_mdio_bus, PDDF_CHAR, NAME_SIZE, (void *)pci_dev, - NULL); - - PDDF_DATA_ATTR(del_mdio_bus, S_IWUSR | S_IRUGO, show_pddf_data, - del_mdio_bus, PDDF_CHAR, NAME_SIZE, (void *)pci_dev, - NULL); - - mdio_privdata->attrs.attr_ch_base_offset = attr_ch_base_offset; - mdio_privdata->attrs.attr_ch_size = attr_ch_size; - mdio_privdata->attrs.attr_num_virt_ch = attr_num_virt_ch; - mdio_privdata->attrs.attr_new_mdio_bus = attr_new_mdio_bus; - mdio_privdata->attrs.attr_del_mdio_bus = attr_del_mdio_bus; - - struct attribute *mdio_bus_attrs[NUM_MDIO_BUS_ATTRS + 1] = { - &mdio_privdata->attrs.attr_ch_base_offset.dev_attr.attr, - &mdio_privdata->attrs.attr_ch_size.dev_attr.attr, - &mdio_privdata->attrs.attr_num_virt_ch.dev_attr.attr, - &mdio_privdata->attrs.attr_new_mdio_bus.dev_attr.attr, - &mdio_privdata->attrs.attr_del_mdio_bus.dev_attr.attr, - NULL, - }; - - memcpy(mdio_privdata->mdio_bus_attrs, mdio_bus_attrs, - sizeof(mdio_privdata->mdio_bus_attrs)); - - mdio_privdata->mdio_bus_attr_group.attrs = - mdio_privdata->mdio_bus_attrs; - - err = sysfs_create_group(mdio_privdata->mdio_kobj, - &mdio_privdata->mdio_bus_attr_group); - if (err) { - pddf_dbg(MULTIFPGA, - KERN_ERR "[%s] sysfs_create_group error, status: %d\n", - __FUNCTION__, err); - goto free_kobj; - } - unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); - xa_store(&mdio_drvdata_map, dev_index, mdio_privdata, GFP_KERNEL); - - return 0; - -free_kobj: - kobject_put(mdio_privdata->mdio_kobj); -free_privdata: - kfree(mdio_privdata); - return err; -} - -static void pddf_multifpgapci_mdio_detach(struct pci_dev *pci_dev, - struct kobject *kobj) -{ - struct mdio_bus_drvdata *mdio_privdata; - int i; - pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, - pci_name(pci_dev)); - - unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); - mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); - if (!mdio_privdata) { - pddf_dbg(MULTIFPGA, - KERN_ERR - "[%s] unable to find mdio module data for device %s\n", - __FUNCTION__, pci_name(pci_dev)); - return; - } - - for (i = 0; i < MDIO_MAX_BUS; i++) { - if (mdio_privdata->mdio_bus_registered[i]) { - pddf_dbg(MULTIFPGA, - KERN_INFO "[%s] unregistering MDIO bus: %s\n", - __FUNCTION__, - mdio_privdata->mdio_buses[i]->name); - mdiobus_unregister(mdio_privdata->mdio_buses[i]); - } - } - if (mdio_privdata->mdio_kobj) { - sysfs_remove_group(mdio_privdata->mdio_kobj, - &mdio_privdata->mdio_bus_attr_group); - kobject_put(mdio_privdata->mdio_kobj); - mdio_privdata->mdio_kobj = NULL; - } - xa_erase(&mdio_drvdata_map, (unsigned long)pci_dev); -} - -static void pddf_multifpgapci_mdio_map_bar(struct pci_dev *pci_dev, - void __iomem *bar_base, - unsigned long bar_start, - unsigned long bar_len) -{ - struct mdio_bus_drvdata *mdio_privdata; - pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, - pci_name(pci_dev)); - unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); - mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); - if (!mdio_privdata) { - pddf_dbg(MULTIFPGA, - KERN_ERR - "[%s] unable to find mdio module data for device %s\n", - __FUNCTION__, pci_name(pci_dev)); - return; - } - struct mdio_bus_sysfs_vals *mdio_pddf_data = - &mdio_privdata->temp_sysfs_vals; - - mdio_privdata->ch_base_addr = bar_base + mdio_pddf_data->ch_base_offset; - mdio_privdata->num_virt_ch = mdio_pddf_data->num_virt_ch; - mdio_privdata->ch_size = mdio_pddf_data->ch_size; -} - -static void pddf_multifpgapci_mdio_unmap_bar(struct pci_dev *pci_dev, - void __iomem *bar_base, - unsigned long bar_start, - unsigned long bar_len) -{ - struct mdio_bus_drvdata *mdio_privdata; - pddf_dbg(MULTIFPGA, KERN_INFO "[%s] pci_dev %s\n", __FUNCTION__, - pci_name(pci_dev)); - unsigned dev_index = multifpgapci_get_pci_dev_index(pci_dev); - mdio_privdata = xa_load(&mdio_drvdata_map, dev_index); - if (!mdio_privdata) { - pddf_dbg(MULTIFPGA, - KERN_ERR - "[%s] unable to find mdio module data for device %s\n", - __FUNCTION__, pci_name(pci_dev)); - return; - } - mdio_privdata->ch_base_addr = NULL; -} - -static struct protocol_ops mdio_protocol_ops = { - .attach = pddf_multifpgapci_mdio_attach, - .detach = pddf_multifpgapci_mdio_detach, - .map_bar = pddf_multifpgapci_mdio_map_bar, - .unmap_bar = pddf_multifpgapci_mdio_unmap_bar, - .name = "mdio", -}; - -static int __init pddf_multifpgapci_mdio_init(void) -{ - pddf_dbg(MULTIFPGA, KERN_INFO "Loading MDIO protocol module\n"); - xa_init(&mdio_drvdata_map); - return multifpgapci_register_protocol("mdio", &mdio_protocol_ops); -} - -static void __exit pddf_multifpgapci_mdio_exit(void) -{ - pddf_dbg(MULTIFPGA, KERN_INFO "Unloading MDIO protocol module\n"); - multifpgapci_unregister_protocol("mdio"); - xa_destroy(&mdio_drvdata_map); -} - -module_init(pddf_multifpgapci_mdio_init); -module_exit(pddf_multifpgapci_mdio_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Nexthop Systems"); -MODULE_DESCRIPTION( - "PDDF MULTIFPGAPCI kernel module for registering MDIO buses."); diff --git a/platform/pddf/i2c/utils/pddfparse.py b/platform/pddf/i2c/utils/pddfparse.py index 607d04f7561d..7b4e04d7c17a 100755 --- a/platform/pddf/i2c/utils/pddfparse.py +++ b/platform/pddf/i2c/utils/pddfparse.py @@ -530,13 +530,7 @@ def create_multifpgapci_device(self, dev, ops): ret = self.create_device(dev['i2c']['dev_attr'], "pddf/devices/multifpgapci/{}/i2c".format(bdf), ops) if ret != 0: return create_ret.append(ret) - - # MDIO specific data store - if 'mdio' in dev: - ret = self.create_device(dev['mdio']['dev_attr'], "pddf/devices/multifpgapci/{}/mdio".format(bdf), ops) - if ret != 0: - return create_ret.append(ret) - + # TODO: add GPIO & SPI specific data stores cmd = "echo 'fpgapci_init' > /sys/kernel/pddf/devices/multifpgapci/{}/dev_ops".format(bdf) @@ -550,11 +544,6 @@ def create_multifpgapci_device(self, dev, ops): if ret != 0: return create_ret.append(ret) - if 'mdio' in dev: - ret = self.create_mdio_bus(bdf, dev['mdio'], ops) - if ret != 0: - return create_ret.append(ret) - if 'gpio' in dev: ret = self.create_gpio_device(bdf, dev['gpio'], ops) if ret != 0: @@ -584,34 +573,6 @@ def create_gpio_device(self, bdf, gpio_dev, ops): return create_ret.append(ret) - def create_mdio_bus(self, bdf, mdio_dev, ops): - for bus in range(int(mdio_dev['dev_attr']['num_virt_ch'], 16)): - cmd = "echo {} > /sys/kernel/pddf/devices/multifpgapci/{}/mdio/new_mdio_bus".format(bus, bdf) - ret = self.runcmd(cmd) - if ret != 0: - return ret - - return 0 - - def create_gpio_device(self, bdf, gpio_dev, ops): - for line in gpio_dev.keys(): - for attr in gpio_dev[line]['attr_list']: - ret = self.create_device(attr, "pddf/devices/multifpgapci/{}/gpio/line".format(bdf), ops) - if ret != 0: - return ret - - cmd = "echo 'init' > /sys/kernel/pddf/devices/multifpgapci/{}/gpio/line/create_line".format(bdf) - ret = self.runcmd(cmd) - if ret != 0: - return ret - - cmd = "echo 'init' > /sys/kernel/pddf/devices/multifpgapci/{}/gpio/create_chip".format(bdf) - ret = self.runcmd(cmd) - if ret != 0: - return ret - - return 0 - ################################################################################################################################# # DELETE DEFS ###################################################################################################################