]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
PCI: endpoint: Add support to associate secondary EPC with EPF
authorKishon Vijay Abraham I <kishon@ti.com>
Mon, 1 Feb 2021 19:57:58 +0000 (01:27 +0530)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 23 Feb 2021 20:10:51 +0000 (14:10 -0600)
In the case of standard endpoint functions, only one endpoint controller
(EPC) will be associated with an endpoint function (EPF). However for
providing NTB (non transparent bridge) functionality, two EPCs should be
associated with a single EPF.  Add support to associate secondary EPC with
EPF. This is in preparation for adding NTB endpoint function driver.

Link: https://lore.kernel.org/r/20210201195809.7342-7-kishon@ti.com
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/endpoint/functions/pci-epf-test.c
drivers/pci/endpoint/pci-ep-cfs.c
drivers/pci/endpoint/pci-epc-core.c
drivers/pci/endpoint/pci-epf-core.c
include/linux/pci-epc.h
include/linux/pci-epf.h

index 7a1f3abfde4847f7c55ae95ccb8bb91bb4b55022..c0ac4e9cbe72fe672b160336ce562dc85108a2f2 100644 (file)
@@ -619,7 +619,8 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
 
                if (epf_test->reg[bar]) {
                        pci_epc_clear_bar(epc, epf->func_no, epf_bar);
-                       pci_epf_free_space(epf, epf_test->reg[bar], bar);
+                       pci_epf_free_space(epf, epf_test->reg[bar], bar,
+                                          PRIMARY_INTERFACE);
                }
        }
 }
@@ -651,7 +652,8 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
 
                ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
                if (ret) {
-                       pci_epf_free_space(epf, epf_test->reg[bar], bar);
+                       pci_epf_free_space(epf, epf_test->reg[bar], bar,
+                                          PRIMARY_INTERFACE);
                        dev_err(dev, "Failed to set BAR%d\n", bar);
                        if (bar == test_reg_bar)
                                return ret;
@@ -771,7 +773,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
        }
 
        base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar,
-                                  epc_features->align);
+                                  epc_features->align, PRIMARY_INTERFACE);
        if (!base) {
                dev_err(dev, "Failed to allocated register space\n");
                return -ENOMEM;
@@ -789,7 +791,8 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
                        continue;
 
                base = pci_epf_alloc_space(epf, bar_size[bar], bar,
-                                          epc_features->align);
+                                          epc_features->align,
+                                          PRIMARY_INTERFACE);
                if (!base)
                        dev_err(dev, "Failed to allocate space for BAR%d\n",
                                bar);
index 3710adf51912de68207fa021ee5325eb8af9782a..6ca9e2f92460b79567f00d632f8dc7257f43e60d 100644 (file)
@@ -94,13 +94,13 @@ static int pci_epc_epf_link(struct config_item *epc_item,
        struct pci_epc *epc = epc_group->epc;
        struct pci_epf *epf = epf_group->epf;
 
-       ret = pci_epc_add_epf(epc, epf);
+       ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE);
        if (ret)
                return ret;
 
        ret = pci_epf_bind(epf);
        if (ret) {
-               pci_epc_remove_epf(epc, epf);
+               pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
                return ret;
        }
 
@@ -120,7 +120,7 @@ static void pci_epc_epf_unlink(struct config_item *epc_item,
        epc = epc_group->epc;
        epf = epf_group->epf;
        pci_epf_unbind(epf);
-       pci_epc_remove_epf(epc, epf);
+       pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
 }
 
 static struct configfs_item_operations pci_epc_item_ops = {
index ea7e7465ce7a6b5ac477e14d0bd1ef27cc26e757..3693eca5b0302ba91fdd1542e2477c5446a823a8 100644 (file)
@@ -493,21 +493,28 @@ EXPORT_SYMBOL_GPL(pci_epc_write_header);
  * pci_epc_add_epf() - bind PCI endpoint function to an endpoint controller
  * @epc: the EPC device to which the endpoint function should be added
  * @epf: the endpoint function to be added
+ * @type: Identifies if the EPC is connected to the primary or secondary
+ *        interface of EPF
  *
  * A PCI endpoint device can have one or more functions. In the case of PCIe,
  * the specification allows up to 8 PCIe endpoint functions. Invoke
  * pci_epc_add_epf() to add a PCI endpoint function to an endpoint controller.
  */
-int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf)
+int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
+                   enum pci_epc_interface_type type)
 {
+       struct list_head *list;
        u32 func_no;
        int ret = 0;
 
-       if (epf->epc)
+       if (IS_ERR_OR_NULL(epc))
+               return -EINVAL;
+
+       if (type == PRIMARY_INTERFACE && epf->epc)
                return -EBUSY;
 
-       if (IS_ERR(epc))
-               return -EINVAL;
+       if (type == SECONDARY_INTERFACE && epf->sec_epc)
+               return -EBUSY;
 
        mutex_lock(&epc->lock);
        func_no = find_first_zero_bit(&epc->function_num_map,
@@ -524,11 +531,17 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf)
        }
 
        set_bit(func_no, &epc->function_num_map);
