summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-07-22 16:18:26 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-07-22 16:18:26 -0500
commitbfa2684123a5d40231bf196a1341990e991286a7 (patch)
treedbc63fdf57f6bb430765017ab5f7a7fa32b41636 /drivers/pci
parent41b77c0693cdd09589322c75cd00ecdb77bb996c (diff)
parentc2b9620359b759d2c73be47689b3a344cb4e091b (diff)
downloadlinux-next-bfa2684123a5d40231bf196a1341990e991286a7.tar.gz
linux-next-bfa2684123a5d40231bf196a1341990e991286a7.zip
Merge branch 'pci/controller/dwc-imx6'
- Remove PERST# checking from pci_host_common_parse_port() so callers can decide whether to fall back to legacy DT binding with PERST# in the host bridge (Sherry Sun) - Fix build issues when PCI_PWRCTRL_GENERIC or PCI_HOST_COMMON is a module (Arnd Bergmann) - Create pwrctrl devices only once by doing it from imx_pcie_probe() instead of imx_pcie_host_init(), which is used during both probe and resume (Sherry Sun) - Use 'dw_pcie_rp->skip_pwrctrl_off' to avoid powering off devices during suspend to preserve wakeup capability (Sherry Sun) - Add runtime PM support for i.MX95 to allow dynamic power management when the link is idle (Richard Zhu) * pci/controller/dwc-imx6: PCI: imx6: Add runtime PM support for i.MX95 PCI: imx6: Add 'skip_pwrctrl_off' flag support PCI: imx6: Move pci_pwrctrl_create_devices() to imx_pcie_probe() PCI: imx6: Fix building against PCI_PWRCTRL_GENERIC PCI: imx6: Fix building against PCI_HOST_COMMON PCI: host-generic: Move legacy DT binding fallback decision to caller of pci_host_common_parse_ports()
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/controller/dwc/Kconfig2
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c72
-rw-r--r--drivers/pci/controller/pci-host-common.c29
3 files changed, 54 insertions, 49 deletions
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index aa0b784c85b4..49a7a2c50ca1 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -126,7 +126,9 @@ config PCI_IMX6_EP
depends on ARCH_MXC || COMPILE_TEST
depends on PCI_ENDPOINT
select PCIE_DW_EP
+ select PCI_HOST_COMMON
select PCI_IMX6
+ select PCI_PWRCTRL_GENERIC
help
Enables support for the PCIe controller in the i.MX SoCs to
work in endpoint mode. The PCI controller on i.MX is based
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 98e1db751132..19a7b7349296 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -121,6 +121,7 @@ enum imx_pcie_variants {
#define IMX_PCIE_FLAG_SKIP_L23_READY BIT(12)
/* Preserve MSI capability for platforms that require it */
#define IMX_PCIE_FLAG_KEEP_MSI_CAP BIT(13)
+#define IMX_PCIE_FLAG_PM_RUNTIME BIT(14)
#define imx_check_flag(pci, val) (pci->drvdata->flags & val)
@@ -1318,6 +1319,18 @@ static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert)
}
}
+static bool imx_pcie_perst_found(struct pci_host_bridge *bridge)
+{
+ struct pci_host_port *port;
+
+ list_for_each_entry(port, &bridge->ports, list) {
+ if (!list_empty(&port->perst))
+ return true;
+ }
+
+ return false;
+}
+
static int imx_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -1330,15 +1343,12 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
/* Parse Root Port nodes if present */
ret = pci_host_common_parse_ports(dev, bridge);
if (ret) {
- if (ret != -ENODEV) {
- dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
- return ret;
- }
+ dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
+ return ret;
+ }
- /*
- * Fall back to legacy binding for DT backwards
- * compatibility
- */
+ /* Fall back to legacy binding for DT backwards compatibility */
+ if (!imx_pcie_perst_found(bridge)) {
ret = imx_pcie_parse_legacy_binding(imx_pcie);
if (ret)
return ret;
@@ -1373,16 +1383,12 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
}
}
- ret = pci_pwrctrl_create_devices(dev);
- if (ret) {
- dev_err(dev, "failed to create pwrctrl devices\n");
- goto err_reg_disable;
- }
-
- ret = pci_pwrctrl_power_on_devices(dev);
- if (ret) {
- dev_err(dev, "failed to power on pwrctrl devices\n");
- goto err_pwrctrl_destroy;
+ if (!pp->skip_pwrctrl_off) {
+ ret = pci_pwrctrl_power_on_devices(dev);
+ if (ret) {
+ dev_err(dev, "failed to power on pwrctrl devices\n");
+ goto err_reg_disable;
+ }
}
ret = imx_pcie_clk_enable(imx_pcie);
@@ -1451,10 +1457,8 @@ err_phy_exit:
err_clk_disable:
imx_pcie_clk_disable(imx_pcie);
err_pwrctrl_power_off:
- pci_pwrctrl_power_off_devices(dev);
-err_pwrctrl_destroy:
- if (ret != -EPROBE_DEFER)
- pci_pwrctrl_destroy_devices(dev);
+ if (!pp->skip_pwrctrl_off)
+ pci_pwrctrl_power_off_devices(dev);
err_reg_disable:
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
@@ -1473,7 +1477,8 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
}
imx_pcie_clk_disable(imx_pcie);
- pci_pwrctrl_power_off_devices(pci->dev);
+ if (!pci->pp.skip_pwrctrl_off)
+ pci_pwrctrl_power_off_devices(pci->dev);
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
}
@@ -1945,11 +1950,15 @@ static int imx_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = pci_pwrctrl_create_devices(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to create pwrctrl devices\n");
+
pci->use_parent_dt_ranges = true;
if (imx_pcie->drvdata->mode == DW_PCIE_EP_TYPE) {
ret = imx_add_pcie_ep(imx_pcie, pdev);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
/*
* FIXME: Only single Device (EPF) is supported due to the
@@ -1957,6 +1966,13 @@ static int imx_pcie_probe(struct platform_device *pdev)
*/
imx_pcie_add_lut_by_rid(imx_pcie, 0);
} else {
+ if (imx_pcie->drvdata->flags & IMX_PCIE_FLAG_PM_RUNTIME) {
+ pm_runtime_no_callbacks(dev);
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret < 0)
+ return ret;
+ }
+
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
pci->pp.skip_l23_ready = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_KEEP_MSI_CAP))
@@ -1964,7 +1980,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
pci->pp.use_atu_msg = true;
ret = dw_pcie_host_init(&pci->pp);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
if (pci_msi_enabled()) {
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI);
@@ -1976,6 +1992,11 @@ static int imx_pcie_probe(struct platform_device *pdev)
}
return 0;
+
+err_pwrctrl_destroy:
+ if (ret != -EPROBE_DEFER)
+ pci_pwrctrl_destroy_devices(dev);
+ return ret;
}
static void imx_pcie_shutdown(struct platform_device *pdev)
@@ -2100,6 +2121,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.flags = IMX_PCIE_FLAG_HAS_SERDES |
IMX_PCIE_FLAG_HAS_LUT |
IMX_PCIE_FLAG_8GT_ECN_ERR051586 |
+ IMX_PCIE_FLAG_PM_RUNTIME |
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
.ltssm_off = IMX95_PE0_GEN_CTRL_3,
.ltssm_mask = IMX95_PCIE_LTSSM_EN,
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index 2ce6f4b66133..da6636505c74 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -108,8 +108,7 @@ parse_child_node:
* dependencies and the driver may fail to operate if required resources
* are missing.
*
- * Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding
- * should be used), Other negative error codes on failure.
+ * Return: 0 on success, negative error codes on failure.
*/
static int pci_host_common_parse_port(struct device *dev,
struct pci_host_bridge *bridge,
@@ -128,22 +127,6 @@ static int pci_host_common_parse_port(struct device *dev,
if (ret)
return ret;
- /*
- * 1. PERST# found in RP or its child nodes - list is not empty,
- * continue
- *
- * 2. PERST# not found in RP/children, but found in RC node -
- * return -ENODEV to fallback legacy binding
- *
- * 3. PERST# not found anywhere - list is empty, continue (optional
- * PERST#)
- */
- if (list_empty(&port->perst)) {
- if (of_property_present(dev->of_node, "reset-gpios") ||
- of_property_present(dev->of_node, "reset-gpio"))
- return -ENODEV;
- }
-
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &bridge->ports);
@@ -158,13 +141,11 @@ static int pci_host_common_parse_port(struct device *dev,
* Iterate through child nodes of the host bridge and parse Root Port
* properties (currently only reset GPIOs).
*
- * Return: 0 on success, -ENODEV if no ports found or PERST# found in RC
- * node (legacy binding should be used), Other negative error codes on
- * failure.
+ * Return: 0 on success or ports not found, negative error codes on failure.
*/
int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *bridge)
{
- int ret = -ENODEV;
+ int ret = 0;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
@@ -174,8 +155,8 @@ int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *brid
goto err_cleanup;
}
- if (ret)
- return ret;
+ if (list_empty(&bridge->ports))
+ return 0;
return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
&bridge->ports);