mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
PCI: imx6: Fix i.MX6Q/DL boot hang caused by improper PHY power sequencing
commit610fa91d98("PCI: imx6: Assert PERST# before enabling regulators") introduced a boot hang on i.MX6Q/DL variants by reordering imx_pcie_host_init() to call imx6q_pcie_enable_ref_clk() (which powered up the PHY) before imx6q_pcie_core_reset() (which powered it back down). Before610fa91d98, the sequence was: 1. imx_pcie_assert_core_reset() - power down PHY (set TEST_PD), set REF_CLK_EN 2. imx_pcie_clk_enable() - power up PHY (clear TEST_PD), set REF_CLK_EN 3. Link training starts with PHY powered up (TEST_PD cleared) 4. Link training succeeds After610fa91d98, the sequence became: 1. imx_pcie_clk_enable() - power up PHY (clear TEST_PD), set REF_CLK_EN 2. imx_pcie_assert_core_reset() - power down PHY (set TEST_PD), set REF_CLK_EN 3. imx_pcie_deassert_core_reset() - does nothing 4. Link training starts with PHY powered down (TEST_PD set) 5. Link training fails and boot hangs when PHY register accesses hang To fix this: - Remove TEST_PD PHY power control from imx6q_pcie_enable_ref_clk() - Remove REF_CLK_EN control from imx6q_pcie_core_reset() - Add TEST_PD PHY power control to imx6qp_pcie_core_reset(), which previously relied on imx6q_pcie_enable_ref_clk() to power up the PHY by clearing TEST_PD - Clear TEST_PD to power on PHY in imx_pcie_deassert_core_reset() These changes together ensure the correct sequence: 1. REF_CLK_EN set in clk_enable() (TEST_PD untouched) 2. TEST_PD set in assert_core_reset() (PHY power off) 3. TEST_PD cleared in deassert_core_reset() (PHY power on) 4. Link training starts with proper PHY state The i.MX6Q/DL PCIe PHY requires approximately 120us between TEST_PD de-assertion and link training start. Add usleep_range(200, 500) in imx6q_pcie_core_reset() after clearing TEST_PD to satisfy this requirement. Add explicit imx_pcie_assert_core_reset() calls in error paths and host_exit() to ensure no power leak. Fixes:610fa91d98("PCI: imx6: Assert PERST# before enabling regulators") Reported-by: Leonardo Costa <leoreis.costa@gmail.com> Closes: https://lore.kernel.org/lkml/20260629143439.361560-1-leoreis.costa@gmail.com/ Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Tested-by: Leonardo Costa <leonardo.costa@toradex.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Link: https://patch.msgid.link/20260728035159.2702021-1-hongxing.zhu@oss.nxp.com
This commit is contained in:
parent
b4bee12ebe
commit
7678e81498
|
|
@ -680,21 +680,12 @@ static int imx_pcie_attach_pd(struct device *dev)
|
|||
|
||||
static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
/* power up core phy and enable ref clock */
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
/*
|
||||
* The async reset input need ref clock to sync internally,
|
||||
* when the ref clock comes after reset, internal synced
|
||||
* reset time is too short, cannot meet the requirement.
|
||||
* Add a ~10us delay here.
|
||||
*/
|
||||
usleep_range(10, 100);
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
} else {
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
}
|
||||
if (enable)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -825,8 +816,16 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
|||
|
||||
static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
||||
{
|
||||
if (assert)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
|
||||
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST,
|
||||
assert ? IMX6Q_GPR1_PCIE_SW_RST : 0);
|
||||
|
||||
if (!assert)
|
||||
usleep_range(200, 500);
|
||||
|
||||
|
|
@ -835,11 +834,15 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
|||
|
||||
static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
||||
{
|
||||
if (!assert)
|
||||
return 0;
|
||||
if (assert)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
if (!assert)
|
||||
usleep_range(200, 500);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1445,6 +1448,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
return 0;
|
||||
|
||||
err_phy_off:
|
||||
imx_pcie_assert_core_reset(imx_pcie);
|
||||
phy_power_off(imx_pcie->phy);
|
||||
err_phy_exit:
|
||||
phy_exit(imx_pcie->phy);
|
||||
|
|
@ -1466,6 +1470,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
|
|||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct imx_pcie *imx_pcie = to_imx_pcie(pci);
|
||||
|
||||
imx_pcie_assert_core_reset(imx_pcie);
|
||||
if (imx_pcie->phy) {
|
||||
if (phy_power_off(imx_pcie->phy))
|
||||
dev_err(pci->dev, "unable to power off PHY\n");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user