-       epf->func_no = func_no;
-       epf->epc = epc;
-
-       list_add_tail(&epf->list, &epc->pci_epf);
+       if (type == PRIMARY_INTERFACE) {
+               epf->func_no = func_no;
+               epf->epc = epc;
+               list = &epf->list;
+       } else {
+               epf->sec_epc_func_no = func_no;
+               epf->sec_epc = epc;
+               list = &epf->sec_epc_list;
+       }
 
+       list_add_tail(list, &epc->pci_epf);
 ret:
        mutex_unlock(&epc->lock);
 
@@ -543,14 +556,26 @@ EXPORT_SYMBOL_GPL(pci_epc_add_epf);
  *
  * Invoke to remove PCI endpoint function from the endpoint controller.
  */
-void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf)
+void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
+                       enum pci_epc_interface_type type)
 {
+       struct list_head *list;
+       u32 func_no = 0;
+
        if (!epc || IS_ERR(epc) || !epf)
                return;
 
+       if (type == PRIMARY_INTERFACE) {
+               func_no = epf->func_no;
+               list = &epf->list;
+       } else {
+               func_no = epf->sec_epc_func_no;
+               list = &epf->sec_epc_list;
+       }
+
        mutex_lock(&epc->lock);
-       clear_bit(epf->func_no, &epc->function_num_map);
-       list_del(&epf->list);
+       clear_bit(func_no, &epc->function_num_map);
+       list_del(list);
        epf->epc = NULL;
        mutex_unlock(&epc->lock);
 }
index e44a317a2a2aaaac484c8c6c1e614fdb565054f7..79329ec6373c37609b95fbb199fa414784fed31d 100644 (file)
@@ -74,24 +74,37 @@ EXPORT_SYMBOL_GPL(pci_epf_bind);
  * @epf: the EPF device from whom to free the memory
  * @addr: the virtual address of the PCI EPF register space
  * @bar: the BAR number corresponding to the register space
+ * @type: Identifies if the allocated space is for primary EPC or secondary EPC
  *
  * Invoke to free the allocated PCI EPF register space.
  */
-void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
+void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,
+                       enum pci_epc_interface_type type)
 {
        struct device *dev = epf->epc->dev.parent;
+       struct pci_epf_bar *epf_bar;
+       struct pci_epc *epc;
 
        if (!addr)
                return;
 
-       dma_free_coherent(dev, epf->bar[bar].size, addr,
-                         epf->bar[bar].phys_addr);
+       if (type == PRIMARY_INTERFACE) {
+               epc = epf->epc;
+               epf_bar = epf->bar;
+       } else {
+               epc = epf->sec_epc;
+               epf_bar = epf->sec_epc_bar;
+       }
+
+       dev = epc->dev.parent;
+       dma_free_coherent(dev, epf_bar[bar].size, addr,
+                         epf_bar[bar].phys_addr);
 
-       epf->bar[bar].phys_addr = 0;
-       epf->bar[bar].addr = NULL;
-       epf->bar[bar].size = 0;
-       epf->bar[bar].barno = 0;
-       epf->bar[bar].flags = 0;
+       epf_bar[bar].phys_addr = 0;
+       epf_bar[bar].addr = NULL;
+       epf_bar[bar].size = 0;
+       epf_bar[bar].barno = 0;
+       epf_bar[bar].flags = 0;
 }
 EXPORT_SYMBOL_GPL(pci_epf_free_space);
 
@@ -101,15 +114,18 @@ EXPORT_SYMBOL_GPL(pci_epf_free_space);
  * @size: the size of the memory that has to be allocated
  * @bar: the BAR number corresponding to the allocated register space
  * @align: alignment size for the allocation region
+ * @type: Identifies if the allocation is for primary EPC or secondary EPC
  *
  * Invoke to allocate memory for the PCI EPF register space.
  */
 void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
