]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: endpoint: Introduce 'epc_deinit' event and notify the EPF drivers
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Thu, 6 Jun 2024 07:26:35 +0000 (12:56 +0530)
committerKrzysztof Wilczyński <kwilczynski@kernel.org>
Thu, 4 Jul 2024 14:40:53 +0000 (14:40 +0000)
As like the 'epc_init' event, that is used to signal the EPF drivers about
the EPC initialization, let's introduce 'epc_deinit' event that is used to
signal EPC deinitialization.

The EPC deinitialization applies only when any sort of fundamental reset
is supported by the endpoint controller as per the PCIe spec.

Reference: PCIe r6.0, sec 4.2.5.9.1 and 6.6.1.

Currently, some EPC drivers like pcie-qcom-ep and pcie-tegra194 support
PERST# as the fundamental reset. So the 'deinit' event will be notified to
the EPF drivers when PERST# assert happens in the above mentioned EPC
drivers.

The EPF drivers, on receiving the event through the epc_deinit() callback
should reset the EPF state machine and also cleanup any configuration that
got affected by the fundamental reset like BAR, DMA etc...

This change also warrants skipping the cleanups in unbind() if already done
in epc_deinit().

Link: https://lore.kernel.org/r/20240606-pci-deinit-v1-2-4395534520dc@linaro.org
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
drivers/pci/controller/dwc/pcie-designware-ep.c
drivers/pci/controller/dwc/pcie-qcom-ep.c
drivers/pci/controller/dwc/pcie-tegra194.c
drivers/pci/endpoint/functions/pci-epf-mhi.c
drivers/pci/endpoint/functions/pci-epf-test.c
drivers/pci/endpoint/pci-epc-core.c
include/linux/pci-epc.h
include/linux/pci-epf.h

index 47391d7d3a7343b20d7f1dc1e80e02de4e16c1f8..2063cf2049e5c317fe7aba18ec40b61157c06641 100644 (file)
@@ -632,7 +632,6 @@ void dw_pcie_ep_cleanup(struct dw_pcie_ep *ep)
        struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
 
        dw_pcie_edma_remove(pci);
-       ep->epc->init_complete = false;
 }
 EXPORT_SYMBOL_GPL(dw_pcie_ep_cleanup);
 
index 1ecf602c225a921d55e3cbda87c9abfb909ee311..b6a5010cc1a7995cd14ab773e82c52d59a6b9f4f 100644 (file)
@@ -507,6 +507,7 @@ static void qcom_pcie_perst_assert(struct dw_pcie *pci)
                return;
        }
 
+       pci_epc_deinit_notify(pci->ep.epc);
        dw_pcie_ep_cleanup(&pci->ep);
        qcom_pcie_disable_resources(pcie_ep);
        pcie_ep->link_status = QCOM_PCIE_EP_LINK_DISABLED;
index 93f5433c5c55096d264b2edb18a1897d59401867..4b28f8beedfedd1af60cb1b441eddca401d3cf2b 100644 (file)
@@ -1715,6 +1715,7 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie)
        if (ret)
                dev_err(pcie->dev, "Failed to go Detect state: %d\n", ret);
 
+       pci_epc_deinit_notify(pcie->pci.ep.epc);
        dw_pcie_ep_cleanup(&pcie->pci.ep);
 
        reset_control_assert(pcie->core_rst);
index 205c02953f259d585539307a21be77963ce034bc..5832989e55e87771118b2cce5c87624ae80bc7bd 100644 (file)
@@ -764,6 +764,24 @@ static int pci_epf_mhi_epc_init(struct pci_epf *epf)
        return 0;
 }
 
+static void pci_epf_mhi_epc_deinit(struct pci_epf *epf)
+{
+       struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf);
+       const struct pci_epf_mhi_ep_info *info = epf_mhi->info;
+       struct pci_epf_bar *epf_bar = &epf->bar[info->bar_num];
+       struct mhi_ep_cntrl *mhi_cntrl = &epf_mhi->mhi_cntrl;
+       struct pci_epc *epc = epf->epc;
+
+       if (mhi_cntrl->mhi_dev) {
+               mhi_ep_power_down(mhi_cntrl);
+               if (info->flags & MHI_EPF_USE_DMA)
+                       pci_epf_mhi_dma_deinit(epf_mhi);
+               mhi_ep_unregister_controller(mhi_cntrl);
+       }
+
+       pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, epf_bar);
+}
+
 static int pci_epf_mhi_link_up(struct pci_epf *epf)
 {
        struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf);
@@ -898,6 +916,7 @@ static void pci_epf_mhi_unbind(struct pci_epf *epf)
 
 static const struct pci_epc_event_ops pci_epf_mhi_event_ops = {
        .epc_init = pci_epf_mhi_epc_init,
+       .epc_deinit = pci_epf_mhi_epc_deinit,
        .link_up = pci_epf_mhi_link_up,
        .link_down = pci_epf_mhi_link_down,
        .bus_master_enable = pci_epf_mhi_bus_master_enable,
index e771be7512a134ec55055014f8c78dff2b53c201..7c2ed6eae53ad16fea6322ee24434ff135716238 100644 (file)
@@ -782,6 +782,15 @@ static int pci_epf_test_epc_init(struct pci_epf *epf)
        return 0;
 }
 
