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 38d1cc5dc18a..4f5a4d8dcc98 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 @@ -37,6 +37,8 @@ "optoe" ], "pddf_kos": [ + "pddf_multifpgapci_i2c_module", + "pddf_multifpgapci_gpio_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..a43dd8d5c73a 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,60 @@ 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); + // Attach all registered protocols to this new FPGA + attach_protocols_for_fpga(dev, fpga_data->kobj); + + 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__); - goto free_gpio_dirs; + KERN_ERR "[%s] create pddf_clients_data_group failed: %d\n", + __FUNCTION__, ret); + goto free_fpga_kobj; } 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] create pddf_clients_data_group failed: %d\n", + KERN_ERR "[%s] sysfs_create_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: - 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); - + fpga_data->fpga_attr_group_initialized = false; 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 +406,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 +509,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 +542,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 +578,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 +699,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 +712,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 +1110,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 +1120,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.");