diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-07-22 16:18:26 -0500 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-07-22 16:18:26 -0500 |
| commit | bfa2684123a5d40231bf196a1341990e991286a7 (patch) | |
| tree | dbc63fdf57f6bb430765017ab5f7a7fa32b41636 /drivers/pci | |
| parent | 41b77c0693cdd09589322c75cd00ecdb77bb996c (diff) | |
| parent | c2b9620359b759d2c73be47689b3a344cb4e091b (diff) | |
| download | linux-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/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 72 | ||||
| -rw-r--r-- | drivers/pci/controller/pci-host-common.c | 29 |
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); |
