Re: [PATCH v3 2/2] phy: qcom: qmp-pcie: Add PHY register retention support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 2/14/2025 10:46 PM, Manivannan Sadhasivam wrote:
On Fri, Feb 14, 2025 at 06:45:39PM +0800, Wenbin Yao wrote:
From: Qiang Yu <quic_qianyu@xxxxxxxxxxx>

Some QCOM PCIe PHYs support no_csr reset. Unlike BCR reset which resets the
whole PHY (hardware and register), no_csr reset only resets PHY hardware
but retains register values, which means PHY setting can be skipped during
PHY init if PCIe link is 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's programmed and no_csr reset is
available, skip BCR reset and PHY register setting to establish the PCIe
link with bootloader - programmed PHY settings.

Signed-off-by: Qiang Yu <quic_qianyu@xxxxxxxxxxx>
Signed-off-by: Wenbin Yao <quic_wenbyao@xxxxxxxxxxx>
---
  drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 96 ++++++++++++++++--------
  1 file changed, 63 insertions(+), 33 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 219266125cf2..b3912c1a6de8 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 skip_init;
void __iomem *serdes;
  	void __iomem *pcs;
@@ -3976,7 +3977,9 @@ 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;
+	bool phy_initialized;
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
  	if (ret) {
@@ -3984,10 +3987,18 @@ 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;
+	phy_initialized = !!(readl(pcs + cfg->regs[QPHY_START_CTRL]));
+	qmp->skip_init = qmp->nocsr_reset && phy_initialized;
+	/*
+	 * Toggle BCR reset for phy that doesn't support no_csr
s/phy/PHY. Here and below.

+	 * reset or has not been initialized
+	 */
+	if (!qmp->skip_init) {
+		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 +4009,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->skip_init) {
+		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 +4024,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->skip_init)
+		reset_control_bulk_assert(cfg->num_resets, qmp->resets);
  err_disable_regulators:
  	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
@@ -4023,7 +4037,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);
I'd flip the if condition for readability:

	if (qmp->nocsr_reset)
		...
	else
		...
clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks); @@ -4042,16 +4059,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->skip_init) {
How about:
	if (qmp->skip_init)
		goto skip_phy_init;

This is my personal preference btw. If anyone feels the other way, feel free
to drop this suggestion.

+		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 +4086,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->skip_init) {
	if (qmp->skip_init)
		goto skip_serdes_start;

+		/* 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 +4120,22 @@ 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);
+	/* If a PHY supports no_csr reset but isn't enabled in the bootloader,
+	 * phy settings can be reused during the D3cold -> D0 cycle. So it is
I cannot parse this sentence. If PHY is not initialized, how can you reuse the
settings? Also what is the D3cold->D0 reference?
If PHY is not initialized, PHY settings will not be reused next time PHY
is powered on as !!(readl(pcs + cfg->regs[QPHY_START_CTRL])) is false.

For PHY driver, D3cold->D0 cycle means PHY power off -> power on.

The comment is not very clear, perhaps we can use this comment:
During PHY is powered off, only qmp->nocsr_reset need to be checked. In this
way, no matter whether the PHY settings were initially programmed by
bootloader or PHY driver itself, we can reuse them when PHY is powered
on next time.

Thanks,
Qiang

+	 * unnecessary to check qmp->skip_init.
+	 */
+	if (!qmp->nocsr_reset) {
	if (qmp->nocsr_reset)
		goto skip_phy_reset;

- Mani





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux