summaryrefslogtreecommitdiff
path: root/drivers/pci/controller
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-09-30 17:17:12 -0500
committerBjorn Helgaas <bhelgaas@google.com>2026-09-30 17:17:12 -0500
commit47a32c7dd8621226a8dde72a13eb834a7d750245 (patch)
tree1d55726b17717712027327520739256cb18a7881 /drivers/pci/controller
parent22d8929a51e83c212c7d3f91acb00d2b252fc0a6 (diff)
parent5a642c81b31bbd426edceb071ba50839c225220a (diff)
downloadlinux-next-47a32c7dd8621226a8dde72a13eb834a7d750245.tar.gz
linux-next-47a32c7dd8621226a8dde72a13eb834a7d750245.zip
Merge branch 'pci/controller/dwc-imx6'
- Fix pwrctrl device leak when .probe() PM runtime setup fail (Zhijian Han) - Fix resource leaks in .probe() error paths (Zhijian Han) - Improve i.MX95 Gen3 link stability by changing MPLLB_BANDWIDTH from default 120 to 140 (Richard Zhu) * pci/controller/dwc-imx6: PCI: imx6: Update MPLLB bandwidth to improve i.MX95 Gen3 PCIe stability PCI: imx6: Fix resource leaks in probe error paths PCI: imx6: Fix pwrctrl device leak on PM runtime setup failure
Diffstat (limited to 'drivers/pci/controller')
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c94
1 files changed, 82 insertions, 12 deletions
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 8383a71534c0..0c98107ca0db 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -80,6 +80,18 @@
#define IMX95_SID_MASK GENMASK(5, 0)
#define IMX95_MAX_LUT 32
+#define IMX95_PCIE_PHY_REG_ADDR 0x3008
+#define IMX95_PCIE_PHY_REG_EN BIT(31)
+#define IMX95_PCIE_PHY_REG_ADDR_MASK GENMASK(15, 0)
+
+#define IMX95_PCIE_PHY_REG_DATA 0x300c
+
+#define IMX95_PCIE_PHY_MPLLB_OVRD_IN 0x2004
+#define IMX95_PCIE_PHY_MPLLB_OVRD_BW_EN BIT(10)
+
+#define IMX95_PCIE_PHY_MPLLB_BW_IN 0x2005
+#define IMX95_PCIE_PHY_MPLLB_BW_VAL 0x8c
+
#define IMX95_PCIE_RST_CTRL 0x3010
#define IMX95_PCIE_COLD_RST BIT(0)
@@ -180,8 +192,12 @@ struct imx_pcie {
struct imx_lut_data luts[IMX95_MAX_LUT];
/* power domain for pcie */
struct device *pd_pcie;
+ /* device link for pcie power domain */
+ struct device_link *pd_link;
/* power domain for pcie phy */
struct device *pd_pcie_phy;
+ /* device link for pcie phy power domain */
+ struct device_link *pd_phy_link;
struct phy *phy;
const struct imx_pcie_drvdata *drvdata;
@@ -270,8 +286,19 @@ static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie)
return 0;
}
+static void imx95_pcie_phy_write(struct imx_pcie *imx_pcie, int addr, u16 data)
+{
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_ADDR,
+ IMX95_PCIE_PHY_REG_EN, IMX95_PCIE_PHY_REG_EN);
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_ADDR,
+ IMX95_PCIE_PHY_REG_ADDR_MASK, addr);
+ regmap_write(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_DATA, data);
+}
+
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
{
+ u32 val;
+
/*
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
* Through Beacon or PERST# De-assertion
@@ -290,6 +317,21 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
IMX95_PCIE_PHY_CR_PARA_SEL,
IMX95_PCIE_PHY_CR_PARA_SEL);
+ /* Flush the IMX95_PCIE_PHY_CR_PARA_SEL update */
+ regmap_read(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, &val);
+
+ /*
+ * A delay is required between the assertion of
+ * IMX95_PCIE_PHY_CR_PARA_SEL and subsequent PHY register write
+ * operation to ensure values are successfully written.
+ */
+ udelay(200);
+
+ imx95_pcie_phy_write(imx_pcie, IMX95_PCIE_PHY_MPLLB_BW_IN,
+ IMX95_PCIE_PHY_MPLLB_BW_VAL);
+ imx95_pcie_phy_write(imx_pcie, IMX95_PCIE_PHY_MPLLB_OVRD_IN,
+ IMX95_PCIE_PHY_MPLLB_OVRD_BW_EN);
+
return 0;
}
@@ -639,10 +681,34 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
}
#endif
+static void imx_pcie_detach_pd_action(void *data)
+{
+ struct imx_pcie *imx_pcie = data;
+
+ if (!IS_ERR_OR_NULL(imx_pcie->pd_phy_link)) {
+ device_link_del(imx_pcie->pd_phy_link);
+ imx_pcie->pd_phy_link = NULL;
+ }
+
+ if (!IS_ERR_OR_NULL(imx_pcie->pd_link)) {
+ device_link_del(imx_pcie->pd_link);
+ imx_pcie->pd_link = NULL;
+ }
+
+ if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie_phy)) {
+ dev_pm_domain_detach(imx_pcie->pd_pcie_phy, true);
+ imx_pcie->pd_pcie_phy = NULL;
+ }
+
+ if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie)) {
+ dev_pm_domain_detach(imx_pcie->pd_pcie, true);
+ imx_pcie->pd_pcie = NULL;
+ }
+}
+
static int imx_pcie_attach_pd(struct device *dev)
{
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
- struct device_link *link;
/* Do nothing when in a single power domain */
if (dev->pm_domain)
@@ -654,11 +720,11 @@ static int imx_pcie_attach_pd(struct device *dev)
/* Do nothing when power domain missing */
if (!imx_pcie->pd_pcie)
return 0;
- link = device_link_add(dev, imx_pcie->pd_pcie,
- DL_FLAG_STATELESS |
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_RPM_ACTIVE);
- if (!link) {
+ imx_pcie->pd_link = device_link_add(dev, imx_pcie->pd_pcie,
+ DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!imx_pcie->pd_link) {
dev_err(dev, "Failed to add device_link to pcie pd\n");
return -EINVAL;
}
@@ -667,11 +733,11 @@ static int imx_pcie_attach_pd(struct device *dev)
if (IS_ERR(imx_pcie->pd_pcie_phy))
return PTR_ERR(imx_pcie->pd_pcie_phy);
- link = device_link_add(dev, imx_pcie->pd_pcie_phy,
- DL_FLAG_STATELESS |
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_RPM_ACTIVE);
- if (!link) {
+ imx_pcie->pd_phy_link = device_link_add(dev, imx_pcie->pd_pcie_phy,
+ DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!imx_pcie->pd_phy_link) {
dev_err(dev, "Failed to add device_link to pcie_phy pd\n");
return -EINVAL;
}
@@ -1951,6 +2017,10 @@ static int imx_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, imx_pcie);
+ ret = devm_add_action_or_reset(dev, imx_pcie_detach_pd_action, imx_pcie);
+ if (ret)
+ return ret;
+
ret = imx_pcie_attach_pd(dev);
if (ret)
return ret;
@@ -1975,7 +2045,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
pm_runtime_no_callbacks(dev);
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret < 0)
- return ret;
+ goto err_pwrctrl_destroy;
}
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))