-                         size_t align)
+                         size_t align, enum pci_epc_interface_type type)
 {
-       void *space;
-       struct device *dev = epf->epc->dev.parent;
+       struct pci_epf_bar *epf_bar;
        dma_addr_t phys_addr;
+       struct pci_epc *epc;
+       struct device *dev;
+       void *space;
 
        if (size < 128)
                size = 128;
@@ -119,17 +135,26 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
        else
                size = roundup_pow_of_two(size);
 
+       if (type == PRIMARY_INTERFACE) {
+               epc = epf->epc;
+               epf_bar = epf->bar;
+       } else {
+               epc = epf->sec_epc;
+               epf_bar = epf->sec_epc_bar;
+       }
+
+       dev = epc->dev.parent;
        space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
        if (!space) {
                dev_err(dev, "failed to allocate mem space\n");
                return NULL;
        }
 
-       epf->bar[bar].phys_addr = phys_addr;
-       epf->bar[bar].addr = space;
-       epf->bar[bar].size = size;
-       epf->bar[bar].barno = bar;
-       epf->bar[bar].flags |= upper_32_bits(size) ?
+       epf_bar[bar].phys_addr = phys_addr;
+       epf_bar[bar].addr = space;
+       epf_bar[bar].size = size;
+       epf_bar[bar].barno = bar;
+       epf_bar[bar].flags |= upper_32_bits(size) ?
                                PCI_BASE_ADDRESS_MEM_TYPE_64 :
                                PCI_BASE_ADDRESS_MEM_TYPE_32;
 
index 88d311bad9846dc65c1abb952c471e2463db717d..d9cb3944fb8738a845ffb48f52ad31435e0edfe1 100644 (file)
 
 struct pci_epc;
 
+enum pci_epc_interface_type {
+       UNKNOWN_INTERFACE = -1,
+       PRIMARY_INTERFACE,
+       SECONDARY_INTERFACE,
+};
+
 enum pci_epc_irq_type {
        PCI_EPC_IRQ_UNKNOWN,
        PCI_EPC_IRQ_LEGACY,
@@ -20,6 +26,19 @@ enum pci_epc_irq_type {
        PCI_EPC_IRQ_MSIX,
 };
 
+static inline const char *
+pci_epc_interface_string(enum pci_epc_interface_type type)
+{
+       switch (type) {
+       case PRIMARY_INTERFACE:
+               return "primary";
+       case SECONDARY_INTERFACE:
+               return "secondary";
+       default:
+               return "UNKNOWN interface";
+       }
+}
+
 /**
  * struct pci_epc_ops - set of function pointers for performing EPC operations
  * @write_header: ops to populate configuration space header
@@ -175,10 +194,12 @@ __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
                 struct module *owner);
 void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc);
 void pci_epc_destroy(struct pci_epc *epc);
-int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf);
+int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
+                   enum pci_epc_interface_type type);
 void pci_epc_linkup(struct pci_epc *epc);
 void pci_epc_init_notify(struct pci_epc *epc);
-void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf);
+void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
+                       enum pci_epc_interface_type type);
 int pci_epc_write_header(struct pci_epc *epc, u8 func_no,
                         struct pci_epf_header *hdr);
 int pci_epc_set_bar(struct pci_epc *epc, u8 func_no,
index f373a134ac048257d761ef785dc72cce659d9474..1dc66824f5a84d5e3bcceb93f6b6d422c2c5b28c 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pci.h>
 
 struct pci_epf;
+enum pci_epc_interface_type;
 
 enum pci_notify_event {
        CORE_INIT,
@@ -119,6 +120,11 @@ struct pci_epf_bar {
  * @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)
  * @lock: mutex to protect pci_epf_ops
+ * @sec_epc: the secondary EPC device to which this EPF device is bound
+ * @sec_epc_list: to add pci_epf as list of PCI endpoint functions to secondary
+ *   EPC device
+ * @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
  */
 struct pci_epf {
        struct device           dev;
@@ -135,6 +141,12 @@ struct pci_epf {
        struct notifier_block   nb;
        /* mutex to protect against concurrent access of pci_epf_ops */
        struct mutex            lock;
+
+       /* Below members are to attach secondary EPC to an endpoint function */
+       struct pci_epc          *sec_epc;
+       struct list_head        sec_epc_list;
+       struct pci_epf_bar      sec_epc_bar[6];
+       u8                      sec_epc_func_no;
 };
 
 /**
@@ -171,8 +183,9 @@ int __pci_epf_register_driver(struct pci_epf_driver *driver,
                              struct module *owner);
 void pci_epf_unregister_driver(struct pci_epf_driver *driver);
 void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
-                         size_t align);
-void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar);
+                         size_t align, enum pci_epc_interface_type type);
+void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,
+                       enum pci_epc_interface_type type);
 int pci_epf_bind(struct pci_epf *epf);
 void pci_epf_unbind(struct pci_epf *epf);
 #endif /* __LINUX_PCI_EPF_H */