From 418970983059aa06302ddd5ca76d441973b537c1 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 11 Dec 2025 14:48:18 +0800 Subject: [PATCH 1/6] dt-bindings: PCI: dwc: Add external reference clock input Add external reference clock input "extref" for a reference clock that comes from external crystal oscillator. Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Acked-by: Conor Dooley Link: https://patch.msgid.link/20251211064821.2707001-2-hongxing.zhu@nxp.com --- .../devicetree/bindings/pci/snps,dw-pcie-common.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml index 6339a76499b2..2c4dc04f9984 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml @@ -105,6 +105,12 @@ properties: define it with this name (for instance pipe, core and aux can be connected to a single source of the periodic signal). const: ref + - description: + Some dwc wrappers (like i.MX95 PCIes) have two reference clock + inputs, one from an internal PLL, the other from an off-chip crystal + oscillator. If present, 'extref' refers to a reference clock from + an external oscillator. + const: extref - description: Clock for the PHY registers interface. Originally this is a PHY-viewport-based interface, but some platform may have From 1352f58d7c8dfb6ba0fbd2041bfc8b4b3966ec67 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 11 Dec 2025 14:48:19 +0800 Subject: [PATCH 2/6] dt-bindings: PCI: pci-imx6: Add external reference clock input i.MX95 PCIes have two reference clock inputs: one from internal PLL. It's wired inside chip and present as "ref" clock. It's not an optional clock. The other from off chip crystal oscillator. The "extref" clock refers to a reference clock from an external crystal oscillator through the CLKIN_N/P pair PADs. It is an optional clock, relied on the board design. Add additional optional external reference clock input for i.MX95 PCIes. Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20251211064821.2707001-3-hongxing.zhu@nxp.com --- Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index ca5f2970f217..12a01f7a5744 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -44,7 +44,7 @@ properties: clock-names: minItems: 3 - maxItems: 5 + maxItems: 6 interrupts: minItems: 1 @@ -212,14 +212,17 @@ allOf: then: properties: clocks: - maxItems: 5 + minItems: 5 + maxItems: 6 clock-names: + minItems: 5 items: - const: pcie - const: pcie_bus - const: pcie_phy - const: pcie_aux - const: ref + - const: extref # Optional unevaluatedProperties: false From d8574ce57d760a958623c8f6bc3c55b5187a7bd7 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 11 Dec 2025 14:48:20 +0800 Subject: [PATCH 3/6] PCI: imx6: Add external reference clock input mode support i.MX95 PCIes have two reference clock inputs: one from internal PLL, the other from off chip crystal oscillator. The "extref" clock refers to a reference clock from an external crystal oscillator. Add external reference clock input mode support for i.MX95 PCIes. Signed-off-by: Richard Zhu Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Link: https://patch.msgid.link/20251211064821.2707001-4-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 4668fc9648bf..a6db1f0f73c3 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -149,6 +149,7 @@ struct imx_pcie { struct gpio_desc *reset_gpiod; struct clk_bulk_data *clks; int num_clks; + bool enable_ext_refclk; struct regmap *iomuxc_gpr; u16 msi_ctrl; u32 controller_id; @@ -241,6 +242,8 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { + bool ext = imx_pcie->enable_ext_refclk; + /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -259,13 +262,12 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, 0); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_SS_RW_REG_0, + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + ext ? IMX95_PCIE_REF_USE_PAD : 0, + IMX95_PCIE_REF_USE_PAD); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, - IMX95_PCIE_REF_CLKEN); + ext ? 0 : IMX95_PCIE_REF_CLKEN); return 0; } @@ -1602,7 +1604,7 @@ static int imx_pcie_probe(struct platform_device *pdev) struct imx_pcie *imx_pcie; struct device_node *np; struct device_node *node = dev->of_node; - int ret, domain; + int i, ret, domain; u16 val; imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL); @@ -1653,6 +1655,9 @@ static int imx_pcie_probe(struct platform_device *pdev) if (imx_pcie->num_clks < 0) return dev_err_probe(dev, imx_pcie->num_clks, "failed to get clocks\n"); + for (i = 0; i < imx_pcie->num_clks; i++) + if (strncmp(imx_pcie->clks[i].id, "extref", 6) == 0) + imx_pcie->enable_ext_refclk = true; if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) { imx_pcie->phy = devm_phy_get(dev, "pcie-phy"); From c577ce2881f9c76892de5ffc1a122e3ef427ecee Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 15 Oct 2025 11:04:25 +0800 Subject: [PATCH 4/6] PCI: dwc: Invoke post_init in dw_pcie_resume_noirq() In some SoCs like i.MX95, CLKREQ# is pulled low by the controller driver before link up. After link up, if the 'supports-clkreq' property is specified in DT, the driver will release CLKREQ# so that it can go high and the endpoint can pull it low whenever required i.e., during exit from L1 Substates. Hence, at the end of dw_pcie_resume_noirq(), invoke the '.post_init()' callback if exists to perform the above mentioned action. Signed-off-by: Richard Zhu [mani: reworded description] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Frank Li Link: https://patch.msgid.link/20251015030428.2980427-9-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pcie-designware-host.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 372207c33a85..9ea5335e5d8c 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1231,6 +1231,9 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (ret) return ret; + if (pci->pp.ops->post_init) + pci->pp.ops->post_init(&pci->pp); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq); From 27a064aba2da6bc58fc36a6b8e889187ae3bf89d Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Wed, 15 Oct 2025 11:04:27 +0800 Subject: [PATCH 5/6] PCI: imx6: Add CLKREQ# override to enable REFCLK for i.MX95 PCIe The CLKREQ# is an open drain, active low signal that is driven low by the card to request reference clock. It's an optional signal added in PCIe CEM r4.0, sec 2. Thus, this signal wouldn't be driven low if it's not exposed on the slot. On the i.MX95 EVK board, REFCLK to the host and endpoint is gated by this CLKREQ# signal. So if the CLKREQ# signal is not driven by the endpoint, it will gate the REFCLK to host too, leading to operational failure. Hence, enable the REFCLK on this SoC by enabling the CLKREQ# override using imx95_pcie_clkreq_override() helper during probe. This override should only be cleared when the CLKREQ# signal is exposed on the slot. Signed-off-by: Richard Zhu [mani: reworded description] Signed-off-by: Manivannan Sadhasivam Tested-by: Alexander Stein Reviewed-by: Frank Li Link: https://patch.msgid.link/20251015030428.2980427-11-hongxing.zhu@nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index a6db1f0f73c3..e523cc08c6d9 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -52,6 +52,8 @@ #define IMX95_PCIE_REF_CLKEN BIT(23) #define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9) #define IMX95_PCIE_SS_RW_REG_1 0xf4 +#define IMX95_PCIE_CLKREQ_OVERRIDE_EN BIT(8) +#define IMX95_PCIE_CLKREQ_OVERRIDE_VAL BIT(9) #define IMX95_PCIE_SYS_AUX_PWR_DET BIT(31) #define IMX95_PE0_GEN_CTRL_1 0x1050 @@ -708,6 +710,22 @@ static int imx7d_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } +static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) +{ + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_EN, + enable ? IMX95_PCIE_CLKREQ_OVERRIDE_EN : 0); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_VAL, + enable ? IMX95_PCIE_CLKREQ_OVERRIDE_VAL : 0); +} + +static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +{ + imx95_pcie_clkreq_override(imx_pcie, enable); + return 0; +} + static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie) { struct dw_pcie *pci = imx_pcie->pci; @@ -1918,6 +1936,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, + .enable_ref_clk = imx95_pcie_enable_ref_clk, }, [IMX8MQ_EP] = { .variant = IMX8MQ_EP, @@ -1974,6 +1993,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .epc_features = &imx95_pcie_epc_features, + .enable_ref_clk = imx95_pcie_enable_ref_clk, .mode = DW_PCIE_EP_TYPE, }, }; From a152a90f53909544fe996fb0fa072ae9e355c452 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 6 Jan 2026 17:19:19 +0530 Subject: [PATCH 6/6] PCI: imx6: Clear CLKREQ# override if 'supports-clkreq' DT property is available CLKREQ# is an optional reference clock request signal defined by the PCIe CEM and M.2 specifications to request REFCLK and exit the L1 Substates. The imx6 controller driver so far forced the CLKREQ# signal to low by enabling the CLKREQ# override logic as the slots do not expose this signal. Now, there are board designs coming up exposing this signal to the endpoint devices. This is identified using the 'supports-clkreq' DT property in the controller node. So when the DT node has this property, clear the CLKREQ# override after link up in host_post_init() callback to allow the endpoint to drive the CLKREQ# signal. Signed-off-by: Richard Zhu [mani: squashed the imx8mm_pcie_clkreq_override helper patch & reworded description] Signed-off-by: Manivannan Sadhasivam --- drivers/pci/controller/dwc/pci-imx6.c | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index e523cc08c6d9..1d8677d7de04 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -138,6 +138,7 @@ struct imx_pcie_drvdata { int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); int (*wait_pll_lock)(struct imx_pcie *pcie); + void (*clr_clkreq_override)(struct imx_pcie *pcie); const struct dw_pcie_host_ops *ops; }; @@ -151,6 +152,7 @@ struct imx_pcie { struct gpio_desc *reset_gpiod; struct clk_bulk_data *clks; int num_clks; + bool supports_clkreq; bool enable_ext_refclk; struct regmap *iomuxc_gpr; u16 msi_ctrl; @@ -689,7 +691,7 @@ static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } -static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +static void imx8mm_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) { int offset = imx_pcie_grp_offset(imx_pcie); @@ -699,6 +701,11 @@ static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) regmap_update_bits(imx_pcie->iomuxc_gpr, offset, IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, enable ? IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0); +} + +static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +{ + imx8mm_pcie_clkreq_override(imx_pcie, enable); return 0; } @@ -726,6 +733,16 @@ static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } +static void imx8mm_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie) +{ + imx8mm_pcie_clkreq_override(imx_pcie, false); +} + +static void imx95_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie) +{ + imx95_pcie_clkreq_override(imx_pcie, false); +} + static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie) { struct dw_pcie *pci = imx_pcie->pci; @@ -1342,6 +1359,12 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); dw_pcie_dbi_ro_wr_dis(pci); } + + /* Clear CLKREQ# override if supports_clkreq is true and link is up */ + if (dw_pcie_link_up(pci) && imx_pcie->supports_clkreq) { + if (imx_pcie->drvdata->clr_clkreq_override) + imx_pcie->drvdata->clr_clkreq_override(imx_pcie); + } } /* @@ -1763,6 +1786,7 @@ static int imx_pcie_probe(struct platform_device *pdev) /* Limit link speed */ pci->max_link_speed = 1; of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed); + imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq"); ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); if (ret < 0 && ret != -ENODEV) @@ -1896,6 +1920,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, .init_phy = imx8mq_pcie_init_phy, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8MM] = { .variant = IMX8MM, @@ -1906,6 +1931,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8MP] = { .variant = IMX8MP, @@ -1916,6 +1942,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8Q] = { .variant = IMX8Q, @@ -1937,6 +1964,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .init_phy = imx95_pcie_init_phy, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .enable_ref_clk = imx95_pcie_enable_ref_clk, + .clr_clkreq_override = imx95_pcie_clr_clkreq_override, }, [IMX8MQ_EP] = { .variant = IMX8MQ_EP,