On Thu, Feb 20, 2025 at 06:22:53PM +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> Some nitpicks below. > --- > drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 60 +++++++++++++++++++----- > 1 file changed, 49 insertions(+), 11 deletions(-) > > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c > index 219266125cf2..6938b72df7fa 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 > + * reset or has not been initialized Please make use of 80 column width. > + */ > + 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_assert(qmp->nocsr_reset); > + else > + reset_control_bulk_assert(cfg->num_resets, qmp->resets); > > clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks); > > @@ -4042,6 +4059,13 @@ static int qmp_pcie_power_on(struct phy *phy) > unsigned int mask, val; > int ret; > > + /* > + * Write CSR register for PHY that doesn't support no_csr > + * reset or has not been initialized Same here. > + */ > + if (qmp->skip_init) > + goto skip_tbls_init; > + > qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], > cfg->pwrdn_ctrl); > > @@ -4053,6 +4077,7 @@ static int qmp_pcie_power_on(struct phy *phy) > qmp_pcie_init_registers(qmp, &cfg->tbls); > qmp_pcie_init_registers(qmp, mode_tbls); > > +skip_tbls_init: > ret = clk_bulk_prepare_enable(qmp->num_pipe_clks, qmp->pipe_clks); > if (ret) > return ret; > @@ -4063,6 +4088,9 @@ static int qmp_pcie_power_on(struct phy *phy) > goto err_disable_pipe_clk; > } > > + if (qmp->skip_init) > + goto skip_serdes_start; > + > /* Pull PHY out of reset state */ > qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); > > @@ -4072,6 +4100,7 @@ static int qmp_pcie_power_on(struct phy *phy) > if (!cfg->skip_start_delay) > usleep_range(1000, 1200); > > +skip_serdes_start: > status = pcs + cfg->regs[QPHY_PCS_STATUS]; > mask = cfg->phy_status; > ret = readl_poll_timeout(status, val, !(val & mask), 200, > @@ -4096,7 +4125,15 @@ static int qmp_pcie_power_off(struct phy *phy) > > clk_bulk_disable_unprepare(qmp->num_pipe_clks, qmp->pipe_clks); > > - /* PHY reset */ > + /* When PHY is powered off, only qmp->nocsr_reset needs to be checked. s/'When PHY is powered off,'/'While powering off the PHY,' > + * In this way, no matter whether the PHY settings were initially > + * programmed by bootloader or PHY driver itself, we can reuse them It is really possible to have bootloader not programming the init sequence for no_csr reset platforms? The comment sounds like it is possible. But I heard the opposite. > + * when PHY is powered on next time. > + */ > + if (qmp->nocsr_reset) > + goto skip_phy_deinit; > + > + /* PHY reset */ Spurious tab before the start of the comment. - Mani -- மணிவண்ணன் சதாசிவம்