]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: qcom-ep: Add support for firmware-managed PCIe Endpoint
authorMrinmay Sarkar <mrinmay.sarkar@oss.qualcomm.com>
Tue, 6 Jan 2026 12:34:46 +0000 (18:04 +0530)
committerManivannan Sadhasivam <mani@kernel.org>
Tue, 13 Jan 2026 15:06:37 +0000 (20:36 +0530)
Some Qualcomm platforms use firmware to manage PCIe resources such as
clocks, resets, and PHY through the SCMI interface. In these cases,
the Linux driver should not perform resource enable or disable
operations directly.

So introduce a `firmware_managed` flag in 'struct qcom_pcie_ep_cfg', and
set it to true for SA8255p SoC. When this flag is set, the driver will skip
the resource handling and rely on runtime PM APIs to let the firmware
handle the resources with the help of power domain.

Signed-off-by: Mrinmay Sarkar <mrinmay.sarkar@oss.qualcomm.com>
[mani: reworded description and tiny code cleanup]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Link: https://patch.msgid.link/20260106-firmware_managed_ep-v5-2-1933432127ec@oss.qualcomm.com
drivers/pci/controller/dwc/pcie-qcom-ep.c

index f1bc0ac81a928b928ab3f8cc7bf82558fc430474..49cc4ffd794740667e6c2249ec0e675bcc16d48a 100644 (file)
@@ -168,11 +168,13 @@ enum qcom_pcie_ep_link_status {
  * @hdma_support: HDMA support on this SoC
  * @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache snooping
  * @disable_mhi_ram_parity_check: Disable MHI RAM data parity error check
+ * @firmware_managed: Set if the controller is firmware managed
  */
 struct qcom_pcie_ep_cfg {
        bool hdma_support;
        bool override_no_snoop;
        bool disable_mhi_ram_parity_check;
+       bool firmware_managed;
 };
 
 /**
@@ -377,6 +379,14 @@ err_disable_clk:
 
 static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep)
 {
+       struct device *dev = pcie_ep->pci.dev;
+
+       pm_runtime_put(dev);
+
+       /* Skip resource disablement if controller is firmware-managed */
+       if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed)
+               return;
+
        icc_set_bw(pcie_ep->icc_mem, 0, 0);
        phy_power_off(pcie_ep->phy);
        phy_exit(pcie_ep->phy);
@@ -390,12 +400,24 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
        u32 val, offset;
        int ret;
 
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret < 0) {
+               dev_err(dev, "Failed to enable device: %d\n", ret);
+               return ret;
+       }
+
+       /* Skip resource enablement if controller is firmware-managed */
+       if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed)
+               goto skip_resources_enable;
+
        ret = qcom_pcie_enable_resources(pcie_ep);
        if (ret) {
                dev_err(dev, "Failed to enable resources: %d\n", ret);
+               pm_runtime_put(dev);
                return ret;
        }
 
+skip_resources_enable:
        /* Perform cleanup that requires refclk */
        pci_epc_deinit_notify(pci->ep.epc);
        dw_pcie_ep_cleanup(&pci->ep);
@@ -630,6 +652,17 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,
                return ret;
        }
 
+       pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN);
+       if (IS_ERR(pcie_ep->reset))
+               return PTR_ERR(pcie_ep->reset);
+
+       pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW);
+       if (IS_ERR(pcie_ep->wake))
+               return PTR_ERR(pcie_ep->wake);
+
+       if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed)
+               return 0;
+
        pcie_ep->num_clks = devm_clk_bulk_get_all(dev, &pcie_ep->clks);
        if (pcie_ep->num_clks < 0) {
                dev_err(dev, "Failed to get clocks\n");
@@ -640,14 +673,6 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,
        if (IS_ERR(pcie_ep->core_reset))
                return PTR_ERR(pcie_ep->core_reset);
 
-       pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN);
-       if (IS_ERR(pcie_ep->reset))
-               return PTR_ERR(pcie_ep->reset);
-
-       pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW);
-       if (IS_ERR(pcie_ep->wake))
-               return PTR_ERR(pcie_ep->wake);
-
        pcie_ep->phy = devm_phy_optional_get(dev, "pciephy");
        if (IS_ERR(pcie_ep->phy))
                ret = PTR_ERR(pcie_ep->phy);
@@ -874,6 +899,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, pcie_ep);
 
+       pm_runtime_get_noresume(dev);
+       pm_runtime_set_active(dev);
+       ret = devm_pm_runtime_enable(dev);
+       if (ret)
+               return ret;
+
        ret = qcom_pcie_ep_get_resources(pdev, pcie_ep);
        if (ret)
                return ret;
@@ -894,6 +925,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
                goto err_disable_irqs;
        }
 
+       ret = pm_runtime_put_sync(dev);
+       if (ret < 0) {
+               dev_err(dev, "Failed to suspend device: %d\n", ret);
+               goto err_disable_irqs;
+       }
+
        pcie_ep->debugfs = debugfs_create_dir(name, NULL);
        qcom_pcie_ep_init_debugfs(pcie_ep);
 
@@ -930,7 +967,15 @@ static const struct qcom_pcie_ep_cfg cfg_1_34_0 = {
        .disable_mhi_ram_parity_check = true,
 };
 
+static const struct qcom_pcie_ep_cfg cfg_1_34_0_fw_managed = {
+       .hdma_support = true,
+       .override_no_snoop = true,
+       .disable_mhi_ram_parity_check = true,
+       .firmware_managed = true,
+};
+
 static const struct of_device_id qcom_pcie_ep_match[] = {
+       { .compatible = "qcom,sa8255p-pcie-ep", .data = &cfg_1_34_0_fw_managed},
        { .compatible = "qcom,sa8775p-pcie-ep", .data = &cfg_1_34_0},
        { .compatible = "qcom,sdx55-pcie-ep", },
        { .compatible = "qcom,sm8450-pcie-ep", },