Message ID | 1511764948-20972-10-git-send-email-hao.wu@intel.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao <hao.wu@intel.com> wrote: > +/* enumerate feature devices under pci device */ > +static int cci_enumerate_feature_devs(struct pci_dev *pcidev) > +{ > + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); > + struct fpga_cdev *cdev; > + struct fpga_enum_info *info; > + resource_size_t start, len; > + void __iomem *base; > + int port_num, bar, i, ret = 0; > + u32 offset; > + u64 v; > + > + /* allocate enumeration info via pci_dev */ > + info = fpga_enum_info_alloc(&pcidev->dev); > + if (!info) > + return -ENOMEM; > + > + /* start to find Device Feature List from Bar 0 */ > + base = cci_pci_ioremap_bar(pcidev, 0); > + if (!base) { > + ret = -ENOMEM; > + goto enum_info_free_exit; > + } > + > + /* > + * PF device has FME and Ports/AFUs, and VF device only has 1 Port/AFU. > + * check them and add related "Device Feature List" info for the next > + * step enumeration. > + */ > + if (feature_is_fme(base)) { > + start = pci_resource_start(pcidev, 0); > + len = pci_resource_len(pcidev, 0); > + > + fpga_enum_info_add_dfl(info, start, len, base); > + > + /* > + * find more Device Feature Lists (e.g Ports) per information > + * indicated by FME module. > + */ > + v = readq(base + FME_HDR_CAP); > + port_num = FIELD_GET(FME_CAP_NUM_PORTS, v); > + > + WARN_ON(port_num > MAX_FPGA_PORT_NUM); > + > + for (i = 0; i < port_num; i++) { > + v = readq(base + FME_HDR_PORT_OFST(i)); > + > + /* skip ports which are not implemented. */ > + if (!(v & FME_PORT_OFST_IMP)) > + continue; > + > + /* > + * add Port's Device Feature List information for next > + * step enumeration. > + */ > + bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v); > + offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v); > + base = cci_pci_ioremap_bar(pcidev, bar); > + if (!base) > + continue; > + > + start = pci_resource_start(pcidev, bar) + offset; > + len = pci_resource_len(pcidev, bar) - offset; > + > + fpga_enum_info_add_dfl(info, start, len, base + offset); > + } > + } else if (feature_is_port(base)) { > + start = pci_resource_start(pcidev, 0); > + len = pci_resource_len(pcidev, 0); > + > + fpga_enum_info_add_dfl(info, start, len, base); > + } else { > + ret = -ENODEV; > + goto enum_info_free_exit; > + } > + > + /* start enumeration with prepared enumeration information */ > + cdev = fpga_enumerate_feature_devs(info); Hi Hao, I appreciate you separating the DFL enumeration code from this PCIe module. This made the pcie part quite small. It should work for embedded platforms just by adding a platform device whose function is to find the DFL structures at some address and then call these same fpga_enum_info_add_dfl adn fpga_enumerate_feature_devs functions. Alan > + if (IS_ERR(cdev)) { > + dev_err(&pcidev->dev, "Enumeration failure\n"); > + ret = PTR_ERR(cdev); > + goto enum_info_free_exit; > + } > + > + drvdata->cdev = cdev; > + > +enum_info_free_exit: > + fpga_enum_info_free(info); > + > + return ret; > +} > + > static > int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) > { > @@ -84,9 +264,22 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) > goto release_region_exit; > } > > - /* TODO: create and add the platform device per feature list */ > - return 0; > + ret = cci_init_drvdata(pcidev); > + if (ret) { > + dev_err(&pcidev->dev, "Fail to init drvdata %d.\n", ret); > + goto release_region_exit; > + } > + > + ret = cci_enumerate_feature_devs(pcidev); > + if (ret) { > + dev_err(&pcidev->dev, "enumeration failure %d.\n", ret); > + goto remove_drvdata_exit; > + } > + > + return ret; > > +remove_drvdata_exit: > + cci_remove_drvdata(pcidev); > release_region_exit: > pci_release_regions(pcidev); > disable_error_report_exit: > @@ -97,6 +290,8 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) > > static void cci_pci_remove(struct pci_dev *pcidev) > { > + cci_remove_feature_devs(pcidev); > + cci_remove_drvdata(pcidev); > pci_release_regions(pcidev); > pci_disable_pcie_error_reporting(pcidev); > pci_disable_device(pcidev); > -- > 1.8.3.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-fpga" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Dec 07, 2017 at 03:41:41PM -0600, Alan Tull wrote: > On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao <hao.wu@intel.com> wrote: > > > +/* enumerate feature devices under pci device */ > > +static int cci_enumerate_feature_devs(struct pci_dev *pcidev) > > +{ > > + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); > > + struct fpga_cdev *cdev; > > + struct fpga_enum_info *info; > > + resource_size_t start, len; > > + void __iomem *base; > > + int port_num, bar, i, ret = 0; > > + u32 offset; > > + u64 v; > > + > > + /* allocate enumeration info via pci_dev */ > > + info = fpga_enum_info_alloc(&pcidev->dev); > > + if (!info) > > + return -ENOMEM; > > + > > + /* start to find Device Feature List from Bar 0 */ > > + base = cci_pci_ioremap_bar(pcidev, 0); > > + if (!base) { > > + ret = -ENOMEM; > > + goto enum_info_free_exit; > > + } > > + > > + /* > > + * PF device has FME and Ports/AFUs, and VF device only has 1 Port/AFU. > > + * check them and add related "Device Feature List" info for the next > > + * step enumeration. > > + */ > > + if (feature_is_fme(base)) { > > + start = pci_resource_start(pcidev, 0); > > + len = pci_resource_len(pcidev, 0); > > + > > + fpga_enum_info_add_dfl(info, start, len, base); > > + > > + /* > > + * find more Device Feature Lists (e.g Ports) per information > > + * indicated by FME module. > > + */ > > + v = readq(base + FME_HDR_CAP); > > + port_num = FIELD_GET(FME_CAP_NUM_PORTS, v); > > + > > + WARN_ON(port_num > MAX_FPGA_PORT_NUM); > > + > > + for (i = 0; i < port_num; i++) { > > + v = readq(base + FME_HDR_PORT_OFST(i)); > > + > > + /* skip ports which are not implemented. */ > > + if (!(v & FME_PORT_OFST_IMP)) > > + continue; > > + > > + /* > > + * add Port's Device Feature List information for next > > + * step enumeration. > > + */ > > + bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v); > > + offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v); > > + base = cci_pci_ioremap_bar(pcidev, bar); > > + if (!base) > > + continue; > > + > > + start = pci_resource_start(pcidev, bar) + offset; > > + len = pci_resource_len(pcidev, bar) - offset; > > + > > + fpga_enum_info_add_dfl(info, start, len, base + offset); > > + } > > + } else if (feature_is_port(base)) { > > + start = pci_resource_start(pcidev, 0); > > + len = pci_resource_len(pcidev, 0); > > + > > + fpga_enum_info_add_dfl(info, start, len, base); > > + } else { > > + ret = -ENODEV; > > + goto enum_info_free_exit; > > + } > > + > > + /* start enumeration with prepared enumeration information */ > > + cdev = fpga_enumerate_feature_devs(info); > > Hi Hao, > > I appreciate you separating the DFL enumeration code from this PCIe > module. This made the pcie part quite small. It should work for > embedded platforms just by adding a platform device whose function is > to find the DFL structures at some address and then call these same > fpga_enum_info_add_dfl adn fpga_enumerate_feature_devs functions. Yes, I think this is the right direction as you suggested. : ) Thanks Hao > > Alan -- To unsubscribe from this list: send the line "unsubscribe linux-fpga" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/fpga/intel-dfl-pci.c b/drivers/fpga/intel-dfl-pci.c index 4774a77..1c7ba22 100644 --- a/drivers/fpga/intel-dfl-pci.c +++ b/drivers/fpga/intel-dfl-pci.c @@ -24,9 +24,52 @@ #include <linux/errno.h> #include <linux/aer.h> +#include "fpga-dfl.h" + #define DRV_VERSION "0.8" #define DRV_NAME "intel-dfl-pci" +struct cci_drvdata { + struct fpga_cdev *cdev; /* container device */ + struct list_head regions; /* list of pci bar mapping region */ +}; + +/* pci bar mapping info */ +struct cci_region { + int bar; + void __iomem *ioaddr; /* pointer to mapped bar region */ + struct list_head node; +}; + +static void __iomem *cci_pci_ioremap_bar(struct pci_dev *pcidev, int bar) +{ + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); + struct cci_region *region; + + list_for_each_entry(region, &drvdata->regions, node) + if (region->bar == bar) { + dev_dbg(&pcidev->dev, "BAR %d region exists\n", bar); + return region->ioaddr; + } + + region = devm_kzalloc(&pcidev->dev, sizeof(*region), GFP_KERNEL); + if (!region) + return NULL; + + region->bar = bar; + region->ioaddr = pci_ioremap_bar(pcidev, bar); + if (!region->ioaddr) { + dev_err(&pcidev->dev, "can't ioremap memory from BAR %d.\n", + bar); + devm_kfree(&pcidev->dev, region); + return NULL; + } + + list_add(®ion->node, &drvdata->regions); + + return region->ioaddr; +} + /* PCI Device ID */ #define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD #define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0 @@ -47,6 +90,143 @@ }; MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl); +static int cci_init_drvdata(struct pci_dev *pcidev) +{ + struct cci_drvdata *drvdata; + + drvdata = devm_kzalloc(&pcidev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + INIT_LIST_HEAD(&drvdata->regions); + + pci_set_drvdata(pcidev, drvdata); + + return 0; +} + +static void cci_pci_release_regions(struct pci_dev *pcidev) +{ + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); + struct cci_region *tmp, *region; + + list_for_each_entry_safe(region, tmp, &drvdata->regions, node) { + list_del(®ion->node); + if (region->ioaddr) + pci_iounmap(pcidev, region->ioaddr); + devm_kfree(&pcidev->dev, region); + } +} + +static void cci_remove_drvdata(struct pci_dev *pcidev) +{ + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); + + cci_pci_release_regions(pcidev); + pci_set_drvdata(pcidev, NULL); + devm_kfree(&pcidev->dev, drvdata); +} + +static void cci_remove_feature_devs(struct pci_dev *pcidev) +{ + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); + + /* remove all children feature devices */ + fpga_remove_feature_devs(drvdata->cdev); +} + +/* enumerate feature devices under pci device */ +static int cci_enumerate_feature_devs(struct pci_dev *pcidev) +{ + struct cci_drvdata *drvdata = pci_get_drvdata(pcidev); + struct fpga_cdev *cdev; + struct fpga_enum_info *info; + resource_size_t start, len; + void __iomem *base; + int port_num, bar, i, ret = 0; + u32 offset; + u64 v; + + /* allocate enumeration info via pci_dev */ + info = fpga_enum_info_alloc(&pcidev->dev); + if (!info) + return -ENOMEM; + + /* start to find Device Feature List from Bar 0 */ + base = cci_pci_ioremap_bar(pcidev, 0); + if (!base) { + ret = -ENOMEM; + goto enum_info_free_exit; + } + + /* + * PF device has FME and Ports/AFUs, and VF device only has 1 Port/AFU. + * check them and add related "Device Feature List" info for the next + * step enumeration. + */ + if (feature_is_fme(base)) { + start = pci_resource_start(pcidev, 0); + len = pci_resource_len(pcidev, 0); + + fpga_enum_info_add_dfl(info, start, len, base); + + /* + * find more Device Feature Lists (e.g Ports) per information + * indicated by FME module. + */ + v = readq(base + FME_HDR_CAP); + port_num = FIELD_GET(FME_CAP_NUM_PORTS, v); + + WARN_ON(port_num > MAX_FPGA_PORT_NUM); + + for (i = 0; i < port_num; i++) { + v = readq(base + FME_HDR_PORT_OFST(i)); + + /* skip ports which are not implemented. */ + if (!(v & FME_PORT_OFST_IMP)) + continue; + + /* + * add Port's Device Feature List information for next + * step enumeration. + */ + bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v); + offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v); + base = cci_pci_ioremap_bar(pcidev, bar); + if (!base) + continue; + + start = pci_resource_start(pcidev, bar) + offset; + len = pci_resource_len(pcidev, bar) - offset; + + fpga_enum_info_add_dfl(info, start, len, base + offset); + } + } else if (feature_is_port(base)) { + start = pci_resource_start(pcidev, 0); + len = pci_resource_len(pcidev, 0); + + fpga_enum_info_add_dfl(info, start, len, base); + } else { + ret = -ENODEV; + goto enum_info_free_exit; + } + + /* start enumeration with prepared enumeration information */ + cdev = fpga_enumerate_feature_devs(info); + if (IS_ERR(cdev)) { + dev_err(&pcidev->dev, "Enumeration failure\n"); + ret = PTR_ERR(cdev); + goto enum_info_free_exit; + } + + drvdata->cdev = cdev; + +enum_info_free_exit: + fpga_enum_info_free(info); + + return ret; +} + static int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) { @@ -84,9 +264,22 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) goto release_region_exit; } - /* TODO: create and add the platform device per feature list */ - return 0; + ret = cci_init_drvdata(pcidev); + if (ret) { + dev_err(&pcidev->dev, "Fail to init drvdata %d.\n", ret); + goto release_region_exit; + } + + ret = cci_enumerate_feature_devs(pcidev); + if (ret) { + dev_err(&pcidev->dev, "enumeration failure %d.\n", ret); + goto remove_drvdata_exit; + } + + return ret; +remove_drvdata_exit: + cci_remove_drvdata(pcidev); release_region_exit: pci_release_regions(pcidev); disable_error_report_exit: @@ -97,6 +290,8 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid) static void cci_pci_remove(struct pci_dev *pcidev) { + cci_remove_feature_devs(pcidev); + cci_remove_drvdata(pcidev); pci_release_regions(pcidev); pci_disable_pcie_error_reporting(pcidev); pci_disable_device(pcidev);