Add support to add virtual function in endpoint core. The virtual
function can only be associated with a physical function instead of a
endpoint controller. Provide APIs to associate a virtual function with
a physical function here.

Signed-off-by: Kishon Vijay Abraham I <kis...@ti.com>
---
 drivers/pci/endpoint/pci-epc-core.c |  2 +-
 drivers/pci/endpoint/pci-epf-core.c | 96 ++++++++++++++++++++++++++++-
 include/linux/pci-epf.h             | 16 ++++-
 3 files changed, 111 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/endpoint/pci-epc-core.c 
b/drivers/pci/endpoint/pci-epc-core.c
index cc8f9eb2b177..fe6d23849502 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -548,7 +548,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf 
*epf,
        u32 func_no;
        int ret = 0;
 
-       if (IS_ERR_OR_NULL(epc))
+       if (IS_ERR_OR_NULL(epc) || epf->is_vf)
                return -EINVAL;
 
        if (type == PRIMARY_INTERFACE && epf->epc)
diff --git a/drivers/pci/endpoint/pci-epf-core.c 
b/drivers/pci/endpoint/pci-epf-core.c
index 7646c8660d42..3f1271a63be5 100644
--- a/drivers/pci/endpoint/pci-epf-core.c
+++ b/drivers/pci/endpoint/pci-epf-core.c
@@ -62,13 +62,20 @@ EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs);
  */
 void pci_epf_unbind(struct pci_epf *epf)
 {
+       struct pci_epf *epf_vf;
+
        if (!epf->driver) {
                dev_WARN(&epf->dev, "epf device not bound to driver\n");
                return;
        }
 
        mutex_lock(&epf->lock);
-       epf->driver->ops->unbind(epf);
+       list_for_each_entry(epf_vf, &epf->pci_vepf, list) {
+               if (epf_vf->is_bound)
+                       epf_vf->driver->ops->unbind(epf_vf);
+       }
+       if (epf->is_bound)
+               epf->driver->ops->unbind(epf);
        mutex_unlock(&epf->lock);
        module_put(epf->driver->owner);
 }
@@ -83,6 +90,7 @@ EXPORT_SYMBOL_GPL(pci_epf_unbind);
  */
 int pci_epf_bind(struct pci_epf *epf)
 {
+       struct pci_epf *epf_vf;
        int ret;
 
        if (!epf->driver) {
@@ -94,13 +102,95 @@ int pci_epf_bind(struct pci_epf *epf)
                return -EAGAIN;
 
        mutex_lock(&epf->lock);
+       list_for_each_entry(epf_vf, &epf->pci_vepf, list) {
+               epf_vf->func_no = epf->func_no;
+               epf_vf->epc = epf->epc;
+               epf_vf->sec_epc = epf->sec_epc;
+               ret = epf_vf->driver->ops->bind(epf_vf);
+               if (ret)
+                       goto ret;
+               epf_vf->is_bound = true;
+       }
+
        ret = epf->driver->ops->bind(epf);
+       if (ret)
+               goto ret;
+       epf->is_bound = true;
+
+       mutex_unlock(&epf->lock);
+       return 0;
+
+ret:
        mutex_unlock(&epf->lock);
+       pci_epf_unbind(epf);
 
        return ret;
 }
 EXPORT_SYMBOL_GPL(pci_epf_bind);
 
+/**
+ * pci_epf_add_vepf() - associate virtual EP function to physical EP function
+ * @epf_pf: the physical EP function to which the virtual EP function should be
+ *   associated
+ * @epf_vf: the virtual EP function to be added
+ *
+ * A physical endpoint function can be associated with multiple virtual
+ * endpoint functions. Invoke pci_epf_add_epf() to add a virtual PCI endpoint
+ * function to a physical PCI endpoint function.
+ */
+int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf)
+{
+       u32 vfunc_no;
+
+       if (IS_ERR_OR_NULL(epf_pf) || IS_ERR_OR_NULL(epf_vf))
+               return -EINVAL;
+
+       if (epf_pf->epc || epf_vf->epc || epf_vf->epf_pf)
+               return -EBUSY;
+
+       if (epf_pf->sec_epc || epf_vf->sec_epc)
+               return -EBUSY;
+
+       mutex_lock(&epf_pf->lock);
+       vfunc_no = find_first_zero_bit(&epf_pf->vfunction_num_map,
+                                      BITS_PER_LONG);
+       if (vfunc_no >= BITS_PER_LONG)
+               return -EINVAL;
+
+       set_bit(vfunc_no, &epf_pf->vfunction_num_map);
+       epf_vf->vfunc_no = vfunc_no;
+
+       epf_vf->epf_pf = epf_pf;
+       epf_vf->is_vf = true;
+
+       list_add_tail(&epf_vf->list, &epf_pf->pci_vepf);
+       mutex_unlock(&epf_pf->lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epf_add_vepf);
+
+/**
+ * pci_epf_remove_vepf() - remove virtual EP function from physical EP function
+ * @epf_pf: the physical EP function from which the virtual EP function should
+ *   be removed
+ * @epf_vf: the virtual EP function to be removed
+ *
+ * Invoke to remove a virtual endpoint function from the physcial endpoint
+ * function.
+ */
+void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf)
+{
+       if (IS_ERR_OR_NULL(epf_pf) || IS_ERR_OR_NULL(epf_vf))
+               return;
+
+       mutex_lock(&epf_pf->lock);
+       clear_bit(epf_vf->vfunc_no, &epf_pf->vfunction_num_map);
+       list_del(&epf_vf->list);
+       mutex_unlock(&epf_pf->lock);
+}
+EXPORT_SYMBOL_GPL(pci_epf_remove_vepf);
+
 /**
  * pci_epf_free_space() - free the allocated PCI EPF register space
  * @epf: the EPF device from whom to free the memory
@@ -317,6 +407,10 @@ struct pci_epf *pci_epf_create(const char *name)
                return ERR_PTR(-ENOMEM);
        }
 
+       /* VFs are numbered starting with 1. So set BIT(0) by default */
+       epf->vfunction_num_map = 1;
+       INIT_LIST_HEAD(&epf->pci_vepf);
+
        dev = &epf->dev;
        device_initialize(dev);
        dev->bus = &pci_epf_bus_type;
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index 6833e2160ef1..1de8fb72ce20 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -118,8 +118,10 @@ struct pci_epf_bar {
  * @header: represents standard configuration header
  * @bar: represents the BAR of EPF device
  * @msi_interrupts: number of MSI interrupts required by this function
- * @func_no: unique function number within this endpoint device
+ * @func_no: unique (physical) function number within this endpoint device
+ * @vfunc_no: unique virtual function number within a physical function
  * @epc: the EPC device to which this EPF device is bound
+ * @epf_pf: the physical EPF device to which this virtual EPF device is bound
  * @driver: the EPF driver to which this EPF device is bound
  * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc
  * @nb: notifier block to notify EPF of any EPC events (like linkup)
@@ -130,6 +132,10 @@ struct pci_epf_bar {
  * @sec_epc_bar: represents the BAR of EPF device associated with secondary EPC
  * @sec_epc_func_no: unique (physical) function number within the secondary EPC
  * @group: configfs group associated with the EPF device
+ * @is_bound: indicates if bind notification to function driver has been 
invoked
+ * @is_vf: true - virtual function, false - physical function
+ * @vfunction_num_map: bitmap to manage virtual function number
+ * @pci_vepf: list of virtual endpoint functions associated with this function
  */
 struct pci_epf {
        struct device           dev;
@@ -139,8 +145,10 @@ struct pci_epf {
        u8                      msi_interrupts;
        u16                     msix_interrupts;
        u8                      func_no;
+       u8                      vfunc_no;
 
        struct pci_epc          *epc;
+       struct pci_epf          *epf_pf;
        struct pci_epf_driver   *driver;
        struct list_head        list;
        struct notifier_block   nb;
@@ -153,6 +161,10 @@ struct pci_epf {
        struct pci_epf_bar      sec_epc_bar[6];
        u8                      sec_epc_func_no;
        struct config_group     *group;
+       unsigned int            is_bound;
+       unsigned int            is_vf;
+       unsigned long           vfunction_num_map;
+       struct list_head        pci_vepf;
 };
 
 /**
@@ -196,4 +208,6 @@ int pci_epf_bind(struct pci_epf *epf);
 void pci_epf_unbind(struct pci_epf *epf);
 struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf,
                                          struct config_group *group);
+int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf);
+void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf);
 #endif /* __LINUX_PCI_EPF_H */
-- 
2.17.1

Reply via email to