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()
This commit is contained in:
Bjorn Helgaas 2026-08-21 16:40:42 -05:00
commit 3e01ebb619
3 changed files with 54 additions and 49 deletions

View File

@ -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

View File

@ -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 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
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,

View File

@ -110,8 +110,7 @@ static int pci_host_common_parse_perst(struct device *dev,
* 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,
@ -130,22 +129,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);
@ -160,13 +143,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"))
@ -176,8 +157,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);