diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-04-13 12:50:23 -0500 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-04-13 12:50:23 -0500 |
| commit | d33fae1754285fa7c2daac0a42a30e82030e68fb (patch) | |
| tree | 558d9a478de81ae9740f688155dc6841a725dc13 | |
| parent | 927e9d9d4e792159268310716a87bd56c5fcc810 (diff) | |
| parent | 88cc4cbe08bba27bb58888d25d336774aa0ccab1 (diff) | |
Merge branch 'pci/controller/dwc-imx6'
- Fix device node reference leak in imx_pcie_probe() (Felix Gu)
- Delay instead of polling for L2/L3 Ready after PME_Turn_off when
suspending i.MX6SX because LTSSM registers are inaccessible (Richard Zhu)
- Separate PERST# assertion (for resetting endpoints) from core reset (for
resetting the RC itself) to prepare for new DTs with PERST# GPIO in
per-Root Port nodes (Sherry Sun)
- Retain the Root Port MSI capability on i.MX7D, i.MX8MM, and i.MX8MQ so
MSI from downstream devices will work (Richard Zhu)
- Fix the i.MX95 reference clock source selection when internal refclk is
used (Franz Schnyder)
* pci/controller/dwc-imx6:
PCI: imx6: Fix reference clock source selection for i.MX95
PCI: imx6: Keep Root Port MSI capability with iMSI-RX to work around hardware bug
PCI: imx6: Separate PERST# assertion from core reset functions
PCI: imx6: Change imx_pcie_deassert_core_reset() return type to void
PCI: imx6: Skip waiting for L2/L3 Ready on i.MX6SX
PCI: imx6: Fix device node reference leak in imx_pcie_probe()
| -rw-r--r-- | drivers/pci/controller/dwc/pci-imx6.c | 59 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-host.c | 2 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.h | 1 |
3 files changed, 35 insertions, 27 deletions
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index f5fe5cfc46c7..e35044cc5218 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -117,6 +117,8 @@ enum imx_pcie_variants { #define IMX_PCIE_FLAG_HAS_LUT BIT(10) #define IMX_PCIE_FLAG_8GT_ECN_ERR051586 BIT(11) #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_check_flag(pci, val) (pci->drvdata->flags & val) @@ -268,8 +270,8 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, - ext ? IMX95_PCIE_REF_USE_PAD : 0, - IMX95_PCIE_REF_USE_PAD); + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, ext ? 0 : IMX95_PCIE_REF_CLKEN); @@ -901,27 +903,14 @@ static void imx_pcie_assert_core_reset(struct imx_pcie *imx_pcie) if (imx_pcie->drvdata->core_reset) imx_pcie->drvdata->core_reset(imx_pcie, true); - - /* Some boards don't have PCIe reset GPIO. */ - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); } -static int imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) +static void imx_pcie_deassert_core_reset(struct imx_pcie *imx_pcie) { reset_control_deassert(imx_pcie->pciephy_reset); if (imx_pcie->drvdata->core_reset) imx_pcie->drvdata->core_reset(imx_pcie, false); - - /* Some boards don't have PCIe reset GPIO. */ - if (imx_pcie->reset_gpiod) { - msleep(100); - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0); - /* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */ - msleep(100); - } - - return 0; } static int imx_pcie_wait_for_speed_change(struct imx_pcie *imx_pcie) @@ -1233,6 +1222,19 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge, imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev)); } +static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert) +{ + if (assert) { + gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); + } else { + if (imx_pcie->reset_gpiod) { + msleep(PCIE_T_PVPERL_MS); + gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0); + msleep(PCIE_RESET_CONFIG_WAIT_MS); + } + } +} + static int imx_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -1255,6 +1257,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) } imx_pcie_assert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, true); if (imx_pcie->drvdata->init_phy) imx_pcie->drvdata->init_phy(imx_pcie); @@ -1292,11 +1295,8 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) /* Make sure that PCIe LTSSM is cleared */ imx_pcie_ltssm_disable(dev); - ret = imx_pcie_deassert_core_reset(imx_pcie); - if (ret < 0) { - dev_err(dev, "pcie deassert core reset failed: %d\n", ret); - goto err_phy_off; - } + imx_pcie_deassert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, false); if (imx_pcie->drvdata->wait_pll_lock) { ret = imx_pcie->drvdata->wait_pll_lock(imx_pcie); @@ -1583,6 +1583,7 @@ static int imx_pcie_suspend_noirq(struct device *dev) * clock which saves some power. */ imx_pcie_assert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, true); imx_pcie->drvdata->enable_ref_clk(imx_pcie, false); } else { return dw_pcie_suspend_noirq(imx_pcie->pci); @@ -1603,9 +1604,8 @@ static int imx_pcie_resume_noirq(struct device *dev) ret = imx_pcie->drvdata->enable_ref_clk(imx_pcie, true); if (ret) return ret; - ret = imx_pcie_deassert_core_reset(imx_pcie); - if (ret) - return ret; + imx_pcie_deassert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, false); /* * Using PCIE_TEST_PD seems to disable MSI and powers down the @@ -1637,7 +1637,6 @@ static int imx_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dw_pcie *pci; struct imx_pcie *imx_pcie; - struct device_node *np; struct device_node *node = dev->of_node; int i, ret, domain; u16 val; @@ -1664,7 +1663,8 @@ static int imx_pcie_probe(struct platform_device *pdev) pci->pp.ops = &imx_pcie_host_dw_pme_ops; /* Find the PHY if one is defined, only imx7d uses it */ - np = of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); + struct device_node *np __free(device_node) = + of_parse_phandle(node, "fsl,imx7d-pcie-phy", 0); if (np) { struct resource res; @@ -1820,6 +1820,8 @@ static int imx_pcie_probe(struct platform_device *pdev) } else { 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)) + pci->pp.keep_rp_msi_en = true; pci->pp.use_atu_msg = true; ret = dw_pcie_host_init(&pci->pp); if (ret < 0) @@ -1843,6 +1845,7 @@ static void imx_pcie_shutdown(struct platform_device *pdev) /* bring down link, so bootloader gets clean state in case of reboot */ imx_pcie_assert_core_reset(imx_pcie); + imx_pcie_assert_perst(imx_pcie, true); } static const struct imx_pcie_drvdata drvdata[] = { @@ -1866,6 +1869,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .variant = IMX6SX, .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | + IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, @@ -1897,6 +1901,7 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX7D] = { .variant = IMX7D, .flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP | IMX_PCIE_FLAG_HAS_APP_RESET | IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_HAS_PHY_RESET, @@ -1909,6 +1914,7 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX8MQ] = { .variant = IMX8MQ, .flags = IMX_PCIE_FLAG_HAS_APP_RESET | + IMX_PCIE_FLAG_KEEP_MSI_CAP | IMX_PCIE_FLAG_HAS_PHY_RESET | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .gpr = "fsl,imx8mq-iomuxc-gpr", @@ -1923,6 +1929,7 @@ static const struct imx_pcie_drvdata drvdata[] = { [IMX8MM] = { .variant = IMX8MM, .flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP | IMX_PCIE_FLAG_HAS_PHYDRV | IMX_PCIE_FLAG_HAS_APP_RESET, .gpr = "fsl,imx8mm-iomuxc-gpr", diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index da152c31bb2e..c9517a348836 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1171,7 +1171,7 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) * the MSI and MSI-X capabilities of the Root Port to allow the drivers * to fall back to INTx instead. */ - if (pp->use_imsi_rx) { + if (pp->use_imsi_rx && !pp->keep_rp_msi_en) { dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI); dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX); } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index ae6389dd9caa..b12c5334552c 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -421,6 +421,7 @@ struct dw_pcie_host_ops { struct dw_pcie_rp { bool use_imsi_rx:1; + bool keep_rp_msi_en:1; bool cfg0_io_shared:1; u64 cfg0_base; void __iomem *va_cfg0_base; |
