[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250121094140.4006801-3-quic_wenbyao@quicinc.com>
Date: Tue, 21 Jan 2025 17:41:40 +0800
From: Wenbin Yao <quic_wenbyao@...cinc.com>
To: vkoul@...nel.org, kishon@...nel.org, p.zabel@...gutronix.de,
dmitry.baryshkov@...aro.org, abel.vesa@...aro.org,
quic_qianyu@...cinc.com, neil.armstrong@...aro.org,
manivannan.sadhasivam@...aro.org, quic_devipriy@...cinc.com,
konrad.dybcio@....qualcomm.com, linux-arm-msm@...r.kernel.org,
linux-phy@...ts.infradead.org, linux-kernel@...r.kernel.org
Cc: quic_wenbyao@...cinc.com
Subject: [PATCH 2/2] phy: qcom: qmp-pcie: Add PHY register retention support
From: Qiang Yu <quic_qianyu@...cinc.com>
Currently, BCR reset and PHY register setting are mandatory for every port
before link training. However, some QCOM PCIe PHYs support no_csr reset.
Different than BCR reset that is used to reset entire PHY including
hardware and register, once no_csr reset is toggled, only PHY hardware will
be reset but PHY registers will be retained, which means PHY setting can
be skipped during PHY init if PCIe link was enabled in booltloader and only
no_csr is toggled after that.
Hence, determine whether the PHY has been enabled in bootloader by
verifying QPHY_START_CTRL register. If it is programmed and no_csr reset is
present, skip BCR reset and PHY register setting, so that PCIe link can be
established with no_csr reset only.
Signed-off-by: Qiang Yu <quic_qianyu@...cinc.com>
Signed-off-by: Wenbin Yao <quic_wenbyao@...cinc.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 91 +++++++++++++++---------
1 file changed, 58 insertions(+), 33 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index ac42e4b01065..7f0802d09812 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -2805,6 +2805,7 @@ struct qmp_pcie {
const struct qmp_phy_cfg *cfg;
bool tcsr_4ln_config;
+ bool phy_initialized;
void __iomem *serdes;
void __iomem *pcs;
@@ -3976,6 +3977,7 @@ static int qmp_pcie_init(struct phy *phy)
{
struct qmp_pcie *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ void __iomem *pcs = qmp->pcs;
int ret;
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
@@ -3984,10 +3986,17 @@ static int qmp_pcie_init(struct phy *phy)
return ret;
}
- ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
- if (ret) {
- dev_err(qmp->dev, "reset assert failed\n");
- goto err_disable_regulators;
+ qmp->phy_initialized = !!(readl(pcs + cfg->regs[QPHY_START_CTRL]));
+ /*
+ * Toggle BCR reset for phy that doesn't support no_csr
+ * reset or has not been initialized
+ */
+ if (!qmp->nocsr_reset || !qmp->phy_initialized) {
+ ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset assert failed\n");
+ goto err_disable_regulators;
+ }
}
ret = reset_control_assert(qmp->nocsr_reset);
@@ -3998,10 +4007,12 @@ static int qmp_pcie_init(struct phy *phy)
usleep_range(200, 300);
- ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
- if (ret) {
- dev_err(qmp->dev, "reset deassert failed\n");
- goto err_assert_reset;
+ if (!qmp->nocsr_reset || !qmp->phy_initialized) {
+ ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
+ if (ret) {
+ dev_err(qmp->dev, "reset deassert failed\n");
+ goto err_assert_reset;
+ }
}
ret = clk_bulk_prepare_enable(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks);
@@ -4011,7 +4022,8 @@ static int qmp_pcie_init(struct phy *phy)
return 0;
err_assert_reset:
- reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (!qmp->nocsr_reset || !qmp->phy_initialized)
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
err_disable_regulators:
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
@@ -4023,7 +4035,10 @@ static int qmp_pcie_exit(struct phy *phy)
struct qmp_pcie *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
- reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ if (!qmp->nocsr_reset)
+ reset_control_bulk_assert(cfg->num_resets, qmp->resets);
+ else
+ reset_control_assert(qmp->nocsr_reset);
clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks);
@@ -4042,16 +4057,22 @@ static int qmp_pcie_power_on(struct phy *phy)
unsigned int mask, val;
int ret;
- qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
- cfg->pwrdn_ctrl);
+ /*
+ * Write CSR register for phy that doesn't support no_csr
+ * reset or has not been initialized
+ */
+ if (!qmp->nocsr_reset || !qmp->phy_initialized) {
+ qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
- if (qmp->mode == PHY_MODE_PCIE_RC)
- mode_tbls = cfg->tbls_rc;
- else
- mode_tbls = cfg->tbls_ep;
+ if (qmp->mode == PHY_MODE_PCIE_RC)
+ mode_tbls = cfg->tbls_rc;
+ else
+ mode_tbls = cfg->tbls_ep;
- qmp_pcie_init_registers(qmp, &cfg->tbls);
- qmp_pcie_init_registers(qmp, mode_tbls);
+ qmp_pcie_init_registers(qmp, &cfg->tbls);
+ qmp_pcie_init_registers(qmp, mode_tbls);
+ }
ret = clk_bulk_prepare_enable(qmp->num_pipe_clks, qmp->pipe_clks);
if (ret)
@@ -4063,15 +4084,16 @@ static int qmp_pcie_power_on(struct phy *phy)
goto err_disable_pipe_clk;
}
- /* Pull PHY out of reset state */
- qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ if (!qmp->nocsr_reset || !qmp->phy_initialized) {
+ /* Pull PHY out of reset state */
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
- /* start SerDes and Phy-Coding-Sublayer */
- qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START);
-
- if (!cfg->skip_start_delay)
- usleep_range(1000, 1200);
+ /* start SerDes and Phy-Coding-Sublayer */
+ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START);
+ if (!cfg->skip_start_delay)
+ usleep_range(1000, 1200);
+ }
status = pcs + cfg->regs[QPHY_PCS_STATUS];
mask = cfg->phy_status;
ret = readl_poll_timeout(status, val, !(val & mask), 200,
@@ -4096,16 +4118,19 @@ static int qmp_pcie_power_off(struct phy *phy)
clk_bulk_disable_unprepare(qmp->num_pipe_clks, qmp->pipe_clks);
- /* PHY reset */
- qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
- /* stop SerDes and Phy-Coding-Sublayer */
- qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL],
- SERDES_START | PCS_START);
+ if (!qmp->nocsr_reset) {
+ /* PHY reset */
+ qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
- /* Put PHY into POWER DOWN state: active low */
- qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
- cfg->pwrdn_ctrl);
+ /* stop SerDes and Phy-Coding-Sublayer */
+ qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL],
+ SERDES_START | PCS_START);
+
+ /* Put PHY into POWER DOWN state: active low */
+ qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+ cfg->pwrdn_ctrl);
+ }
return 0;
}
--
2.34.1
Powered by blists - more mailing lists