[PATCH AUTOSEL 6.19-6.18] PCI: imx6: Add CLKREQ# override to enable REFCLK for i.MX95 PCIe

Sasha Levin
2026-02-14T21:27:19Z

From: Richard Zhu <hongxing.zhu@nxp.com>

[ Upstream commit 27a064aba2da6bc58fc36a6b8e889187ae3bf89d ]

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 <hongxing.zhu@nxp.com>
[mani: reworded description]
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20251015030428.2980427-11-hongxing.zhu@nxp.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

This is very instructive. The same author (Richard Zhu) previously fixed
the same kind of issue for i.MX8MM — CLKREQ# not being asserted causing
operational failures. That commit had a `Fixes:` tag and was clearly a
bug fix. This commit for i.MX95 is the same pattern — the same bug, same
root cause, same type of fix, just for a different SoC.

### Summary Assessment

**What the commit fixes**: On i.MX95, PCIe is non-functional when the
endpoint doesn't drive the CLKREQ# signal (which is optional per PCIe
CEM spec). Without this fix, the reference clock to the host is gated,
causing complete operational failure of PCIe.

**Nature of the fix**: This is a hardware workaround — it enables
CLKREQ# override so the reference clock isn't gated. It's analogous to
the `imx8mm_pcie_enable_ref_clk` which does the same thing (assert
CLKREQ# override) for i.MX8MQ/8MM/8MP.

**Dependencies**: The `enable_ref_clk` callback infrastructure (commit
256867b74625a) must be present. This was added in the 6.11 timeframe
along with i.MX95 support. The code should exist in stable trees that
have i.MX95 support.

**Risk**: Very low — only affects i.MX95 PCIe. Simple register writes.
Uses well-established callback pattern already in use by all other SoC
variants.

**User impact**: HIGH for i.MX95 users — PCIe may be completely non-
functional without this fix when CLKREQ# is not driven by the endpoint.

**Stable criteria**:
- Fixes a real bug (PCIe non-functional) — YES
- Obviously correct (simple register writes, tested, reviewed) — YES
- Small and contained (~30 lines, single file) — YES
- No new features (it's a hardware workaround for existing platform) —
YES

This is essentially a hardware quirk/workaround that makes an existing
driver work correctly. It falls squarely into the category of hardware-
specific workarounds that are acceptable for stable backporting. The
same class of fix was previously done for i.MX8MM with a `Fixes:` tag.

**YES**

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 4668fc9648bff..34f8f69ddfae9 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
@@ -706,6 +708,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;
@@ -1913,6 +1931,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,
@@ -1969,6 +1988,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,
},
};
--
2.51.0