+static void pci_epf_test_epc_deinit(struct pci_epf *epf)
+{
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+
+       cancel_delayed_work(&epf_test->cmd_handler);
+       pci_epf_test_clean_dma_chan(epf_test);
+       pci_epf_test_clear_bar(epf);
+}
+
 static int pci_epf_test_link_up(struct pci_epf *epf)
 {
        struct pci_epf_test *epf_test = epf_get_drvdata(epf);
@@ -803,6 +812,7 @@ static int pci_epf_test_link_down(struct pci_epf *epf)
 
 static const struct pci_epc_event_ops pci_epf_test_event_ops = {
        .epc_init = pci_epf_test_epc_init,
+       .epc_deinit = pci_epf_test_epc_deinit,
        .link_up = pci_epf_test_link_up,
        .link_down = pci_epf_test_link_down,
 };
@@ -905,10 +915,13 @@ static int pci_epf_test_bind(struct pci_epf *epf)
 static void pci_epf_test_unbind(struct pci_epf *epf)
 {
        struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+       struct pci_epc *epc = epf->epc;
 
        cancel_delayed_work(&epf_test->cmd_handler);
-       pci_epf_test_clean_dma_chan(epf_test);
-       pci_epf_test_clear_bar(epf);
+       if (epc->init_complete) {
+               pci_epf_test_clean_dma_chan(epf_test);
+               pci_epf_test_clear_bar(epf);
+       }
        pci_epf_test_free_space(epf);
 }
 
index 56b60330355d45bae2e8467c0b4694cd625de0df..47a91dcb07d7b6d546096118c26aa1ec34f75edb 100644 (file)
@@ -774,6 +774,31 @@ void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf)
 }
 EXPORT_SYMBOL_GPL(pci_epc_notify_pending_init);
 
+/**
+ * pci_epc_deinit_notify() - Notify the EPF device about EPC deinitialization
+ * @epc: the EPC device whose deinitialization is completed
+ *
+ * Invoke to notify the EPF device that the EPC deinitialization is completed.
+ */
+void pci_epc_deinit_notify(struct pci_epc *epc)
+{
+       struct pci_epf *epf;
+
+       if (IS_ERR_OR_NULL(epc))
+               return;
+
+       mutex_lock(&epc->list_lock);
+       list_for_each_entry(epf, &epc->pci_epf, list) {
+               mutex_lock(&epf->lock);
+               if (epf->event_ops && epf->event_ops->epc_deinit)
+                       epf->event_ops->epc_deinit(epf);
+               mutex_unlock(&epf->lock);
+       }
+       epc->init_complete = false;
+       mutex_unlock(&epc->list_lock);
+}
+EXPORT_SYMBOL_GPL(pci_epc_deinit_notify);
+
 /**
  * pci_epc_bus_master_enable_notify() - Notify the EPF device that the EPC
  *                                     device has received the Bus Master
index 11115cd0fe5b4b6b3f1994ae542472a84f02865b..85bdf2adb76073b02d9eed76f4ba9625f7488b07 100644 (file)
@@ -197,6 +197,8 @@ struct pci_epc_features {
 
 #define to_pci_epc(device) container_of((device), struct pci_epc, dev)
 
+#ifdef CONFIG_PCI_ENDPOINT
+
 #define pci_epc_create(dev, ops)    \
                __pci_epc_create((dev), (ops), THIS_MODULE)
 #define devm_pci_epc_create(dev, ops)    \
@@ -226,6 +228,7 @@ void pci_epc_linkup(struct pci_epc *epc);
 void pci_epc_linkdown(struct pci_epc *epc);
 void pci_epc_init_notify(struct pci_epc *epc);
 void pci_epc_notify_pending_init(struct pci_epc *epc, struct pci_epf *epf);
+void pci_epc_deinit_notify(struct pci_epc *epc);
 void pci_epc_bus_master_enable_notify(struct pci_epc *epc);
 void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
                        enum pci_epc_interface_type type);
@@ -272,4 +275,14 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
                                     phys_addr_t *phys_addr, size_t size);
 void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
                           void __iomem *virt_addr, size_t size);
+
+#else
+static inline void pci_epc_init_notify(struct pci_epc *epc)
+{
+}
+
+static inline void pci_epc_deinit_notify(struct pci_epc *epc)
+{
+}
+#endif /* CONFIG_PCI_ENDPOINT */
 #endif /* __LINUX_PCI_EPC_H */
index dc759eb7157c35db6b9b5c27d0e8d1c415357fee..0639d4dc8986696af76ff7907760ecc9e3a14c07 100644 (file)
@@ -71,12 +71,14 @@ struct pci_epf_ops {
 /**
  * struct pci_epc_event_ops - Callbacks for capturing the EPC events
  * @epc_init: Callback for the EPC initialization complete event
+ * @epc_deinit: Callback for the EPC deinitialization event
  * @link_up: Callback for the EPC link up event
  * @link_down: Callback for the EPC link down event
  * @bus_master_enable: Callback for the EPC Bus Master Enable event
  */
 struct pci_epc_event_ops {
        int (*epc_init)(struct pci_epf *epf);
+       void (*epc_deinit)(struct pci_epf *epf);
        int (*link_up)(struct pci_epf *epf);
        int (*link_down)(struct pci_epf *epf);
        int (*bus_master_enable)(struct pci_epf *epf);